touchpad: parse the TOUCHPAD_RESOLUTION property
[platform/upstream/libinput.git] / src / evdev-mt-touchpad.c
index ae37ab1..998249f 100644 (file)
@@ -29,7 +29,9 @@
 
 #include "evdev-mt-touchpad.h"
 
-#define DEFAULT_ACCEL_NUMERATOR 1200.0
+/* Number found by trial-and error, seems to be 1200, divided by the
+ * TP_MAGIC_SLOWDOWN in filter.c */
+#define DEFAULT_ACCEL_NUMERATOR 3000.0
 #define DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR 700.0
 #define DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT 500 /* ms */
 
@@ -46,7 +48,7 @@ tp_hysteresis(int in, int center, int margin)
                return center + diff + margin;
 }
 
-static inline struct tp_motion *
+static inline struct device_coords *
 tp_motion_history_offset(struct tp_touch *t, int offset)
 {
        int offset_index =
@@ -64,8 +66,8 @@ tp_filter_motion(struct tp_dispatch *tp,
 {
        struct motion_params motion;
 
-       motion.dx = *dx * tp->accel.x_scale_coeff;
-       motion.dy = *dy * tp->accel.y_scale_coeff;
+       motion.dx = *dx;
+       motion.dy = *dy;
 
        if (dx_unaccel)
                *dx_unaccel = motion.dx;
@@ -87,8 +89,7 @@ tp_motion_history_push(struct tp_touch *t)
        if (t->history.count < TOUCHPAD_HISTORY_LENGTH)
                t->history.count++;
 
-       t->history.samples[motion_index].x = t->x;
-       t->history.samples[motion_index].y = t->y;
+       t->history.samples[motion_index] = t->point;
        t->history.index = motion_index;
 }
 
@@ -96,23 +97,22 @@ static inline void
 tp_motion_hysteresis(struct tp_dispatch *tp,
                     struct tp_touch *t)
 {
-       int x = t->x,
-           y = t->y;
+       int x = t->point.x,
+           y = t->point.y;
 
        if (t->history.count == 0) {
-               t->hysteresis.center_x = t->x;
-               t->hysteresis.center_y = t->y;
+               t->hysteresis_center = t->point;
        } else {
                x = tp_hysteresis(x,
-                                 t->hysteresis.center_x,
-                                 tp->hysteresis.margin_x);
+                                 t->hysteresis_center.x,
+                                 tp->hysteresis_margin.x);
                y = tp_hysteresis(y,
-                                 t->hysteresis.center_y,
-                                 tp->hysteresis.margin_y);
-               t->hysteresis.center_x = x;
-               t->hysteresis.center_y = y;
-               t->x = x;
-               t->y = y;
+                                 t->hysteresis_center.y,
+                                 tp->hysteresis_margin.y);
+               t->hysteresis_center.x = x;
+               t->hysteresis_center.y = y;
+               t->point.x = x;
+               t->point.y = y;
        }
 }
 
@@ -227,7 +227,6 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
        }
 
        t->dirty = true;
-       t->is_pointer = false;
        t->palm.is_palm = false;
        t->state = TOUCH_END;
        t->pinned.is_pinned = false;
@@ -253,23 +252,26 @@ tp_estimate_delta(int x0, int x1, int x2, int x3)
        return (x0 + x1 - x2 - x3) / 4.0;
 }
 
