touchpad: Don't process fake touches if they are not dirty
[platform/upstream/libinput.git] / src / evdev-mt-touchpad.c
1 /*
2  * Copyright © 2014 Red Hat, Inc.
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and
5  * its documentation for any purpose is hereby granted without fee, provided
6  * that the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of the copyright holders not be used in
9  * advertising or publicity pertaining to distribution of the software
10  * without specific, written prior permission.  The copyright holders make
11  * no representations about the suitability of this software for any
12  * purpose.  It is provided "as is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  */
22
23 #include "config.h"
24
25 #include <assert.h>
26 #include <math.h>
27 #include <stdbool.h>
28 #include <limits.h>
29
30 #include "evdev-mt-touchpad.h"
31
32 #define DEFAULT_ACCEL_NUMERATOR 1200.0
33 #define DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR 700.0
34
35 static inline int
36 tp_hysteresis(int in, int center, int margin)
37 {
38         int diff = in - center;
39         if (abs(diff) <= margin)
40                 return center;
41
42         if (diff > margin)
43                 return center + diff - margin;
44         else
45                 return center + diff + margin;
46 }
47
48 static inline struct tp_motion *
49 tp_motion_history_offset(struct tp_touch *t, int offset)
50 {
51         int offset_index =
52                 (t->history.index - offset + TOUCHPAD_HISTORY_LENGTH) %
53                 TOUCHPAD_HISTORY_LENGTH;
54
55         return &t->history.samples[offset_index];
56 }
57
58 static void
59 tp_filter_motion(struct tp_dispatch *tp,
60                  double *dx, double *dy, uint64_t time)
61 {
62         struct motion_params motion;
63
64         motion.dx = *dx * tp->accel.x_scale_coeff;
65         motion.dy = *dy * tp->accel.y_scale_coeff;
66
67         if (motion.dx != 0.0 || motion.dy != 0.0)
68                 filter_dispatch(tp->filter, &motion, tp, time);
69
70         *dx = motion.dx;
71         *dy = motion.dy;
72 }
73
74 static inline void
75 tp_motion_history_push(struct tp_touch *t)
76 {
77         int motion_index = (t->history.index + 1) % TOUCHPAD_HISTORY_LENGTH;
78
79         if (t->history.count < TOUCHPAD_HISTORY_LENGTH)
80                 t->history.count++;
81
82         t->history.samples[motion_index].x = t->x;
83         t->history.samples[motion_index].y = t->y;
84         t->history.index = motion_index;
85 }
86
87 static inline void
88 tp_motion_hysteresis(struct tp_dispatch *tp,
89                      struct tp_touch *t)
90 {
91         int x = t->x,
92             y = t->y;
93
94         if (t->history.count == 0) {
95                 t->hysteresis.center_x = t->x;
96                 t->hysteresis.center_y = t->y;
97         } else {
98                 x = tp_hysteresis(x,
99                                   t->hysteresis.center_x,
100                                   tp->hysteresis.margin_x);
101                 y = tp_hysteresis(y,
102                                   t->hysteresis.center_y,
103                                   tp->hysteresis.margin_y);
104                 t->hysteresis.center_x = x;
105                 t->hysteresis.center_y = y;
106                 t->x = x;
107                 t->y = y;
108         }
109 }
110
111 static inline void
112 tp_motion_history_reset(struct tp_touch *t)
113 {
114         t->history.count = 0;
115 }
116
117 static inline struct tp_touch *
118 tp_current_touch(struct tp_dispatch *tp)
119 {
120         return &tp->touches[min(tp->slot, tp->ntouches - 1)];
121 }
122
123 static inline struct tp_touch *
124 tp_get_touch(struct tp_dispatch *tp, unsigned int slot)
125 {
126         assert(slot < tp->ntouches);
127         return &tp->touches[slot];
128 }
129
130 static inline void
131 tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
132 {
133         if (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE)
134                 return;
135
136         tp_motion_history_reset(t);
137         t->dirty = true;
138         t->state = TOUCH_BEGIN;
139         t->pinned.is_pinned = false;
140         t->millis = time;
141         tp->nfingers_down++;
142         assert(tp->nfingers_down >= 1);
143         tp->queued |= TOUCHPAD_EVENT_MOTION;
144 }
145
146 static inline void
147 tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
148 {
149         if (t->state == TOUCH_END || t->state == TOUCH_NONE)
150                 return;
151
152         t->dirty = true;
153         t->is_pointer = false;
154         t->palm.is_palm = false;
155         t->state = TOUCH_END;
156         t->pinned.is_pinned = false;
157         t->millis = time;
158         assert(tp->nfingers_down >= 1);
159         tp->nfingers_down--;
160         tp->queued |= TOUCHPAD_EVENT_MOTION;
161 }
162
163 static double
164 tp_estimate_delta(int x0, int x1, int x2, int x3)
165 {
166         return (x0 + x1 - x2 - x3) / 4;
167 }
168
169 void
170 tp_get_delta(struct tp_touch *t, double *dx, double *dy)
171 {
172         if (t->history.count < 4) {
173                 *dx = 0;
174                 *dy = 0;
175                 return;
176         }
177
178         *dx = tp_estimate_delta(tp_motion_history_offset(t, 0)->x,
179                                 tp_motion_history_offset(t, 1)->x,
180                                 tp_motion_history_offset(t, 2)->x,
181                                 tp_motion_history_offset(t, 3)->x);
182         *dy = tp_estimate_delta(tp_motion_history_offset(t, 0)->y,
183                                 tp_motion_history_offset(t, 1)->y,
184                                 tp_motion_history_offset(t, 2)->y,
185                                 tp_motion_history_offset(t, 3)->y);
186 }
187
188 static void
189 tp_process_absolute(struct tp_dispatch *tp,
190                     const struct input_event *e,
191                     uint64_t time)
192 {
193         struct tp_touch *t = tp_current_touch(tp);
194
195         switch(e->code) {
196         case ABS_MT_POSITION_X:
197                 t->x = e->value;
198                 t->millis = time;
199                 t->dirty = true;
200                 tp->queued |= TOUCHPAD_EVENT_MOTION;
201                 break;
202         case ABS_MT_POSITION_Y:
203                 t->y = e->value;
204                 t->millis = time;
205                 t->dirty = true;
206                 tp->queued |= TOUCHPAD_EVENT_MOTION;
207                 break;
208         case ABS_MT_SLOT:
209                 tp->slot = e->value;
210                 break;
211         case ABS_MT_TRACKING_ID:
212                 if (e->value != -1)
213                         tp_begin_touch(tp, t, time);
214                 else
215                         tp_end_touch(tp, t, time);
216         }
217 }
218
219 static void
220 tp_process_absolute_st(struct tp_dispatch *tp,
221                        const struct input_event *e,
222                        uint64_t time)
223 {
224         struct tp_touch *t = tp_current_touch(tp);
225
226         switch(e->code) {
227         case ABS_X:
228                 t->x = e->value;
229                 t->millis = time;
230                 t->dirty = true;
231                 tp->queued |= TOUCHPAD_EVENT_MOTION;
232                 break;
233         case ABS_Y:
234                 t->y = e->value;
235                 t->millis = time;
236                 t->dirty = true;
237                 tp->queued |= TOUCHPAD_EVENT_MOTION;
238                 break;
239         }
240 }
241
242 static void
243 tp_process_fake_touch(struct tp_dispatch *tp,
244                       const struct input_event *e,
245                       uint64_t time)
246 {
247         struct tp_touch *t;
248         unsigned int fake_touches;
249         unsigned int nfake_touches;
250         unsigned int i;
251         unsigned int shift;
252
253         if (e->code != BTN_TOUCH &&
254             (e->code < BTN_TOOL_DOUBLETAP || e->code > BTN_TOOL_QUADTAP))
255                 return;
256
257         shift = e->code == BTN_TOUCH ? 0 : (e->code - BTN_TOOL_DOUBLETAP + 1);
258
259         if (e->value)
260                 tp->fake_touches |= 1 << shift;
261         else
262                 tp->fake_touches &= ~(0x1 << shift);
263
264         fake_touches = tp->fake_touches;
265         nfake_touches = 0;
266         while (fake_touches) {
267                 nfake_touches++;
268                 fake_touches >>= 1;
269         }
270
271         for (i = 0; i < tp->ntouches; i++) {
272                 t = tp_get_touch(tp, i);
273                 if (i < nfake_touches) {
274                         tp_begin_touch(tp, t, time);
275                         t->fake =true;
276                 } else
277                         tp_end_touch(tp, t, time);
278         }
279
280         assert(tp->nfingers_down == nfake_touches);
281 }
282
283 static void
284 tp_process_key(struct tp_dispatch *tp,
285                const struct input_event *e,
286                uint64_t time)
287 {
288         switch (e->code) {
289                 case BTN_LEFT:
290                 case BTN_MIDDLE:
291                 case BTN_RIGHT:
292                         tp_process_button(tp, e, time);
293                         break;
294                 case BTN_TOUCH:
295                 case BTN_TOOL_DOUBLETAP:
296                 case BTN_TOOL_TRIPLETAP:
297                 case BTN_TOOL_QUADTAP:
298                         if (!tp->has_mt)
299                                 tp_process_fake_touch(tp, e, time);
300                         break;
301         }
302 }
303
304 static void
305 tp_unpin_finger(struct tp_dispatch *tp, struct tp_touch *t)
306 {
307         unsigned int xdist, ydist;
308
309         if (!t->pinned.is_pinned)
310                 return;
311
312         xdist = abs(t->x - t->pinned.center_x);
313         ydist = abs(t->y - t->pinned.center_y);
314
315         if (xdist * xdist + ydist * ydist >=
316                         tp->buttons.motion_dist * tp->buttons.motion_dist) {
317                 t->pinned.is_pinned = false;
318                 tp_set_pointer(tp, t);
319         }
320 }
321
322 static void
323 tp_pin_fingers(struct tp_dispatch *tp)
324 {
325         struct tp_touch *t;
326
327         tp_for_each_touch(tp, t) {
328                 t->is_pointer = false;
329                 t->pinned.is_pinned = true;
330                 t->pinned.center_x = t->x;
331                 t->pinned.center_y = t->y;
332         }
333 }
334
335 static int
336 tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
337 {
338         return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
339                 !t->palm.is_palm &&
340                 !t->pinned.is_pinned && tp_button_touch_active(tp, t);
341 }
342
343 void
344 tp_set_pointer(struct tp_dispatch *tp, struct tp_touch *t)
345 {
346         struct tp_touch *tmp = NULL;
347
348         /* Only set the touch as pointer if we don't have one yet */
349         tp_for_each_touch(tp, tmp) {
350                 if (tmp->is_pointer)
351                         return;
352         }
353
354         if (tp_touch_active(tp, t))
355                 t->is_pointer = true;
356 }
357
358 static void
359 tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
360 {
361         const int PALM_TIMEOUT = 200; /* ms */
362         const int DIRECTIONS = NE|E|SE|SW|W|NW;
363
364         /* If labelled a touch as palm, we unlabel as palm when
365            we move out of the palm edge zone within the timeout, provided
366            the direction is within 45 degrees of the horizontal.
367          */
368         if (t->palm.is_palm) {
369                 if (time < t->palm.time + PALM_TIMEOUT &&
370                     (t->x > tp->palm.left_edge && t->x < tp->palm.right_edge)) {
371                         int dirs = vector_get_direction(t->x - t->palm.x, t->y - t->palm.y);
372                         if ((dirs & DIRECTIONS) && !(dirs & ~DIRECTIONS)) {
373                                 t->palm.is_palm = false;
374                                 tp_set_pointer(tp, t);
375                         }
376                 }
377                 return;
378         }
379
380         /* palm must start in exclusion zone, it's ok to move into
381            the zone without being a palm */
382         if (t->state != TOUCH_BEGIN ||
383             (t->x > tp->palm.left_edge && t->x < tp->palm.right_edge))
384                 return;
385
386         /* don't detect palm in software button areas, it's
387            likely that legitimate touches start in the area
388            covered by the exclusion zone */
389         if (tp->buttons.is_clickpad &&
390             tp_button_is_inside_softbutton_area(tp, t))
391                 return;
392
393         t->palm.is_palm = true;
394         t->palm.time = time;
395         t->palm.x = t->x;
396         t->palm.y = t->y;
397 }
398
399 static void
400 tp_process_state(struct tp_dispatch *tp, uint64_t time)
401 {
402         struct tp_touch *t;
403         struct tp_touch *first = tp_get_touch(tp, 0);
404
405         tp_for_each_touch(tp, t) {
406                 if (!tp->has_mt && t != first && first->fake) {
407                         t->x = first->x;
408                         t->y = first->y;
409                         if (!t->dirty)
410                                 t->dirty = first->dirty;
411                 }
412
413                 if (!t->dirty)
414                         continue;
415
416                 tp_palm_detect(tp, t, time);
417
418                 tp_motion_hysteresis(tp, t);
419                 tp_motion_history_push(t);
420
421                 tp_unpin_finger(tp, t);
422         }
423
424         tp_button_handle_state(tp, time);
425
426         /*
427          * We have a physical button down event on a clickpad. To avoid
428          * spurious pointer moves by the clicking finger we pin all fingers.
429          * We unpin fingers when they move more then a certain threshold to
430          * to allow drag and drop.
431          */
432         if ((tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) &&
433             tp->buttons.is_clickpad)
434                 tp_pin_fingers(tp);
435 }
436
437 static void
438 tp_post_process_state(struct tp_dispatch *tp, uint64_t time)
439 {
440         struct tp_touch *t;
441
442         tp_for_each_touch(tp, t) {
443                 if (!t->dirty)
444                         continue;
445
446                 if (t->state == TOUCH_END) {
447                         t->state = TOUCH_NONE;
448                         t->fake = false;
449                 } else if (t->state == TOUCH_BEGIN)
450                         t->state = TOUCH_UPDATE;
451
452                 t->dirty = false;
453         }
454
455         tp->buttons.old_state = tp->buttons.state;
456
457         tp->queued = TOUCHPAD_EVENT_NONE;
458 }
459
460 static void
461 tp_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
462 {
463         struct tp_touch *t;
464         int nchanged = 0;
465         double dx = 0, dy =0;
466         double tmpx, tmpy;
467
468         tp_for_each_touch(tp, t) {
469                 if (tp_touch_active(tp, t) && t->dirty) {
470                         nchanged++;
471                         tp_get_delta(t, &tmpx, &tmpy);
472
473                         dx += tmpx;
474                         dy += tmpy;
475                 }
476                 /* Stop spurious MOTION events at the end of scrolling */
477                 t->is_pointer = false;
478         }
479
480         if (nchanged == 0)
481                 return;
482
483         dx /= nchanged;
484         dy /= nchanged;
485
486         tp_filter_motion(tp, &dx, &dy, time);
487
488         /* Require at least five px scrolling to start */
489         if (dy <= -5.0 || dy >= 5.0)
490                 tp->scroll.direction |= (1 << LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
491
492         if (dx <= -5.0 || dx >= 5.0)
493                 tp->scroll.direction |= (1 << LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
494
495         if (dy != 0.0 &&
496             (tp->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))) {
497                 pointer_notify_axis(&tp->device->base,
498                                     time,
499                                     LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
500                                     dy);
501         }
502
503         if (dx != 0.0 &&
504             (tp->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))) {
505                 pointer_notify_axis(&tp->device->base,
506                                     time,
507                                     LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
508                                     dx);
509         }
510 }
511
512 static void
513 tp_stop_scroll_events(struct tp_dispatch *tp, uint64_t time)
514 {
515         /* terminate scrolling with a zero scroll event */
516         if (tp->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))
517                 pointer_notify_axis(&tp->device->base,
518                                     time,
519                                     LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
520                                     0);
521         if (tp->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
522                 pointer_notify_axis(&tp->device->base,
523                                     time,
524                                     LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
525                                     0);
526
527         tp->scroll.direction = 0;
528 }
529
530 static int
531 tp_post_scroll_events(struct tp_dispatch *tp, uint64_t time)
532 {
533         struct tp_touch *t;
534         int nfingers_down = 0;
535
536         /* Only count active touches for 2 finger scrolling */
537         tp_for_each_touch(tp, t) {
538                 if (tp_touch_active(tp, t))
539                         nfingers_down++;
540         }
541
542         if (nfingers_down != 2) {
543                 tp_stop_scroll_events(tp, time);
544                 return 0;
545         }
546
547         tp_post_twofinger_scroll(tp, time);
548         return 1;
549 }
550
551 static void
552 tp_post_events(struct tp_dispatch *tp, uint64_t time)
553 {
554         struct tp_touch *t = tp_current_touch(tp);
555         double dx, dy;
556         int consumed = 0;
557
558         consumed |= tp_tap_handle_state(tp, time);
559         consumed |= tp_post_button_events(tp, time);
560
561         if (consumed) {
562                 tp_stop_scroll_events(tp, time);
563                 return;
564         }
565
566         if (tp_post_scroll_events(tp, time) != 0)
567                 return;
568
569         if (!t->is_pointer) {
570                 tp_for_each_touch(tp, t) {
571                         if (t->is_pointer)
572                                 break;
573                 }
574         }
575
576         if (!t->is_pointer ||
577             !t->dirty ||
578             t->history.count < TOUCHPAD_MIN_SAMPLES)
579                 return;
580
581         tp_get_delta(t, &dx, &dy);
582         tp_filter_motion(tp, &dx, &dy, time);
583
584         if (dx != 0.0 || dy != 0.0)
585                 pointer_notify_motion(&tp->device->base, time, dx, dy);
586 }
587
588 static void
589 tp_process(struct evdev_dispatch *dispatch,
590            struct evdev_device *device,
591            struct input_event *e,
592            uint64_t time)
593 {
594         struct tp_dispatch *tp =
595                 (struct tp_dispatch *)dispatch;
596
597         switch (e->type) {
598         case EV_ABS:
599                 if (tp->has_mt)
600                         tp_process_absolute(tp, e, time);
601                 else
602                         tp_process_absolute_st(tp, e, time);
603                 break;
604         case EV_KEY:
605                 tp_process_key(tp, e, time);
606                 break;
607         case EV_SYN:
608                 tp_process_state(tp, time);
609                 tp_post_events(tp, time);
610                 tp_post_process_state(tp, time);
611                 break;
612         }
613 }
614
615 static void
616 tp_destroy(struct evdev_dispatch *dispatch)
617 {
618         struct tp_dispatch *tp =
619                 (struct tp_dispatch*)dispatch;
620
621         tp_destroy_tap(tp);
622         tp_destroy_buttons(tp);
623
624         filter_destroy(tp->filter);
625         free(tp->touches);
626         free(tp);
627 }
628
629 static struct evdev_dispatch_interface tp_interface = {
630         tp_process,
631         tp_destroy
632 };
633
634 static void
635 tp_init_touch(struct tp_dispatch *tp,
636               struct tp_touch *t)
637 {
638         t->tp = tp;
639 }
640
641 static int
642 tp_init_slots(struct tp_dispatch *tp,
643               struct evdev_device *device)
644 {
645         size_t i;
646         const struct input_absinfo *absinfo;
647
648         absinfo = libevdev_get_abs_info(device->evdev, ABS_MT_SLOT);
649         if (absinfo) {
650                 tp->ntouches = absinfo->maximum + 1;
651                 tp->slot = absinfo->value;
652                 tp->has_mt = true;
653         } else {
654                 struct map {
655                         unsigned int code;
656                         int ntouches;
657                 } max_touches[] = {
658                         { BTN_TOOL_QUINTTAP, 5 },
659                         { BTN_TOOL_QUADTAP, 4 },
660                         { BTN_TOOL_TRIPLETAP, 3 },
661                         { BTN_TOOL_DOUBLETAP, 2 },
662                 };
663                 struct map *m;
664
665                 tp->slot = 0;
666                 tp->has_mt = false;
667                 tp->ntouches = 1;
668
669                 ARRAY_FOR_EACH(max_touches, m) {
670                         if (libevdev_has_event_code(device->evdev,
671                                                     EV_KEY,
672                                                     m->code)) {
673                                 tp->ntouches = m->ntouches;
674                                 break;
675                         }
676                 }
677         }
678         tp->touches = calloc(tp->ntouches,
679                              sizeof(struct tp_touch));
680         if (!tp->touches)
681                 return -1;
682
683         for (i = 0; i < tp->ntouches; i++)
684                 tp_init_touch(tp, &tp->touches[i]);
685
686         return 0;
687 }
688
689 static int
690 tp_init_accel(struct tp_dispatch *tp, double diagonal)
691 {
692         struct motion_filter *accel;
693         int res_x, res_y;
694
695         if (tp->has_mt) {
696                 res_x = libevdev_get_abs_resolution(tp->device->evdev,
697                                                     ABS_MT_POSITION_X);
698                 res_y = libevdev_get_abs_resolution(tp->device->evdev,
699                                                     ABS_MT_POSITION_Y);
700         } else {
701                 res_x = libevdev_get_abs_resolution(tp->device->evdev,
702                                                     ABS_X);
703                 res_y = libevdev_get_abs_resolution(tp->device->evdev,
704                                                     ABS_Y);
705         }
706
707         /*
708          * Not all touchpads report the same amount of units/mm (resolution).
709          * Normalize motion events to a resolution of 15.74 units/mm
710          * (== 400 dpi) as base (unaccelerated) speed. This also evens out any
711          * differences in x and y resolution, so that a circle on the
712          * touchpad does not turn into an elipse on the screen.
713          *
714          * We pick 400dpi as thats one of the many default resolutions
715          * for USB mice, so we end up with a similar base speed on the device.
716          */
717         if (res_x > 1 && res_y > 1) {
718                 tp->accel.x_scale_coeff = (400/25.4) / res_x;
719                 tp->accel.y_scale_coeff = (400/25.4) / res_y;
720         } else {
721         /*
722          * For touchpads where the driver does not provide resolution, fall
723          * back to scaling motion events based on the diagonal size in units.
724          */
725                 tp->accel.x_scale_coeff = DEFAULT_ACCEL_NUMERATOR / diagonal;
726                 tp->accel.y_scale_coeff = DEFAULT_ACCEL_NUMERATOR / diagonal;
727         }
728
729         accel = create_pointer_accelator_filter(
730                         pointer_accel_profile_smooth_simple);
731         if (accel == NULL)
732                 return -1;
733
734         tp->filter = accel;
735
736         return 0;
737 }
738
739 static int
740 tp_init_scroll(struct tp_dispatch *tp)
741 {
742         tp->scroll.direction = 0;
743
744         return 0;
745 }
746
747 static int
748 tp_init_palmdetect(struct tp_dispatch *tp,
749                    struct evdev_device *device)
750 {
751         int width;
752
753         tp->palm.right_edge = INT_MAX;
754         tp->palm.left_edge = INT_MIN;
755
756         width = abs(device->abs.absinfo_x->maximum -
757                     device->abs.absinfo_x->minimum);
758
759         /* Apple touchpads are always big enough to warrant palm detection */
760         if (evdev_device_get_id_vendor(device) != VENDOR_ID_APPLE) {
761                 /* We don't know how big the touchpad is */
762                 if (device->abs.absinfo_x->resolution == 1)
763                         return 0;
764
765                 /* Enable palm detection on touchpads >= 80 mm. Anything smaller
766                    probably won't need it, until we find out it does */
767                 if (width/device->abs.absinfo_x->resolution < 80)
768                         return 0;
769         }
770
771         /* palm edges are 5% of the width on each side */
772         tp->palm.right_edge = device->abs.absinfo_x->maximum - width * 0.05;
773         tp->palm.left_edge = device->abs.absinfo_x->minimum + width * 0.05;
774
775         return 0;
776 }
777
778
779 static int
780 tp_init(struct tp_dispatch *tp,
781         struct evdev_device *device)
782 {
783         int width, height;
784         double diagonal;
785
786         tp->base.interface = &tp_interface;
787         tp->device = device;
788
789         if (tp_init_slots(tp, device) != 0)
790                 return -1;
791
792         width = abs(device->abs.absinfo_x->maximum -
793                     device->abs.absinfo_x->minimum);
794         height = abs(device->abs.absinfo_y->maximum -
795                      device->abs.absinfo_y->minimum);
796         diagonal = sqrt(width*width + height*height);
797
798         tp->hysteresis.margin_x =
799                 diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
800         tp->hysteresis.margin_y =
801                 diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
802
803         if (tp_init_scroll(tp) != 0)
804                 return -1;
805
806         if (tp_init_accel(tp, diagonal) != 0)
807                 return -1;
808
809         if (tp_init_tap(tp) != 0)
810                 return -1;
811
812         if (tp_init_buttons(tp, device) != 0)
813                 return -1;
814
815         if (tp_init_palmdetect(tp, device) != 0)
816                 return -1;
817
818         return 0;
819 }
820
821 struct evdev_dispatch *
822 evdev_mt_touchpad_create(struct evdev_device *device)
823 {
824         struct tp_dispatch *tp;
825
826         tp = zalloc(sizeof *tp);
827         if (!tp)
828                 return NULL;
829
830         if (tp_init(tp, device) != 0) {
831                 tp_destroy(&tp->base);
832                 return NULL;
833         }
834
835         return  &tp->base;
836 }