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