-void
-tp_get_delta(struct tp_touch *t, double *dx, double *dy)
+struct normalized_coords
+tp_get_delta(struct tp_touch *t)
 {
-       if (t->history.count < TOUCHPAD_MIN_SAMPLES) {
-               *dx = 0;
-               *dy = 0;
-               return;
-       }
+       double dx, dy; /* in device coords */
+       struct normalized_coords normalized = { 0.0, 0.0 };
+
+       if (t->history.count < TOUCHPAD_MIN_SAMPLES)
+               return normalized;
+
+       dx = tp_estimate_delta(tp_motion_history_offset(t, 0)->x,
+                              tp_motion_history_offset(t, 1)->x,
+                              tp_motion_history_offset(t, 2)->x,
+                              tp_motion_history_offset(t, 3)->x);
+       dy = tp_estimate_delta(tp_motion_history_offset(t, 0)->y,
+                              tp_motion_history_offset(t, 1)->y,
+                              tp_motion_history_offset(t, 2)->y,
+                              tp_motion_history_offset(t, 3)->y);
+       tp_normalize_delta(t->tp, dx, dy, &normalized);
 
-       *dx = tp_estimate_delta(tp_motion_history_offset(t, 0)->x,
-                               tp_motion_history_offset(t, 1)->x,
-                               tp_motion_history_offset(t, 2)->x,
-                               tp_motion_history_offset(t, 3)->x);
-       *dy = tp_estimate_delta(tp_motion_history_offset(t, 0)->y,
-                               tp_motion_history_offset(t, 1)->y,
-                               tp_motion_history_offset(t, 2)->y,
-                               tp_motion_history_offset(t, 3)->y);
+       return normalized;
 }
 
 static void
