From b8a2e5bc5f8a1fc126a3e20f2e6c5745afaca89d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 22 Jan 2015 16:41:50 +0100 Subject: [PATCH 01/12] touchpad: Add an API for touchpad gesture events For touchscreens we always send raw touch events to the compositor, and the compositor or application toolkits do gesture recognition. This makes sense because on a touchscreen which window / widget the touches are over is important context to know to interpret gestures. On touchpads however we never send raw events since a touchpad is an absolute device which primary function is to send pointer motion delta-s, so we always need to do processing (and a lot of it) on the raw events. Moreover there is nothing underneath the finger which influences how to interpret gestures, and there is a lot of touchpad and libinput configuration specific context necessary for gesture recognition. E.g. is this a clickpad, and if so are softbuttons or clickfinger used? What is the size of the softbuttons? Is this a true multi-touch touchpad or a semi multi-touch touchpad which only gives us a bounding box enclosing the fingers? Etc. So for touchpads it is better to do gesture processing in libinput, this commit adds an initial implementation of a Gesture event API which only supports swipe gestures, other gestures will be added later following the same model wrt, having clear start and stop events and the number of fingers involved being fixed once a gesture sequence starts. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Acked-by: Jason Gerecke --- src/evdev.c | 3 + src/evdev.h | 3 +- src/libinput-private.h | 8 +++ src/libinput.c | 96 ++++++++++++++++++++++++++ src/libinput.h | 148 ++++++++++++++++++++++++++++++++++++++++- src/libinput.sym | 11 +++ test/litest.c | 9 +++ tools/event-debug.c | 46 +++++++++++++ tools/event-gui.c | 4 ++ 9 files changed, 325 insertions(+), 3 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 346f11ab..12617245 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1924,6 +1924,7 @@ evdev_configure_device(struct evdev_device *device) if (udev_tags & EVDEV_UDEV_TAG_TOUCHPAD) { device->dispatch = evdev_mt_touchpad_create(device); + device->seat_caps |= EVDEV_DEVICE_GESTURE; log_info(libinput, "input device '%s', %s is a touchpad\n", device->devname, devnode); @@ -2287,6 +2288,8 @@ evdev_device_has_capability(struct evdev_device *device, return !!(device->seat_caps & EVDEV_DEVICE_KEYBOARD); case LIBINPUT_DEVICE_CAP_TOUCH: return !!(device->seat_caps & EVDEV_DEVICE_TOUCH); + case LIBINPUT_DEVICE_CAP_GESTURE: + return !!(device->seat_caps & EVDEV_DEVICE_GESTURE); default: return 0; } diff --git a/src/evdev.h b/src/evdev.h index aa548d2d..77db1b47 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -59,7 +59,8 @@ enum evdev_event_type { enum evdev_device_seat_capability { EVDEV_DEVICE_POINTER = (1 << 0), EVDEV_DEVICE_KEYBOARD = (1 << 1), - EVDEV_DEVICE_TOUCH = (1 << 2) + EVDEV_DEVICE_TOUCH = (1 << 2), + EVDEV_DEVICE_GESTURE = (1 << 5), }; enum evdev_device_tags { diff --git a/src/libinput-private.h b/src/libinput-private.h index d11f000f..d0299c61 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -365,6 +365,14 @@ touch_notify_touch_up(struct libinput_device *device, int32_t slot, int32_t seat_slot); +void +gesture_notify_swipe(struct libinput_device *device, + uint64_t time, + enum libinput_event_type type, + int finger_count, + const struct normalized_coords *delta, + const struct normalized_coords *unaccel); + void touch_notify_frame(struct libinput_device *device, uint64_t time); diff --git a/src/libinput.c b/src/libinput.c index d164604c..ceb47387 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -114,6 +114,14 @@ struct libinput_event_touch { struct device_coords point; }; +struct libinput_event_gesture { + struct libinput_event base; + uint32_t time; + int finger_count; + struct normalized_coords delta; + struct normalized_coords delta_unaccel; +}; + static void libinput_default_log_func(struct libinput *libinput, enum libinput_log_priority priority, @@ -237,6 +245,19 @@ libinput_event_get_touch_event(struct libinput_event *event) return (struct libinput_event_touch *) event; } +LIBINPUT_EXPORT struct libinput_event_gesture * +libinput_event_get_gesture_event(struct libinput_event *event) +{ + require_event_type(libinput_event_get_context(event), + event->type, + NULL, + LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, + LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, + LIBINPUT_EVENT_GESTURE_SWIPE_END); + + return (struct libinput_event_gesture *) event; +} + LIBINPUT_EXPORT struct libinput_event_device_notify * libinput_event_get_device_notify_event(struct libinput_event *event) { @@ -637,6 +658,44 @@ libinput_event_touch_get_y(struct libinput_event_touch *event) return evdev_convert_to_mm(device->abs.absinfo_y, event->point.y); } +LIBINPUT_EXPORT uint32_t +libinput_event_gesture_get_time(struct libinput_event_gesture *event) +{ + return event->time; +} + +LIBINPUT_EXPORT int +libinput_event_gesture_get_finger_count(struct libinput_event_gesture *event) +{ + return event->finger_count; +} + +LIBINPUT_EXPORT double +libinput_event_gesture_get_dx(struct libinput_event_gesture *event) +{ + return event->delta.x; +} + +LIBINPUT_EXPORT double +libinput_event_gesture_get_dy(struct libinput_event_gesture *event) +{ + return event->delta.y; +} + +LIBINPUT_EXPORT double +libinput_event_gesture_get_dx_unaccelerated( + struct libinput_event_gesture *event) +{ + return event->delta_unaccel.x; +} + +LIBINPUT_EXPORT double +libinput_event_gesture_get_dy_unaccelerated( + struct libinput_event_gesture *event) +{ + return event->delta_unaccel.y; +} + struct libinput_source * libinput_add_fd(struct libinput *libinput, int fd, @@ -1086,6 +1145,9 @@ device_has_cap(struct libinput_device *device, case LIBINPUT_DEVICE_CAP_TOUCH: capability = "CAP_TOUCH"; break; + case LIBINPUT_DEVICE_CAP_GESTURE: + capability = "CAP_GESTURE"; + break; } log_bug_libinput(device->seat->libinput, @@ -1342,6 +1404,34 @@ touch_notify_frame(struct libinput_device *device, &touch_event->base); } +void +gesture_notify_swipe(struct libinput_device *device, + uint64_t time, + enum libinput_event_type type, + int finger_count, + const struct normalized_coords *delta, + const struct normalized_coords *unaccel) +{ + struct libinput_event_gesture *gesture_event; + + if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_GESTURE)) + return; + + gesture_event = zalloc(sizeof *gesture_event); + if (!gesture_event) + return; + + *gesture_event = (struct libinput_event_gesture) { + .time = time, + .finger_count = finger_count, + .delta = *delta, + .delta_unaccel = *unaccel, + }; + + post_device_event(device, time, type, + &gesture_event->base); +} + static void libinput_post_event(struct libinput *libinput, struct libinput_event *event) @@ -1607,6 +1697,12 @@ libinput_event_touch_get_base_event(struct libinput_event_touch *event) return &event->base; } +LIBINPUT_EXPORT struct libinput_event * +libinput_event_gesture_get_base_event(struct libinput_event_gesture *event) +{ + return &event->base; +} + LIBINPUT_EXPORT struct libinput_device_group * libinput_device_group_ref(struct libinput_device_group *group) { diff --git a/src/libinput.h b/src/libinput.h index 5df71836..5cc7a4c5 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -57,7 +57,8 @@ enum libinput_log_priority { enum libinput_device_capability { LIBINPUT_DEVICE_CAP_KEYBOARD = 0, LIBINPUT_DEVICE_CAP_POINTER = 1, - LIBINPUT_DEVICE_CAP_TOUCH = 2 + LIBINPUT_DEVICE_CAP_TOUCH = 2, + LIBINPUT_DEVICE_CAP_GESTURE = 5, }; /** @@ -176,7 +177,11 @@ enum libinput_event_type { * Signals the end of a set of touchpoints at one device sample * time. This event has no coordinate information attached. */ - LIBINPUT_EVENT_TOUCH_FRAME + LIBINPUT_EVENT_TOUCH_FRAME, + + LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN = 800, + LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, + LIBINPUT_EVENT_GESTURE_SWIPE_END, }; /** @@ -360,6 +365,19 @@ libinput_event_get_keyboard_event(struct libinput_event *event); struct libinput_event_touch * libinput_event_get_touch_event(struct libinput_event *event); +/** + * @ingroup event + * + * Return the gesture event that is this input event. If the event type does + * not match the gesture event types, this function returns NULL. + * + * The inverse of this function is libinput_event_gesture_get_base_event(). + * + * @return A gesture event, or NULL for other events + */ +struct libinput_event_gesture * +libinput_event_get_gesture_event(struct libinput_event *event); + /** * @ingroup event * @@ -924,6 +942,132 @@ libinput_event_touch_get_y_transformed(struct libinput_event_touch *event, struct libinput_event * libinput_event_touch_get_base_event(struct libinput_event_touch *event); +/** + * @defgroup event_gesture Gesture events + * + * Gesture events are generated when a gesture is recognized on a touchpad. + * + * Gesture sequences always start with a LIBINPUT_EVENT_GESTURE_FOO_START + * event. All following gesture events will be of the + * LIBINPUT_EVENT_GESTURE_FOO_UPDATE type until a + * LIBINPUT_EVENT_GESTURE_FOO_END is generated which signals the end of the + * gesture. + */ + +/** + * @ingroup event_gesture + * + * @return The event time for this event + */ +uint32_t +libinput_event_gesture_get_time(struct libinput_event_gesture *event); + +/** + * @ingroup event_gesture + * + * @return The generic libinput_event of this event + */ +struct libinput_event * +libinput_event_gesture_get_base_event(struct libinput_event_gesture *event); + +/** + * @ingroup event_gesture + * + * Return the number of fingers used for a gesture. This can be used e.g. + * to differentiate between 3 or 4 finger swipes. + * + * This function can be called on all gesture events and the returned finger + * count value will not change during a sequence. + * + * @return the number of fingers used for a gesture + */ +int +libinput_event_gesture_get_finger_count(struct libinput_event_gesture *event); + +/** + * @ingroup event_gesture + * + * Return the delta between the last event and the current event. For gesture + * events that are not of type @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, this + * function returns 0. + * + * If a device employs pointer acceleration, the delta returned by this + * function is the accelerated delta. + * + * Relative motion deltas are normalized to represent those of a device with + * 1000dpi resolution. See @ref motion_normalization for more details. + * + * @note It is an application bug to call this function for events other than + * @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE. + * + * @return the relative x movement since the last event + */ +double +libinput_event_gesture_get_dx(struct libinput_event_gesture *event); + +/** + * @ingroup event_gesture + * + * Return the delta between the last event and the current event. For gesture + * events that are not of type @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, this + * function returns 0. + * + * If a device employs pointer acceleration, the delta returned by this + * function is the accelerated delta. + * + * Relative motion deltas are normalized to represent those of a device with + * 1000dpi resolution. See @ref motion_normalization for more details. + * + * @note It is an application bug to call this function for events other than + * @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE. + * + * @return the relative y movement since the last event + */ +double +libinput_event_gesture_get_dy(struct libinput_event_gesture *event); + +/** + * @ingroup event_gesture + * + * Return the relative delta of the unaccelerated motion vector of the + * current event. For gesture events that are not of type @ref + * LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, this function returns 0. + * + * Relative unaccelerated motion deltas are normalized to represent those of a + * device with 1000dpi resolution. See @ref motion_normalization for more + * details. Note that unaccelerated events are not equivalent to 'raw' events + * as read from the device. + * + * @note It is an application bug to call this function for events other than + * @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE. + * + * @return the unaccelerated relative x movement since the last event + */ +double +libinput_event_gesture_get_dx_unaccelerated( + struct libinput_event_gesture *event); + +/** + * @ingroup event_gesture + * + * Return the relative delta of the unaccelerated motion vector of the + * current event. For gesture events that are not of type @ref + * LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, this function returns 0. + * + * Relative unaccelerated motion deltas are normalized to represent those of a + * device with 1000dpi resolution. See @ref motion_normalization for more + * details. Note that unaccelerated events are not equivalent to 'raw' events + * as read from the device. + * + * @note It is an application bug to call this function for events other than + * @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE. + * + * @return the unaccelerated relative y movement since the last event + */ +double +libinput_event_gesture_get_dy_unaccelerated( + struct libinput_event_gesture *event); + /** * @defgroup base Initialization and manipulation of libinput contexts */ diff --git a/src/libinput.sym b/src/libinput.sym index 2c023367..f81ce0d7 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -146,3 +146,14 @@ LIBINPUT_0.19.0 { libinput_device_config_tap_get_drag_lock_enabled; libinput_device_config_tap_get_default_drag_lock_enabled; } LIBINPUT_0.15.0; + +TOUCHPAD_GESTURES { + libinput_event_gesture_get_base_event; + libinput_event_gesture_get_dx; + libinput_event_gesture_get_dx_unaccelerated; + libinput_event_gesture_get_dy; + libinput_event_gesture_get_dy_unaccelerated; + libinput_event_gesture_get_finger_count; + libinput_event_gesture_get_time; + libinput_event_get_gesture_event; +} LIBINPUT_0.19.0; diff --git a/test/litest.c b/test/litest.c index df521fe6..115c9ca8 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1655,6 +1655,15 @@ litest_event_type_str(struct libinput_event *event) case LIBINPUT_EVENT_TOUCH_FRAME: str = "TOUCH FRAME"; break; + case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN: + str = "GESTURE SWIPE START"; + break; + case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE: + str = "GESTURE SWIPE UPDATE"; + break; + case LIBINPUT_EVENT_GESTURE_SWIPE_END: + str = "GESTURE SWIPE END"; + break; } return str; } diff --git a/tools/event-debug.c b/tools/event-debug.c index 7f6c54ab..f3e52b45 100644 --- a/tools/event-debug.c +++ b/tools/event-debug.c @@ -90,6 +90,15 @@ print_event_header(struct libinput_event *ev) case LIBINPUT_EVENT_TOUCH_FRAME: type = "TOUCH_FRAME"; break; + case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN: + type = "GESTURE_SWIPE_BEGIN"; + break; + case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE: + type = "GESTURE_SWIPE_UPDATE"; + break; + case LIBINPUT_EVENT_GESTURE_SWIPE_END: + type = "GESTURE_SWIPE_END"; + break; } printf("%-7s %s ", libinput_device_get_sysname(dev), type); @@ -135,6 +144,9 @@ print_device_notify(struct libinput_event *ev) if (libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TOUCH)) printf("t"); + if (libinput_device_has_capability(dev, + LIBINPUT_DEVICE_CAP_GESTURE)) + printf("g"); if (libinput_device_get_size(dev, &w, &h) == 0) printf("\tsize %.2f/%.2fmm", w, h); @@ -281,6 +293,31 @@ print_touch_event_with_coords(struct libinput_event *ev) xmm, ymm); } +static void +print_gesture_event_without_coords(struct libinput_event *ev) +{ + struct libinput_event_gesture *t = libinput_event_get_gesture_event(ev); + + print_event_time(libinput_event_gesture_get_time(t)); + printf("%d\n", libinput_event_gesture_get_finger_count(t)); +} + +static void +print_gesture_event_with_coords(struct libinput_event *ev) +{ + struct libinput_event_gesture *t = libinput_event_get_gesture_event(ev); + double dx = libinput_event_gesture_get_dx(t); + double dy = libinput_event_gesture_get_dy(t); + double dx_unaccel = libinput_event_gesture_get_dx_unaccelerated(t); + double dy_unaccel = libinput_event_gesture_get_dy_unaccelerated(t); + + print_event_time(libinput_event_gesture_get_time(t)); + + printf("%d %5.2f/%5.2f (%5.2f/%5.2f unaccelerated)\n", + libinput_event_gesture_get_finger_count(t), + dx, dy, dx_unaccel, dy_unaccel); +} + static int handle_and_print_events(struct libinput *li) { @@ -330,6 +367,15 @@ handle_and_print_events(struct libinput *li) case LIBINPUT_EVENT_TOUCH_FRAME: print_touch_event_without_coords(ev); break; + case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN: + print_gesture_event_without_coords(ev); + break; + case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE: + print_gesture_event_with_coords(ev); + break; + case LIBINPUT_EVENT_GESTURE_SWIPE_END: + print_gesture_event_without_coords(ev); + break; } libinput_event_destroy(ev); diff --git a/tools/event-gui.c b/tools/event-gui.c index 5736e979..0c2896a7 100644 --- a/tools/event-gui.c +++ b/tools/event-gui.c @@ -473,6 +473,10 @@ handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data) return FALSE; } break; + case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN: + case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE: + case LIBINPUT_EVENT_GESTURE_SWIPE_END: + break; } libinput_event_destroy(ev); From 37c000ad5e444339c98a568dfad2ce5740c38665 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 18 Feb 2015 09:00:25 +0100 Subject: [PATCH 02/12] touchpad: Add support for swipe gestures Add support for swipe gestures. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Acked-by: Jason Gerecke --- src/evdev-mt-touchpad-gestures.c | 39 ++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index e85a9d77..e6fde0d6 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -76,6 +76,8 @@ tp_get_average_touches_delta(struct tp_dispatch *tp) static void tp_gesture_start(struct tp_dispatch *tp, uint64_t time) { + const struct normalized_coords zero = { 0.0, 0.0 }; + if (tp->gesture.started) return; @@ -83,6 +85,13 @@ tp_gesture_start(struct tp_dispatch *tp, uint64_t time) case 2: /* NOP */ break; + case 3: + case 4: + gesture_notify_swipe(&tp->device->base, time, + LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, + tp->gesture.finger_count, + &zero, &zero); + break; } tp->gesture.started = true; } @@ -141,6 +150,23 @@ tp_gesture_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time) &delta); } +static void +tp_gesture_post_swipe(struct tp_dispatch *tp, uint64_t time) +{ + struct normalized_coords delta, unaccel; + + unaccel = tp_get_average_touches_delta(tp); + delta = tp_filter_motion(tp, &unaccel, time); + + if (!normalized_is_zero(delta) || !normalized_is_zero(unaccel)) { + tp_gesture_start(tp, time); + gesture_notify_swipe(&tp->device->base, time, + LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, + tp->gesture.finger_count, + &delta, &unaccel); + } +} + void tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time) { @@ -165,6 +191,10 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time) case 2: tp_gesture_post_twofinger_scroll(tp, time); break; + case 3: + case 4: + tp_gesture_post_swipe(tp, time); + break; } } @@ -182,6 +212,8 @@ tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time) void tp_gesture_stop(struct tp_dispatch *tp, uint64_t time) { + const struct normalized_coords zero = { 0.0, 0.0 }; + if (!tp->gesture.started) return; @@ -189,6 +221,13 @@ tp_gesture_stop(struct tp_dispatch *tp, uint64_t time) case 2: tp_gesture_stop_twofinger_scroll(tp, time); break; + case 3: + case 4: + gesture_notify_swipe(&tp->device->base, time, + LIBINPUT_EVENT_GESTURE_SWIPE_END, + tp->gesture.finger_count, + &zero, &zero); + break; } tp->gesture.started = false; } From 98b1e212ac458ab0c1757b276a5128b575adfe05 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 4 Mar 2015 15:24:04 +0100 Subject: [PATCH 03/12] touchpad: Extend the touchpad gesture API with pinch gestures Extend the touchpad gesture API with pinch gestures. Note that this new API offers a single event stream for both pinch and rotate data, this is deliberate as some applications may be interested in getting both at the same time. Applications which are only interested in one or the other can simply ignore the other. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Acked-by: Jason Gerecke --- src/libinput-private.h | 9 +++++ src/libinput.c | 61 +++++++++++++++++++++++++++----- src/libinput.h | 80 +++++++++++++++++++++++++++++++++++------- src/libinput.sym | 2 ++ test/litest.c | 9 +++++ tools/event-debug.c | 27 +++++++++++++- tools/event-gui.c | 3 ++ 7 files changed, 170 insertions(+), 21 deletions(-) diff --git a/src/libinput-private.h b/src/libinput-private.h index d0299c61..9a2643cf 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -373,6 +373,15 @@ gesture_notify_swipe(struct libinput_device *device, const struct normalized_coords *delta, const struct normalized_coords *unaccel); +void +gesture_notify_pinch(struct libinput_device *device, + uint64_t time, + enum libinput_event_type type, + const struct normalized_coords *delta, + const struct normalized_coords *unaccel, + double scale, + double angle); + void touch_notify_frame(struct libinput_device *device, uint64_t time); diff --git a/src/libinput.c b/src/libinput.c index ceb47387..84e623f1 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -120,6 +120,8 @@ struct libinput_event_gesture { int finger_count; struct normalized_coords delta; struct normalized_coords delta_unaccel; + double scale; + double angle; }; static void @@ -253,7 +255,10 @@ libinput_event_get_gesture_event(struct libinput_event *event) NULL, LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, - LIBINPUT_EVENT_GESTURE_SWIPE_END); + LIBINPUT_EVENT_GESTURE_SWIPE_END, + LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, + LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, + LIBINPUT_EVENT_GESTURE_PINCH_END); return (struct libinput_event_gesture *) event; } @@ -696,6 +701,18 @@ libinput_event_gesture_get_dy_unaccelerated( return event->delta_unaccel.y; } +LIBINPUT_EXPORT double +libinput_event_gesture_get_scale(struct libinput_event_gesture *event) +{ + return event->scale; +} + +LIBINPUT_EXPORT double +libinput_event_gesture_get_angle_delta(struct libinput_event_gesture *event) +{ + return event->angle; +} + struct libinput_source * libinput_add_fd(struct libinput *libinput, int fd, @@ -1404,13 +1421,15 @@ touch_notify_frame(struct libinput_device *device, &touch_event->base); } -void -gesture_notify_swipe(struct libinput_device *device, - uint64_t time, - enum libinput_event_type type, - int finger_count, - const struct normalized_coords *delta, - const struct normalized_coords *unaccel) +static void +gesture_notify(struct libinput_device *device, + uint64_t time, + enum libinput_event_type type, + int finger_count, + const struct normalized_coords *delta, + const struct normalized_coords *unaccel, + double scale, + double angle) { struct libinput_event_gesture *gesture_event; @@ -1426,12 +1445,38 @@ gesture_notify_swipe(struct libinput_device *device, .finger_count = finger_count, .delta = *delta, .delta_unaccel = *unaccel, + .scale = scale, + .angle = angle, }; post_device_event(device, time, type, &gesture_event->base); } +void +gesture_notify_swipe(struct libinput_device *device, + uint64_t time, + enum libinput_event_type type, + int finger_count, + const struct normalized_coords *delta, + const struct normalized_coords *unaccel) +{ + gesture_notify(device, time, type, finger_count, delta, unaccel, + 0.0, 0.0); +} + +void +gesture_notify_pinch(struct libinput_device *device, + uint64_t time, + enum libinput_event_type type, + const struct normalized_coords *delta, + const struct normalized_coords *unaccel, + double scale, + double angle) +{ + gesture_notify(device, time, type, 2, delta, unaccel, scale, angle); +} + static void libinput_post_event(struct libinput *libinput, struct libinput_event *event) diff --git a/src/libinput.h b/src/libinput.h index 5cc7a4c5..e1a2dd7e 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -182,6 +182,9 @@ enum libinput_event_type { LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN = 800, LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, LIBINPUT_EVENT_GESTURE_SWIPE_END, + LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, + LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, + LIBINPUT_EVENT_GESTURE_PINCH_END, }; /** @@ -988,8 +991,8 @@ libinput_event_gesture_get_finger_count(struct libinput_event_gesture *event); * @ingroup event_gesture * * Return the delta between the last event and the current event. For gesture - * events that are not of type @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, this - * function returns 0. + * events that are not of type @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE or + * @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, this function returns 0. * * If a device employs pointer acceleration, the delta returned by this * function is the accelerated delta. @@ -998,7 +1001,8 @@ libinput_event_gesture_get_finger_count(struct libinput_event_gesture *event); * 1000dpi resolution. See @ref motion_normalization for more details. * * @note It is an application bug to call this function for events other than - * @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE. + * @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE or + * @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE. * * @return the relative x movement since the last event */ @@ -1009,8 +1013,8 @@ libinput_event_gesture_get_dx(struct libinput_event_gesture *event); * @ingroup event_gesture * * Return the delta between the last event and the current event. For gesture - * events that are not of type @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, this - * function returns 0. + * events that are not of type @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE or + * @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, this function returns 0. * * If a device employs pointer acceleration, the delta returned by this * function is the accelerated delta. @@ -1019,7 +1023,8 @@ libinput_event_gesture_get_dx(struct libinput_event_gesture *event); * 1000dpi resolution. See @ref motion_normalization for more details. * * @note It is an application bug to call this function for events other than - * @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE. + * @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE or + * @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE. * * @return the relative y movement since the last event */ @@ -1030,8 +1035,9 @@ libinput_event_gesture_get_dy(struct libinput_event_gesture *event); * @ingroup event_gesture * * Return the relative delta of the unaccelerated motion vector of the - * current event. For gesture events that are not of type @ref - * LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, this function returns 0. + * current event. For gesture events that are not of type + * @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE or + * @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, this function returns 0. * * Relative unaccelerated motion deltas are normalized to represent those of a * device with 1000dpi resolution. See @ref motion_normalization for more @@ -1039,7 +1045,8 @@ libinput_event_gesture_get_dy(struct libinput_event_gesture *event); * as read from the device. * * @note It is an application bug to call this function for events other than - * @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE. + * @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE or + * @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE. * * @return the unaccelerated relative x movement since the last event */ @@ -1051,8 +1058,9 @@ libinput_event_gesture_get_dx_unaccelerated( * @ingroup event_gesture * * Return the relative delta of the unaccelerated motion vector of the - * current event. For gesture events that are not of type @ref - * LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, this function returns 0. + * current event. For gesture events that are not of type + * @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE or + * @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, this function returns 0. * * Relative unaccelerated motion deltas are normalized to represent those of a * device with 1000dpi resolution. See @ref motion_normalization for more @@ -1060,7 +1068,8 @@ libinput_event_gesture_get_dx_unaccelerated( * as read from the device. * * @note It is an application bug to call this function for events other than - * @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE. + * @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE or + * @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE. * * @return the unaccelerated relative y movement since the last event */ @@ -1068,6 +1077,53 @@ double libinput_event_gesture_get_dy_unaccelerated( struct libinput_event_gesture *event); +/** + * @ingroup event_gesture + * + * Return the absolute scale of a pinch gesture, the scale is the division + * of the current distance between the fingers and the distance at the start + * of the gesture. The scale begins at 1.0, and if e.g. the fingers moved + * together by 50% then the scale will become 0.5, if they move twice as far + * apart as initially the scale becomes 2.0, etc. + * + * For gesture events that are not of type @ref + * LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, this function returns 0. + * + * @note It is an application bug to call this function for events other than + * @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE. + * + * @return the absolute scale of a pinch gesture + */ +double +libinput_event_gesture_get_scale(struct libinput_event_gesture *event); + +/** + * @ingroup event_gesture + * + * Return the angle delta in degrees between the last and the current @ref + * LIBINPUT_EVENT_GESTURE_PINCH_UPDATE event. For gesture events that + * are not of type @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, this + * function returns 0. + * + * The angle delta is defined as the change in angle of the line formed by + * the 2 fingers of a pinch gesture. Clockwise rotation is represented + * by a postive delta, counter-clockwise by a negative delta. If e.g. the + * fingers are on the 12 and 6 location of a clock face plate and they move + * to the 1 resp. 7 location in a single event then the angle delta is + * 30 degrees. + * + * If more than two fingers are present, the angle represents the rotation + * around the center of gravity. The calculation of the center of gravity is + * implementation-dependent. + * + * @note It is an application bug to call this function for events other than + * @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE. + * + * @return the angle delta since the last event + */ +double +libinput_event_gesture_get_angle_delta(struct libinput_event_gesture *event); + /** * @defgroup base Initialization and manipulation of libinput contexts */ diff --git a/src/libinput.sym b/src/libinput.sym index f81ce0d7..0060ba87 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -148,12 +148,14 @@ LIBINPUT_0.19.0 { } LIBINPUT_0.15.0; TOUCHPAD_GESTURES { + libinput_event_gesture_get_angle_delta; libinput_event_gesture_get_base_event; libinput_event_gesture_get_dx; libinput_event_gesture_get_dx_unaccelerated; libinput_event_gesture_get_dy; libinput_event_gesture_get_dy_unaccelerated; libinput_event_gesture_get_finger_count; + libinput_event_gesture_get_scale; libinput_event_gesture_get_time; libinput_event_get_gesture_event; } LIBINPUT_0.19.0; diff --git a/test/litest.c b/test/litest.c index 115c9ca8..b8758470 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1664,6 +1664,15 @@ litest_event_type_str(struct libinput_event *event) case LIBINPUT_EVENT_GESTURE_SWIPE_END: str = "GESTURE SWIPE END"; break; + case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN: + str = "GESTURE PINCH START"; + break; + case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE: + str = "GESTURE PINCH UPDATE"; + break; + case LIBINPUT_EVENT_GESTURE_PINCH_END: + str = "GESTURE PINCH END"; + break; } return str; } diff --git a/tools/event-debug.c b/tools/event-debug.c index f3e52b45..1902dd35 100644 --- a/tools/event-debug.c +++ b/tools/event-debug.c @@ -99,6 +99,15 @@ print_event_header(struct libinput_event *ev) case LIBINPUT_EVENT_GESTURE_SWIPE_END: type = "GESTURE_SWIPE_END"; break; + case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN: + type = "GESTURE_PINCH_BEGIN"; + break; + case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE: + type = "GESTURE_PINCH_UPDATE"; + break; + case LIBINPUT_EVENT_GESTURE_PINCH_END: + type = "GESTURE_PINCH_END"; + break; } printf("%-7s %s ", libinput_device_get_sysname(dev), type); @@ -310,12 +319,19 @@ print_gesture_event_with_coords(struct libinput_event *ev) double dy = libinput_event_gesture_get_dy(t); double dx_unaccel = libinput_event_gesture_get_dx_unaccelerated(t); double dy_unaccel = libinput_event_gesture_get_dy_unaccelerated(t); + double scale = libinput_event_gesture_get_scale(t); + double angle = libinput_event_gesture_get_angle_delta(t); print_event_time(libinput_event_gesture_get_time(t)); - printf("%d %5.2f/%5.2f (%5.2f/%5.2f unaccelerated)\n", + printf("%d %5.2f/%5.2f (%5.2f/%5.2f unaccelerated)", libinput_event_gesture_get_finger_count(t), dx, dy, dx_unaccel, dy_unaccel); + + if (libinput_event_get_type(ev) == LIBINPUT_EVENT_GESTURE_PINCH_UPDATE) + printf(" %5.2f @ %5.2f\n", scale, angle); + else + printf("\n"); } static int @@ -376,6 +392,15 @@ handle_and_print_events(struct libinput *li) case LIBINPUT_EVENT_GESTURE_SWIPE_END: print_gesture_event_without_coords(ev); break; + case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN: + print_gesture_event_without_coords(ev); + break; + case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE: + print_gesture_event_with_coords(ev); + break; + case LIBINPUT_EVENT_GESTURE_PINCH_END: + print_gesture_event_without_coords(ev); + break; } libinput_event_destroy(ev); diff --git a/tools/event-gui.c b/tools/event-gui.c index 0c2896a7..3806014b 100644 --- a/tools/event-gui.c +++ b/tools/event-gui.c @@ -476,6 +476,9 @@ handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data) case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN: case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE: case LIBINPUT_EVENT_GESTURE_SWIPE_END: + case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN: + case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE: + case LIBINPUT_EVENT_GESTURE_PINCH_END: break; } From ad26785f6a112b469757ecfff284d9937da2b6ac Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 5 Mar 2015 12:44:13 +0100 Subject: [PATCH 04/12] touchpad: Implement pinch gesture support Implement touchpad pinch (and rotate) gesture support. Note that two two-finger scrolling tests are slightly tweaked to assure that there is enough touch movement to allow the scroll-or-pinch detect code to do its work. Signed-off-by: Hans de Goede Acked-by: Jason Gerecke --- src/evdev-mt-touchpad-gestures.c | 284 ++++++++++++++++++++++++++++++- src/evdev-mt-touchpad.h | 18 ++ src/libinput-private.h | 33 ++++ test/touchpad.c | 4 +- 4 files changed, 329 insertions(+), 10 deletions(-) diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index e6fde0d6..ce52df22 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -23,7 +23,6 @@ #include "config.h" -#include #include #include #include @@ -31,6 +30,7 @@ #include "evdev-mt-touchpad.h" #define DEFAULT_GESTURE_SWITCH_TIMEOUT 100 /* ms */ +#define DEFAULT_GESTURE_2FG_SCROLL_TIMEOUT 1000 /* ms */ static struct normalized_coords tp_get_touches_delta(struct tp_dispatch *tp, bool average) @@ -76,6 +76,7 @@ tp_get_average_touches_delta(struct tp_dispatch *tp) static void tp_gesture_start(struct tp_dispatch *tp, uint64_t time) { + struct libinput *libinput = tp->device->base.seat->libinput; const struct normalized_coords zero = { 0.0, 0.0 }; if (tp->gesture.started) @@ -83,7 +84,22 @@ tp_gesture_start(struct tp_dispatch *tp, uint64_t time) switch (tp->gesture.finger_count) { case 2: - /* NOP */ + switch (tp->gesture.twofinger_state) { + case GESTURE_2FG_STATE_NONE: + case GESTURE_2FG_STATE_UNKNOWN: + log_bug_libinput(libinput, + "%s in unknown gesture mode\n", + __func__); + break; + case GESTURE_2FG_STATE_SCROLL: + /* NOP */ + break; + case GESTURE_2FG_STATE_PINCH: + gesture_notify_pinch(&tp->device->base, time, + LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, + &zero, &zero, 0.0, 0.0); + break; + } break; case 3: case 4: @@ -119,19 +135,191 @@ tp_gesture_post_pointer_motion(struct tp_dispatch *tp, uint64_t time) } } +static unsigned int +tp_gesture_get_active_touches(struct tp_dispatch *tp, + struct tp_touch **touches, + unsigned int count) +{ + unsigned int i, n = 0; + struct tp_touch *t; + + memset(touches, 0, count * sizeof(struct tp_touch *)); + + for (i = 0; i < tp->num_slots; i++) { + t = &tp->touches[i]; + if (tp_touch_active(tp, t)) { + touches[n++] = t; + if (n == count) + return count; + } + } + + /* + * This can happen when the user does .e.g: + * 1) Put down 1st finger in center (so active) + * 2) Put down 2nd finger in a button area (so inactive) + * 3) Put down 3th finger somewhere, gets reported as a fake finger, + * so gets same coordinates as 1st -> active + * + * We could avoid this by looking at all touches, be we really only + * want to look at real touches. + */ + return n; +} + +static int +tp_gesture_get_direction(struct tp_dispatch *tp, struct tp_touch *touch) +{ + struct normalized_coords normalized; + struct device_float_coords delta; + double move_threshold; + + /* + * Semi-mt touchpads have somewhat inaccurate coordinates when + * 2 fingers are down, so use a slightly larger threshold. + */ + if (tp->semi_mt) + move_threshold = TP_MM_TO_DPI_NORMALIZED(4); + else + move_threshold = TP_MM_TO_DPI_NORMALIZED(3); + + delta = device_delta(touch->point, touch->gesture.initial); + normalized = tp_normalize_delta(tp, delta); + + if (normalized_length(normalized) < move_threshold) + return UNDEFINED_DIRECTION; + + return normalized_get_direction(normalized); +} + static void -tp_gesture_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time) +tp_gesture_get_pinch_info(struct tp_dispatch *tp, + double *distance, + double *angle, + struct device_float_coords *center) +{ + struct normalized_coords normalized; + struct device_float_coords delta; + struct tp_touch *first = tp->gesture.touches[0], + *second = tp->gesture.touches[1]; + + delta = device_delta(first->point, second->point); + normalized = tp_normalize_delta(tp, delta); + *distance = normalized_length(normalized); + + if (!tp->semi_mt) + *angle = atan2(normalized.y, normalized.x) * 180.0 / M_PI; + else + *angle = 0.0; + + *center = device_average(first->point, second->point); +} + +static void +tp_gesture_set_scroll_buildup(struct tp_dispatch *tp) +{ + struct device_float_coords d0, d1; + struct device_float_coords average; + struct tp_touch *first = tp->gesture.touches[0], + *second = tp->gesture.touches[1]; + + d0 = device_delta(first->point, first->gesture.initial); + d1 = device_delta(second->point, second->gesture.initial); + + average = device_float_average(d0, d1); + tp->device->scroll.buildup = tp_normalize_delta(tp, average); +} + +static enum tp_gesture_2fg_state +tp_gesture_twofinger_handle_state_none(struct tp_dispatch *tp, uint64_t time) +{ + struct tp_touch *first, *second; + + if (tp_gesture_get_active_touches(tp, tp->gesture.touches, 2) != 2) + return GESTURE_2FG_STATE_NONE; + + first = tp->gesture.touches[0]; + second = tp->gesture.touches[1]; + + tp->gesture.initial_time = time; + first->gesture.initial = first->point; + second->gesture.initial = second->point; + + return GESTURE_2FG_STATE_UNKNOWN; +} + +static enum tp_gesture_2fg_state +tp_gesture_twofinger_handle_state_unknown(struct tp_dispatch *tp, uint64_t time) +{ + struct normalized_coords normalized; + struct device_float_coords delta; + struct tp_touch *first = tp->gesture.touches[0], + *second = tp->gesture.touches[1]; + int dir1, dir2; + + delta = device_delta(first->point, second->point); + normalized = tp_normalize_delta(tp, delta); + + /* If fingers are further than 3 cm apart assume pinch */ + if (normalized_length(normalized) > TP_MM_TO_DPI_NORMALIZED(30)) { + tp_gesture_get_pinch_info(tp, + &tp->gesture.initial_distance, + &tp->gesture.angle, + &tp->gesture.center); + tp->gesture.prev_scale = 1.0; + return GESTURE_2FG_STATE_PINCH; + } + + /* Elif fingers have been close together for a while, scroll */ + if (time > (tp->gesture.initial_time + DEFAULT_GESTURE_2FG_SCROLL_TIMEOUT)) { + tp_gesture_set_scroll_buildup(tp); + return GESTURE_2FG_STATE_SCROLL; + } + + /* Else wait for both fingers to have moved */ + dir1 = tp_gesture_get_direction(tp, first); + dir2 = tp_gesture_get_direction(tp, second); + if (dir1 == UNDEFINED_DIRECTION || dir2 == UNDEFINED_DIRECTION) + return GESTURE_2FG_STATE_UNKNOWN; + + /* + * If both touches are moving in the same direction assume scroll. + * + * In some cases (semi-mt touchpads) We may seen one finger move + * e.g. N/NE and the other W/NW so we not only check for overlapping + * directions, but also for neighboring bits being set. + * The ((dira & 0x80) && (dirb & 0x01)) checks are to check for bit 0 + * and 7 being set as they also represent neighboring directions. + */ + if (((dir1 | (dir1 >> 1)) & dir2) || + ((dir2 | (dir2 >> 1)) & dir1) || + ((dir1 & 0x80) && (dir2 & 0x01)) || + ((dir2 & 0x80) && (dir1 & 0x01))) { + tp_gesture_set_scroll_buildup(tp); + return GESTURE_2FG_STATE_SCROLL; + } else { + tp_gesture_get_pinch_info(tp, + &tp->gesture.initial_distance, + &tp->gesture.angle, + &tp->gesture.center); + tp->gesture.prev_scale = 1.0; + return GESTURE_2FG_STATE_PINCH; + } +} + +static enum tp_gesture_2fg_state +tp_gesture_twofinger_handle_state_scroll(struct tp_dispatch *tp, uint64_t time) { struct normalized_coords delta; if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG) - return; + return GESTURE_2FG_STATE_SCROLL; /* On some semi-mt models slot 0 is more accurate, so for semi-mt * we only use slot 0. */ if (tp->semi_mt) { if (!tp->touches[0].dirty) - return; + return GESTURE_2FG_STATE_SCROLL; delta = tp_get_delta(&tp->touches[0]); } else { @@ -141,13 +329,72 @@ tp_gesture_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time) delta = tp_filter_motion(tp, &delta, time); if (normalized_is_zero(delta)) - return; + return GESTURE_2FG_STATE_SCROLL; tp_gesture_start(tp, time); evdev_post_scroll(tp->device, time, LIBINPUT_POINTER_AXIS_SOURCE_FINGER, &delta); + + return GESTURE_2FG_STATE_SCROLL; +} + +static enum tp_gesture_2fg_state +tp_gesture_twofinger_handle_state_pinch(struct tp_dispatch *tp, uint64_t time) +{ + double angle, angle_delta, distance, scale; + struct device_float_coords center, fdelta; + struct normalized_coords delta, unaccel; + + tp_gesture_get_pinch_info(tp, &distance, &angle, ¢er); + + scale = distance / tp->gesture.initial_distance; + + angle_delta = angle - tp->gesture.angle; + tp->gesture.angle = angle; + if (angle_delta > 180.0) + angle_delta -= 360.0; + else if (angle_delta < -180.0) + angle_delta += 360.0; + + fdelta = device_float_delta(center, tp->gesture.center); + tp->gesture.center = center; + unaccel = tp_normalize_delta(tp, fdelta); + delta = tp_filter_motion(tp, &unaccel, time); + + if (normalized_is_zero(delta) && normalized_is_zero(unaccel) && + scale == tp->gesture.prev_scale && angle_delta == 0.0) + return GESTURE_2FG_STATE_PINCH; + + tp_gesture_start(tp, time); + gesture_notify_pinch(&tp->device->base, time, + LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, + &delta, &unaccel, scale, angle_delta); + + tp->gesture.prev_scale = scale; + + return GESTURE_2FG_STATE_PINCH; +} + +static void +tp_gesture_post_twofinger(struct tp_dispatch *tp, uint64_t time) +{ + if (tp->gesture.twofinger_state == GESTURE_2FG_STATE_NONE) + tp->gesture.twofinger_state = + tp_gesture_twofinger_handle_state_none(tp, time); + + if (tp->gesture.twofinger_state == GESTURE_2FG_STATE_UNKNOWN) + tp->gesture.twofinger_state = + tp_gesture_twofinger_handle_state_unknown(tp, time); + + if (tp->gesture.twofinger_state == GESTURE_2FG_STATE_SCROLL) + tp->gesture.twofinger_state = + tp_gesture_twofinger_handle_state_scroll(tp, time); + + if (tp->gesture.twofinger_state == GESTURE_2FG_STATE_PINCH) + tp->gesture.twofinger_state = + tp_gesture_twofinger_handle_state_pinch(tp, time); } static void @@ -189,7 +436,7 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time) tp_gesture_post_pointer_motion(tp, time); break; case 2: - tp_gesture_post_twofinger_scroll(tp, time); + tp_gesture_post_twofinger(tp, time); break; case 3: case 4: @@ -212,14 +459,33 @@ tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time) void tp_gesture_stop(struct tp_dispatch *tp, uint64_t time) { + struct libinput *libinput = tp->device->base.seat->libinput; + enum tp_gesture_2fg_state twofinger_state = tp->gesture.twofinger_state; const struct normalized_coords zero = { 0.0, 0.0 }; + tp->gesture.twofinger_state = GESTURE_2FG_STATE_NONE; + if (!tp->gesture.started) return; switch (tp->gesture.finger_count) { case 2: - tp_gesture_stop_twofinger_scroll(tp, time); + switch (twofinger_state) { + case GESTURE_2FG_STATE_NONE: + case GESTURE_2FG_STATE_UNKNOWN: + log_bug_libinput(libinput, + "%s in unknown gesture mode\n", + __func__); + break; + case GESTURE_2FG_STATE_SCROLL: + tp_gesture_stop_twofinger_scroll(tp, time); + break; + case GESTURE_2FG_STATE_PINCH: + gesture_notify_pinch(&tp->device->base, time, + LIBINPUT_EVENT_GESTURE_PINCH_END, + &zero, &zero, 0.0, 0.0); + break; + } break; case 3: case 4: @@ -279,6 +545,8 @@ tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time) int tp_init_gesture(struct tp_dispatch *tp) { + tp->gesture.twofinger_state = GESTURE_2FG_STATE_NONE; + libinput_timer_init(&tp->gesture.finger_count_switch_timer, tp->device->base.seat->libinput, tp_gesture_finger_count_switch_timeout, tp); diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index edad6110..a9b75742 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -129,6 +129,13 @@ enum tp_edge_scroll_touch_state { EDGE_SCROLL_TOUCH_STATE_AREA, }; +enum tp_gesture_2fg_state { + GESTURE_2FG_STATE_NONE, + GESTURE_2FG_STATE_UNKNOWN, + GESTURE_2FG_STATE_SCROLL, + GESTURE_2FG_STATE_PINCH, +}; + struct tp_touch { struct tp_dispatch *tp; enum touch_state state; @@ -181,6 +188,10 @@ struct tp_touch { struct device_coords first; /* first coordinates if is_palm == true */ uint32_t time; /* first timestamp if is_palm == true */ } palm; + + struct { + struct device_coords initial; + } gesture; }; struct tp_dispatch { @@ -216,6 +227,13 @@ struct tp_dispatch { unsigned int finger_count; unsigned int finger_count_pending; struct libinput_timer finger_count_switch_timer; + enum tp_gesture_2fg_state twofinger_state; + struct tp_touch *touches[2]; + uint64_t initial_time; + double initial_distance; + double prev_scale; + double angle; + struct device_float_coords center; } gesture; struct { diff --git a/src/libinput-private.h b/src/libinput-private.h index 9a2643cf..67324aa3 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -410,6 +410,39 @@ device_delta(struct device_coords a, struct device_coords b) return delta; } +static inline struct device_float_coords +device_average(struct device_coords a, struct device_coords b) +{ + struct device_float_coords average; + + average.x = (a.x + b.x) / 2.0; + average.y = (a.y + b.y) / 2.0; + + return average; +} + +static inline struct device_float_coords +device_float_delta(struct device_float_coords a, struct device_float_coords b) +{ + struct device_float_coords delta; + + delta.x = a.x - b.x; + delta.y = a.y - b.y; + + return delta; +} + +static inline struct device_float_coords +device_float_average(struct device_float_coords a, struct device_float_coords b) +{ + struct device_float_coords average; + + average.x = (a.x + b.x) / 2.0; + average.y = (a.y + b.y) / 2.0; + + return average; +} + static inline double normalized_length(struct normalized_coords norm) { diff --git a/test/touchpad.c b/test/touchpad.c index 443c8c1f..1f11cfa6 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -1271,7 +1271,7 @@ START_TEST(touchpad_2fg_scroll_slow_distance) y_move = 7.0 * y->resolution / (y->maximum - y->minimum) * 100; } else { - y_move = 10.0; + y_move = 20.0; } litest_drain_events(li); @@ -1320,7 +1320,7 @@ START_TEST(touchpad_2fg_scroll_source) litest_drain_events(li); - test_2fg_scroll(dev, 0, 20, 0); + test_2fg_scroll(dev, 0, 30, 0); litest_wait_for_event_of_type(li, LIBINPUT_EVENT_POINTER_AXIS, -1); while ((event = libinput_get_event(li))) { From 9fae0f8c3e9e9be40d290efc9d4092d6da18881c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 29 Apr 2015 13:19:51 +0200 Subject: [PATCH 05/12] touchpad: Allow querying whether a gesture ended normally or was cancelled Signed-off-by: Hans de Goede --- src/evdev-mt-touchpad-gestures.c | 20 +++++++---------- src/evdev-mt-touchpad.c | 6 +++--- src/evdev-mt-touchpad.h | 2 +- src/libinput-private.h | 11 ++++++++++ src/libinput.c | 37 ++++++++++++++++++++++++++++++-- src/libinput.h | 17 +++++++++++++++ src/libinput.sym | 1 + tools/event-debug.c | 4 +++- 8 files changed, 79 insertions(+), 19 deletions(-) diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index ce52df22..840d9ad3 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -422,7 +422,7 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time) /* When tap-and-dragging, or a clickpad is clicked force 1fg mode */ if (tp_tap_dragging(tp) || (tp->buttons.is_clickpad && tp->buttons.state)) { - tp_gesture_stop(tp, time); + tp_gesture_stop(tp, time, 1); tp->gesture.finger_count = 1; tp->gesture.finger_count_pending = 0; } @@ -457,11 +457,10 @@ tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time) } void -tp_gesture_stop(struct tp_dispatch *tp, uint64_t time) +tp_gesture_stop(struct tp_dispatch *tp, uint64_t time, int cancelled) { struct libinput *libinput = tp->device->base.seat->libinput; enum tp_gesture_2fg_state twofinger_state = tp->gesture.twofinger_state; - const struct normalized_coords zero = { 0.0, 0.0 }; tp->gesture.twofinger_state = GESTURE_2FG_STATE_NONE; @@ -481,18 +480,15 @@ tp_gesture_stop(struct tp_dispatch *tp, uint64_t time) tp_gesture_stop_twofinger_scroll(tp, time); break; case GESTURE_2FG_STATE_PINCH: - gesture_notify_pinch(&tp->device->base, time, - LIBINPUT_EVENT_GESTURE_PINCH_END, - &zero, &zero, 0.0, 0.0); + gesture_notify_pinch_end(&tp->device->base, time, + cancelled); break; } break; case 3: case 4: - gesture_notify_swipe(&tp->device->base, time, - LIBINPUT_EVENT_GESTURE_SWIPE_END, - tp->gesture.finger_count, - &zero, &zero); + gesture_notify_swipe_end(&tp->device->base, time, + tp->gesture.finger_count, cancelled); break; } tp->gesture.started = false; @@ -506,7 +502,7 @@ tp_gesture_finger_count_switch_timeout(uint64_t now, void *data) if (!tp->gesture.finger_count_pending) return; - tp_gesture_stop(tp, now); /* End current gesture */ + tp_gesture_stop(tp, now, 1); /* End current gesture */ tp->gesture.finger_count = tp->gesture.finger_count_pending; tp->gesture.finger_count_pending = 0; } @@ -524,7 +520,7 @@ tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time) if (active_touches != tp->gesture.finger_count) { /* If all fingers are lifted immediately end the gesture */ if (active_touches == 0) { - tp_gesture_stop(tp, time); + tp_gesture_stop(tp, time, 0); tp->gesture.finger_count = 0; tp->gesture.finger_count_pending = 0; /* Immediately switch to new mode to avoid initial latency */ diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 24743406..fac2e4df 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -797,7 +797,7 @@ tp_post_events(struct tp_dispatch *tp, uint64_t time) tp->palm.trackpoint_active || tp->dwt.keyboard_active) { tp_edge_scroll_stop_events(tp, time); - tp_gesture_stop(tp, time); + tp_gesture_stop(tp, time, 1); return; } @@ -976,7 +976,7 @@ tp_trackpoint_event(uint64_t time, struct libinput_event *event, void *data) if (!tp->palm.trackpoint_active) { tp_edge_scroll_stop_events(tp, time); - tp_gesture_stop(tp, time); + tp_gesture_stop(tp, time, 1); tp_tap_suspend(tp, time); tp->palm.trackpoint_active = true; } @@ -1053,7 +1053,7 @@ tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data) if (!tp->dwt.keyboard_active) { tp_edge_scroll_stop_events(tp, time); - tp_gesture_stop(tp, time); + tp_gesture_stop(tp, time, 1); tp_tap_suspend(tp, time); tp->dwt.keyboard_active = true; timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1; diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index a9b75742..b8a8712c 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -447,7 +447,7 @@ void tp_remove_gesture(struct tp_dispatch *tp); void -tp_gesture_stop(struct tp_dispatch *tp, uint64_t time); +tp_gesture_stop(struct tp_dispatch *tp, uint64_t time, int cancelled); void tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time); diff --git a/src/libinput-private.h b/src/libinput-private.h index 67324aa3..3fa6d26a 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -373,6 +373,12 @@ gesture_notify_swipe(struct libinput_device *device, const struct normalized_coords *delta, const struct normalized_coords *unaccel); +void +gesture_notify_swipe_end(struct libinput_device *device, + uint64_t time, + int finger_count, + int cancelled); + void gesture_notify_pinch(struct libinput_device *device, uint64_t time, @@ -382,6 +388,11 @@ gesture_notify_pinch(struct libinput_device *device, double scale, double angle); +void +gesture_notify_pinch_end(struct libinput_device *device, + uint64_t time, + int cancelled); + void touch_notify_frame(struct libinput_device *device, uint64_t time); diff --git a/src/libinput.c b/src/libinput.c index 84e623f1..16c3c4f4 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -118,6 +118,7 @@ struct libinput_event_gesture { struct libinput_event base; uint32_t time; int finger_count; + int cancelled; struct normalized_coords delta; struct normalized_coords delta_unaccel; double scale; @@ -675,6 +676,12 @@ libinput_event_gesture_get_finger_count(struct libinput_event_gesture *event) return event->finger_count; } +LIBINPUT_EXPORT int +libinput_event_gesture_get_cancelled(struct libinput_event_gesture *event) +{ + return event->cancelled; +} + LIBINPUT_EXPORT double libinput_event_gesture_get_dx(struct libinput_event_gesture *event) { @@ -1426,6 +1433,7 @@ gesture_notify(struct libinput_device *device, uint64_t time, enum libinput_event_type type, int finger_count, + int cancelled, const struct normalized_coords *delta, const struct normalized_coords *unaccel, double scale, @@ -1443,6 +1451,7 @@ gesture_notify(struct libinput_device *device, *gesture_event = (struct libinput_event_gesture) { .time = time, .finger_count = finger_count, + .cancelled = cancelled, .delta = *delta, .delta_unaccel = *unaccel, .scale = scale, @@ -1461,10 +1470,22 @@ gesture_notify_swipe(struct libinput_device *device, const struct normalized_coords *delta, const struct normalized_coords *unaccel) { - gesture_notify(device, time, type, finger_count, delta, unaccel, + gesture_notify(device, time, type, finger_count, 0, delta, unaccel, 0.0, 0.0); } +void +gesture_notify_swipe_end(struct libinput_device *device, + uint64_t time, + int finger_count, + int cancelled) +{ + const struct normalized_coords zero = { 0.0, 0.0 }; + + gesture_notify(device, time, LIBINPUT_EVENT_GESTURE_SWIPE_END, + finger_count, cancelled, &zero, &zero, 0.0, 0.0); +} + void gesture_notify_pinch(struct libinput_device *device, uint64_t time, @@ -1474,7 +1495,19 @@ gesture_notify_pinch(struct libinput_device *device, double scale, double angle) { - gesture_notify(device, time, type, 2, delta, unaccel, scale, angle); + gesture_notify(device, time, type, 2, 0, delta, unaccel, + scale, angle); +} + +void +gesture_notify_pinch_end(struct libinput_device *device, + uint64_t time, + int cancelled) +{ + const struct normalized_coords zero = { 0.0, 0.0 }; + + gesture_notify(device, time, LIBINPUT_EVENT_GESTURE_PINCH_END, + 2, cancelled, &zero, &zero, 0.0, 0.0); } static void diff --git a/src/libinput.h b/src/libinput.h index e1a2dd7e..a7164d3f 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -987,6 +987,23 @@ libinput_event_gesture_get_base_event(struct libinput_event_gesture *event); int libinput_event_gesture_get_finger_count(struct libinput_event_gesture *event); +/** + * @ingroup event_gesture + * + * Return if the gesture ended normally, or if it was cancelled. + * For gesture events that are not of type + * @ref LIBINPUT_EVENT_GESTURE_SWIPE_END or + * @ref LIBINPUT_EVENT_GESTURE_PINCH_END, this function returns 0. + * + * @note It is an application bug to call this function for events other than + * @ref LIBINPUT_EVENT_GESTURE_SWIPE_END or + * @ref LIBINPUT_EVENT_GESTURE_PINCH_END. + * + * @return 0 or 1, with 1 indicating that the gesture was cancelled. + */ +int +libinput_event_gesture_get_cancelled(struct libinput_event_gesture *event); + /** * @ingroup event_gesture * diff --git a/src/libinput.sym b/src/libinput.sym index 0060ba87..1f8b9d40 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -150,6 +150,7 @@ LIBINPUT_0.19.0 { TOUCHPAD_GESTURES { libinput_event_gesture_get_angle_delta; libinput_event_gesture_get_base_event; + libinput_event_gesture_get_cancelled; libinput_event_gesture_get_dx; libinput_event_gesture_get_dx_unaccelerated; libinput_event_gesture_get_dy; diff --git a/tools/event-debug.c b/tools/event-debug.c index 1902dd35..38ded5ee 100644 --- a/tools/event-debug.c +++ b/tools/event-debug.c @@ -306,9 +306,11 @@ static void print_gesture_event_without_coords(struct libinput_event *ev) { struct libinput_event_gesture *t = libinput_event_get_gesture_event(ev); + int finger_count = libinput_event_gesture_get_finger_count(t); + int cancelled = libinput_event_gesture_get_cancelled(t); print_event_time(libinput_event_gesture_get_time(t)); - printf("%d\n", libinput_event_gesture_get_finger_count(t)); + printf("%d%s\n", finger_count, cancelled ? " cancelled" : ""); } static void From 7f6fe7351dcb91fe7dd783f762ba48d6546d1815 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 22 May 2015 10:09:22 +1000 Subject: [PATCH 06/12] touchpad: split tp_gesture_stop into stop and cancel No functional changes Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-gestures.c | 22 +++++++++++++++++----- src/evdev-mt-touchpad.c | 6 +++--- src/evdev-mt-touchpad.h | 5 ++++- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index 840d9ad3..90835d72 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -422,7 +422,7 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time) /* When tap-and-dragging, or a clickpad is clicked force 1fg mode */ if (tp_tap_dragging(tp) || (tp->buttons.is_clickpad && tp->buttons.state)) { - tp_gesture_stop(tp, time, 1); + tp_gesture_cancel(tp, time); tp->gesture.finger_count = 1; tp->gesture.finger_count_pending = 0; } @@ -456,8 +456,8 @@ tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time) LIBINPUT_POINTER_AXIS_SOURCE_FINGER); } -void -tp_gesture_stop(struct tp_dispatch *tp, uint64_t time, int cancelled) +static void +tp_gesture_end(struct tp_dispatch *tp, uint64_t time, bool cancelled) { struct libinput *libinput = tp->device->base.seat->libinput; enum tp_gesture_2fg_state twofinger_state = tp->gesture.twofinger_state; @@ -494,6 +494,18 @@ tp_gesture_stop(struct tp_dispatch *tp, uint64_t time, int cancelled) tp->gesture.started = false; } +void +tp_gesture_cancel(struct tp_dispatch *tp, uint64_t time) +{ + tp_gesture_end(tp, time, true); +} + +void +tp_gesture_stop(struct tp_dispatch *tp, uint64_t time) +{ + tp_gesture_end(tp, time, false); +} + static void tp_gesture_finger_count_switch_timeout(uint64_t now, void *data) { @@ -502,7 +514,7 @@ tp_gesture_finger_count_switch_timeout(uint64_t now, void *data) if (!tp->gesture.finger_count_pending) return; - tp_gesture_stop(tp, now, 1); /* End current gesture */ + tp_gesture_cancel(tp, now); /* End current gesture */ tp->gesture.finger_count = tp->gesture.finger_count_pending; tp->gesture.finger_count_pending = 0; } @@ -520,7 +532,7 @@ tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time) if (active_touches != tp->gesture.finger_count) { /* If all fingers are lifted immediately end the gesture */ if (active_touches == 0) { - tp_gesture_stop(tp, time, 0); + tp_gesture_stop(tp, time); tp->gesture.finger_count = 0; tp->gesture.finger_count_pending = 0; /* Immediately switch to new mode to avoid initial latency */ diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index fac2e4df..e90bd1e4 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -797,7 +797,7 @@ tp_post_events(struct tp_dispatch *tp, uint64_t time) tp->palm.trackpoint_active || tp->dwt.keyboard_active) { tp_edge_scroll_stop_events(tp, time); - tp_gesture_stop(tp, time, 1); + tp_gesture_cancel(tp, time); return; } @@ -976,7 +976,7 @@ tp_trackpoint_event(uint64_t time, struct libinput_event *event, void *data) if (!tp->palm.trackpoint_active) { tp_edge_scroll_stop_events(tp, time); - tp_gesture_stop(tp, time, 1); + tp_gesture_cancel(tp, time); tp_tap_suspend(tp, time); tp->palm.trackpoint_active = true; } @@ -1053,7 +1053,7 @@ tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data) if (!tp->dwt.keyboard_active) { tp_edge_scroll_stop_events(tp, time); - tp_gesture_stop(tp, time, 1); + tp_gesture_cancel(tp, time); tp_tap_suspend(tp, time); tp->dwt.keyboard_active = true; timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1; diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index b8a8712c..df8be94f 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -447,7 +447,10 @@ void tp_remove_gesture(struct tp_dispatch *tp); void -tp_gesture_stop(struct tp_dispatch *tp, uint64_t time, int cancelled); +tp_gesture_stop(struct tp_dispatch *tp, uint64_t time); + +void +tp_gesture_cancel(struct tp_dispatch *tp, uint64_t time); void tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time); From 9b938d65910b31dde8e405e599e7e7da21c7b174 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 22 May 2015 10:58:56 +1000 Subject: [PATCH 07/12] gestures: check for valid types on the gesture event API Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-gestures.c | 3 +- src/libinput-private.h | 1 + src/libinput.c | 65 +++++++++++++++++++++++++++++++- src/libinput.h | 13 ++++++- 4 files changed, 78 insertions(+), 4 deletions(-) diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index 90835d72..cd853ecf 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -97,7 +97,7 @@ tp_gesture_start(struct tp_dispatch *tp, uint64_t time) case GESTURE_2FG_STATE_PINCH: gesture_notify_pinch(&tp->device->base, time, LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, - &zero, &zero, 0.0, 0.0); + &zero, &zero, 1.0, 0.0); break; } break; @@ -481,6 +481,7 @@ tp_gesture_end(struct tp_dispatch *tp, uint64_t time, bool cancelled) break; case GESTURE_2FG_STATE_PINCH: gesture_notify_pinch_end(&tp->device->base, time, + tp->gesture.prev_scale, cancelled); break; } diff --git a/src/libinput-private.h b/src/libinput-private.h index 3fa6d26a..d9dcffc8 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -391,6 +391,7 @@ gesture_notify_pinch(struct libinput_device *device, void gesture_notify_pinch_end(struct libinput_device *device, uint64_t time, + double scale, int cancelled); void diff --git a/src/libinput.c b/src/libinput.c index 16c3c4f4..93a6eb83 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -667,30 +667,68 @@ libinput_event_touch_get_y(struct libinput_event_touch *event) LIBINPUT_EXPORT uint32_t libinput_event_gesture_get_time(struct libinput_event_gesture *event) { + require_event_type(libinput_event_get_context(&event->base), + event->base.type, + 0, + LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, + LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, + LIBINPUT_EVENT_GESTURE_PINCH_END, + LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, + LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, + LIBINPUT_EVENT_GESTURE_SWIPE_END); + return event->time; } LIBINPUT_EXPORT int libinput_event_gesture_get_finger_count(struct libinput_event_gesture *event) { + require_event_type(libinput_event_get_context(&event->base), + event->base.type, + 0, + LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, + LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, + LIBINPUT_EVENT_GESTURE_PINCH_END, + LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, + LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, + LIBINPUT_EVENT_GESTURE_SWIPE_END); + return event->finger_count; } LIBINPUT_EXPORT int libinput_event_gesture_get_cancelled(struct libinput_event_gesture *event) { + require_event_type(libinput_event_get_context(&event->base), + event->base.type, + 0, + LIBINPUT_EVENT_GESTURE_PINCH_END, + LIBINPUT_EVENT_GESTURE_SWIPE_END); + return event->cancelled; } LIBINPUT_EXPORT double libinput_event_gesture_get_dx(struct libinput_event_gesture *event) { + require_event_type(libinput_event_get_context(&event->base), + event->base.type, + 0.0, + LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, + LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE); + return event->delta.x; } LIBINPUT_EXPORT double libinput_event_gesture_get_dy(struct libinput_event_gesture *event) { + require_event_type(libinput_event_get_context(&event->base), + event->base.type, + 0.0, + LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, + LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE); + return event->delta.y; } @@ -698,6 +736,12 @@ LIBINPUT_EXPORT double libinput_event_gesture_get_dx_unaccelerated( struct libinput_event_gesture *event) { + require_event_type(libinput_event_get_context(&event->base), + event->base.type, + 0.0, + LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, + LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE); + return event->delta_unaccel.x; } @@ -705,18 +749,36 @@ LIBINPUT_EXPORT double libinput_event_gesture_get_dy_unaccelerated( struct libinput_event_gesture *event) { + require_event_type(libinput_event_get_context(&event->base), + event->base.type, + 0.0, + LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, + LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE); + return event->delta_unaccel.y; } LIBINPUT_EXPORT double libinput_event_gesture_get_scale(struct libinput_event_gesture *event) { + require_event_type(libinput_event_get_context(&event->base), + event->base.type, + 0.0, + LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, + LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, + LIBINPUT_EVENT_GESTURE_PINCH_END); + return event->scale; } LIBINPUT_EXPORT double libinput_event_gesture_get_angle_delta(struct libinput_event_gesture *event) { + require_event_type(libinput_event_get_context(&event->base), + event->base.type, + 0.0, + LIBINPUT_EVENT_GESTURE_PINCH_UPDATE); + return event->angle; } @@ -1502,12 +1564,13 @@ gesture_notify_pinch(struct libinput_device *device, void gesture_notify_pinch_end(struct libinput_device *device, uint64_t time, + double scale, int cancelled) { const struct normalized_coords zero = { 0.0, 0.0 }; gesture_notify(device, time, LIBINPUT_EVENT_GESTURE_PINCH_END, - 2, cancelled, &zero, &zero, 0.0, 0.0); + 2, cancelled, &zero, &zero, scale, 0.0); } static void diff --git a/src/libinput.h b/src/libinput.h index a7164d3f..15d70fbf 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -1103,10 +1103,19 @@ libinput_event_gesture_get_dy_unaccelerated( * together by 50% then the scale will become 0.5, if they move twice as far * apart as initially the scale becomes 2.0, etc. * - * For gesture events that are not of type @ref - * LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, this function returns 0. + * For gesture events that are of type @ref + * LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, this function returns 1.0. + * + * For gesture events that are of type @ref + * LIBINPUT_EVENT_GESTURE_PINCH_END, this function returns the scale value + * of the most recent @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE event (if + * any) or 1.0 otherwise. + * + * For all other events this function returns 0. * * @note It is an application bug to call this function for events other than + * @ref LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, @ref + * LIBINPUT_EVENT_GESTURE_PINCH_END or * @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE. * * @return the absolute scale of a pinch gesture From a29155c9cb9d0decd56518c9cf6d2ede34264ae6 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 9 Jun 2015 14:23:07 +1000 Subject: [PATCH 08/12] gestures: allow any gesture event type for gesture_get_dx/dy and get_angle For start/end, dx/dy is always 0.0, and there is no need to make calling this function for start/end a caller bug. It just unnecessarily complicates the caller's codepath. Same for get_angle Signed-off-by: Peter Hutterer --- src/libinput.c | 28 +++++++++++++++++++++++----- src/libinput.h | 19 ------------------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/libinput.c b/src/libinput.c index 93a6eb83..563ad0d3 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -714,8 +714,12 @@ libinput_event_gesture_get_dx(struct libinput_event_gesture *event) require_event_type(libinput_event_get_context(&event->base), event->base.type, 0.0, + LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, - LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE); + LIBINPUT_EVENT_GESTURE_PINCH_END, + LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, + LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, + LIBINPUT_EVENT_GESTURE_SWIPE_END); return event->delta.x; } @@ -726,8 +730,12 @@ libinput_event_gesture_get_dy(struct libinput_event_gesture *event) require_event_type(libinput_event_get_context(&event->base), event->base.type, 0.0, + LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, - LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE); + LIBINPUT_EVENT_GESTURE_PINCH_END, + LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, + LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, + LIBINPUT_EVENT_GESTURE_SWIPE_END); return event->delta.y; } @@ -739,8 +747,12 @@ libinput_event_gesture_get_dx_unaccelerated( require_event_type(libinput_event_get_context(&event->base), event->base.type, 0.0, + LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, - LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE); + LIBINPUT_EVENT_GESTURE_PINCH_END, + LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, + LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, + LIBINPUT_EVENT_GESTURE_SWIPE_END); return event->delta_unaccel.x; } @@ -752,8 +764,12 @@ libinput_event_gesture_get_dy_unaccelerated( require_event_type(libinput_event_get_context(&event->base), event->base.type, 0.0, + LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, - LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE); + LIBINPUT_EVENT_GESTURE_PINCH_END, + LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, + LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, + LIBINPUT_EVENT_GESTURE_SWIPE_END); return event->delta_unaccel.y; } @@ -777,7 +793,9 @@ libinput_event_gesture_get_angle_delta(struct libinput_event_gesture *event) require_event_type(libinput_event_get_context(&event->base), event->base.type, 0.0, - LIBINPUT_EVENT_GESTURE_PINCH_UPDATE); + LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, + LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, + LIBINPUT_EVENT_GESTURE_PINCH_END); return event->angle; } diff --git a/src/libinput.h b/src/libinput.h index 15d70fbf..d23f8fdb 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -1017,10 +1017,6 @@ libinput_event_gesture_get_cancelled(struct libinput_event_gesture *event); * Relative motion deltas are normalized to represent those of a device with * 1000dpi resolution. See @ref motion_normalization for more details. * - * @note It is an application bug to call this function for events other than - * @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE or - * @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE. - * * @return the relative x movement since the last event */ double @@ -1039,10 +1035,6 @@ libinput_event_gesture_get_dx(struct libinput_event_gesture *event); * Relative motion deltas are normalized to represent those of a device with * 1000dpi resolution. See @ref motion_normalization for more details. * - * @note It is an application bug to call this function for events other than - * @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE or - * @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE. - * * @return the relative y movement since the last event */ double @@ -1061,10 +1053,6 @@ libinput_event_gesture_get_dy(struct libinput_event_gesture *event); * details. Note that unaccelerated events are not equivalent to 'raw' events * as read from the device. * - * @note It is an application bug to call this function for events other than - * @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE or - * @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE. - * * @return the unaccelerated relative x movement since the last event */ double @@ -1084,10 +1072,6 @@ libinput_event_gesture_get_dx_unaccelerated( * details. Note that unaccelerated events are not equivalent to 'raw' events * as read from the device. * - * @note It is an application bug to call this function for events other than - * @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE or - * @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE. - * * @return the unaccelerated relative y movement since the last event */ double @@ -1142,9 +1126,6 @@ libinput_event_gesture_get_scale(struct libinput_event_gesture *event); * around the center of gravity. The calculation of the center of gravity is * implementation-dependent. * - * @note It is an application bug to call this function for events other than - * @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE. - * * @return the angle delta since the last event */ double From f3f2b312ead5516fdf06ec6bf925e577a1fedbed Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 22 May 2015 10:40:16 +1000 Subject: [PATCH 09/12] tools: add swipe support to the event-gui Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- tools/event-gui.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/tools/event-gui.c b/tools/event-gui.c index 3806014b..0c484cb6 100644 --- a/tools/event-gui.c +++ b/tools/event-gui.c @@ -72,6 +72,12 @@ struct window { /* l/m/r mouse buttons */ int l, m, r; + /* touchpad swipe */ + struct { + int nfingers; + double x, y; + } swipe; + struct libinput_device *devices[50]; }; @@ -104,11 +110,28 @@ draw(GtkWidget *widget, cairo_t *cr, gpointer data) { struct window *w = data; struct touch *t; + int i; cairo_set_source_rgb(cr, 1, 1, 1); cairo_rectangle(cr, 0, 0, w->width, w->height); cairo_fill(cr); + /* swipe */ + cairo_save(cr); + cairo_translate(cr, w->swipe.x, w->swipe.y); + for (i = 0; i < w->swipe.nfingers; i++) { + cairo_set_source_rgb(cr, .8, .8, .4); + cairo_arc(cr, (i - 2) * 40, 0, 20, 0, 2 * M_PI); + cairo_fill(cr); + } + + for (i = 0; i < 4; i++) { /* 4 fg max */ + cairo_set_source_rgb(cr, 0, 0, 0); + cairo_arc(cr, (i - 2) * 40, 0, 20, 0, 2 * M_PI); + cairo_stroke(cr); + } + cairo_restore(cr); + /* draw pointer sprite */ cairo_set_source_rgb(cr, 0, 0, 0); cairo_save(cr); @@ -186,6 +209,9 @@ map_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data) w->hx = w->width/2; w->hy = w->height/2; + w->swipe.x = w->width/2; + w->swipe.y = w->height/2; + g_signal_connect(G_OBJECT(w->area), "draw", G_CALLBACK(draw), w); window = gdk_event_get_window(event); @@ -428,6 +454,37 @@ handle_event_button(struct libinput_event *ev, struct window *w) } +static void +handle_event_swipe(struct libinput_event *ev, struct window *w) +{ + struct libinput_event_gesture *g = libinput_event_get_gesture_event(ev); + int nfingers; + double dx, dy; + + nfingers = libinput_event_gesture_get_finger_count(g); + + switch (libinput_event_get_type(ev)) { + case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN: + w->swipe.nfingers = nfingers; + w->swipe.x = w->width/2; + w->swipe.y = w->height/2; + break; + case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE: + dx = libinput_event_gesture_get_dx(g); + dy = libinput_event_gesture_get_dy(g); + w->swipe.x += dx; + w->swipe.y += dy; + break; + case LIBINPUT_EVENT_GESTURE_SWIPE_END: + w->swipe.nfingers = 0; + w->swipe.x = w->width/2; + w->swipe.y = w->height/2; + break; + default: + abort(); + } +} + static gboolean handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data) { @@ -476,6 +533,8 @@ handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data) case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN: case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE: case LIBINPUT_EVENT_GESTURE_SWIPE_END: + handle_event_swipe(ev, w); + break; case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN: case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE: case LIBINPUT_EVENT_GESTURE_PINCH_END: From bca183ec8e0ecec7040fdbe620a3ec42e9a597ba Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 22 May 2015 11:14:41 +1000 Subject: [PATCH 10/12] tools: handle pinch event in event-gui Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- tools/event-gui.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/tools/event-gui.c b/tools/event-gui.c index 0c484cb6..058c42dc 100644 --- a/tools/event-gui.c +++ b/tools/event-gui.c @@ -78,6 +78,13 @@ struct window { double x, y; } swipe; + struct { + int nfingers; + double scale; + double angle; + double x, y; + } pinch; + struct libinput_device *devices[50]; }; @@ -110,7 +117,7 @@ draw(GtkWidget *widget, cairo_t *cr, gpointer data) { struct window *w = data; struct touch *t; - int i; + int i, offset; cairo_set_source_rgb(cr, 1, 1, 1); cairo_rectangle(cr, 0, 0, w->width, w->height); @@ -132,6 +139,26 @@ draw(GtkWidget *widget, cairo_t *cr, gpointer data) } cairo_restore(cr); + /* pinch */ + cairo_save(cr); + offset = w->pinch.scale * 100; + cairo_translate(cr, w->pinch.x, w->pinch.y); + cairo_rotate(cr, w->pinch.angle * M_PI/180.0); + if (w->pinch.nfingers > 0) { + cairo_set_source_rgb(cr, .4, .4, .8); + cairo_arc(cr, offset, -offset, 20, 0, 2 * M_PI); + cairo_arc(cr, -offset, offset, 20, 0, 2 * M_PI); + cairo_fill(cr); + } + + cairo_set_source_rgb(cr, 0, 0, 0); + cairo_arc(cr, offset, -offset, 20, 0, 2 * M_PI); + cairo_stroke(cr); + cairo_arc(cr, -offset, offset, 20, 0, 2 * M_PI); + cairo_stroke(cr); + + cairo_restore(cr); + /* draw pointer sprite */ cairo_set_source_rgb(cr, 0, 0, 0); cairo_save(cr); @@ -212,6 +239,10 @@ map_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data) w->swipe.x = w->width/2; w->swipe.y = w->height/2; + w->pinch.scale = 1.0; + w->pinch.x = w->width/2; + w->pinch.y = w->height/2; + g_signal_connect(G_OBJECT(w->area), "draw", G_CALLBACK(draw), w); window = gdk_event_get_window(event); @@ -485,6 +516,41 @@ handle_event_swipe(struct libinput_event *ev, struct window *w) } } +static void +handle_event_pinch(struct libinput_event *ev, struct window *w) +{ + struct libinput_event_gesture *g = libinput_event_get_gesture_event(ev); + int nfingers; + double dx, dy; + + nfingers = libinput_event_gesture_get_finger_count(g); + + switch (libinput_event_get_type(ev)) { + case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN: + w->pinch.nfingers = nfingers; + w->pinch.x = w->width/2; + w->pinch.y = w->height/2; + break; + case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE: + dx = libinput_event_gesture_get_dx(g); + dy = libinput_event_gesture_get_dy(g); + w->pinch.x += dx; + w->pinch.y += dy; + w->pinch.scale = libinput_event_gesture_get_scale(g); + w->pinch.angle += libinput_event_gesture_get_angle_delta(g); + break; + case LIBINPUT_EVENT_GESTURE_PINCH_END: + w->pinch.nfingers = 0; + w->pinch.x = w->width/2; + w->pinch.y = w->height/2; + w->pinch.angle = 0.0; + w->pinch.scale = 1.0; + break; + default: + abort(); + } +} + static gboolean handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data) { @@ -538,6 +604,7 @@ handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data) case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN: case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE: case LIBINPUT_EVENT_GESTURE_PINCH_END: + handle_event_pinch(ev, w); break; } From 89d3b7bc58fd6908712a8230cd6c3e71de46c79e Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 11 Jun 2015 10:45:22 +1000 Subject: [PATCH 11/12] doc: add documentation for touchpad gestures Signed-off-by: Peter Hutterer --- doc/Makefile.am | 4 + doc/gestures.dox | 91 +++++ doc/svg/pinch-gestures.svg | 612 +++++++++++++++++++++++++++++++ doc/svg/swipe-gestures.svg | 512 ++++++++++++++++++++++++++ doc/svg/touchscreen-gestures.svg | 440 ++++++++++++++++++++++ src/libinput.h | 2 + 6 files changed, 1661 insertions(+) create mode 100644 doc/gestures.dox create mode 100644 doc/svg/pinch-gestures.svg create mode 100644 doc/svg/swipe-gestures.svg create mode 100644 doc/svg/touchscreen-gestures.svg diff --git a/doc/Makefile.am b/doc/Makefile.am index 58ae2161..efd4d857 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -14,6 +14,7 @@ header_files = \ $(srcdir)/clickpad-softbuttons.dox \ $(srcdir)/device-configuration-via-udev.dox \ $(srcdir)/faqs.dox \ + $(srcdir)/gestures.dox \ $(srcdir)/normalization-of-relative-motion.dox \ $(srcdir)/palm-detection.dox \ $(srcdir)/scrolling.dox \ @@ -33,8 +34,11 @@ diagram_files = \ $(srcdir)/svg/button-scrolling.svg \ $(srcdir)/svg/edge-scrolling.svg \ $(srcdir)/svg/palm-detection.svg \ + $(srcdir)/svg/pinch-gestures.svg \ + $(srcdir)/svg/swipe-gestures.svg \ $(srcdir)/svg/tap-n-drag.svg \ $(srcdir)/svg/top-software-buttons.svg \ + $(srcdir)/svg/touchscreen-gestures.svg \ $(srcdir)/svg/twofinger-scrolling.svg html/index.html: libinput.doxygen $(header_files) $(diagram_files) diff --git a/doc/gestures.dox b/doc/gestures.dox new file mode 100644 index 00000000..a8cef36d --- /dev/null +++ b/doc/gestures.dox @@ -0,0 +1,91 @@ +/** +@page gestures Gestures + +libinput supports basic gestures on touchpads and other indirect input +devices. Two types of gestures are supported: @ref gestures_pinch and @ref +gestures_swipe. Support for gestures depends on the hardware device, most +touchpads support both gestures and any device that may send gesture events +has the @ref LIBINPUT_DEVICE_CAP_GESTURE capability set. + +Note that libinput **does not** support gestures on touchscreens, see +@ref gestures_touchscreens. + +@section gestures_lifetime Lifetime of a gesture + +A gesture's lifetime has three distinct stages: begin, update and end, each +with their own event types. Begin is sent when the fingers are first set +down or libinput decides that the gesture begins. For @ref gestures_pinch +this sets the initial scale. Any events changing properties of the gesture +are sent as update events. On termination of the gesture, an end event is +sent. + +A gesture includes the finger count (see +libinput_event_gesture_get_finger_count()) and that finger count remains the +same for the lifetime of a gesture. Thus, if a user puts down a fourth +finger during a three-finger swipe gesture, libinput will end +the three-finger gesture and, if applicable, start a four-finger swipe +gesture. A caller may decide that those gestures are semantically identical +and continue the two gestures as one single gesture. + +@see LIBINPUT_EVENT_GESTURE_PINCH_BEGIN +@see LIBINPUT_EVENT_GESTURE_PINCH_UPDATE +@see LIBINPUT_EVENT_GESTURE_PINCH_END +@see LIBINPUT_EVENT_GESTURE_PINCH_BEGIN +@see LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE +@see LIBINPUT_EVENT_GESTURE_SWIPE_END + +@section gestures_pinch Pinch gestures + +Pinch gestures are executed when two or more fingers are located on the +touchpad and are either changing the relative distance to each other +(pinching) or are changing the relative angle (rotate). Pinch gestures may +change both rotation and distance at the same time. For such gestures, +libinput calculates a logical center for the gestures and provides the +caller with the delta x/y coordinates of that center, the relative angle of +the fingers compared to the previous event, and the absolute scale compared +to the initial finger position. + +@image html pinch-gestures.svg "The pinch and rotate gestures" + +The illustration above shows a basic pinch in the left image and a rotate in +the right angle. Not shown is a movement of the logical center if the +fingers move unevenly. Such a movement is supported by libinput, it is +merely left out of the illustration. + +Note that while position and angle is relative to the previous event, the +scale is always absolute and a multiplier of the initial finger position's +scale. + +@section gestures_swipe Swipe gestures + +Swipe gestures are executed when three or more fingers are moved +synchronously in the same direction. libinput provides x and y coordinates +in the gesture and thus allows swipe gestures in any direction, including +the tracing of complex paths. It is up to the caller to interpret the +gesture into an action or limit a gesture to specific directions only. + +@image html swipe-gestures.svg "The swipe gestures" + +The illustration above shows a vertical three-finger swipe. The coordinates +provided during the gesture are the movements of the logical center. + +@section gestures_touchscreens Touchscreen gestures + +Touchscreen gestures are **not** interpreted by libinput. Rather, any touch +point is passed to the caller and any interpretation of gestures is up to +the caller or, eventually, the X or Wayland client. + +Interpreting gestures on a touchscreen requires context that libinput does +not have, such as the location of windows and other virtual objects on the +screen as well as the context of those virtual objects: + +@image html touchscreen-gestures.svg "Context-sensitivity of touchscreen gestures" + +In this example, the finger movements are identical but in the left case +both fingers are located within the same window, thus suggesting an attemp +to zoom. In the right case both fingers are located on a window border, +thus suggesting a window movement. libinput only has knowledge of the finger +coordinates (and even then only in device coordinates, not in screen +coordinates) and thus cannot differentiate the two. + +*/ diff --git a/doc/svg/pinch-gestures.svg b/doc/svg/pinch-gestures.svg new file mode 100644 index 00000000..4dca4cf4 --- /dev/null +++ b/doc/svg/pinch-gestures.svg @@ -0,0 +1,612 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + logicalcenter + initial scale (1.0) + current scale (> 1.0) + + logicalcenter + + + + + deltaangle + + diff --git a/doc/svg/swipe-gestures.svg b/doc/svg/swipe-gestures.svg new file mode 100644 index 00000000..a88f646b --- /dev/null +++ b/doc/svg/swipe-gestures.svg @@ -0,0 +1,512 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + logicalcenter + + + + + + + diff --git a/doc/svg/touchscreen-gestures.svg b/doc/svg/touchscreen-gestures.svg new file mode 100644 index 00000000..8452c7e0 --- /dev/null +++ b/doc/svg/touchscreen-gestures.svg @@ -0,0 +1,440 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libinput.h b/src/libinput.h index d23f8fdb..3595fd1d 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -955,6 +955,8 @@ libinput_event_touch_get_base_event(struct libinput_event_touch *event); * LIBINPUT_EVENT_GESTURE_FOO_UPDATE type until a * LIBINPUT_EVENT_GESTURE_FOO_END is generated which signals the end of the * gesture. + * + * See @ref gestures for more information on gesture handling. */ /** From 31df68c1e4eb8d18fd1c2eace5ea24f4638afaa3 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 6 Jul 2015 14:10:48 +1000 Subject: [PATCH 12/12] libinput.sym: make the touchpad gestures part of the 0.20 API Signed-off-by: Peter Hutterer --- src/libinput.sym | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libinput.sym b/src/libinput.sym index 1f8b9d40..ce4ab6c7 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -147,7 +147,7 @@ LIBINPUT_0.19.0 { libinput_device_config_tap_get_default_drag_lock_enabled; } LIBINPUT_0.15.0; -TOUCHPAD_GESTURES { +LIBINPUT_0.20.0 { libinput_event_gesture_get_angle_delta; libinput_event_gesture_get_base_event; libinput_event_gesture_get_cancelled;