touchpad: Protect tp_begin_touch and tp_end_touch against being called twice
[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                 } else if (!t->dirty) {
412                         continue;
413                 }
414
415                 tp_palm_detect(tp, t, time);
416
417                 tp_motion_hysteresis(tp, t);
418                 tp_motion_history_push(t);
419
420                 tp_unpin_finger(tp, t);
421         }
422
423         tp_button_handle_state(tp, time);
424
425         /*
426          * We have a physical button down event on a clickpad. To avoid
427          * spurious pointer moves by the clicking finger we pin all fingers.
428          * We unpin fingers when they move more then a certain threshold to
429          * to allow drag and drop.
430          */
431         if ((tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) &&
432             tp->buttons.is_clickpad)
433                 tp_pin_fingers(tp);
434 }
435
436 static void
437 tp_post_process_state(struct tp_dispatch *tp, uint64_t time)
438 {
439         struct tp_touch *t;
440
441         tp_for_each_touch(tp, t) {
442                 if (!t->dirty)
443                         continue;
444
445                 if (t->state == TOUCH_END) {
446                         t->state = TOUCH_NONE;
447                         t->fake = false;
448                 } else if (t->state == TOUCH_BEGIN)
449                         t->state = TOUCH_UPDATE;
450
451                 t->dirty = false;
452         }
453
454         tp->buttons.old_state = tp->buttons.state;
455
456         tp->queued = TOUCHPAD_EVENT_NONE;
457 }
458
459 static void
460 tp_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
461 {
462         struct tp_touch *t;
463         int nchanged = 0;
464         double dx = 0, dy =0;
465         double tmpx, tmpy;
466
467         tp_for_each_touch(tp, t) {
468                 if (tp_touch_active(tp, t) && t->dirty) {
469                         nchanged++;
470                         tp_get_delta(t, &tmpx, &tmpy);
471
472                         dx += tmpx;
473                         dy += tmpy;
474                 }
475                 /* Stop spurious MOTION events at the end of scrolling */
476                 t->is_pointer = false;
477         }
478
479         if (nchanged == 0)
480                 return;
481
482         dx /= nchanged;
483         dy /= nchanged;
484
485         tp_filter_motion(tp, &dx, &dy, time);
486
487         /* Require at least five px scrolling to start */
488         if (dy <= -5.0 || dy >= 5.0)
489                 tp->scroll.direction |= (1 << LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
490
491         if (dx <= -5.0 || dx >= 5.0)
492                 tp->scroll.direction |= (1 << LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
493
494         if (dy != 0.0 &&
495             (tp->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))) {
496                 pointer_notify_axis(&tp->device->base,
497                                     time,
498                                     LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
499                                     dy);
500         }
501
502         if (dx != 0.0 &&
503             (tp->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))) {
504                 pointer_notify_axis(&tp->device->base,
505                                     time,
506                                     LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
507                                     dx);
508         }
509 }
510
511 static void
512 tp_stop_scroll_events(struct tp_dispatch *tp, uint64_t time)
513 {
514         /* terminate scrolling with a zero scroll event */
515         if (tp->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))
516                 pointer_notify_axis(&tp->device->base,
517                                     time,
518                                     LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
519                                     0);
520         if (tp->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
521                 pointer_notify_axis(&tp->device->base,
522                                     time,
523                                     LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
524                                     0);
525
526         tp->scroll.direction = 0;
527 }
528
529 static int
530 tp_post_scroll_events(struct tp_dispatch *tp, uint64_t time)
531 {
532         struct tp_touch *t;
533         int nfingers_down = 0;
534
535         /* Only count active touches for 2 finger scrolling */
536         tp_for_each_touch(tp, t) {
537                 if (tp_touch_active(tp, t))
538                         nfingers_down++;
539         }
540
541         if (nfingers_down != 2) {
542                 tp_stop_scroll_events(tp, time);
543                 return 0;
544         }
545
546         tp_post_twofinger_scroll(tp, time);
547         return 1;
548 }
549
550 static void
551 tp_post_events(struct tp_dispatch *tp, uint64_t time)
552 {
553         struct tp_touch *t = tp_current_touch(tp);
554         double dx, dy;
555         int consumed = 0;
556
557         consumed |= tp_tap_handle_state(tp, time);
558         consumed |= tp_post_button_events(tp, time);
559
560         if (consumed) {
561                 tp_stop_scroll_events(tp, time);
562                 return;
563         }
564
565         if (tp_post_scroll_events(tp, time) != 0)
566                 return;
567
568         if (!t->is_pointer) {
569                 tp_for_each_touch(tp, t) {
570                         if (t->is_pointer)
571                                 break;
572                 }
573         }
574
575         if (!t->is_pointer ||
576             !t->dirty ||
577             t->history.count < TOUCHPAD_MIN_SAMPLES)
578                 return;
579
580         tp_get_delta(t, &dx, &dy);
581         tp_filter_motion(tp, &dx, &dy, time);
582
583         if (dx != 0.0 || dy != 0.0)
584                 pointer_notify_motion(&tp->device->base, time, dx, dy);
585 }
586
587 static void
588 tp_process(struct evdev_dispatch *dispatch,
589            struct evdev_device *device,
590            struct input_event *e,
591            uint64_t time)
592 {
593         struct tp_dispatch *tp =
594                 (struct tp_dispatch *)dispatch;
595
596         switch (e->type) {
597         case EV_ABS:
598                 if (tp->has_mt)
599                         tp_process_absolute(tp, e, time);
600                 else
601                         tp_process_absolute_st(tp, e, time);
602                 break;
603         case EV_KEY:
604                 tp_process_key(tp, e, time);
605                 break;
606         case EV_SYN:
607                 tp_process_state(tp, time);
608                 tp_post_events(tp, time);
609                 tp_post_process_state(tp, time);
610                 break;
611         }
612 }
613
614 static void
615 tp_destroy(struct evdev_dispatch *dispatch)
616 {
617         struct tp_dispatch *tp =
618                 (struct tp_dispatch*)dispatch;
619
620         tp_destroy_tap(tp);
621         tp_destroy_buttons(tp);
622
623         filter_destroy(tp->filter);
624         free(tp->touches);
625         free(tp);
626 }
627
628 static struct evdev_dispatch_interface tp_interface = {
629         tp_process,
630         tp_destroy
631 };
632
633 static void
634 tp_init_touch(struct tp_dispatch *tp,
635               struct tp_touch *t)
636 {
637         t->tp = tp;
638 }
639
640 static int
641 tp_init_slots(struct tp_dispatch *tp,
642               struct evdev_device *device)
643 {
644         size_t i;
645         const struct input_absinfo *absinfo;
646
647         absinfo = libevdev_get_abs_info(device->evdev, ABS_MT_SLOT);
648         if (absinfo) {
649                 tp->ntouches = absinfo->maximum + 1;
650                 tp->slot = absinfo->value;
651                 tp->has_mt = true;
652         } else {
653                 struct map {
654                         unsigned int code;
655                         int ntouches;
656                 } max_touches[] = {
657                         { BTN_TOOL_QUINTTAP, 5 },
658                         { BTN_TOOL_QUADTAP, 4 },
659                         { BTN_TOOL_TRIPLETAP, 3 },
660                         { BTN_TOOL_DOUBLETAP, 2 },
661                 };
662                 struct map *m;
663
664                 tp->slot = 0;
665                 tp->has_mt = false;
666                 tp->ntouches = 1;
667
668                 ARRAY_FOR_EACH(max_touches, m) {
669                         if (libevdev_has_event_code(device->evdev,
670                                                     EV_KEY,
671                                                     m->code)) {
672                                 tp->ntouches = m->ntouches;
673                                 break;
674                         }
675                 }
676         }
677         tp->touches = calloc(tp->ntouches,
678                              sizeof(struct tp_touch));
679         if (!tp->touches)
680                 return -1;
681
682         for (i = 0; i < tp->ntouches; i++)
683                 tp_init_touch(tp, &tp->touches[i]);
684
685         return 0;
686 }
687
688 static int
689 tp_init_accel(struct tp_dispatch *tp, double diagonal)
690 {
691         struct motion_filter *accel;
692         int res_x, res_y;
693
694         if (tp->has_mt) {
695                 res_x = libevdev_get_abs_resolution(tp->device->evdev,
696                                                     ABS_MT_POSITION_X);
697                 res_y = libevdev_get_abs_resolution(tp->device->evdev,
698                                                     ABS_MT_POSITION_Y);
699         } else {
700                 res_x = libevdev_get_abs_resolution(tp->device->evdev,
701                                                     ABS_X);
702                 res_y = libevdev_get_abs_resolution(tp->device->evdev,
703                                                     ABS_Y);
704         }
705
706         /*
707          * Not all touchpads report the same amount of units/mm (resolution).
708          * Normalize motion events to a resolution of 15.74 units/mm
709          * (== 400 dpi) as base (unaccelerated) speed. This also evens out any
710          * differences in x and y resolution, so that a circle on the
711          * touchpad does not turn into an elipse on the screen.
712          *
713          * We pick 400dpi as thats one of the many default resolutions
714          * for USB mice, so we end up with a similar base speed on the device.
715          */
716         if (res_x > 1 && res_y > 1) {
717                 tp->accel.x_scale_coeff = (400/25.4) / res_x;
718                 tp->accel.y_scale_coeff = (400/25.4) / res_y;
719         } else {
720         /*
721          * For touchpads where the driver does not provide resolution, fall
722          * back to scaling motion events based on the diagonal size in units.
723          */
724                 tp->accel.x_scale_coeff = DEFAULT_ACCEL_NUMERATOR / diagonal;
725                 tp->accel.y_scale_coeff = DEFAULT_ACCEL_NUMERATOR / diagonal;
726         }
727
728         accel = create_pointer_accelator_filter(
729                         pointer_accel_profile_smooth_simple);
730         if (accel == NULL)
731                 return -1;
732
733         tp->filter = accel;
734
735         return 0;
736 }
737
738 static int
739 tp_init_scroll(struct tp_dispatch *tp)
740 {
741         tp->scroll.direction = 0;
742
743         return 0;
744 }
745
746 static int
747 tp_init_palmdetect(struct tp_dispatch *tp,
748                    struct evdev_device *device)
749 {
750         int width;
751
752         tp->palm.right_edge = INT_MAX;
753         tp->palm.left_edge = INT_MIN;
754
755         width = abs(device->abs.absinfo_x->maximum -
756                     device->abs.absinfo_x->minimum);
757
758         /* Apple touchpads are always big enough to warrant palm detection */
759         if (evdev_device_get_id_vendor(device) != VENDOR_ID_APPLE) {
760                 /* We don't know how big the touchpad is */
761                 if (device->abs.absinfo_x->resolution == 1)
762                         return 0;
763
764                 /* Enable palm detection on touchpads >= 80 mm. Anything smaller
765                    probably won't need it, until we find out it does */
766                 if (width/device->abs.absinfo_x->resolution < 80)
767                         return 0;
768         }
769
770         /* palm edges are 5% of the width on each side */
771         tp->palm.right_edge = device->abs.absinfo_x->maximum - width * 0.05;
772         tp->palm.left_edge = device->abs.absinfo_x->minimum + width * 0.05;
773
774         return 0;
775 }
776
777
778 static int
779 tp_init(struct tp_dispatch *tp,
780         struct evdev_device *device)
781 {
782         int width, height;
783         double diagonal;
784
785         tp->base.interface = &tp_interface;
786         tp->device = device;
787
788         if (tp_init_slots(tp, device) != 0)
789                 return -1;
790
791         width = abs(device->abs.absinfo_x->maximum -
792                     device->abs.absinfo_x->minimum);
793         height = abs(device->abs.absinfo_y->maximum -
794                      device->abs.absinfo_y->minimum);
795         diagonal = sqrt(width*width + height*height);
796
797         tp->hysteresis.margin_x =
798                 diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
799         tp->hysteresis.margin_y =
800                 diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
801
802         if (tp_init_scroll(tp) != 0)
803                 return -1;
804
805         if (tp_init_accel(tp, diagonal) != 0)
806                 return -1;
807
808         if (tp_init_tap(tp) != 0)
809                 return -1;
810
811         if (tp_init_buttons(tp, device) != 0)
812                 return -1;
813
814         if (tp_init_palmdetect(tp, device) != 0)
815                 return -1;
816
817         return 0;
818 }
819
820 struct evdev_dispatch *
821 evdev_mt_touchpad_create(struct evdev_device *device)
822 {
823         struct tp_dispatch *tp;
824
825         tp = zalloc(sizeof *tp);
826         if (!tp)
827                 return NULL;
828
829         if (tp_init(tp, device) != 0) {
830                 tp_destroy(&tp->base);
831                 return NULL;
832         }
833
834         return  &tp->base;
835 }