@@ -281,13 +283,13 @@ tp_process_absolute(struct tp_dispatch *tp,
 
        switch(e->code) {
        case ABS_MT_POSITION_X:
-               t->x = e->value;
+               t->point.x = e->value;
                t->millis = time;
                t->dirty = true;
                tp->queued |= TOUCHPAD_EVENT_MOTION;
                break;
        case ABS_MT_POSITION_Y:
-               t->y = e->value;
+               t->point.y = e->value;
                t->millis = time;
                t->dirty = true;
                tp->queued |= TOUCHPAD_EVENT_MOTION;
@@ -312,13 +314,13 @@ tp_process_absolute_st(struct tp_dispatch *tp,
 
        switch(e->code) {
        case ABS_X:
-               t->x = e->value;
+               t->point.x = e->value;
                t->millis = time;
                t->dirty = true;
                tp->queued |= TOUCHPAD_EVENT_MOTION;
                break;
        case ABS_Y:
-               t->y = e->value;
+               t->point.y = e->value;
                t->millis = time;
                t->dirty = true;
                tp->queued |= TOUCHPAD_EVENT_MOTION;
@@ -418,19 +420,18 @@ tp_unpin_finger(struct tp_dispatch *tp, struct tp_touch *t)
        if (!t->pinned.is_pinned)
                return;
 
-       xdist = abs(t->x - t->pinned.center_x);
-       ydist = abs(t->y - t->pinned.center_y);
+       xdist = abs(t->point.x - t->pinned.center.x);
+       ydist = abs(t->point.y - t->pinned.center.y);
 
        if (xdist * xdist + ydist * ydist >=
                        tp->buttons.motion_dist * tp->buttons.motion_dist) {
                t->pinned.is_pinned = false;
-               tp_set_pointer(tp, t);
                return;
        }
 
        /* The finger may slowly drift, adjust the center */
-       t->pinned.center_x = t->x + t->pinned.center_x / 2;
-       t->pinned.center_y = t->y + t->pinned.center_y / 2;
+       t->pinned.center.x = t->point.x + t->pinned.center.x / 2;
+       t->pinned.center.y = t->point.y + t->pinned.center.y / 2;
 }
 
 static void
@@ -439,14 +440,12 @@ tp_pin_fingers(struct tp_dispatch *tp)
        struct tp_touch *t;
 
        tp_for_each_touch(tp, t) {
-               t->is_pointer = false;
                t->pinned.is_pinned = true;
-               t->pinned.center_x = t->x;
-               t->pinned.center_y = t->y;
+               t->pinned.center = t->point;
        }
 }
 
-static int
+int
 tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
 {
        return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
@@ -456,21 +455,6 @@ tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
                tp_edge_scroll_touch_active(tp, t);
 }
 
-void
-tp_set_pointer(struct tp_dispatch *tp, struct tp_touch *t)
-{
-       struct tp_touch *tmp = NULL;
-
-       /* Only set the touch as pointer if we don't have one yet */
-       tp_for_each_touch(tp, tmp) {
-               if (tmp->is_pointer)
-                       return;
-       }
-
-       if (tp_touch_active(tp, t))
-               t->is_pointer = true;
-}
-
 static void
 tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
 {
@@ -483,11 +467,11 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
         */
        if (t->palm.is_palm) {
                if (time < t->palm.time + PALM_TIMEOUT &&
-                   (t->x > tp->palm.left_edge && t->x < tp->palm.right_edge)) {
-                       int dirs = vector_get_direction(t->x - t->palm.x, t->y - t->palm.y);
+                   (t->point.x > tp->palm.left_edge && t->point.x < tp->palm.right_edge)) {
+                       int dirs = vector_get_direction(t->point.x - t->palm.first.x,
+                                                       t->point.y - t->palm.first.y);
                        if ((dirs & DIRECTIONS) && !(dirs & ~DIRECTIONS)) {
                                t->palm.is_palm = false;
-                               tp_set_pointer(tp, t);
                        }
                }
                return;
@@ -496,7 +480,7 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
        /* palm must start in exclusion zone, it's ok to move into
           the zone without being a palm */
        if (t->state != TOUCH_BEGIN ||
-           (t->x > tp->palm.left_edge && t->x < tp->palm.right_edge))
+           (t->point.x > tp->palm.left_edge && t->point.x < tp->palm.right_edge))
                return;
 
        /* don't detect palm in software button areas, it's
@@ -508,155 +492,7 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
 
        t->palm.is_palm = true;
        t->palm.time = time;
-       t->palm.x = t->x;
-       t->palm.y = t->y;
-}
-
-static void
-tp_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
-{
-       struct tp_touch *t;
-       int nchanged = 0;
-       double dx = 0, dy =0;
-       double tmpx, tmpy;
-
-       tp_for_each_touch(tp, t) {
-               if (tp_touch_active(tp, t) && t->dirty) {
-                       nchanged++;
-                       tp_get_delta(t, &tmpx, &tmpy);
-
-                       dx += tmpx;
-                       dy += tmpy;
-               }
-               /* Stop spurious MOTION events at the end of scrolling */
-               t->is_pointer = false;
-       }
-
-       if (nchanged == 0)
-               return;
-
-       dx /= nchanged;
-       dy /= nchanged;
-
-       tp_filter_motion(tp, &dx, &dy, NULL, NULL, time);
-
-       evdev_post_scroll(tp->device,
-                         time,
-                         LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
-                         dx, dy);
-       tp->scroll.twofinger_state = TWOFINGER_SCROLL_STATE_ACTIVE;
-}
-
-static void
-tp_twofinger_stop_scroll(struct tp_dispatch *tp, uint64_t time)
-{
-       struct tp_touch *t, *ptr = NULL;
-       int nfingers_down = 0;
-
-       evdev_stop_scroll(tp->device,
-                         time,
-                         LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
-
-       /* If we were scrolling and now there's exactly 1 active finger,
-          switch back to pointer movement */
-       if (tp->scroll.twofinger_state == TWOFINGER_SCROLL_STATE_ACTIVE) {
-               tp_for_each_touch(tp, t) {
-                       if (tp_touch_active(tp, t)) {
-                               nfingers_down++;
-                               if (ptr == NULL)
-                                       ptr = t;
-                       }
-               }
-
-               if (nfingers_down == 1)
-                       tp_set_pointer(tp, ptr);
-       }
-
-       tp->scroll.twofinger_state = TWOFINGER_SCROLL_STATE_NONE;
-}
-
-static int
-tp_twofinger_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
-{
-       struct tp_touch *t;
-       int nfingers_down = 0;
-
-       /* No 2fg scrolling during tap-n-drag */
-       if (tp_tap_dragging(tp))
-               return 0;
-
-       /* No 2fg scrolling while a clickpad is clicked */
-       if (tp->buttons.is_clickpad && tp->buttons.state)
-               return 0;
-
-       /* Only count active touches for 2 finger scrolling */
-       tp_for_each_touch(tp, t) {
-               if (tp_touch_active(tp, t))
-                       nfingers_down++;
-       }
-
-       if (nfingers_down == 2) {
-               tp_post_twofinger_scroll(tp, time);
-               return 1;
-       }
-
-       tp_twofinger_stop_scroll(tp, time);
-
-       return 0;
-}
-
-static void
-tp_scroll_handle_state(struct tp_dispatch *tp, uint64_t time)
-{
-       /* Note this must be always called, so that it knows the state of
-        * touches when the scroll-mode changes.
-        */
-       tp_edge_scroll_handle_state(tp, time);
-}
-
-static int
-tp_post_scroll_events(struct tp_dispatch *tp, uint64_t time)
-{
-       struct libinput *libinput = tp->device->base.seat->libinput;
-
-       switch (tp->scroll.method) {
-       case LIBINPUT_CONFIG_SCROLL_NO_SCROLL:
-               break;
-       case LIBINPUT_CONFIG_SCROLL_2FG:
-               return tp_twofinger_scroll_post_events(tp, time);
-       case LIBINPUT_CONFIG_SCROLL_EDGE:
-               return tp_edge_scroll_post_events(tp, time);
-       case LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN:
-               log_bug_libinput(libinput, "Unexpected scroll mode\n");
-               break;
-       }
-       return 0;
-}
-
-static void
-tp_stop_scroll_events(struct tp_dispatch *tp, uint64_t time)
-{
-       struct libinput *libinput = tp->device->base.seat->libinput;
-
-       switch (tp->scroll.method) {
-       case LIBINPUT_CONFIG_SCROLL_NO_SCROLL:
-               break;
-       case LIBINPUT_CONFIG_SCROLL_2FG:
-               tp_twofinger_stop_scroll(tp, time);
-               break;
-       case LIBINPUT_CONFIG_SCROLL_EDGE:
-               tp_edge_scroll_stop_events(tp, time);
-               break;
-       case LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN:
-               log_bug_libinput(libinput, "Unexpected scroll mode\n");
-               break;
-       }
-}
-
-static void
-tp_remove_scroll(struct tp_dispatch *tp)
-{
-       tp_remove_edge_scroll(tp);
+       t->palm.first = t->point;
 }
 
 static void
@@ -731,8 +567,7 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time)
                        tp_motion_history_reset(t);
 
                if (i >= tp->real_touches && t->state != TOUCH_NONE) {
-                       t->x = first->x;
-                       t->y = first->y;
+                       t->point = first->point;
                        if (!t->dirty)
                                t->dirty = first->dirty;
                }
@@ -749,7 +584,7 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time)
        }
 
        tp_button_handle_state(tp, time);
