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