touchpad: make tp_estimate_delta return fractions
[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.0;
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, start;
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 single touch tps we use BTN_TOUCH for begin / end of touch 0 */
272         start = tp->has_mt ? tp->real_touches : 0;
273         for (i = start; i < tp->ntouches; i++) {
274                 t = tp_get_touch(tp, i);
275                 if (i < nfake_touches)
276                         tp_begin_touch(tp, t, time);
277                 else
278                         tp_end_touch(tp, t, time);
279         }
280
281         /* On mt the actual touch info may arrive after BTN_TOOL_FOO */
282         assert(tp->has_mt || tp->nfingers_down == nfake_touches);
283 }
284
285 static void
286 tp_process_key(struct tp_dispatch *tp,
287                const struct input_event *e,
288                uint64_t time)
289 {
290         switch (e->code) {
291                 case BTN_LEFT:
292                 case BTN_MIDDLE:
293                 case BTN_RIGHT:
294                         tp_process_button(tp, e, time);
295                         break;
296                 case BTN_TOUCH:
297                 case BTN_TOOL_DOUBLETAP:
298                 case BTN_TOOL_TRIPLETAP:
299                 case BTN_TOOL_QUADTAP:
300                         tp_process_fake_touch(tp, e, time);
301                         break;
302         }
303 }
304
305 static void
306 tp_unpin_finger(struct tp_dispatch *tp, struct tp_touch *t)
307 {
308         unsigned int xdist, ydist;
309
310         if (!t->pinned.is_pinned)
311                 return;
312
313         xdist = abs(t->x - t->pinned.center_x);
314         ydist = abs(t->y - t->pinned.center_y);
315
316         if (xdist * xdist + ydist * ydist >=
317                         tp->buttons.motion_dist * tp->buttons.motion_dist) {
318                 t->pinned.is_pinned = false;
319                 tp_set_pointer(tp, t);
320         }
321 }
322
323 static void
324 tp_pin_fingers(struct tp_dispatch *tp)
325 {
326         struct tp_touch *t;
327
328         tp_for_each_touch(tp, t) {
329                 t->is_pointer = false;
330                 t->pinned.is_pinned = true;
331                 t->pinned.center_x = t->x;
332                 t->pinned.center_y = t->y;
333         }
334 }
335
336 static int
337 tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
338 {
339         return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
340                 !t->palm.is_palm &&
341                 !t->pinned.is_pinned && tp_button_touch_active(tp, t);
342 }
343
344 void
345 tp_set_pointer(struct tp_dispatch *tp, struct tp_touch *t)
346 {
347         struct tp_touch *tmp = NULL;
348
349         /* Only set the touch as pointer if we don't have one yet */
350         tp_for_each_touch(tp, tmp) {
351                 if (tmp->is_pointer)
352                         return;
353         }
354
355         if (tp_touch_active(tp, t))
356                 t->is_pointer = true;
357 }
358
359 static void
360 tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
361 {
362         const int PALM_TIMEOUT = 200; /* ms */
363         const int DIRECTIONS = NE|E|SE|SW|W|NW;
364
365         /* If labelled a touch as palm, we unlabel as palm when
366            we move out of the palm edge zone within the timeout, provided
367            the direction is within 45 degrees of the horizontal.
368          */
369         if (t->palm.is_palm) {
370                 if (time < t->palm.time + PALM_TIMEOUT &&
371                     (t->x > tp->palm.left_edge && t->x < tp->palm.right_edge)) {
372                         int dirs = vector_get_direction(t->x - t->palm.x, t->y - t->palm.y);
373                         if ((dirs & DIRECTIONS) && !(dirs & ~DIRECTIONS)) {
374                                 t->palm.is_palm = false;
375                                 tp_set_pointer(tp, t);
376                         }
377                 }
378                 return;
379         }
380
381         /* palm must start in exclusion zone, it's ok to move into
382            the zone without being a palm */
383         if (t->state != TOUCH_BEGIN ||
384             (t->x > tp->palm.left_edge && t->x < tp->palm.right_edge))
385                 return;
386
387         /* don't detect palm in software button areas, it's
388            likely that legitimate touches start in the area
389            covered by the exclusion zone */
390         if (tp->buttons.is_clickpad &&
391             tp_button_is_inside_softbutton_area(tp, t))
392                 return;
393
394         t->palm.is_palm = true;
395         t->palm.time = time;
396         t->palm.x = t->x;
397         t->palm.y = t->y;
398 }
399
400 static void
401 tp_process_state(struct tp_dispatch *tp, uint64_t time)
402 {
403         struct tp_touch *t;
404         struct tp_touch *first = tp_get_touch(tp, 0);
405         unsigned int i;
406
407         for (i = 0; i < tp->ntouches; i++) {
408                 t = tp_get_touch(tp, i);
409                 if (i >= tp->real_touches && t->state != TOUCH_NONE) {
410                         t->x = first->x;
411                         t->y = first->y;
412                         if (!t->dirty)
413                                 t->dirty = first->dirty;
414                 }
415
416                 if (!t->dirty)
417                         continue;
418
419                 tp_palm_detect(tp, t, time);
420
421                 tp_motion_hysteresis(tp, t);
422                 tp_motion_history_push(t);
423
424                 tp_unpin_finger(tp, t);
425         }
426
427         tp_button_handle_state(tp, time);
428
429         /*
430          * We have a physical button down event on a clickpad. To avoid
431          * spurious pointer moves by the clicking finger we pin all fingers.
432          * We unpin fingers when they move more then a certain threshold to
433          * to allow drag and drop.
434          */
435         if ((tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) &&
436             tp->buttons.is_clickpad)
437                 tp_pin_fingers(tp);
438 }
439
440 static void
441 tp_post_process_state(struct tp_dispatch *tp, uint64_t time)
442 {
443         struct tp_touch *t;
444
445         tp_for_each_touch(tp, t) {
446                 if (!t->dirty)
447                         continue;
448
449                 if (t->state == TOUCH_END)
450                         t->state = TOUCH_NONE;
451                 else if (t->state == TOUCH_BEGIN)
452                         t->state = TOUCH_UPDATE;
453
454                 t->dirty = false;
455         }
456
457         tp->buttons.old_state = tp->buttons.state;
458
459         tp->queued = TOUCHPAD_EVENT_NONE;
460 }
461
462 static void
463 tp_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
464 {
465         struct tp_touch *t;
466         int nchanged = 0;
467         double dx = 0, dy =0;
468         double tmpx, tmpy;
469
470         tp_for_each_touch(tp, t) {
471                 if (tp_touch_active(tp, t) && t->dirty) {
472                         nchanged++;
473                         tp_get_delta(t, &tmpx, &tmpy);
474
475                         dx += tmpx;
476                         dy += tmpy;
477                 }
478                 /* Stop spurious MOTION events at the end of scrolling */
479                 t->is_pointer = false;
480         }
481
482         if (nchanged == 0)
483                 return;
484
485         dx /= nchanged;
486         dy /= nchanged;
487
488         tp_filter_motion(tp, &dx, &dy, time);
489
490         /* Require at least five px scrolling to start */
491         if (dy <= -5.0 || dy >= 5.0)
492                 tp->scroll.direction |= (1 << LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
493
494         if (dx <= -5.0 || dx >= 5.0)
495                 tp->scroll.direction |= (1 << LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
496
497         if (dy != 0.0 &&
498             (tp->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))) {
499                 pointer_notify_axis(&tp->device->base,
500                                     time,
501                                     LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
502                                     dy);
503         }
504
505         if (dx != 0.0 &&
506             (tp->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))) {
507                 pointer_notify_axis(&tp->device->base,
508                                     time,
509                                     LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
510                                     dx);
511         }
512 }
513
514 static void
515 tp_stop_scroll_events(struct tp_dispatch *tp, uint64_t time)
516 {
517         /* terminate scrolling with a zero scroll event */
518         if (tp->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))
519                 pointer_notify_axis(&tp->device->base,
520                                     time,
521                                     LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
522                                     0);
523         if (tp->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
524                 pointer_notify_axis(&tp->device->base,
525                                     time,
526                                     LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
527                                     0);
528
529         tp->scroll.direction = 0;
530 }
531
532 static int
533 tp_post_scroll_events(struct tp_dispatch *tp, uint64_t time)
534 {
535         struct tp_touch *t;
536         int nfingers_down = 0;
537
538         /* Only count active touches for 2 finger scrolling */
539         tp_for_each_touch(tp, t) {
540                 if (tp_touch_active(tp, t))
541                         nfingers_down++;
542         }
543
544         if (nfingers_down != 2) {
545                 tp_stop_scroll_events(tp, time);
546                 return 0;
547         }
548
549         tp_post_twofinger_scroll(tp, time);
550         return 1;
551 }
552
553 static void
554 tp_post_events(struct tp_dispatch *tp, uint64_t time)
555 {
556         struct tp_touch *t = tp_current_touch(tp);
557         double dx, dy;
558         int consumed = 0;
559
560         consumed |= tp_tap_handle_state(tp, time);
561         consumed |= tp_post_button_events(tp, time);
562
563         if (consumed) {
564                 tp_stop_scroll_events(tp, time);
565                 return;
566         }
567
568         if (tp_post_scroll_events(tp, time) != 0)
569                 return;
570
571         if (!t->is_pointer) {
572                 tp_for_each_touch(tp, t) {
573                         if (t->is_pointer)
574                                 break;
575                 }
576         }
577
578         if (!t->is_pointer ||
579             !t->dirty ||
580             t->history.count < TOUCHPAD_MIN_SAMPLES)
581                 return;
582
583         tp_get_delta(t, &dx, &dy);
584         tp_filter_motion(tp, &dx, &dy, time);
585
586         if (dx != 0.0 || dy != 0.0)
587                 pointer_notify_motion(&tp->device->base, time, dx, dy);
588 }
589
590 static void
591 tp_process(struct evdev_dispatch *dispatch,
592            struct evdev_device *device,
593            struct input_event *e,
594            uint64_t time)
595 {
596         struct tp_dispatch *tp =
597                 (struct tp_dispatch *)dispatch;
598
599         switch (e->type) {
600         case EV_ABS:
601                 if (tp->has_mt)
602                         tp_process_absolute(tp, e, time);
603                 else
604                         tp_process_absolute_st(tp, e, time);
605                 break;
606         case EV_KEY:
607                 tp_process_key(tp, e, time);
608                 break;
609         case EV_SYN:
610                 tp_process_state(tp, time);
611                 tp_post_events(tp, time);
612                 tp_post_process_state(tp, time);
613                 break;
614         }
615 }
616
617 static void
618 tp_destroy(struct evdev_dispatch *dispatch)
619 {
620         struct tp_dispatch *tp =
621                 (struct tp_dispatch*)dispatch;
622
623         tp_destroy_tap(tp);
624         tp_destroy_buttons(tp);
625
626         filter_destroy(tp->filter);
627         free(tp->touches);
628         free(tp);
629 }
630
631 static struct evdev_dispatch_interface tp_interface = {
632         tp_process,
633         tp_destroy
634 };
635
636 static void
637 tp_init_touch(struct tp_dispatch *tp,
638               struct tp_touch *t)
639 {
640         t->tp = tp;
641 }
642
643 static int
644 tp_init_slots(struct tp_dispatch *tp,
645               struct evdev_device *device)
646 {
647         const struct input_absinfo *absinfo;
648         struct map {
649                 unsigned int code;
650                 int ntouches;
651         } max_touches[] = {
652                 { BTN_TOOL_QUINTTAP, 5 },
653                 { BTN_TOOL_QUADTAP, 4 },
654                 { BTN_TOOL_TRIPLETAP, 3 },
655                 { BTN_TOOL_DOUBLETAP, 2 },
656         };
657         struct map *m;
658         unsigned int i, n_btn_tool_touches = 1;
659
660         absinfo = libevdev_get_abs_info(device->evdev, ABS_MT_SLOT);
661         if (absinfo) {
662                 tp->real_touches = absinfo->maximum + 1;
663                 tp->slot = absinfo->value;
664                 tp->has_mt = true;
665         } else {
666                 tp->real_touches = 1;
667                 tp->slot = 0;
668                 tp->has_mt = false;
669         }
670
671         ARRAY_FOR_EACH(max_touches, m) {
672                 if (libevdev_has_event_code(device->evdev,
673                                             EV_KEY,
674                                             m->code)) {
675                         n_btn_tool_touches = m->ntouches;
676                         break;
677                 }
678         }
679
680         tp->ntouches = max(tp->real_touches, n_btn_tool_touches);
681         tp->touches = calloc(tp->ntouches, sizeof(struct tp_touch));
682         if (!tp->touches)
683                 return -1;
684
685         for (i = 0; i < tp->ntouches; i++)
686                 tp_init_touch(tp, &tp->touches[i]);
687
688         return 0;
689 }
690
691 static int
692 tp_init_accel(struct tp_dispatch *tp, double diagonal)
693 {
694         struct motion_filter *accel;
695         int res_x, res_y;
696
697         if (tp->has_mt) {
698                 res_x = libevdev_get_abs_resolution(tp->device->evdev,
699                                                     ABS_MT_POSITION_X);
700                 res_y = libevdev_get_abs_resolution(tp->device->evdev,
701                                                     ABS_MT_POSITION_Y);
702         } else {
703                 res_x = libevdev_get_abs_resolution(tp->device->evdev,
704                                                     ABS_X);
705                 res_y = libevdev_get_abs_resolution(tp->device->evdev,
706                                                     ABS_Y);
707         }
708
709         /*
710          * Not all touchpads report the same amount of units/mm (resolution).
711          * Normalize motion events to a resolution of 15.74 units/mm
712          * (== 400 dpi) as base (unaccelerated) speed. This also evens out any
713          * differences in x and y resolution, so that a circle on the
714          * touchpad does not turn into an elipse on the screen.
715          *
716          * We pick 400dpi as thats one of the many default resolutions
717          * for USB mice, so we end up with a similar base speed on the device.
718          */
719         if (res_x > 1 && res_y > 1) {
720                 tp->accel.x_scale_coeff = (400/25.4) / res_x;
721                 tp->accel.y_scale_coeff = (400/25.4) / res_y;
722         } else {
723         /*
724          * For touchpads where the driver does not provide resolution, fall
725          * back to scaling motion events based on the diagonal size in units.
726          */
727                 tp->accel.x_scale_coeff = DEFAULT_ACCEL_NUMERATOR / diagonal;
728                 tp->accel.y_scale_coeff = DEFAULT_ACCEL_NUMERATOR / diagonal;
729         }
730
731         accel = create_pointer_accelator_filter(
732                         pointer_accel_profile_smooth_simple);
733         if (accel == NULL)
734                 return -1;
735
736         tp->filter = accel;
737
738         return 0;
739 }
740
741 static int
742 tp_init_scroll(struct tp_dispatch *tp)
743 {
744         tp->scroll.direction = 0;
745
746         return 0;
747 }
748
749 static int
750 tp_init_palmdetect(struct tp_dispatch *tp,
751                    struct evdev_device *device)
752 {
753         int width;
754
755         tp->palm.right_edge = INT_MAX;
756         tp->palm.left_edge = INT_MIN;
757
758         width = abs(device->abs.absinfo_x->maximum -
759                     device->abs.absinfo_x->minimum);
760
761         /* Apple touchpads are always big enough to warrant palm detection */
762         if (evdev_device_get_id_vendor(device) != VENDOR_ID_APPLE) {
763                 /* We don't know how big the touchpad is */
764                 if (device->abs.absinfo_x->resolution == 1)
765                         return 0;
766
767                 /* Enable palm detection on touchpads >= 80 mm. Anything smaller
768                    probably won't need it, until we find out it does */
769                 if (width/device->abs.absinfo_x->resolution < 80)
770                         return 0;
771         }
772
773         /* palm edges are 5% of the width on each side */
774         tp->palm.right_edge = device->abs.absinfo_x->maximum - width * 0.05;
775         tp->palm.left_edge = device->abs.absinfo_x->minimum + width * 0.05;
776
777         return 0;
778 }
779
780
781 static int
782 tp_init(struct tp_dispatch *tp,
783         struct evdev_device *device)
784 {
785         int width, height;
786         double diagonal;
787
788         tp->base.interface = &tp_interface;
789         tp->device = device;
790
791         if (tp_init_slots(tp, device) != 0)
792                 return -1;
793
794         width = abs(device->abs.absinfo_x->maximum -
795                     device->abs.absinfo_x->minimum);
796         height = abs(device->abs.absinfo_y->maximum -
797                      device->abs.absinfo_y->minimum);
798         diagonal = sqrt(width*width + height*height);
799
800         tp->hysteresis.margin_x =
801                 diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
802         tp->hysteresis.margin_y =
803                 diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
804
805         if (tp_init_scroll(tp) != 0)
806                 return -1;
807
808         if (tp_init_accel(tp, diagonal) != 0)
809                 return -1;
810
811         if (tp_init_tap(tp) != 0)
812                 return -1;
813
814         if (tp_init_buttons(tp, device) != 0)
815                 return -1;
816
817         if (tp_init_palmdetect(tp, device) != 0)
818                 return -1;
819
820         return 0;
821 }
822
823 struct evdev_dispatch *
824 evdev_mt_touchpad_create(struct evdev_device *device)
825 {
826         struct tp_dispatch *tp;
827
828         tp = zalloc(sizeof *tp);
829         if (!tp)
830                 return NULL;
831
832         if (tp_init(tp, device) != 0) {
833                 tp_destroy(&tp->base);
834                 return NULL;
835         }
836
837         return  &tp->base;
838 }