-       tp_scroll_handle_state(tp, time);
+       tp_edge_scroll_handle_state(tp, time);
 
        /*
         * We have a physical button down event on a clickpad. To avoid
@@ -760,6 +595,8 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time)
        if ((tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) &&
            tp->buttons.is_clickpad)
                tp_pin_fingers(tp);
+
+       tp_gesture_handle_state(tp, time);
 }
 
 static void
@@ -791,63 +628,6 @@ tp_post_process_state(struct tp_dispatch *tp, uint64_t time)
 }
 
 static void
-tp_get_pointer_delta(struct tp_dispatch *tp, double *dx, double *dy)
-{
-       struct tp_touch *t = tp_current_touch(tp);
-
-       if (!t->is_pointer) {
-               tp_for_each_touch(tp, t) {
-                       if (t->is_pointer)
-                               break;
-               }
-       }
-
-       if (!t->is_pointer || !t->dirty)
-               return;
-
-       tp_get_delta(t, dx, dy);
-}
-
-static void
-tp_get_active_touches_delta(struct tp_dispatch *tp, double *dx, double *dy)
-{
-       struct tp_touch *t;
-       double tdx, tdy;
-       unsigned int i;
-
-       for (i = 0; i < tp->real_touches; i++) {
-               t = tp_get_touch(tp, i);
-
-               if (!tp_touch_active(tp, t) || !t->dirty)
-                       continue;
-
-               tp_get_delta(t, &tdx, &tdy);
-               *dx += tdx;
-               *dy += tdy;
-       }
-}
-
-static void
-tp_post_pointer_motion(struct tp_dispatch *tp, uint64_t time)
-{
-       double dx = 0.0, dy = 0.0;
-       double dx_unaccel, dy_unaccel;
-
-       /* When a clickpad is clicked, combine motion of all active touches */
-       if (tp->buttons.is_clickpad && tp->buttons.state)
-               tp_get_active_touches_delta(tp, &dx, &dy);
-       else
-               tp_get_pointer_delta(tp, &dx, &dy);
-
-       tp_filter_motion(tp, &dx, &dy, &dx_unaccel, &dy_unaccel, time);
-
-       if (dx != 0.0 || dy != 0.0 || dx_unaccel != 0.0 || dy_unaccel != 0.0) {
-               pointer_notify_motion(&tp->device->base, time,
-                                     dx, dy, dx_unaccel, dy_unaccel);
-       }
-}
-
-static void
 tp_post_events(struct tp_dispatch *tp, uint64_t time)
 {
        int filter_motion = 0;
@@ -862,14 +642,15 @@ tp_post_events(struct tp_dispatch *tp, uint64_t time)
        filter_motion |= tp_post_button_events(tp, time);
 
        if (filter_motion || tp->sendevents.trackpoint_active) {
-               tp_stop_scroll_events(tp, time);
+               tp_edge_scroll_stop_events(tp, time);
+               tp_gesture_stop(tp, time);
                return;
        }
 
-       if (tp_post_scroll_events(tp, time) != 0)
+       if (tp_edge_scroll_post_events(tp, time) != 0)
                return;
 
-       tp_post_pointer_motion(tp, time);
+       tp_gesture_post_events(tp, time);
 }
 
 static void
@@ -925,7 +706,8 @@ tp_remove(struct evdev_dispatch *dispatch)
        tp_remove_tap(tp);
        tp_remove_buttons(tp);
        tp_remove_sendevents(tp);
-       tp_remove_scroll(tp);
+       tp_remove_edge_scroll(tp);
+       tp_remove_gesture(tp);
 }
 
 static void
@@ -934,7 +716,6 @@ tp_destroy(struct evdev_dispatch *dispatch)
        struct tp_dispatch *tp =
                (struct tp_dispatch*)dispatch;
 
-
        free(tp->touches);
        free(tp);
 }
@@ -1018,9 +799,8 @@ tp_trackpoint_event(uint64_t time, struct libinput_event *event, void *data)
                return;
 
        if (!tp->sendevents.trackpoint_active) {
-               evdev_stop_scroll(tp->device,
-                                 time,
-                                 LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
+               tp_edge_scroll_stop_events(tp, time);
+               tp_gesture_stop(tp, time);
                tp_tap_suspend(tp, time);
                tp->sendevents.trackpoint_active = true;
        }
@@ -1183,17 +963,8 @@ tp_init_accel(struct tp_dispatch *tp, double diagonal)
 {
        int res_x, res_y;
 
-       if (tp->has_mt) {
-               res_x = libevdev_get_abs_resolution(tp->device->evdev,
-                                                   ABS_MT_POSITION_X);
-               res_y = libevdev_get_abs_resolution(tp->device->evdev,
-                                                   ABS_MT_POSITION_Y);
-       } else {
-               res_x = libevdev_get_abs_resolution(tp->device->evdev,
-                                                   ABS_X);
-               res_y = libevdev_get_abs_resolution(tp->device->evdev,
-                                                   ABS_Y);
-       }
+       res_x = tp->device->abs.absinfo_x->resolution;
+       res_y = tp->device->abs.absinfo_y->resolution;
 
        /*
         * Not all touchpads report the same amount of units/mm (resolution).
@@ -1205,19 +976,6 @@ tp_init_accel(struct tp_dispatch *tp, double diagonal)
        if (res_x > 1 && res_y > 1) {
                tp->accel.x_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_x;
                tp->accel.y_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_y;
-
-               /* FIXME: once normalized, touchpads see the same
-                  acceleration as mice. that is technically correct but
-                  subjectively wrong, we expect a touchpad to be a lot
-                  slower than a mouse.
-                  For now, apply a magic factor here until this is
-                  fixed in the actual filter code.
-                */
-               {
-                       const double MAGIC = 0.4;
-                       tp->accel.x_scale_coeff *= MAGIC;
-                       tp->accel.y_scale_coeff *= MAGIC;
-               }
        } else {
        /*
         * For touchpads where the driver does not provide resolution, fall
@@ -1227,7 +985,9 @@ tp_init_accel(struct tp_dispatch *tp, double diagonal)
                tp->accel.y_scale_coeff = DEFAULT_ACCEL_NUMERATOR / diagonal;
        }
 
-       if (evdev_device_init_pointer_acceleration(tp->device) == -1)
+       if (evdev_device_init_pointer_acceleration(
+                                       tp->device,
+                                       touchpad_accel_profile_linear) == -1)
                return -1;
 
        return 0;
@@ -1255,11 +1015,14 @@ tp_scroll_config_scroll_method_set_method(struct libinput_device *device,
 {
        struct evdev_device *evdev = (struct evdev_device*)device;
        struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
+       uint64_t time = libinput_now(device->seat->libinput);
 
        if (method == tp->scroll.method)
                return LIBINPUT_CONFIG_STATUS_SUCCESS;
 
-       tp_stop_scroll_events(tp, libinput_now(device->seat->libinput));
+       tp_edge_scroll_stop_events(tp, time);
+       tp_gesture_stop_twofinger_scroll(tp, time);
+
        tp->scroll.method = method;
 
        return LIBINPUT_CONFIG_STATUS_SUCCESS;
@@ -1354,6 +1117,32 @@ tp_init_sendevents(struct tp_dispatch *tp,
        return 0;
 }
 
+static void
+tp_fix_resolution(struct tp_dispatch *tp, struct evdev_device *device)
+{
+       struct libinput *libinput = device->base.seat->libinput;
+       const char *prop;
+       unsigned int resx, resy;
+
+       prop = udev_device_get_property_value(device->udev_device,
+                                             "TOUCHPAD_RESOLUTION");
+       if (!prop)
+               return;
+
+       if (parse_touchpad_resolution_property(prop, &resx, &resy) == -1) {
+               log_error(libinput,
+                         "Touchpad resolution property set for '%s', but invalid.\n",
+                         device->devname);
+               return;
+       }
+
+       if (evdev_fix_abs_resolution(device,
+                                tp->has_mt ? ABS_MT_POSITION_X : ABS_X,
+                                tp->has_mt ? ABS_MT_POSITION_Y : ABS_Y,
+                                resx, resy))
+               device->abs.fake_resolution = 0;
+}
+
 static int
 tp_init(struct tp_dispatch *tp,
        struct evdev_device *device)
@@ -1367,15 +1156,17 @@ tp_init(struct tp_dispatch *tp,
        if (tp_init_slots(tp, device) != 0)
                return -1;
 
+       tp_fix_resolution(tp, device);
+
        width = abs(device->abs.absinfo_x->maximum -
                    device->abs.absinfo_x->minimum);
        height = abs(device->abs.absinfo_y->maximum -
                     device->abs.absinfo_y->minimum);
        diagonal = sqrt(width*width + height*height);
 
-       tp->hysteresis.margin_x =
+       tp->hysteresis_margin.x =
                diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
-       tp->hysteresis.margin_y =
+       tp->hysteresis_margin.y =
                diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
 
        if (tp_init_accel(tp, diagonal) != 0)
@@ -1396,6 +1187,9 @@ tp_init(struct tp_dispatch *tp,
        if (tp_init_scroll(tp, device) != 0)
                return -1;
 
+       if (tp_init_gesture(tp) != 0)
+               return -1;
+
        device->seat_caps |= EVDEV_DEVICE_POINTER;
 
        return 0;