diff --git a/proto/protocol.xml b/proto/protocol.xml
index 824b871..b3727e8 100644
--- a/proto/protocol.xml
+++ b/proto/protocol.xml
@@ -1640,6 +1640,344 @@
+
+
+ Interface for swipe gesture requests and events.
+
+ This interface is only provided once per device and where a client
+ requests ei_gesture_swipe.release the interface does not get re-initialized. An
+ EIS implementation may adjust the behavior of the device (including removing
+ the device) if the interface is released.
+
+ Note that for a client to receive objects of this type, it must announce
+ support for this interface in ei_handshake.interface_version.
+
+
+
+
+
+
+ Notification that the client is no longer interested in swipe gestures.
+ The EIS implementation will release any resources related to this gesture and
+ send the ei_gesture_swipe.destroyed event once complete.
+
+
+
+
+
+ Starts a new swipe gesture with the given number of fingers.
+
+ The finger count must never be zero. While theoretically it is
+ possible to have single-finger swipe gestures, clients are advised
+ to avoid these as an EIS implementation is likely to immediately cancel
+ (or silently ignore) these.
+
+ Only one swipe gesture may be active at any time, it is a protocol
+ violation to send a ei_gesture_swipe.begin request before
+ terminating the previous gesture (if any) with
+ ei_gesture_swipe.end.
+
+ A swipe gesture may be terminated by the EIS implementation
+ with an ei_gesture_swipe.cancelled event.
+
+
+
+
+
+
+ Moves the gesture's logical center by the given delta.
+
+ This request may be sent at most once per device frame and only
+ after a gesture has begun via ei_gesture_swipe.begin and only
+ before the gesture terminates with ei_gesture_swipe.end.
+
+ This request must not be sent in the same frame as begin or end.
+
+
+
+
+
+
+
+ Logically ends this gesture.
+
+
+
+
+
+
+
+
+ This gesture has been removed and a client should release all
+ associated resources.
+
+ This ei_gesture_swipe object will be destroyed by the EIS
+ implementation immediately after this event is sent
+ and as such the client must not attempt to use
+ it after that point.
+
+
+
+
+
+
+ The current gesture has been cancelled by the EIS implementation.
+ The client must discard the current gesture and not send further
+ ei_gesture_swipe.update and ei_gesture_swipe.end events. Due to the asynchronous
+ nature of the protocol, an EIS implementation must allow
+ for ei_gesture_swipe.update and ei_gesture_swipe.end events to be received after
+ sending ei_gesture_swipe.cancelled. These events must be discarded.
+
+ The client may begin a new gesture in the future using ei_gesture_swipe.begin.
+
+
+
+
+
+
+ See the ei_gesture_swipe.begin request for details.
+
+
+
+
+
+
+ See the ei_gesture_swipe.update request for details.
+
+
+
+
+
+
+
+ See the ei_gesture_swipe.end request for details.
+
+
+
+
+
+
+
+ Interface for pinch gesture requests and events.
+
+ This interface is only provided once per device and where a client
+ requests ei_gesture_pinch.release the interface does not get re-initialized. An
+ EIS implementation may adjust the behavior of the device (including removing
+ the device) if the interface is released.
+
+ Note that for a client to receive objects of this type, it must announce
+ support for this interface in ei_handshake.interface_version.
+
+
+
+
+
+
+ Notification that the client is no longer interested in pinch gestures.
+ The EIS implementation will release any resources related to this gesture and
+ send the ei_gesture_pinch.destroyed event once complete.
+
+
+
+
+
+ Starts a new pinch gesture with the given number of fingers.
+
+ The finger count for pinch gestures must be at least 2.
+
+ Only one pinch gesture may be active at any time, it is a protocol
+ violation to send a ei_gesture_pinch.begin request before
+ terminating the previous gesture (if any) with
+ ei_gesture_pinch.end.
+
+ A pinch gesture may be terminated by the EIS implementation
+ with an ei_gesture_pinch.cancelled event.
+
+
+
+
+
+
+ Moves the gesture's logical center by the given delta and/or
+ updates the logical scale of the gesture and/or logically
+ rotates the gesture.
+
+ The gesture's scale is an absolute normalized
+ scale. Each pinch gesture starts at the scale of 1.0,
+ subsequent events increase or decrease that scale as the
+ pinch is widened or reduced. For example a scale of 2.0
+ indicates the fingers are now twice as far apart relative
+ to the corresponding ei_gesture_pinch.begin.
+
+ The rotation is the relative angle, in degrees, compared
+ to the previous ei_gesture_pinch.begin or
+ ei_gesture_pinch.update. A positive angle indicates CW rotation,
+ a negative angle CCW rotation.
+
+ This request may be sent at most once per device frame and only
+ after a gesture has begun via ei_gesture_pinch.begin and only
+ before the gesture terminates with ei_gesture_pinch.end.
+
+ This request must not be sent in the same frame as begin or end.
+
+
+
+
+
+
+
+
+
+ Logically ends this gesture.
+
+
+
+
+
+
+
+
+ This gesture has been removed and a client should release all
+ associated resources.
+
+ This ei_gesture_pinch object will be destroyed by the EIS
+ implementation immediately after this event is sent
+ and as such the client must not attempt to use
+ it after that point.
+
+
+
+
+
+
+ The current gesture has been cancelled by the EIS implementation.
+ The client must discard the current gesture and not send further
+ ei_gesture_pinch.update and ei_gesture_pinch.end events. Due to the asynchronous
+ nature of the protocol, an EIS implementation must allow
+ for ei_gesture_pinch.update and ei_gesture_pinch.end events to be received after
+ sending ei_gesture_pinch.cancelled. These events must be discarded.
+
+ The client may begin a new gesture in the future using ei_gesture_pinch.begin.
+
+
+
+
+
+
+ See the ei_gesture_pinch.begin request for details.
+
+
+
+
+
+
+ See the ei_gesture_pinch.update request for details.
+
+
+
+
+
+
+
+
+
+ See the ei_gesture_pinch.end request for details.
+
+
+
+
+
+
+
+ Interface for hold gesture requests and events.
+
+ This interface is only provided once per device and where a client
+ requests ei_gesture_hold.release the interface does not get re-initialized. An
+ EIS implementation may adjust the behavior of the device (including removing
+ the device) if the interface is released.
+
+ Note that for a client to receive objects of this type, it must announce
+ support for this interface in ei_handshake.interface_version.
+
+
+
+
+
+
+ Notification that the client is no longer interested in hold gestures.
+ The EIS implementation will release any resources related to this gesture and
+ send the ei_gesture_hold.destroyed event once complete.
+
+
+
+
+
+ Starts a new hold gesture with the given number of fingers.
+
+ The finger count must never be zero.
+
+ Only one hold gesture may be active at any time, it is a protocol
+ violation to send a ei_gesture_hold.begin request before
+ terminating the previous gesture (if any) with
+ ei_gesture_hold.end.
+
+ A hold gesture may be terminated by the EIS implementation
+ with an ei_gesture_hold.cancelled event.
+
+
+
+
+
+
+ Logically ends this gesture.
+
+
+
+
+
+
+
+
+ This gesture has been removed and a client should release all
+ associated resources.
+
+ This ei_gesture_hold object will be destroyed by the EIS
+ implementation immediately after this event is sent
+ and as such the client must not attempt to use
+ it after that point.
+
+
+
+
+
+
+ The current gesture has been cancelled by the EIS implementation.
+ The client must discard the current gesture and not send further
+ ei_gesture_hold.end events. Due to the asynchronous
+ nature of the protocol, an EIS implementation must allow for
+ ei_gesture_hold.end events to be received after
+ sending ei_gesture_hold.cancelled. These events must be discarded.
+
+ The client may begin a new gesture in the future using ei_gesture_hold.begin.
+
+
+
+
+
+
+ See the ei_gesture_hold.begin request for details.
+
+
+
+
+
+
+ See the ei_gesture_hold.end request for details.
+
+
+
+
+
Interface for text-based requests and events.
@@ -1755,6 +2093,7 @@
within the same frame is undefined.
+
diff --git a/src/libei-device.c b/src/libei-device.c
index 8c0913e..a7d11db 100644
--- a/src/libei-device.c
+++ b/src/libei-device.c
@@ -91,6 +91,9 @@ ei_device_destroy(struct ei_device *device)
ei_button_unref(device->button);
ei_touchscreen_unref(device->touchscreen);
ei_keyboard_unref(device->keyboard);
+ ei_gesture_swipe_unref(device->swipe);
+ ei_gesture_pinch_unref(device->pinch);
+ ei_gesture_hold_unref(device->hold);
ei_text_unref(device->text);
ei_seat_unref(seat);
free(device->name);
@@ -256,6 +259,8 @@ handle_msg_done(struct ei_device *device)
mask_add(device->capabilities, EI_DEVICE_CAP_KEYBOARD);
if (device->touchscreen)
mask_add(device->capabilities, EI_DEVICE_CAP_TOUCH);
+ if (device->swipe || device->pinch || device->hold)
+ mask_add(device->capabilities, EI_DEVICE_CAP_GESTURES);
if (device->text)
mask_add(device->capabilities, EI_DEVICE_CAP_TEXT);
@@ -265,6 +270,7 @@ handle_msg_done(struct ei_device *device)
!ei_device_has_capability(device, EI_DEVICE_CAP_TOUCH) &&
!ei_device_has_capability(device, EI_DEVICE_CAP_BUTTON) &&
!ei_device_has_capability(device, EI_DEVICE_CAP_SCROLL) &&
+ !ei_device_has_capability(device, EI_DEVICE_CAP_GESTURES) &&
!ei_device_has_capability(device, EI_DEVICE_CAP_TEXT)) {
log_debug(ei,
"Rejecting device %#" PRIx64 " '%s' with no known capabilities",
@@ -279,7 +285,7 @@ handle_msg_done(struct ei_device *device)
ei_queue_device_added_event(device);
ei_device_done(device);
log_debug(ei,
- "Added device %#" PRIx64 " '%s' caps: %s%s%s%s%s%s%s seat: %s",
+ "Added device %#" PRIx64 " '%s' caps: %s%s%s%s%s%s%s%s seat: %s",
ei_device_get_id(device),
ei_device_get_name(device),
ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) ? "p" : "",
@@ -288,6 +294,7 @@ handle_msg_done(struct ei_device *device)
ei_device_has_capability(device, EI_DEVICE_CAP_TOUCH) ? "t" : "",
ei_device_has_capability(device, EI_DEVICE_CAP_BUTTON) ? "b" : "",
ei_device_has_capability(device, EI_DEVICE_CAP_SCROLL) ? "s" : "",
+ ei_device_has_capability(device, EI_DEVICE_CAP_GESTURES) ? "g" : "",
ei_device_has_capability(device, EI_DEVICE_CAP_TEXT) ? "x" : "",
ei_seat_get_name(ei_device_get_seat(device)));
return NULL;
@@ -329,6 +336,14 @@ handle_msg_paused(struct ei_device *device, uint32_t serial)
} \
} while(0)
+#define DISCONNECT_IF_RECEIVER_CONTEXT(device_) do {\
+ struct ei *ei_ = ei_device_get_context(device_); \
+ if (!ei_is_sender(ei_)) { \
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_MODE, \
+ "Invalid event from sender EIS context. Disconnecting"); \
+ } \
+} while(0)
+
static struct brei_result *
handle_msg_start_emulating(struct ei_device *device, uint32_t serial, uint32_t sequence)
{
@@ -479,6 +494,24 @@ handle_msg_interface(struct ei_device *device, object_id_t id, const char *name,
"Duplicate ei_touchscreen interface object on device");
device->touchscreen = ei_touchscreen_new(device, id, version);
+ } else if (streq(name, EI_GESTURE_SWIPE_INTERFACE_NAME)) {
+ DISCONNECT_IF_INVALID_VERSION(ei, ei_gesture_swipe, id, version);
+ if (device->swipe)
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Duplicate ei_gesture_swipe object on device");
+ device->swipe = ei_gesture_swipe_new(device, id, version);
+ } else if (streq(name, EI_GESTURE_PINCH_INTERFACE_NAME)) {
+ DISCONNECT_IF_INVALID_VERSION(ei, ei_gesture_pinch, id, version);
+ if (device->pinch)
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Duplicate ei_gesture_pinch object on device");
+ device->pinch = ei_gesture_pinch_new(device, id, version);
+ } else if (streq(name, EI_GESTURE_HOLD_INTERFACE_NAME)) {
+ DISCONNECT_IF_INVALID_VERSION(ei, ei_gesture_hold, id, version);
+ if (device->hold)
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Duplicate ei_gesture_hold object on device");
+ device->hold = ei_gesture_hold_new(device, id, version);
} else if (streq(name, EI_TEXT_INTERFACE_NAME)) {
DISCONNECT_IF_INVALID_VERSION(ei, ei_text, id, version);
if (device->text)
@@ -935,6 +968,386 @@ ei_device_get_touchscreen_interface(struct ei_device *device)
return &touchscreen_interface;
}
+static struct brei_result *
+handle_msg_gesture_swipe_destroy(struct ei_gesture_swipe *swipe, uint32_t serial)
+{
+ struct ei *ei = ei_gesture_swipe_get_context(swipe);
+ ei_update_serial(ei, serial);
+
+ struct ei_device *device = ei_gesture_swipe_get_device(swipe);
+ ei_gesture_swipe_unref(steal(&device->swipe));
+
+ return NULL;
+}
+
+static struct brei_result *
+handle_msg_gesture_swipe_begin(struct ei_gesture_swipe *swipe, uint32_t nfingers)
+{
+ struct ei_device *device = ei_gesture_swipe_get_device(swipe);
+
+ DISCONNECT_IF_SENDER_CONTEXT(device);
+
+ if (!ei_device_has_capability(device, EI_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Swipe begin event for non-gesture device");
+ }
+
+ if (nfingers == 0) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Swipe begin event with zero fingers");
+ }
+
+ if (device->state == EI_DEVICE_STATE_EMULATING) {
+ if (device->gesture_state.swipe.is_active) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Swipe begin event while swipe is active");
+ }
+ device->gesture_state.swipe.is_active = true;
+ device->gesture_state.swipe.nfingers = nfingers;
+ ei_queue_swipe_begin_event(device, nfingers);
+ return NULL;
+ }
+
+ return maybe_error_on_device_state(device, "swipe begin");
+}
+
+static struct brei_result *
+handle_msg_gesture_swipe_update(struct ei_gesture_swipe *swipe, float dx, float dy)
+{
+ struct ei_device *device = ei_gesture_swipe_get_device(swipe);
+
+ DISCONNECT_IF_SENDER_CONTEXT(device);
+
+ if (!ei_device_has_capability(device, EI_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Swipe update event for non-gesture device");
+ }
+
+ if (!device->gesture_state.swipe.is_active) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Swipe update event while no swipe is active");
+ }
+
+ if (device->state == EI_DEVICE_STATE_EMULATING) {
+ ei_queue_swipe_update_event(device, dx, dy);
+ return NULL;
+ }
+
+ return maybe_error_on_device_state(device, "swipe update");
+}
+
+static struct brei_result *
+handle_msg_gesture_swipe_end(struct ei_gesture_swipe *swipe, uint32_t is_cancelled)
+{
+ struct ei_device *device = ei_gesture_swipe_get_device(swipe);
+
+ DISCONNECT_IF_SENDER_CONTEXT(device);
+
+ if (!ei_device_has_capability(device, EI_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Swipe end event for non-gesture device");
+ }
+
+ if (!device->gesture_state.swipe.is_active) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Swipe end event while no swipe is active");
+ }
+
+ if (device->state == EI_DEVICE_STATE_EMULATING) {
+ ei_queue_swipe_end_event(device, !!is_cancelled);
+ device->gesture_state.swipe.is_active = false;
+ return NULL;
+ }
+
+ return maybe_error_on_device_state(device, "swipe end");
+}
+
+static struct brei_result *
+handle_msg_gesture_swipe_cancelled(struct ei_gesture_swipe *swipe, uint32_t serial)
+{
+ struct ei *ei = ei_gesture_swipe_get_context(swipe);
+ struct ei_device *device = ei_gesture_swipe_get_device(swipe);
+ ei_update_serial(ei, serial);
+
+ DISCONNECT_IF_RECEIVER_CONTEXT(device);
+
+ if (!ei_device_has_capability(device, EI_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Swipe cancelled event for non-gesture device");
+ }
+
+ /* EIS may have cancelled us while we've ended the gesture,
+ * swallow the cancel if that happened */
+ if (device->gesture_state.swipe.current_swipe) {
+ if (device->state == EI_DEVICE_STATE_EMULATING)
+ ei_queue_swipe_cancelled_event(device);
+ device->gesture_state.swipe.current_swipe = NULL;
+ return NULL;
+ }
+
+ return maybe_error_on_device_state(device, "swipe cancel");
+}
+
+static const struct ei_gesture_swipe_interface swipe_interface = {
+ .destroyed = handle_msg_gesture_swipe_destroy,
+ .begin = handle_msg_gesture_swipe_begin,
+ .update = handle_msg_gesture_swipe_update,
+ .end = handle_msg_gesture_swipe_end,
+ .cancelled = handle_msg_gesture_swipe_cancelled,
+};
+
+const struct ei_gesture_swipe_interface *
+ei_device_get_gesture_swipe_interface(struct ei_device *device)
+{
+ return &swipe_interface;
+}
+
+static struct brei_result *
+handle_msg_gesture_pinch_destroy(struct ei_gesture_pinch *pinch, uint32_t serial)
+{
+ struct ei *ei = ei_gesture_pinch_get_context(pinch);
+ ei_update_serial(ei, serial);
+
+ struct ei_device *device = ei_gesture_pinch_get_device(pinch);
+ ei_gesture_pinch_unref(steal(&device->pinch));
+
+ return NULL;
+}
+
+static struct brei_result *
+handle_msg_gesture_pinch_begin(struct ei_gesture_pinch *pinch, uint32_t nfingers)
+{
+ struct ei_device *device = ei_gesture_pinch_get_device(pinch);
+
+ DISCONNECT_IF_SENDER_CONTEXT(device);
+
+ if (!ei_device_has_capability(device, EI_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Pinch begin event for non-gesture device");
+ }
+
+ if (nfingers < 2) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Pinch begin event with fewer than 2 fingers");
+ }
+
+ if (device->state == EI_DEVICE_STATE_EMULATING) {
+ if (device->gesture_state.pinch.is_active) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Pinch begin event while pinch is active");
+ }
+ device->gesture_state.pinch.is_active = true;
+ device->gesture_state.pinch.nfingers = nfingers;
+ ei_queue_pinch_begin_event(device, nfingers);
+ return NULL;
+ }
+
+ return maybe_error_on_device_state(device, "pinch begin");
+}
+
+static struct brei_result *
+handle_msg_gesture_pinch_update(struct ei_gesture_pinch *pinch,
+ float dx,
+ float dy,
+ float scale,
+ float rotation)
+{
+ struct ei_device *device = ei_gesture_pinch_get_device(pinch);
+
+ DISCONNECT_IF_SENDER_CONTEXT(device);
+
+ if (!ei_device_has_capability(device, EI_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Pinch update event for non-gesture device");
+ }
+
+ if (!device->gesture_state.pinch.is_active) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Pinch update event while no pinch is active");
+ }
+
+ if (device->state == EI_DEVICE_STATE_EMULATING) {
+ ei_queue_pinch_update_event(device, dx, dy, scale, rotation);
+ return NULL;
+ }
+
+ return maybe_error_on_device_state(device, "pinch update");
+}
+
+static struct brei_result *
+handle_msg_gesture_pinch_end(struct ei_gesture_pinch *pinch, uint32_t is_cancelled)
+{
+ struct ei_device *device = ei_gesture_pinch_get_device(pinch);
+
+ DISCONNECT_IF_SENDER_CONTEXT(device);
+
+ if (!ei_device_has_capability(device, EI_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Pinch end event for non-gesture device");
+ }
+
+ if (!device->gesture_state.pinch.is_active) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Pinch end event while no pinch is active");
+ }
+
+ if (device->state == EI_DEVICE_STATE_EMULATING) {
+ ei_queue_pinch_end_event(device, !!is_cancelled);
+ device->gesture_state.pinch.is_active = false;
+ return NULL;
+ }
+
+ return maybe_error_on_device_state(device, "pinch end");
+}
+
+static struct brei_result *
+handle_msg_gesture_pinch_cancelled(struct ei_gesture_pinch *pinch, uint32_t serial)
+{
+ struct ei *ei = ei_gesture_pinch_get_context(pinch);
+ struct ei_device *device = ei_gesture_pinch_get_device(pinch);
+ ei_update_serial(ei, serial);
+
+ DISCONNECT_IF_RECEIVER_CONTEXT(device);
+
+ if (!ei_device_has_capability(device, EI_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Pinch cancelled event for non-gesture device");
+ }
+
+ /* EIS may have cancelled us while we've ended the gesture,
+ * swallow the cancel if that happened */
+ if (device->gesture_state.pinch.current_pinch) {
+ if (device->state == EI_DEVICE_STATE_EMULATING)
+ ei_queue_pinch_cancelled_event(device);
+ device->gesture_state.pinch.current_pinch = NULL;
+ return NULL;
+ }
+
+ return maybe_error_on_device_state(device, "pinch cancel");
+}
+
+static const struct ei_gesture_pinch_interface pinch_interface = {
+ .destroyed = handle_msg_gesture_pinch_destroy,
+ .begin = handle_msg_gesture_pinch_begin,
+ .update = handle_msg_gesture_pinch_update,
+ .end = handle_msg_gesture_pinch_end,
+ .cancelled = handle_msg_gesture_pinch_cancelled,
+};
+
+const struct ei_gesture_pinch_interface *
+ei_device_get_gesture_pinch_interface(struct ei_device *device)
+{
+ return &pinch_interface;
+}
+
+static struct brei_result *
+handle_msg_gesture_hold_destroy(struct ei_gesture_hold *hold, uint32_t serial)
+{
+ struct ei *ei = ei_gesture_hold_get_context(hold);
+ ei_update_serial(ei, serial);
+
+ struct ei_device *device = ei_gesture_hold_get_device(hold);
+ ei_gesture_hold_unref(steal(&device->hold));
+
+ return NULL;
+}
+
+static struct brei_result *
+handle_msg_gesture_hold_begin(struct ei_gesture_hold *hold, uint32_t nfingers)
+{
+ struct ei_device *device = ei_gesture_hold_get_device(hold);
+
+ DISCONNECT_IF_SENDER_CONTEXT(device);
+
+ if (!ei_device_has_capability(device, EI_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Hold begin event for non-gesture device");
+ }
+
+ if (nfingers == 0) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Hold begin event with zero fingers");
+ }
+
+ if (device->state == EI_DEVICE_STATE_EMULATING) {
+ if (device->gesture_state.hold.is_active) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Hold begin event while hold is active");
+ }
+ device->gesture_state.hold.is_active = true;
+ device->gesture_state.hold.nfingers = nfingers;
+ ei_queue_hold_begin_event(device, nfingers);
+ return NULL;
+ }
+
+ return maybe_error_on_device_state(device, "hold begin");
+}
+
+static struct brei_result *
+handle_msg_gesture_hold_end(struct ei_gesture_hold *hold, uint32_t is_cancelled)
+{
+ struct ei_device *device = ei_gesture_hold_get_device(hold);
+
+ DISCONNECT_IF_SENDER_CONTEXT(device);
+
+ if (!ei_device_has_capability(device, EI_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Hold end event for non-gesture device");
+ }
+
+ if (!device->gesture_state.hold.is_active) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Hold end event while no hold is active");
+ }
+
+ if (device->state == EI_DEVICE_STATE_EMULATING) {
+ ei_queue_hold_end_event(device, !!is_cancelled);
+ device->gesture_state.hold.is_active = false;
+ return NULL;
+ }
+
+ return maybe_error_on_device_state(device, "hold end");
+}
+
+static struct brei_result *
+handle_msg_gesture_hold_cancelled(struct ei_gesture_hold *hold, uint32_t serial)
+{
+ struct ei *ei = ei_gesture_hold_get_context(hold);
+ struct ei_device *device = ei_gesture_hold_get_device(hold);
+ ei_update_serial(ei, serial);
+
+ DISCONNECT_IF_RECEIVER_CONTEXT(device);
+
+ if (!ei_device_has_capability(device, EI_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Hold cancelled event for non-gesture device");
+ }
+
+ /* EIS may have cancelled us while we've ended the gesture,
+ * swallow the cancel if that happened */
+ if (device->gesture_state.hold.current_hold) {
+ if (device->state == EI_DEVICE_STATE_EMULATING)
+ ei_queue_hold_cancelled_event(device);
+ device->gesture_state.hold.current_hold = NULL;
+ return NULL;
+ }
+
+ return maybe_error_on_device_state(device, "hold cancel");
+}
+
+static const struct ei_gesture_hold_interface hold_interface = {
+ .destroyed = handle_msg_gesture_hold_destroy,
+ .begin = handle_msg_gesture_hold_begin,
+ .end = handle_msg_gesture_hold_end,
+ .cancelled = handle_msg_gesture_hold_cancelled,
+};
+
+const struct ei_gesture_hold_interface *
+ei_device_get_gesture_hold_interface(struct ei_device *device)
+{
+ return &hold_interface;
+}
+
static struct brei_result *
handle_msg_text_destroy(struct ei_text *text, uint32_t serial)
{
@@ -1147,6 +1560,12 @@ ei_device_send_release(struct ei_device *device)
ei_scroll_request_release(device->scroll);
if (device->button)
ei_button_request_release(device->button);
+ if (device->swipe)
+ ei_gesture_swipe_request_release(device->swipe);
+ if (device->pinch)
+ ei_gesture_pinch_request_release(device->pinch);
+ if (device->hold)
+ ei_gesture_hold_request_release(device->hold);
int rc = ei_device_request_release(device);
if (rc)
@@ -1207,6 +1626,9 @@ ei_device_removed_by_server(struct ei_device *device)
ei_touchscreen_unref(steal(&device->touchscreen));
ei_scroll_unref(steal(&device->scroll));
ei_button_unref(steal(&device->button));
+ ei_gesture_swipe_unref(steal(&device->swipe));
+ ei_gesture_pinch_unref(steal(&device->pinch));
+ ei_gesture_hold_unref(steal(&device->hold));
ei_unregister_object(ei, &device->proto_object);
ei_queue_device_removed_event(device);
@@ -1282,6 +1704,7 @@ ei_device_has_capability(struct ei_device *device, enum ei_device_capability cap
case EI_DEVICE_CAP_TOUCH:
case EI_DEVICE_CAP_BUTTON:
case EI_DEVICE_CAP_SCROLL:
+ case EI_DEVICE_CAP_GESTURES:
case EI_DEVICE_CAP_TEXT:
return mask_all(device->capabilities, cap);
}
@@ -2045,3 +2468,21 @@ ei_device_frame(struct ei_device *device, uint64_t time)
ei_disconnect(ei_device_get_context(device));
return;
}
+
+_public_ struct ei_swipe *
+ei_device_swipe_new(struct ei_device *device, uint32_t nfingers)
+{
+ return ei_swipe_new(device, nfingers);
+}
+
+_public_ struct ei_pinch *
+ei_device_pinch_new(struct ei_device *device, uint32_t nfingers)
+{
+ return ei_pinch_new(device, nfingers);
+}
+
+_public_ struct ei_hold *
+ei_device_hold_new(struct ei_device *device, uint32_t nfingers)
+{
+ return ei_hold_new(device, nfingers);
+}
diff --git a/src/libei-device.h b/src/libei-device.h
index 4525262..fa8e2ac 100644
--- a/src/libei-device.h
+++ b/src/libei-device.h
@@ -66,6 +66,9 @@ struct ei_device {
struct ei_button *button;
struct ei_keyboard *keyboard;
struct ei_touchscreen *touchscreen;
+ struct ei_gesture_swipe *swipe;
+ struct ei_gesture_pinch *pinch;
+ struct ei_gesture_hold *hold;
struct ei_text *text;
struct list link;
@@ -87,6 +90,33 @@ struct ei_device {
bool x_is_cancelled, y_is_cancelled;
} scroll_state;
+ struct {
+ struct {
+ /* receiver */
+ bool is_active;
+ uint32_t nfingers;
+
+ /* sender */
+ struct ei_swipe *current_swipe; /* no ref! */
+ } swipe;
+ struct {
+ /* receiver */
+ bool is_active;
+ uint32_t nfingers;
+
+ /* sender */
+ struct ei_pinch *current_pinch; /* no ref! */
+ } pinch;
+ struct {
+ /* receiver */
+ bool is_active;
+ uint32_t nfingers;
+
+ /* sender */
+ struct ei_hold *current_hold; /* no ref! */
+ } hold;
+ } gesture_state;
+
struct ei_keymap *keymap;
char *pending_region_mapping_id;
@@ -127,6 +157,13 @@ OBJECT_DECLARE_GETTER(ei_device, scroll_interface, const struct ei_scroll_interf
OBJECT_DECLARE_GETTER(ei_device, button_interface, const struct ei_button_interface *);
OBJECT_DECLARE_GETTER(ei_device, keyboard_interface, const struct ei_keyboard_interface *);
OBJECT_DECLARE_GETTER(ei_device, touchscreen_interface, const struct ei_touchscreen_interface *);
+OBJECT_DECLARE_GETTER(ei_device,
+ gesture_swipe_interface,
+ const struct ei_gesture_swipe_interface *);
+OBJECT_DECLARE_GETTER(ei_device,
+ gesture_pinch_interface,
+ const struct ei_gesture_pinch_interface *);
+OBJECT_DECLARE_GETTER(ei_device, gesture_hold_interface, const struct ei_gesture_hold_interface *);
OBJECT_DECLARE_GETTER(ei_device, text_interface, const struct ei_text_interface *);
OBJECT_DECLARE_SETTER(ei_device, type, enum ei_device_type);
OBJECT_DECLARE_SETTER(ei_device, name, const char *);
diff --git a/src/libei-event.c b/src/libei-event.c
index e982aec..080ecdd 100644
--- a/src/libei-event.c
+++ b/src/libei-event.c
@@ -63,6 +63,17 @@ ei_event_type_to_string(enum ei_event_type type)
CASE_RETURN_STRING(EI_EVENT_TOUCH_DOWN);
CASE_RETURN_STRING(EI_EVENT_TOUCH_UP);
CASE_RETURN_STRING(EI_EVENT_TOUCH_MOTION);
+ CASE_RETURN_STRING(EI_EVENT_SWIPE_BEGIN);
+ CASE_RETURN_STRING(EI_EVENT_SWIPE_UPDATE);
+ CASE_RETURN_STRING(EI_EVENT_SWIPE_END);
+ CASE_RETURN_STRING(EI_EVENT_SWIPE_CANCELLED);
+ CASE_RETURN_STRING(EI_EVENT_PINCH_BEGIN);
+ CASE_RETURN_STRING(EI_EVENT_PINCH_UPDATE);
+ CASE_RETURN_STRING(EI_EVENT_PINCH_END);
+ CASE_RETURN_STRING(EI_EVENT_PINCH_CANCELLED);
+ CASE_RETURN_STRING(EI_EVENT_HOLD_BEGIN);
+ CASE_RETURN_STRING(EI_EVENT_HOLD_END);
+ CASE_RETURN_STRING(EI_EVENT_HOLD_CANCELLED);
CASE_RETURN_STRING(EI_EVENT_TEXT_KEYSYM);
CASE_RETURN_STRING(EI_EVENT_TEXT_UTF8);
}
@@ -98,6 +109,17 @@ ei_event_destroy(struct ei_event *event)
case EI_EVENT_TOUCH_DOWN:
case EI_EVENT_TOUCH_UP:
case EI_EVENT_TOUCH_MOTION:
+ case EI_EVENT_SWIPE_BEGIN:
+ case EI_EVENT_SWIPE_UPDATE:
+ case EI_EVENT_SWIPE_END:
+ case EI_EVENT_SWIPE_CANCELLED:
+ case EI_EVENT_PINCH_BEGIN:
+ case EI_EVENT_PINCH_UPDATE:
+ case EI_EVENT_PINCH_END:
+ case EI_EVENT_PINCH_CANCELLED:
+ case EI_EVENT_HOLD_BEGIN:
+ case EI_EVENT_HOLD_END:
+ case EI_EVENT_HOLD_CANCELLED:
case EI_EVENT_TEXT_KEYSYM:
break;
case EI_EVENT_TEXT_UTF8:
@@ -363,6 +385,110 @@ ei_event_touch_get_is_cancel(struct ei_event *event)
return event->touch.is_cancel;
}
+_public_ uint32_t
+ei_event_swipe_get_finger_count(struct ei_event *event)
+{
+ require_event_type(event,
+ 0,
+ EI_EVENT_SWIPE_BEGIN,
+ EI_EVENT_SWIPE_UPDATE,
+ EI_EVENT_SWIPE_END);
+
+ return event->gestures.nfingers;
+}
+
+_public_ double
+ei_event_swipe_get_dx(struct ei_event *event)
+{
+ require_event_type(event, 0.0, EI_EVENT_SWIPE_UPDATE);
+
+ return event->gestures.dx;
+}
+
+_public_ double
+ei_event_swipe_get_dy(struct ei_event *event)
+{
+ require_event_type(event, 0.0, EI_EVENT_SWIPE_UPDATE);
+
+ return event->gestures.dy;
+}
+
+_public_ bool
+ei_event_swipe_get_is_cancelled(struct ei_event *event)
+{
+ require_event_type(event, false, EI_EVENT_SWIPE_END);
+
+ return event->gestures.is_cancelled;
+}
+
+_public_ uint32_t
+ei_event_pinch_get_finger_count(struct ei_event *event)
+{
+ require_event_type(event,
+ 0,
+ EI_EVENT_PINCH_BEGIN,
+ EI_EVENT_PINCH_UPDATE,
+ EI_EVENT_PINCH_END);
+
+ return event->gestures.nfingers;
+}
+
+_public_ double
+ei_event_pinch_get_dx(struct ei_event *event)
+{
+ require_event_type(event, 0.0, EI_EVENT_PINCH_UPDATE);
+
+ return event->gestures.dx;
+}
+
+_public_ double
+ei_event_pinch_get_dy(struct ei_event *event)
+{
+ require_event_type(event, 0.0, EI_EVENT_PINCH_UPDATE);
+
+ return event->gestures.dy;
+}
+
+_public_ double
+ei_event_pinch_get_rotation(struct ei_event *event)
+{
+ require_event_type(event, 0.0, EI_EVENT_PINCH_UPDATE);
+
+ return event->gestures.degrees;
+}
+
+_public_ double
+ei_event_pinch_get_scale(struct ei_event *event)
+{
+ require_event_type(event, 0.0, EI_EVENT_PINCH_UPDATE);
+
+ return event->gestures.scale;
+}
+
+_public_ bool
+ei_event_pinch_get_is_cancelled(struct ei_event *event)
+{
+ require_event_type(event, false, EI_EVENT_PINCH_END);
+
+ return event->gestures.is_cancelled;
+}
+
+_public_ uint32_t
+ei_event_hold_get_finger_count(struct ei_event *event)
+{
+ require_event_type(event, 0, EI_EVENT_HOLD_BEGIN, EI_EVENT_HOLD_END);
+
+ return event->gestures.nfingers;
+}
+
+_public_ bool
+ei_event_hold_get_is_cancelled(struct ei_event *event)
+{
+ require_event_type(event, false, EI_EVENT_HOLD_END);
+
+ return event->gestures.is_cancelled;
+}
+
_public_ uint32_t
ei_event_text_get_keysym(struct ei_event *event)
{
@@ -403,6 +529,17 @@ ei_event_get_time(struct ei_event *event)
EI_EVENT_TOUCH_DOWN,
EI_EVENT_TOUCH_UP,
EI_EVENT_TOUCH_MOTION,
+ EI_EVENT_SWIPE_BEGIN,
+ EI_EVENT_SWIPE_UPDATE,
+ EI_EVENT_SWIPE_END,
+ EI_EVENT_SWIPE_CANCELLED,
+ EI_EVENT_PINCH_BEGIN,
+ EI_EVENT_PINCH_UPDATE,
+ EI_EVENT_PINCH_END,
+ EI_EVENT_PINCH_CANCELLED,
+ EI_EVENT_HOLD_BEGIN,
+ EI_EVENT_HOLD_END,
+ EI_EVENT_HOLD_CANCELLED,
EI_EVENT_TEXT_KEYSYM,
EI_EVENT_TEXT_UTF8,
EI_EVENT_FRAME);
diff --git a/src/libei-event.h b/src/libei-event.h
index 451c22c..98ebc83 100644
--- a/src/libei-event.h
+++ b/src/libei-event.h
@@ -81,6 +81,13 @@ struct ei_event {
struct {
struct ei_pingpong *pingpong;
} sync;
+ struct {
+ uint32_t nfingers;
+ double dx, dy;
+ double degrees;
+ double scale;
+ bool is_cancelled;
+ } gestures;
};
};
diff --git a/src/libei-gestures.c b/src/libei-gestures.c
new file mode 100644
index 0000000..8ba60af
--- /dev/null
+++ b/src/libei-gestures.c
@@ -0,0 +1,769 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2024 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include
+#include
+
+#include "util-bits.h"
+#include "util-io.h"
+#include "util-macros.h"
+#include "util-mem.h"
+#include "util-strings.h"
+#include "util-version.h"
+
+#include "ei-proto.h"
+#include "libei-private.h"
+
+static void
+ei_gesture_swipe_destroy(struct ei_gesture_swipe *swipe)
+{
+ struct ei *ei = ei_gesture_swipe_get_context(swipe);
+ ei_unregister_object(ei, &swipe->proto_object);
+}
+
+OBJECT_IMPLEMENT_REF(ei_gesture_swipe);
+OBJECT_IMPLEMENT_UNREF_CLEANUP(ei_gesture_swipe);
+
+static OBJECT_IMPLEMENT_CREATE(ei_gesture_swipe);
+static OBJECT_IMPLEMENT_PARENT(ei_gesture_swipe, ei_device);
+OBJECT_IMPLEMENT_GETTER_AS_REF(ei_gesture_swipe, proto_object, const struct brei_object *);
+
+struct ei_device *
+ei_gesture_swipe_get_device(struct ei_gesture_swipe *swipe)
+{
+ return ei_gesture_swipe_parent(swipe);
+}
+
+struct ei *
+ei_gesture_swipe_get_context(struct ei_gesture_swipe *swipe)
+{
+ return ei_device_get_context(ei_gesture_swipe_get_device(swipe));
+}
+
+const struct ei_gesture_swipe_interface *
+ei_gesture_swipe_get_interface(struct ei_gesture_swipe *swipe)
+{
+ struct ei_device *device = ei_gesture_swipe_get_device(swipe);
+ return ei_device_get_gesture_swipe_interface(device);
+}
+
+struct ei_gesture_swipe *
+ei_gesture_swipe_new(struct ei_device *device, object_id_t id, uint32_t version)
+{
+ struct ei_gesture_swipe *swipe = ei_gesture_swipe_create(&device->object);
+ struct ei *ei = ei_device_get_context(device);
+
+ swipe->proto_object.id = id;
+ swipe->proto_object.implementation = swipe;
+ swipe->proto_object.interface = &ei_gesture_swipe_proto_interface;
+ swipe->proto_object.version = version;
+ ei_register_object(ei, &swipe->proto_object);
+
+ return swipe; /* ref owned by caller */
+}
+
+static void
+ei_gesture_pinch_destroy(struct ei_gesture_pinch *pinch)
+{
+ struct ei *ei = ei_gesture_pinch_get_context(pinch);
+ ei_unregister_object(ei, &pinch->proto_object);
+}
+
+OBJECT_IMPLEMENT_REF(ei_gesture_pinch);
+OBJECT_IMPLEMENT_UNREF_CLEANUP(ei_gesture_pinch);
+
+static OBJECT_IMPLEMENT_CREATE(ei_gesture_pinch);
+static OBJECT_IMPLEMENT_PARENT(ei_gesture_pinch, ei_device);
+OBJECT_IMPLEMENT_GETTER_AS_REF(ei_gesture_pinch, proto_object, const struct brei_object *);
+
+struct ei_device *
+ei_gesture_pinch_get_device(struct ei_gesture_pinch *pinch)
+{
+ return ei_gesture_pinch_parent(pinch);
+}
+
+struct ei *
+ei_gesture_pinch_get_context(struct ei_gesture_pinch *pinch)
+{
+ return ei_device_get_context(ei_gesture_pinch_get_device(pinch));
+}
+
+const struct ei_gesture_pinch_interface *
+ei_gesture_pinch_get_interface(struct ei_gesture_pinch *pinch)
+{
+ struct ei_device *device = ei_gesture_pinch_get_device(pinch);
+ return ei_device_get_gesture_pinch_interface(device);
+}
+
+struct ei_gesture_pinch *
+ei_gesture_pinch_new(struct ei_device *device, object_id_t id, uint32_t version)
+{
+ struct ei_gesture_pinch *pinch = ei_gesture_pinch_create(&device->object);
+ struct ei *ei = ei_device_get_context(device);
+
+ pinch->proto_object.id = id;
+ pinch->proto_object.implementation = pinch;
+ pinch->proto_object.interface = &ei_gesture_pinch_proto_interface;
+ pinch->proto_object.version = version;
+ ei_register_object(ei, &pinch->proto_object);
+
+ return pinch; /* ref owned by caller */
+}
+
+static void
+ei_gesture_hold_destroy(struct ei_gesture_hold *hold)
+{
+ struct ei *ei = ei_gesture_hold_get_context(hold);
+ ei_unregister_object(ei, &hold->proto_object);
+}
+
+OBJECT_IMPLEMENT_REF(ei_gesture_hold);
+OBJECT_IMPLEMENT_UNREF_CLEANUP(ei_gesture_hold);
+
+static OBJECT_IMPLEMENT_CREATE(ei_gesture_hold);
+static OBJECT_IMPLEMENT_PARENT(ei_gesture_hold, ei_device);
+OBJECT_IMPLEMENT_GETTER_AS_REF(ei_gesture_hold, proto_object, const struct brei_object *);
+
+struct ei_device *
+ei_gesture_hold_get_device(struct ei_gesture_hold *hold)
+{
+ return ei_gesture_hold_parent(hold);
+}
+
+struct ei *
+ei_gesture_hold_get_context(struct ei_gesture_hold *hold)
+{
+ return ei_device_get_context(ei_gesture_hold_get_device(hold));
+}
+
+const struct ei_gesture_hold_interface *
+ei_gesture_hold_get_interface(struct ei_gesture_hold *hold)
+{
+ struct ei_device *device = ei_gesture_hold_get_device(hold);
+ return ei_device_get_gesture_hold_interface(device);
+}
+
+struct ei_gesture_hold *
+ei_gesture_hold_new(struct ei_device *device, object_id_t id, uint32_t version)
+{
+ struct ei_gesture_hold *hold = ei_gesture_hold_create(&device->object);
+ struct ei *ei = ei_device_get_context(device);
+
+ hold->proto_object.id = id;
+ hold->proto_object.implementation = hold;
+ hold->proto_object.interface = &ei_gesture_hold_proto_interface;
+ hold->proto_object.version = version;
+ ei_register_object(ei, &hold->proto_object);
+
+ return hold; /* ref owned by caller */
+}
+
+static void
+ei_gesture_destroy(struct ei_gesture *gesture)
+{
+ ei_device_unref(gesture->device);
+}
+
+static OBJECT_IMPLEMENT_INIT(ei_gesture);
+OBJECT_IMPLEMENT_REF(ei_gesture);
+OBJECT_IMPLEMENT_UNREF(ei_gesture);
+
+struct ei_device *
+ei_gesture_get_device(struct ei_gesture *gesture)
+{
+ return gesture->device;
+}
+
+struct ei *
+ei_gesture_get_context(struct ei_gesture *gesture)
+{
+ return ei_device_get_context(ei_gesture_get_device(gesture));
+}
+
+static void
+ei_gesture_init(struct ei_gesture *gesture, struct ei_device *device, uint32_t nfingers)
+{
+ /* Not using the device as parent object because we need a ref
+ * to it */
+ ei_gesture_init_object(gesture, NULL);
+
+ gesture->state = EI_GESTURE_STATE_NEW;
+ gesture->device = ei_device_ref(device);
+ gesture->nfingers = nfingers;
+}
+
+struct ei_swipe *
+ei_swipe_new(struct ei_device *device, uint32_t nfingers)
+{
+ if (!ei_device_has_capability(device, EI_DEVICE_CAP_GESTURES)) {
+ log_bug_client(ei_device_get_context(device),
+ "%s: device is not a gesture device",
+ __func__);
+ return NULL;
+ }
+
+ if (nfingers == 0) {
+ log_bug_client(ei_device_get_context(device),
+ "zero-finger gestures are not possible");
+ return NULL;
+ }
+
+ if (!device->swipe)
+ return NULL;
+
+ struct ei_swipe *swipe = xalloc(sizeof *swipe);
+ ei_gesture_init(&swipe->base, device, nfingers);
+
+ return swipe;
+}
+
+_public_ struct ei_swipe *
+ei_swipe_ref(struct ei_swipe *swipe)
+{
+ return (struct ei_swipe *)ei_gesture_ref(&swipe->base);
+}
+
+_public_ struct ei_swipe *
+ei_swipe_unref(struct ei_swipe *swipe)
+{
+ if (!swipe)
+ return NULL;
+
+ struct ei_device *device = ei_gesture_get_device(&swipe->base);
+
+ if (swipe == device->gesture_state.swipe.current_swipe) {
+ if (swipe->base.state == EI_GESTURE_STATE_BEGUN &&
+ device->state == EI_DEVICE_STATE_EMULATING) {
+ ei_swipe_cancel(swipe);
+ }
+ }
+
+ return (struct ei_swipe *)ei_gesture_unref(&swipe->base);
+}
+
+struct ei *
+ei_swipe_get_context(struct ei_swipe *swipe)
+{
+ return ei_gesture_get_context(&swipe->base);
+}
+
+_public_ void
+ei_swipe_begin(struct ei_swipe *swipe)
+{
+ struct ei_device *device = swipe->base.device;
+ struct ei *ei = ei_device_get_context(device);
+
+ if (swipe->base.state != EI_GESTURE_STATE_NEW) {
+ log_bug_client(ei_gesture_get_context(&swipe->base), "gesture cannot begin twice");
+ return;
+ }
+
+ if (device->gesture_state.swipe.current_swipe) {
+ log_bug_client(
+ ei_gesture_get_context(&swipe->base),
+ "a different swipe gesture is already in process, ignoring this one");
+ return;
+ }
+
+ if (device->state != EI_DEVICE_STATE_EMULATING) {
+ log_bug_client(ei_device_get_context(device),
+ "%s: device is not emulating",
+ __func__);
+ return;
+ }
+
+ if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
+ return;
+
+ if (!device->swipe)
+ return;
+
+ if (swipe->base.nfingers == 0) {
+ log_bug_client(ei_device_get_context(device),
+ "zero-finger gestures are invalid, ignoring gesture");
+ return;
+ }
+
+ device->gesture_state.swipe.current_swipe = swipe;
+
+ device->send_frame_event = true;
+
+ swipe->base.state = EI_GESTURE_STATE_BEGUN;
+ int rc = ei_gesture_swipe_request_begin(device->swipe, swipe->base.nfingers);
+ if (rc)
+ ei_disconnect(ei);
+}
+
+_public_ void
+ei_swipe_update(struct ei_swipe *swipe, double x, double y)
+{
+ struct ei_device *device = swipe->base.device;
+ struct ei *ei = ei_device_get_context(device);
+
+ if (swipe->base.state != EI_GESTURE_STATE_BEGUN) {
+ log_bug_client(ei_gesture_get_context(&swipe->base),
+ "gesture in invalid state for update");
+ return;
+ }
+
+ if (device->gesture_state.swipe.current_swipe != swipe) {
+ log_bug_client(
+ ei_gesture_get_context(&swipe->base),
+ "a different swipe gesture is already in process, ignoring this one");
+ return;
+ }
+
+ /* FIXME: what do we do with a gesture when the device stops emulating? */
+ if (device->state != EI_DEVICE_STATE_EMULATING) {
+ log_bug_client(ei_device_get_context(device),
+ "%s: device is not emulating",
+ __func__);
+ return;
+ }
+
+ if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
+ return;
+
+ if (!device->swipe)
+ return;
+
+ device->send_frame_event = true;
+
+ int rc = ei_gesture_swipe_request_update(device->swipe, x, y);
+ if (rc)
+ ei_disconnect(ei);
+}
+
+static void
+ei_swipe_end_cancel(struct ei_swipe *swipe, bool is_cancel)
+{
+ struct ei_device *device = swipe->base.device;
+ struct ei *ei = ei_device_get_context(device);
+
+ if (swipe->base.state != EI_GESTURE_STATE_BEGUN) {
+ log_bug_client(ei_gesture_get_context(&swipe->base),
+ "gesture in invalid state for end/cancel");
+ return;
+ }
+
+ if (device->gesture_state.swipe.current_swipe != swipe) {
+ log_bug_client(
+ ei_gesture_get_context(&swipe->base),
+ "a different swipe gesture is already in process, ignoring this one");
+ return;
+ }
+
+ /* FIXME: what do we do with a gesture when the device stops emulating? */
+ if (device->state != EI_DEVICE_STATE_EMULATING) {
+ log_bug_client(ei_device_get_context(device),
+ "%s: device is not emulating",
+ __func__);
+ return;
+ }
+
+ if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
+ return;
+
+ if (!device->swipe)
+ return;
+
+ swipe->base.state = EI_GESTURE_STATE_ENDED;
+ device->gesture_state.swipe.current_swipe = NULL;
+
+ device->send_frame_event = true;
+
+ int rc = ei_gesture_swipe_request_end(device->swipe, is_cancel);
+ if (rc)
+ ei_disconnect(ei);
+}
+
+_public_ void
+ei_swipe_end(struct ei_swipe *swipe)
+{
+ ei_swipe_end_cancel(swipe, false);
+}
+
+_public_ void
+ei_swipe_cancel(struct ei_swipe *swipe)
+{
+ ei_swipe_end_cancel(swipe, true);
+}
+
+struct ei_pinch *
+ei_pinch_new(struct ei_device *device, uint32_t nfingers)
+{
+ if (!ei_device_has_capability(device, EI_DEVICE_CAP_GESTURES)) {
+ log_bug_client(ei_device_get_context(device),
+ "%s: device is not a gesture device",
+ __func__);
+ return NULL;
+ }
+
+ if (nfingers < 2) {
+ log_bug_client(ei_device_get_context(device),
+ "pinch gestures require at least 2 fingers");
+ return NULL;
+ }
+
+ if (!device->pinch)
+ return NULL;
+
+ /* Not using the device as parent object because we need a ref
+ * to it */
+ struct ei_pinch *pinch = xalloc(sizeof *pinch);
+ ei_gesture_init(&pinch->base, device, nfingers);
+
+ return pinch;
+}
+
+_public_ struct ei_pinch *
+ei_pinch_ref(struct ei_pinch *pinch)
+{
+ return (struct ei_pinch *)ei_gesture_ref(&pinch->base);
+}
+
+_public_ struct ei_pinch *
+ei_pinch_unref(struct ei_pinch *pinch)
+{
+ if (!pinch)
+ return NULL;
+
+ struct ei_device *device = ei_gesture_get_device(&pinch->base);
+
+ if (pinch == device->gesture_state.pinch.current_pinch) {
+ if (pinch->base.state == EI_GESTURE_STATE_BEGUN &&
+ device->state == EI_DEVICE_STATE_EMULATING) {
+ ei_pinch_cancel(pinch);
+ }
+ }
+
+ return (struct ei_pinch *)ei_gesture_unref(&pinch->base);
+}
+
+struct ei *
+ei_pinch_get_context(struct ei_pinch *pinch)
+{
+ return ei_gesture_get_context(&pinch->base);
+}
+
+_public_ void
+ei_pinch_begin(struct ei_pinch *pinch)
+{
+ struct ei_device *device = pinch->base.device;
+ struct ei *ei = ei_device_get_context(device);
+
+ if (pinch->base.state != EI_GESTURE_STATE_NEW) {
+ log_bug_client(ei_gesture_get_context(&pinch->base), "gesture cannot begin twice");
+ return;
+ }
+
+ if (device->gesture_state.pinch.current_pinch) {
+ log_bug_client(
+ ei_gesture_get_context(&pinch->base),
+ "a different pinch gesture is already in process, ignoring this one");
+ return;
+ }
+
+ if (device->state != EI_DEVICE_STATE_EMULATING) {
+ log_bug_client(ei_device_get_context(device),
+ "%s: device is not emulating",
+ __func__);
+ return;
+ }
+
+ if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
+ return;
+
+ if (!device->pinch)
+ return;
+
+ if (pinch->base.nfingers == 0) {
+ log_bug_client(ei_device_get_context(device),
+ "zero-finger gestures are invalid, ignoring gesture");
+ return;
+ }
+
+ device->gesture_state.pinch.current_pinch = pinch;
+
+ device->send_frame_event = true;
+
+ pinch->base.state = EI_GESTURE_STATE_BEGUN;
+ int rc = ei_gesture_pinch_request_begin(device->pinch, pinch->base.nfingers);
+ if (rc)
+ ei_disconnect(ei);
+}
+
+_public_ void
+ei_pinch_update(struct ei_pinch *pinch, double x, double y, double scale, double rotation)
+{
+ struct ei_device *device = pinch->base.device;
+ struct ei *ei = ei_device_get_context(device);
+
+ if (pinch->base.state != EI_GESTURE_STATE_BEGUN) {
+ log_bug_client(ei_gesture_get_context(&pinch->base),
+ "gesture in invalid state for update");
+ return;
+ }
+
+ if (device->gesture_state.pinch.current_pinch != pinch) {
+ log_bug_client(
+ ei_gesture_get_context(&pinch->base),
+ "a different pinch gesture is already in process, ignoring this one");
+ return;
+ }
+
+ /* FIXME: what do we do with a gesture when the device stops emulating? */
+ if (device->state != EI_DEVICE_STATE_EMULATING) {
+ log_bug_client(ei_device_get_context(device),
+ "%s: device is not emulating",
+ __func__);
+ return;
+ }
+
+ if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
+ return;
+
+ if (!device->pinch)
+ return;
+
+ device->send_frame_event = true;
+
+ int rc = ei_gesture_pinch_request_update(device->pinch, x, y, scale, rotation);
+ if (rc)
+ ei_disconnect(ei);
+}
+
+static void
+ei_pinch_end_cancel(struct ei_pinch *pinch, bool is_cancel)
+{
+ struct ei_device *device = pinch->base.device;
+ struct ei *ei = ei_device_get_context(device);
+
+ if (pinch->base.state != EI_GESTURE_STATE_BEGUN) {
+ log_bug_client(ei_gesture_get_context(&pinch->base),
+ "gesture in invalid state for end/cancel");
+ return;
+ }
+
+ if (device->gesture_state.pinch.current_pinch != pinch) {
+ log_bug_client(
+ ei_gesture_get_context(&pinch->base),
+ "a different pinch gesture is already in process, ignoring this one");
+ return;
+ }
+
+ /* FIXME: what do we do with a gesture when the device stops emulating? */
+ if (device->state != EI_DEVICE_STATE_EMULATING) {
+ log_bug_client(ei_device_get_context(device),
+ "%s: device is not emulating",
+ __func__);
+ return;
+ }
+
+ if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
+ return;
+
+ if (!device->pinch)
+ return;
+
+ pinch->base.state = EI_GESTURE_STATE_ENDED;
+ device->gesture_state.pinch.current_pinch = NULL;
+
+ device->send_frame_event = true;
+
+ int rc = ei_gesture_pinch_request_end(device->pinch, is_cancel);
+ if (rc)
+ ei_disconnect(ei);
+}
+
+_public_ void
+ei_pinch_end(struct ei_pinch *pinch)
+{
+ ei_pinch_end_cancel(pinch, false);
+}
+
+_public_ void
+ei_pinch_cancel(struct ei_pinch *pinch)
+{
+ ei_pinch_end_cancel(pinch, true);
+}
+
+struct ei_hold *
+ei_hold_new(struct ei_device *device, uint32_t nfingers)
+{
+ if (!ei_device_has_capability(device, EI_DEVICE_CAP_GESTURES)) {
+ log_bug_client(ei_device_get_context(device),
+ "%s: device is not a gesture device",
+ __func__);
+ return NULL;
+ }
+
+ if (nfingers == 0) {
+ log_bug_client(ei_device_get_context(device),
+ "zero-finger gestures are not possible");
+ return NULL;
+ }
+
+ if (!device->hold)
+ return NULL;
+
+ /* Not using the device as parent object because we need a ref
+ * to it */
+ struct ei_hold *hold = xalloc(sizeof *hold);
+ ei_gesture_init(&hold->base, device, nfingers);
+
+ return hold;
+}
+
+_public_ struct ei_hold *
+ei_hold_ref(struct ei_hold *hold)
+{
+ return (struct ei_hold *)ei_gesture_ref(&hold->base);
+}
+
+_public_ struct ei_hold *
+ei_hold_unref(struct ei_hold *hold)
+{
+ if (!hold)
+ return NULL;
+
+ struct ei_device *device = ei_gesture_get_device(&hold->base);
+
+ if (hold == device->gesture_state.hold.current_hold) {
+ if (hold->base.state == EI_GESTURE_STATE_BEGUN &&
+ device->state == EI_DEVICE_STATE_EMULATING) {
+ ei_hold_cancel(hold);
+ }
+ }
+
+ return (struct ei_hold *)ei_gesture_unref(&hold->base);
+}
+
+struct ei *
+ei_hold_get_context(struct ei_hold *hold)
+{
+ return ei_gesture_get_context(&hold->base);
+}
+
+_public_ void
+ei_hold_begin(struct ei_hold *hold)
+{
+ struct ei_device *device = hold->base.device;
+ struct ei *ei = ei_device_get_context(device);
+
+ if (hold->base.state != EI_GESTURE_STATE_NEW) {
+ log_bug_client(ei_gesture_get_context(&hold->base), "gesture cannot begin twice");
+ return;
+ }
+
+ if (device->gesture_state.hold.current_hold) {
+ log_bug_client(ei_gesture_get_context(&hold->base),
+ "a different hold gesture is already in process, ignoring this one");
+ return;
+ }
+
+ if (device->state != EI_DEVICE_STATE_EMULATING) {
+ log_bug_client(ei_device_get_context(device),
+ "%s: device is not emulating",
+ __func__);
+ return;
+ }
+
+ if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
+ return;
+
+ if (!device->hold)
+ return;
+
+ if (hold->base.nfingers == 0) {
+ log_bug_client(ei_device_get_context(device),
+ "zero-finger gestures are invalid, ignoring gesture");
+ return;
+ }
+
+ device->gesture_state.hold.current_hold = hold;
+
+ device->send_frame_event = true;
+
+ hold->base.state = EI_GESTURE_STATE_BEGUN;
+ int rc = ei_gesture_hold_request_begin(device->hold, hold->base.nfingers);
+ if (rc)
+ ei_disconnect(ei);
+}
+
+static void
+ei_hold_end_cancel(struct ei_hold *hold, bool is_cancel)
+{
+ struct ei_device *device = hold->base.device;
+ struct ei *ei = ei_device_get_context(device);
+
+ if (hold->base.state != EI_GESTURE_STATE_BEGUN) {
+ log_bug_client(ei_gesture_get_context(&hold->base),
+ "gesture in invalid state for end/cancel");
+ return;
+ }
+
+ if (device->gesture_state.hold.current_hold != hold) {
+ log_bug_client(ei_gesture_get_context(&hold->base),
+ "a different hold gesture is already in process, ignoring this one");
+ return;
+ }
+
+ /* FIXME: what do we do with a gesture when the device stops emulating? */
+ if (device->state != EI_DEVICE_STATE_EMULATING) {
+ log_bug_client(ei_device_get_context(device),
+ "%s: device is not emulating",
+ __func__);
+ return;
+ }
+
+ if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
+ return;
+
+ if (!device->hold)
+ return;
+
+ hold->base.state = EI_GESTURE_STATE_ENDED;
+ device->gesture_state.hold.current_hold = NULL;
+
+ device->send_frame_event = true;
+
+ int rc = ei_gesture_hold_request_end(device->hold, is_cancel);
+ if (rc)
+ ei_disconnect(ei);
+}
+
+_public_ void
+ei_hold_end(struct ei_hold *hold)
+{
+ ei_hold_end_cancel(hold, false);
+}
+
+_public_ void
+ei_hold_cancel(struct ei_hold *hold)
+{
+ ei_hold_end_cancel(hold, true);
+}
diff --git a/src/libei-gestures.h b/src/libei-gestures.h
new file mode 100644
index 0000000..2efaf13
--- /dev/null
+++ b/src/libei-gestures.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2024 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include "util-list.h"
+#include "util-object.h"
+
+#include "brei-shared.h"
+
+struct ei;
+struct ei_device;
+
+/* This is the protocol-only object, not exposed in the API. See ei_swipe() for
+ * the C API object */
+struct ei_gesture_swipe {
+ struct object object;
+ struct brei_object proto_object;
+};
+
+OBJECT_DECLARE_GETTER(ei_gesture_swipe, context, struct ei *);
+OBJECT_DECLARE_GETTER(ei_gesture_swipe, device, struct ei_device *);
+OBJECT_DECLARE_GETTER(ei_gesture_swipe, proto_object, const struct brei_object *);
+OBJECT_DECLARE_GETTER(ei_gesture_swipe, interface, const struct ei_gesture_swipe_interface *);
+OBJECT_DECLARE_REF(ei_gesture_swipe);
+OBJECT_DECLARE_UNREF(ei_gesture_swipe);
+
+struct ei_gesture_swipe *
+ei_gesture_swipe_new(struct ei_device *device, object_id_t id, uint32_t version);
+
+/* This is the protocol-only object, not exposed in the API. See ei_pinch() for
+ * the C API object */
+struct ei_gesture_pinch {
+ struct object object;
+ struct brei_object proto_object;
+};
+
+OBJECT_DECLARE_GETTER(ei_gesture_pinch, context, struct ei *);
+OBJECT_DECLARE_GETTER(ei_gesture_pinch, device, struct ei_device *);
+OBJECT_DECLARE_GETTER(ei_gesture_pinch, proto_object, const struct brei_object *);
+OBJECT_DECLARE_GETTER(ei_gesture_pinch, interface, const struct ei_gesture_pinch_interface *);
+OBJECT_DECLARE_REF(ei_gesture_pinch);
+OBJECT_DECLARE_UNREF(ei_gesture_pinch);
+
+struct ei_gesture_pinch *
+ei_gesture_pinch_new(struct ei_device *device, object_id_t id, uint32_t version);
+
+/* This is the protocol-only object, not exposed in the API. See ei_hold() for
+ * the C API object */
+struct ei_gesture_hold {
+ struct object object;
+ struct brei_object proto_object;
+};
+
+OBJECT_DECLARE_GETTER(ei_gesture_hold, context, struct ei *);
+OBJECT_DECLARE_GETTER(ei_gesture_hold, device, struct ei_device *);
+OBJECT_DECLARE_GETTER(ei_gesture_hold, proto_object, const struct brei_object *);
+OBJECT_DECLARE_GETTER(ei_gesture_hold, interface, const struct ei_gesture_hold_interface *);
+OBJECT_DECLARE_REF(ei_gesture_hold);
+OBJECT_DECLARE_UNREF(ei_gesture_hold);
+
+struct ei_gesture_hold *
+ei_gesture_hold_new(struct ei_device *device, object_id_t id, uint32_t version);
+
+enum ei_gesture_state { EI_GESTURE_STATE_NEW, EI_GESTURE_STATE_BEGUN, EI_GESTURE_STATE_ENDED };
+
+/* This is the parent object for all the C API gestures */
+struct ei_gesture {
+ struct object object;
+ struct ei_device *device;
+ enum ei_gesture_state state;
+ uint32_t nfingers;
+};
+
+OBJECT_DECLARE_REF(ei_gesture);
+OBJECT_DECLARE_UNREF(ei_gesture);
+OBJECT_DECLARE_GETTER(ei_gesture, context, struct ei *);
+OBJECT_DECLARE_GETTER(ei_gesture, device, struct ei_device *);
+
+struct ei_swipe {
+ struct ei_gesture base;
+};
+
+struct ei_pinch {
+ struct ei_gesture base;
+};
+
+struct ei_hold {
+ struct ei_gesture base;
+};
+
+struct ei_swipe *
+ei_swipe_new(struct ei_device *device, uint32_t nfingers);
+OBJECT_DECLARE_GETTER(ei_swipe, context, struct ei *);
+
+struct ei_pinch *
+ei_pinch_new(struct ei_device *device, uint32_t nfingers);
+OBJECT_DECLARE_GETTER(ei_pinch, context, struct ei *);
+
+struct ei_hold *
+ei_hold_new(struct ei_device *device, uint32_t nfingers);
+OBJECT_DECLARE_GETTER(ei_hold, context, struct ei *);
diff --git a/src/libei-handshake.c b/src/libei-handshake.c
index 0bc6da2..c4bd80e 100644
--- a/src/libei-handshake.c
+++ b/src/libei-handshake.c
@@ -116,6 +116,15 @@ ei_handshake_initialize(struct ei_handshake *setup, uint32_t version)
ei_handshake_request_interface_version(setup,
EI_TOUCHSCREEN_INTERFACE_NAME,
v->ei_touchscreen);
+ ei_handshake_request_interface_version(setup,
+ EI_GESTURE_SWIPE_INTERFACE_NAME,
+ v->ei_gesture_swipe);
+ ei_handshake_request_interface_version(setup,
+ EI_GESTURE_PINCH_INTERFACE_NAME,
+ v->ei_gesture_pinch);
+ ei_handshake_request_interface_version(setup,
+ EI_GESTURE_HOLD_INTERFACE_NAME,
+ v->ei_gesture_hold);
ei_handshake_request_interface_version(setup, EI_TEXT_INTERFACE_NAME, v->ei_text);
}
diff --git a/src/libei-private.h b/src/libei-private.h
index 141f3bc..29c9ae2 100644
--- a/src/libei-private.h
+++ b/src/libei-private.h
@@ -39,6 +39,7 @@
#include "libei-connection.h"
#include "libei-device.h"
#include "libei-event.h"
+#include "libei-gestures.h"
#include "libei-handshake.h"
#include "libei-keyboard.h"
#include "libei-pingpong.h"
@@ -77,6 +78,9 @@ struct ei_interface_versions {
uint32_t ei_button;
uint32_t ei_keyboard;
uint32_t ei_touchscreen;
+ uint32_t ei_gesture_swipe;
+ uint32_t ei_gesture_pinch;
+ uint32_t ei_gesture_hold;
uint32_t ei_text;
};
@@ -254,6 +258,43 @@ ei_queue_text_utf8_event(struct ei_device *device, const char *utf8);
void
ei_sync_event_send_done(struct ei_event *e);
+void
+ei_queue_swipe_begin_event(struct ei_device *device, uint32_t nfingers);
+
+void
+ei_queue_swipe_update_event(struct ei_device *device, double dx, double dy);
+
+void
+ei_queue_swipe_end_event(struct ei_device *device, bool is_cancelled);
+
+void
+ei_queue_swipe_cancelled_event(struct ei_device *device);
+
+void
+ei_queue_pinch_begin_event(struct ei_device *device, uint32_t nfingers);
+
+void
+ei_queue_pinch_update_event(struct ei_device *device,
+ double dx,
+ double dy,
+ double scale,
+ double rotation);
+
+void
+ei_queue_pinch_end_event(struct ei_device *device, bool is_cancelled);
+
+void
+ei_queue_pinch_cancelled_event(struct ei_device *device);
+
+void
+ei_queue_hold_begin_event(struct ei_device *device, uint32_t nfingers);
+
+void
+ei_queue_hold_end_event(struct ei_device *device, bool is_cancelled);
+
+void
+ei_queue_hold_cancelled_event(struct ei_device *device);
+
_printf_(6, 7) void
ei_log_msg(struct ei *ei,
enum ei_log_priority priority,
diff --git a/src/libei-seat.c b/src/libei-seat.c
index ebb6b89..0d888e1 100644
--- a/src/libei-seat.c
+++ b/src/libei-seat.c
@@ -251,6 +251,10 @@ ei_seat_has_capability(struct ei_seat *seat, enum ei_device_capability cap)
return seat->capabilities.map[EI_SCROLL_INTERFACE_INDEX] != 0;
case EI_DEVICE_CAP_BUTTON:
return seat->capabilities.map[EI_BUTTON_INTERFACE_INDEX] != 0;
+ case EI_DEVICE_CAP_GESTURES:
+ return seat->capabilities.map[EI_GESTURE_SWIPE_INTERFACE_INDEX] != 0 ||
+ seat->capabilities.map[EI_GESTURE_PINCH_INTERFACE_INDEX] != 0 ||
+ seat->capabilities.map[EI_GESTURE_HOLD_INTERFACE_INDEX] != 0;
case EI_DEVICE_CAP_TEXT:
return seat->capabilities.map[EI_TEXT_INTERFACE_INDEX] != 0;
}
@@ -287,6 +291,10 @@ ei_seat_cap_mask(struct ei_seat *seat, enum ei_device_capability cap)
return seat->capabilities.map[EI_BUTTON_INTERFACE_INDEX];
case EI_DEVICE_CAP_SCROLL:
return seat->capabilities.map[EI_SCROLL_INTERFACE_INDEX];
+ case EI_DEVICE_CAP_GESTURES:
+ return seat->capabilities.map[EI_GESTURE_SWIPE_INTERFACE_INDEX] |
+ seat->capabilities.map[EI_GESTURE_PINCH_INTERFACE_INDEX] |
+ seat->capabilities.map[EI_GESTURE_HOLD_INTERFACE_INDEX];
case EI_DEVICE_CAP_TEXT:
return seat->capabilities.map[EI_TEXT_INTERFACE_INDEX];
}
diff --git a/src/libei.c b/src/libei.c
index a6ec98a..17af78e 100644
--- a/src/libei.c
+++ b/src/libei.c
@@ -132,6 +132,9 @@ ei_create_context(bool is_sender, void *user_data)
.ei_keyboard = VERSION_V(1),
.ei_text = VERSION_V(1),
.ei_touchscreen = VERSION_V(2),
+ .ei_gesture_swipe = VERSION_V(1),
+ .ei_gesture_pinch = VERSION_V(1),
+ .ei_gesture_hold = VERSION_V(1),
};
/* This must be v1 until the server tells us otherwise */
ei->handshake = ei_handshake_new(ei, VERSION_V(1));
@@ -250,6 +253,17 @@ update_event_timestamp(struct ei_event *event, uint64_t time)
case EI_EVENT_TOUCH_DOWN:
case EI_EVENT_TOUCH_UP:
case EI_EVENT_TOUCH_MOTION:
+ case EI_EVENT_SWIPE_BEGIN:
+ case EI_EVENT_SWIPE_UPDATE:
+ case EI_EVENT_SWIPE_END:
+ case EI_EVENT_SWIPE_CANCELLED:
+ case EI_EVENT_PINCH_BEGIN:
+ case EI_EVENT_PINCH_UPDATE:
+ case EI_EVENT_PINCH_END:
+ case EI_EVENT_PINCH_CANCELLED:
+ case EI_EVENT_HOLD_BEGIN:
+ case EI_EVENT_HOLD_END:
+ case EI_EVENT_HOLD_CANCELLED:
case EI_EVENT_TEXT_KEYSYM:
case EI_EVENT_TEXT_UTF8:
if (event->timestamp != 0) {
@@ -287,6 +301,17 @@ queue_event(struct ei *ei, struct ei_event *event)
case EI_EVENT_TOUCH_DOWN:
case EI_EVENT_TOUCH_UP:
case EI_EVENT_TOUCH_MOTION:
+ case EI_EVENT_SWIPE_BEGIN:
+ case EI_EVENT_SWIPE_UPDATE:
+ case EI_EVENT_SWIPE_END:
+ case EI_EVENT_SWIPE_CANCELLED:
+ case EI_EVENT_PINCH_BEGIN:
+ case EI_EVENT_PINCH_UPDATE:
+ case EI_EVENT_PINCH_END:
+ case EI_EVENT_PINCH_CANCELLED:
+ case EI_EVENT_HOLD_BEGIN:
+ case EI_EVENT_HOLD_END:
+ case EI_EVENT_HOLD_CANCELLED:
case EI_EVENT_TEXT_KEYSYM:
case EI_EVENT_TEXT_UTF8:
prefix = "pending ";
@@ -630,6 +655,136 @@ ei_queue_touch_cancel_event(struct ei_device *device, uint32_t touchid)
queue_event(ei_device_get_context(device), e);
}
+void
+ei_queue_swipe_begin_event(struct ei_device *device, uint32_t nfingers)
+{
+ struct ei_event *e = ei_event_new_for_device(device);
+
+ e->type = EI_EVENT_SWIPE_BEGIN;
+ e->gestures.nfingers = nfingers;
+
+ queue_event(ei_device_get_context(device), e);
+}
+
+void
+ei_queue_swipe_update_event(struct ei_device *device, double dx, double dy)
+{
+ struct ei_event *e = ei_event_new_for_device(device);
+
+ e->type = EI_EVENT_SWIPE_UPDATE;
+ e->gestures.nfingers = device->gesture_state.swipe.nfingers;
+ e->gestures.dx = dx;
+ e->gestures.dy = dy;
+
+ queue_event(ei_device_get_context(device), e);
+}
+
+void
+ei_queue_swipe_end_event(struct ei_device *device, bool is_cancelled)
+{
+ struct ei_event *e = ei_event_new_for_device(device);
+
+ e->type = EI_EVENT_SWIPE_END;
+ e->gestures.nfingers = device->gesture_state.swipe.nfingers;
+ e->gestures.is_cancelled = is_cancelled;
+
+ queue_event(ei_device_get_context(device), e);
+}
+
+void
+ei_queue_swipe_cancelled_event(struct ei_device *device)
+{
+ struct ei_event *e = ei_event_new_for_device(device);
+
+ e->type = EI_EVENT_SWIPE_CANCELLED;
+
+ queue_event(ei_device_get_context(device), e);
+}
+
+void
+ei_queue_pinch_begin_event(struct ei_device *device, uint32_t nfingers)
+{
+ struct ei_event *e = ei_event_new_for_device(device);
+
+ e->type = EI_EVENT_PINCH_BEGIN;
+ e->gestures.nfingers = nfingers;
+
+ queue_event(ei_device_get_context(device), e);
+}
+
+void
+ei_queue_pinch_update_event(struct ei_device *device,
+ double dx,
+ double dy,
+ double scale,
+ double rotation)
+{
+ struct ei_event *e = ei_event_new_for_device(device);
+ e->type = EI_EVENT_PINCH_UPDATE;
+ e->gestures.nfingers = device->gesture_state.pinch.nfingers;
+ e->gestures.scale = scale;
+ e->gestures.degrees = rotation;
+ e->gestures.dx = dx;
+ e->gestures.dy = dy;
+
+ queue_event(ei_device_get_context(device), e);
+}
+
+void
+ei_queue_pinch_end_event(struct ei_device *device, bool is_cancelled)
+{
+ struct ei_event *e = ei_event_new_for_device(device);
+
+ e->type = EI_EVENT_PINCH_END;
+ e->gestures.nfingers = device->gesture_state.pinch.nfingers;
+ e->gestures.is_cancelled = is_cancelled;
+
+ queue_event(ei_device_get_context(device), e);
+}
+
+void
+ei_queue_pinch_cancelled_event(struct ei_device *device)
+{
+ struct ei_event *e = ei_event_new_for_device(device);
+
+ e->type = EI_EVENT_PINCH_CANCELLED;
+
+ queue_event(ei_device_get_context(device), e);
+}
+
+void
+ei_queue_hold_begin_event(struct ei_device *device, uint32_t nfingers)
+{
+ struct ei_event *e = ei_event_new_for_device(device);
+
+ e->type = EI_EVENT_HOLD_BEGIN;
+ e->gestures.nfingers = nfingers;
+
+ queue_event(ei_device_get_context(device), e);
+}
+
+void
+ei_queue_hold_end_event(struct ei_device *device, bool is_cancelled)
+{
+ struct ei_event *e = ei_event_new_for_device(device);
+
+ e->type = EI_EVENT_HOLD_END;
+ e->gestures.nfingers = device->gesture_state.hold.nfingers;
+ e->gestures.is_cancelled = is_cancelled;
+
+ queue_event(ei_device_get_context(device), e);
+}
+
+void
+ei_queue_hold_cancelled_event(struct ei_device *device)
+{
+ struct ei_event *e = ei_event_new_for_device(device);
+
+ e->type = EI_EVENT_HOLD_CANCELLED;
+
+ queue_event(ei_device_get_context(device), e);
+}
+
void
ei_queue_text_keysym_event(struct ei_device *device, uint32_t keysym, bool is_press)
{
diff --git a/src/libei.h b/src/libei.h
index 6ffebc6..3588134 100644
--- a/src/libei.h
+++ b/src/libei.h
@@ -215,6 +215,39 @@ struct ei_region;
*/
struct ei_touch;
+/**
+ * @struct ei_swipe
+ *
+ * A single swipe gesture.
+ *
+ * @see ei_device_swipe_new
+ * @see ei_swipe_begin
+ * @see ei_swipe_update
+ * @see ei_swipe_end
+ */
+struct ei_swipe;
+/**
+ * @struct ei_pinch
+ *
+ * A single pinch gesture.
+ *
+ * @see ei_device_pinch_new
+ * @see ei_pinch_begin
+ * @see ei_pinch_update
+ * @see ei_pinch_end
+ */
+struct ei_pinch;
+/**
+ * @struct ei_hold
+ *
+ * A single hold gesture.
+ *
+ * @see ei_device_hold_new
+ * @see ei_hold_begin
+ * @see ei_hold_end
+ */
+struct ei_hold;
+
/**
* @struct ei_ping
*
@@ -297,12 +330,24 @@ enum ei_device_capability {
* The device can send button events
*/
EI_DEVICE_CAP_BUTTON = (1 << 5),
+ /**
+ * The device can send gesture events.
+ *
+ * The ei protocol separates available gestures
+ * into swipe, pinch and hold and a device may support
+ * any number of those independently. For simplicity,
+ * the libei C API only provides one capability that
+ * is set if the device supports any of those gestures.
+ *
+ * @since 1.6
+ */
+ EI_DEVICE_CAP_GESTURES = (1 << 6),
/**
* The device can send text-like data
*
* @since 1.6
*/
- EI_DEVICE_CAP_TEXT = (1 << 6),
+ EI_DEVICE_CAP_TEXT = (1 << 7),
};
/**
@@ -406,6 +451,7 @@ enum ei_event_type {
* - any buttons or keys logically down are released
* - any modifiers logically down are released
* - any touches logically down are released
+ * - any gestures logically active are ended
*
* Sender clients must wait until @ref EI_EVENT_DEVICE_RESUMED
* before sending events.
@@ -606,6 +652,127 @@ enum ei_event_type {
*/
EI_EVENT_TOUCH_MOTION,
+ /**
+ * Event for a swipe begin. Only one swipe gesture may be active
+ * at any time.
+ *
+ * @note This event is only generated on a receiver ei context.
+ * See ei_device_swipe_new() and ei_swipe_begin() for the sender context API.
+ *
+ * @since 1.6
+ */
+ EI_EVENT_SWIPE_BEGIN = 900,
+ /**
+ * Event for an update to the current ongoing swipe gesture.
+ *
+ * This library accumulates all updates to the gesture within the same
+ * device frame into one gesture event.
+ *
+ * @note This event is only generated on a receiver ei context.
+ * See ei_device_swipe_new() and ei_swipe_update() for the sender context API.
+ *
+ * @since 1.6
+ */
+ EI_EVENT_SWIPE_UPDATE,
+ /**
+ * Event for the logical end to the current ongoing swipe gesture.
+ *
+ * @note This event is only generated on a receiver ei context.
+ * See ei_device_swipe_new() and ei_swipe_end() for the sender context API.
+ *
+ * @since 1.6
+ */
+ EI_EVENT_SWIPE_END,
+ /**
+ * Event for an EIS-initiated cancellation of a gesture.
+ *
+ * If this event is received the current on going gesture should be
+ * released without further updates or sending an end event (see
+ * ei_swipe_unref()).
+ *
+ * @note This event is only generated on a sender ei context.
+ *
+ * @since 1.6
+ */
+ EI_EVENT_SWIPE_CANCELLED,
+
+ /**
+ * Event for a pinch begin. Only one pinch gesture may be active
+ * at any time.
+ *
+ * @note This event is only generated on a receiver ei context.
+ * See ei_device_pinch_new() and ei_pinch_begin() for the sender context API.
+ *
+ * @since 1.6
+ */
+ EI_EVENT_PINCH_BEGIN = 910,
+ /**
+ * Event for an update to the current ongoing pinch gesture.
+ *
+ * This library accumulates all updates to the gesture within the same
+ * device frame into one gesture event, e.g. where the gestures moves
+ * and rotates only one update event is generated.
+ *
+ * @note This event is only generated on a receiver ei context.
+ * See ei_device_pinch_new() and ei_pinch_update() for the sender context API.
+ *
+ * @since 1.6
+ */
+ EI_EVENT_PINCH_UPDATE,
+ /**
+ * Event for the logical end to the current ongoing pinch gesture.
+ *
+ * @note This event is only generated on a receiver ei context.
+ * See ei_device_pinch_new() and ei_pinch_end() for the sender context API.
+ *
+ * @since 1.6
+ */
+ EI_EVENT_PINCH_END,
+ /**
+ * Event for an EIS-initiated cancellation of a gesture.
+ *
+ * If this event is received the current on going gesture should be
+ * released without further updates or sending an end event (see
+ * ei_pinch_unref()).
+ *
+ * @note This event is only generated on a sender ei context.
+ *
+ * @since 1.6
+ */
+ EI_EVENT_PINCH_CANCELLED,
+
+ /**
+ * Event for a hold begin. Only one hold gesture may be active
+ * at any time.
+ *
+ * @note This event is only generated on a receiver ei context.
+ * See ei_device_hold_new() and ei_hold_begin() for the sender context API.
+ *
+ * @since 1.6
+ */
+ EI_EVENT_HOLD_BEGIN = 920,
+ /**
+ * Event for the logical end to the current ongoing hold gesture.
+ *
+ * @note This event is only generated on a receiver ei context.
+ * See ei_device_hold_new() and ei_hold_end() for the sender context API.
+ *
+ * @since 1.6
+ */
+ EI_EVENT_HOLD_END,
+ /**
+ * Event for an EIS-initiated cancellation of a gesture.
+ *
+ * If this event is received the current on going gesture should be
+ * released without further updates or sending an end event (see
+ * ei_hold_unref()).
+ *
+ * @note This event is only generated on a sender ei context.
+ *
+ * @since 1.6
+ */
+ EI_EVENT_HOLD_CANCELLED,
+
/**
* Event for a single keysym logically pressed/released on this device.
* The keysym is an XKB-compatible keysym (not key code!) and may not be
@@ -616,7 +783,7 @@ enum ei_event_type {
*
* @since 1.6
*/
- EI_EVENT_TEXT_KEYSYM = 900,
+ EI_EVENT_TEXT_KEYSYM = 930,
/**
* Event for a UTF-8 compatible text sequence sent by this device.
@@ -2079,6 +2246,297 @@ ei_touch_get_user_data(struct ei_touch *touch);
struct ei_device *
ei_touch_get_device(struct ei_touch *touch);
+/**
+ * @ingroup libei-sender
+ *
+ * Initiate a new swipe gesture on a device with the @ref EI_DEVICE_CAP_GESTURES
+ * capability. This gesture does not immediately send events, use
+ * ei_swipe_begin(), ei_swipe_update() and ei_swipe_end().
+ *
+ * The returned gesture has a refcount of at least 1, use ei_swipe_unref() to
+ * release resources associated with this gesture
+ *
+ * This method is only available on an ei sender context.
+ *
+ * If the underlying device does not support swipe gestures, this function
+ * returns NULL.
+ *
+ * @param device A device with @ref EI_DEVICE_CAP_GESTURES capability
+ * @param nfingers The number of fingers, must be greater than 0
+ * @return A new swipe gesture or NULL on error
+ *
+ * @since 1.6
+ */
+struct ei_swipe *
+ei_device_swipe_new(struct ei_device *device, uint32_t nfingers);
+
+/**
+ * @ingroup libei-sender
+ *
+ * Starts the swipe gesture.
+ *
+ * This function can only be called once on an ei_swipe object. Further
+ * calls to ei_swipe_begin() on the same object are silently ignored.
+ *
+ * @param swipe A newly created swipe gesture
+ *
+ * @since 1.6
+ */
+void
+ei_swipe_begin(struct ei_swipe *swipe);
+
+/**
+ * @ingroup libei-sender
+ *
+ * Update the swipe gestures's position by the given relative delta.
+ *
+ * @since 1.6
+ */
+void
+ei_swipe_update(struct ei_swipe *swipe, double x, double y);
+
+/**
+ * @ingroup libei-sender
+ *
+ * Release this swipe. After this call, the swipe event becomes inert and
+ * no longer responds to either ei_swipe_begin(),
+ * ei_swipe_update(), ei_swipe_cancel() or
+ * ei_swipe_end() and the caller should call ei_swipe_unref().
+ *
+ * @since 1.6
+ */
+void
+ei_swipe_end(struct ei_swipe *swipe);
+
+/**
+ * @ingroup libei-sender
+ *
+ * Cancel this swipe. After this call, the swipe event becomes inert and
+ * no longer responds to either ei_swipe_begin(),
+ * ei_swipe_update(), ei_swipe_cancel() or
+ * ei_swipe_end() and the caller should call ei_swipe_unref().
+ *
+ * @since 1.6
+ */
+void
+ei_swipe_cancel(struct ei_swipe *swipe);
+
+/**
+ * @ingroup libei-sender
+ *
+ * Increase the refcount of this struct by one. Use ei_swipe_unref() to
+ * decrease the refcount.
+ *
+ * @return the argument passed into the function
+ *
+ * @since 1.6
+ */
+struct ei_swipe *
+ei_swipe_ref(struct ei_swipe *swipe);
+
+/**
+ * @ingroup libei-sender
+ *
+ * Decrease the refcount of this struct by one. When the refcount reaches
+ * zero, all allocated resources for this struct are released.
+ *
+ * @return always NULL
+ *
+ * @since 1.6
+ */
+struct ei_swipe *
+ei_swipe_unref(struct ei_swipe *swipe);
+
+/**
+ * @ingroup libei-sender
+ *
+ * Initiate a new pinch gesture on a device with the @ref EI_DEVICE_CAP_GESTURES
+ * capability. This gesture does not immediately send events, use
+ * ei_pinch_begin(), ei_pinch_update() and ei_pinch_end().
+ *
+ * The returned gesture has a refcount of at least 1, use ei_pinch_unref() to
+ * release resources associated with this gesture
+ *
+ * This method is only available on an ei sender context.
+ *
+ * If the underlying device does not support pinch gestures, this function
+ * returns NULL.
+ *
+ * @param device A device with @ref EI_DEVICE_CAP_GESTURES capability
+ * @param nfingers The number of fingers, must be at least 2
+ * @return A new pinch gesture or NULL on error
+ *
+ * @since 1.6
+ */
+struct ei_pinch *
+ei_device_pinch_new(struct ei_device *device, uint32_t nfingers);
+
+/**
+ * @ingroup libei-sender
+ *
+ * Starts the pinch gesture.
+ *
+ * This function can only be called once on an ei_pinch object. Further
+ * calls to ei_pinch_begin() on the same object are silently ignored.
+ *
+ * @param pinch A newly created pinch gesture
+ *
+ * @since 1.6
+ */
+void
+ei_pinch_begin(struct ei_pinch *pinch);
+
+/**
+ * @ingroup libei-sender
+ *
+ * Update the pinch gestures's position by the given relative delta.
+ *
+ * @since 1.6
+ */
+void
+ei_pinch_update(struct ei_pinch *pinch, double x, double y, double scale, double rotation);
+
+/**
+ * @ingroup libei-sender
+ *
+ * Release this pinch. After this call, the pinch event becomes inert and
+ * no longer responds to either ei_pinch_begin(),
+ * ei_pinch_update(), ei_pinch_cancel() or
+ * ei_pinch_end() and the caller should call ei_pinch_unref().
+ *
+ * @since 1.6
+ */
+void
+ei_pinch_end(struct ei_pinch *pinch);
+
+/**
+ * @ingroup libei-sender
+ *
+ * Cancel this pinch. After this call, the pinch event becomes inert and
+ * no longer responds to either ei_pinch_begin(),
+ * ei_pinch_update(), ei_pinch_cancel() or
+ * ei_pinch_end() and the caller should call ei_pinch_unref().
+ *
+ * @since 1.6
+ */
+void
+ei_pinch_cancel(struct ei_pinch *pinch);
+
+/**
+ * @ingroup libei-sender
+ *
+ * Increase the refcount of this struct by one. Use ei_pinch_unref() to
+ * decrease the refcount.
+ *
+ * @return the argument passed into the function
+ *
+ * @since 1.6
+ */
+struct ei_pinch *
+ei_pinch_ref(struct ei_pinch *pinch);
+
+/**
+ * @ingroup libei-sender
+ *
+ * Decrease the refcount of this struct by one. When the refcount reaches
+ * zero, all allocated resources for this struct are released.
+ *
+ * @return always NULL
+ *
+ * @since 1.6
+ */
+struct ei_pinch *
+ei_pinch_unref(struct ei_pinch *pinch);
+
+/**
+ * @ingroup libei-sender
+ *
+ * Initiate a new hold gesture on a device with the @ref EI_DEVICE_CAP_GESTURES
+ * capability. This gesture does not immediately send events, use
+ * ei_hold_begin() and ei_hold_end().
+ *
+ * The returned gesture has a refcount of at least 1, use ei_hold_unref() to
+ * release resources associated with this gesture
+ *
+ * This method is only available on an ei sender context.
+ *
+ * If the underlying device does not support hold gestures, this function
+ * returns NULL.
+ *
+ * @param device A device with @ref EI_DEVICE_CAP_GESTURES capability
+ * @param nfingers The number of fingers, must be greater than 0
+ * @return A new hold gesture or NULL on error
+ *
+ * @since 1.6
+ */
+struct ei_hold *
+ei_device_hold_new(struct ei_device *device, uint32_t nfingers);
+
+/**
+ * @ingroup libei-sender
+ *
+ * Starts the hold gesture.
+ *
+ * This function can only be called once on an ei_hold object. Further
+ * calls to ei_hold_begin() on the same object are silently ignored.
+ *
+ * @param hold A newly created hold gesture
+ *
+ * @since 1.6
+ */
+void
+ei_hold_begin(struct ei_hold *hold);
+
+/**
+ * @ingroup libei-sender
+ *
+ * Release this hold. After this call, the hold event becomes inert and
+ * no longer responds to either ei_hold_begin(), ei_hold_cancel() or
+ * ei_hold_end() and the caller should call ei_hold_unref().
+ *
+ * @since 1.6
+ */
+void
+ei_hold_end(struct ei_hold *hold);
+
+/**
+ * @ingroup libei-sender
+ *
+ * Cancel this hold. After this call, the hold event becomes inert and
+ * no longer responds to either ei_hold_begin(), ei_hold_cancel() or
+ * ei_hold_end() and the caller should call ei_hold_unref().
+ *
+ * @since 1.6
+ */
+void
+ei_hold_cancel(struct ei_hold *hold);
+
+/**
+ * @ingroup libei-sender
+ *
+ * Increase the refcount of this struct by one. Use ei_hold_unref() to
+ * decrease the refcount.
+ *
+ * @return the argument passed into the function
+ *
+ * @since 1.6
+ */
+struct ei_hold *
+ei_hold_ref(struct ei_hold *hold);
+
+/**
+ * @ingroup libei-sender
+ *
+ * Decrease the refcount of this struct by one. When the refcount reaches
+ * zero, all allocated resources for this struct are released.
+ *
+ * @return always NULL
+ *
+ * @since 1.6
+ */
+struct ei_hold *
+ei_hold_unref(struct ei_hold *hold);
+
/**
* Return the seat from this event.
*
@@ -2341,6 +2799,145 @@ ei_event_touch_get_y(struct ei_event *event);
bool
ei_event_touch_get_is_cancel(struct ei_event *event);
+/**
+ * @ingroup libei-receiver
+ *
+ * For an event of type @ref EI_EVENT_SWIPE_BEGIN,
+ * @ref EI_EVENT_SWIPE_UPDATE or @ref EI_EVENT_SWIPE_END
+ * return the finger count for this gesture.
+ *
+ * @since 1.6
+ */
+uint32_t
+ei_event_swipe_get_finger_count(struct ei_event *event);
+
+/**
+ * @ingroup libei-receiver
+ *
+ * For an event of type @ref EI_EVENT_SWIPE_UPDATE
+ * return the relative x movement of the logical center
+ * of the gesture in logical pixels or mm, depending on the device type.
+ *
+ * @since 1.6
+ */
+double
+ei_event_swipe_get_dx(struct ei_event *event);
+
+/**
+ * @ingroup libei-receiver
+ *
+ * For an event of type @ref EI_EVENT_SWIPE_UPDATE
+ * return the relative y movement of the logical center
+ * of the gesture in logical pixels or mm, depending on the device type.
+ *
+ * @since 1.6
+ */
+double
+ei_event_swipe_get_dy(struct ei_event *event);
+
+/**
+ * @ingroup libei-receiver
+ *
+ * For an event of type @ref EI_EVENT_SWIPE_END
+ * return true if the gesture was cancelled, false otherwise.
+ *
+ * @since 1.6
+ */
+bool
+ei_event_swipe_get_is_cancelled(struct ei_event *event);
+
+/**
+ * @ingroup libei-receiver
+ *
+ * For an event of type @ref EI_EVENT_PINCH_BEGIN,
+ * @ref EI_EVENT_PINCH_UPDATE or @ref EI_EVENT_PINCH_END
+ * return the finger count for this gesture.
+ *
+ * @since 1.6
+ */
+uint32_t
+ei_event_pinch_get_finger_count(struct ei_event *event);
+
+/**
+ * @ingroup libei-receiver
+ *
+ * For an event of type @ref EI_EVENT_PINCH_UPDATE
+ * return the relative x movement of the logical center
+ * of the gesture in logical pixels or mm, depending on the device type.
+ *
+ * @since 1.6
+ */
+double
+ei_event_pinch_get_dx(struct ei_event *event);
+
+/**
+ * @ingroup libei-receiver
+ *
+ * For an event of type @ref EI_EVENT_PINCH_UPDATE
+ * return the relative y movement of the logical center
+ * of the gesture in logical pixels or mm, depending on the device type.
+ *
+ * @since 1.6
+ */
+double
+ei_event_pinch_get_dy(struct ei_event *event);
+
+/**
+ * @ingroup libei-receiver
+ *
+ * For an event of type @ref EI_EVENT_PINCH_UPDATE
+ * return the relative angle of rotation in degrees clockwise.
+ *
+ * @since 1.6
+ */
+double
+ei_event_pinch_get_rotation(struct ei_event *event);
+
+/**
+ * @ingroup libei-receiver
+ *
+ * For an event of type @ref EI_EVENT_PINCH_UPDATE
+ * return the normalized scale of the gesture.
+ *
+ * @since 1.6
+ */
+double
+ei_event_pinch_get_scale(struct ei_event *event);
+
+/**
+ * @ingroup libei-receiver
+ *
+ * For an event of type @ref EI_EVENT_PINCH_END
+ * return true if the gesture was cancelled, false otherwise.
+ *
+ * @since 1.6
+ */
+bool
+ei_event_pinch_get_is_cancelled(struct ei_event *event);
+
+/**
+ * @ingroup libei-receiver
+ *
+ * For an event of type @ref EI_EVENT_HOLD_BEGIN
+ * or @ref EI_EVENT_HOLD_END
+ * return the finger count for this gesture.
+ *
+ * @since 1.6
+ */
+uint32_t
+ei_event_hold_get_finger_count(struct ei_event *event);
+
+/**
+ * @ingroup libei-receiver
+ *
+ * For an event of type @ref EI_EVENT_HOLD_END
+ * return true if the gesture was cancelled, false otherwise.
+ *
+ * @since 1.6
+ */
+bool
+ei_event_hold_get_is_cancelled(struct ei_event *event);
+
/**
* @ingroup libei-receiver
*
diff --git a/src/libeis-client.c b/src/libeis-client.c
index 518cc85..16e518a 100644
--- a/src/libeis-client.c
+++ b/src/libeis-client.c
@@ -539,6 +539,9 @@ eis_client_new(struct eis *eis, int fd)
.ei_keyboard = VERSION_V(1),
.ei_text = VERSION_V(1),
.ei_touchscreen = VERSION_V(2),
+ .ei_gesture_swipe = VERSION_V(1),
+ .ei_gesture_pinch = VERSION_V(1),
+ .ei_gesture_hold = VERSION_V(1),
};
struct source *s = source_new(fd, client_dispatch, client);
int rc = sink_add_source(eis->sink, s);
diff --git a/src/libeis-client.h b/src/libeis-client.h
index 1a63581..e3002f7 100644
--- a/src/libeis-client.h
+++ b/src/libeis-client.h
@@ -52,6 +52,9 @@ struct eis_client_interface_versions {
uint32_t ei_button;
uint32_t ei_keyboard;
uint32_t ei_touchscreen;
+ uint32_t ei_gesture_swipe;
+ uint32_t ei_gesture_pinch;
+ uint32_t ei_gesture_hold;
uint32_t ei_text;
};
diff --git a/src/libeis-device.c b/src/libeis-device.c
index 6335f02..86b14e8 100644
--- a/src/libeis-device.c
+++ b/src/libeis-device.c
@@ -33,6 +33,7 @@
#include "util-time.h"
#include "eis-proto.h"
+#include "libeis-gestures.h"
#include "libeis-private.h"
static_assert((int)EIS_DEVICE_TYPE_VIRTUAL == EIS_DEVICE_DEVICE_TYPE_VIRTUAL, "ABI mismatch");
@@ -147,6 +148,9 @@ eis_device_destroy(struct eis_device *device)
eis_pointer_unref(device->pointer);
eis_touchscreen_unref(device->touchscreen);
eis_keyboard_unref(device->keyboard);
+ eis_gesture_swipe_unref(device->swipe);
+ eis_gesture_pinch_unref(device->pinch);
+ eis_gesture_hold_unref(device->hold);
eis_text_unref(device->text);
free(device->name);
@@ -812,6 +816,309 @@ eis_device_get_touchscreen_interface(struct eis_device *device)
return &touchscreen_interface;
}
+static struct brei_result *
+client_msg_gesture_swipe_release(struct eis_gesture_swipe *swipe)
+{
+ struct eis_device *device = eis_gesture_swipe_get_device(swipe);
+
+ if (!eis_device_has_capability(device, EIS_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "swipe release event for non-gesture device");
+ }
+
+ if (device->swipe) {
+ eis_gesture_swipe_event_destroyed(
+ device->swipe,
+ eis_client_get_next_serial(eis_device_get_client(device)));
+ eis_gesture_swipe_unref(steal(&device->swipe));
+ }
+
+ return 0;
+}
+
+static struct brei_result *
+client_msg_gesture_swipe_begin(struct eis_gesture_swipe *swipe, uint32_t nfingers)
+{
+ struct eis_device *device = eis_gesture_swipe_get_device(swipe);
+
+ DISCONNECT_IF_RECEIVER_CONTEXT(device);
+
+ if (!eis_device_has_capability(device, EIS_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Swipe begin event for non-gesture device");
+ }
+
+ if (nfingers == 0) {
+ return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Swipe begin event with zero fingers");
+ }
+
+ if (device->state == EIS_DEVICE_STATE_EMULATING) {
+ if (device->gesture_state.swipe.is_active) {
+ return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Swipe begin event while swipe is active");
+ }
+ device->gesture_state.swipe.is_active = true;
+ device->gesture_state.swipe.nfingers = nfingers;
+ eis_queue_swipe_begin_event(device, nfingers);
+ return NULL;
+ }
+
+ return maybe_error_on_device_state(device, "swipe begin");
+}
+
+static struct brei_result *
+client_msg_gesture_swipe_update(struct eis_gesture_swipe *swipe, float dx, float dy)
+{
+ struct eis_device *device = eis_gesture_swipe_get_device(swipe);
+
+ DISCONNECT_IF_RECEIVER_CONTEXT(device);
+
+ if (!eis_device_has_capability(device, EIS_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Swipe update event for non-gesture device");
+ }
+
+ if (device->gesture_state.swipe.is_active) {
+ if (device->state == EIS_DEVICE_STATE_EMULATING)
+ eis_queue_swipe_update_event(device, dx, dy);
+ return NULL;
+ }
+
+ return maybe_error_on_device_state(device, "swipe update");
+}
+
+static struct brei_result *
+client_msg_gesture_swipe_end(struct eis_gesture_swipe *swipe, uint32_t is_cancelled)
+{
+ struct eis_device *device = eis_gesture_swipe_get_device(swipe);
+
+ DISCONNECT_IF_RECEIVER_CONTEXT(device);
+
+ if (!eis_device_has_capability(device, EIS_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Swipe end event for non-gesture device");
+ }
+
+ if (device->gesture_state.swipe.is_active) {
+ if (device->state == EIS_DEVICE_STATE_EMULATING)
+ eis_queue_swipe_end_event(device, !!is_cancelled);
+ device->gesture_state.swipe.is_active = false;
+ return NULL;
+ }
+
+ return maybe_error_on_device_state(device, "swipe end");
+}
+
+static const struct eis_gesture_swipe_interface swipe_interface = {
+ .release = client_msg_gesture_swipe_release,
+ .begin = client_msg_gesture_swipe_begin,
+ .update = client_msg_gesture_swipe_update,
+ .end = client_msg_gesture_swipe_end,
+};
+
+const struct eis_gesture_swipe_interface *
+eis_device_get_gesture_swipe_interface(struct eis_device *device)
+{
+ return &swipe_interface;
+}
+
+static struct brei_result *
+client_msg_gesture_pinch_release(struct eis_gesture_pinch *pinch)
+{
+ struct eis_device *device = eis_gesture_pinch_get_device(pinch);
+
+ if (!eis_device_has_capability(device, EIS_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Pinch release event for non-gesture device");
+ }
+
+ if (device->pinch) {
+ eis_gesture_pinch_event_destroyed(
+ device->pinch,
+ eis_client_get_next_serial(eis_device_get_client(device)));
+ eis_gesture_pinch_unref(steal(&device->pinch));
+ }
+
+ return 0;
+}
+
+static struct brei_result *
+client_msg_gesture_pinch_begin(struct eis_gesture_pinch *pinch, uint32_t nfingers)
+{
+ struct eis_device *device = eis_gesture_pinch_get_device(pinch);
+
+ DISCONNECT_IF_RECEIVER_CONTEXT(device);
+
+ if (!eis_device_has_capability(device, EIS_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Pinch begin event for non-gesture device");
+ }
+
+ if (nfingers < 2) {
+ return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Pinch begin event with fewer than 2 fingers");
+ }
+
+ if (device->state == EIS_DEVICE_STATE_EMULATING) {
+ if (device->gesture_state.pinch.is_active) {
+ return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Pinch begin event while pinch is active");
+ }
+ device->gesture_state.pinch.is_active = true;
+ device->gesture_state.pinch.nfingers = nfingers;
+ eis_queue_pinch_begin_event(device, nfingers);
+ return NULL;
+ }
+
+ return maybe_error_on_device_state(device, "pinch begin");
+}
+
+static struct brei_result *
+client_msg_gesture_pinch_update(struct eis_gesture_pinch *pinch,
+ float dx,
+ float dy,
+ float scale,
+ float rotation)
+{
+ struct eis_device *device = eis_gesture_pinch_get_device(pinch);
+
+ DISCONNECT_IF_RECEIVER_CONTEXT(device);
+
+ if (!eis_device_has_capability(device, EIS_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Pinch update event for non-gesture device");
+ }
+
+ if (device->gesture_state.pinch.is_active) {
+ if (device->state == EIS_DEVICE_STATE_EMULATING)
+ eis_queue_pinch_update_event(device, dx, dy, scale, rotation);
+ return NULL;
+ }
+
+ return maybe_error_on_device_state(device, "pinch update");
+}
+
+static struct brei_result *
+client_msg_gesture_pinch_end(struct eis_gesture_pinch *pinch, uint32_t is_cancelled)
+{
+ struct eis_device *device = eis_gesture_pinch_get_device(pinch);
+
+ DISCONNECT_IF_RECEIVER_CONTEXT(device);
+
+ if (!eis_device_has_capability(device, EIS_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Pinch end event for non-gesture device");
+ }
+
+ if (device->gesture_state.pinch.is_active) {
+ if (device->state == EIS_DEVICE_STATE_EMULATING)
+ eis_queue_pinch_end_event(device, !!is_cancelled);
+ device->gesture_state.pinch.is_active = false;
+ return NULL;
+ }
+
+ return maybe_error_on_device_state(device, "pinch end");
+}
+
+static const struct eis_gesture_pinch_interface pinch_interface = {
+ .release = client_msg_gesture_pinch_release,
+ .begin = client_msg_gesture_pinch_begin,
+ .update = client_msg_gesture_pinch_update,
+ .end = client_msg_gesture_pinch_end,
+};
+
+const struct eis_gesture_pinch_interface *
+eis_device_get_gesture_pinch_interface(struct eis_device *device)
+{
+ return &pinch_interface;
+}
+
+static struct brei_result *
+client_msg_gesture_hold_release(struct eis_gesture_hold *hold)
+{
+ struct eis_device *device = eis_gesture_hold_get_device(hold);
+
+ if (!eis_device_has_capability(device, EIS_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "hold release event for non-gesture device");
+ }
+
+ if (device->hold) {
+ eis_gesture_hold_event_destroyed(
+ device->hold,
+ eis_client_get_next_serial(eis_device_get_client(device)));
+ eis_gesture_hold_unref(steal(&device->hold));
+ }
+
+ return 0;
+}
+
+static struct brei_result *
+client_msg_gesture_hold_begin(struct eis_gesture_hold *hold, uint32_t nfingers)
+{
+ struct eis_device *device = eis_gesture_hold_get_device(hold);
+
+ DISCONNECT_IF_RECEIVER_CONTEXT(device);
+
+ if (!eis_device_has_capability(device, EIS_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Hold begin event for non-gesture device");
+ }
+
+ if (nfingers == 0) {
+ return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Hold begin event with zero fingers");
+ }
+
+ if (device->state == EIS_DEVICE_STATE_EMULATING) {
+ if (device->gesture_state.hold.is_active) {
+ return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Hold begin event while hold is active");
+ }
+ device->gesture_state.hold.is_active = true;
+ device->gesture_state.hold.nfingers = nfingers;
+ eis_queue_hold_begin_event(device, nfingers);
+ return NULL;
+ }
+
+ return maybe_error_on_device_state(device, "hold begin");
+}
+
+static struct brei_result *
+client_msg_gesture_hold_end(struct eis_gesture_hold *hold, uint32_t is_cancelled)
+{
+ struct eis_device *device = eis_gesture_hold_get_device(hold);
+
+ DISCONNECT_IF_RECEIVER_CONTEXT(device);
+
+ if (!eis_device_has_capability(device, EIS_DEVICE_CAP_GESTURES)) {
+ return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "Hold end event for non-gesture device");
+ }
+
+ if (device->gesture_state.hold.is_active) {
+ if (device->state == EIS_DEVICE_STATE_EMULATING)
+ eis_queue_hold_end_event(device, !!is_cancelled);
+ device->gesture_state.hold.is_active = false;
+ return NULL;
+ }
+
+ return maybe_error_on_device_state(device, "hold end");
+}
+
+static const struct eis_gesture_hold_interface hold_interface = {
+ .release = client_msg_gesture_hold_release,
+ .begin = client_msg_gesture_hold_begin,
+ .end = client_msg_gesture_hold_end,
+};
+
+const struct eis_gesture_hold_interface *
+eis_device_get_gesture_hold_interface(struct eis_device *device)
+{
+ return &hold_interface;
+}
+
static struct brei_result *
client_msg_text_release(struct eis_text *text)
{
@@ -958,6 +1265,7 @@ eis_device_configure_capability(struct eis_device *device, enum eis_device_capab
case EIS_DEVICE_CAP_TOUCH:
case EIS_DEVICE_CAP_BUTTON:
case EIS_DEVICE_CAP_SCROLL:
+ case EIS_DEVICE_CAP_GESTURES:
case EIS_DEVICE_CAP_TEXT:
mask_add(device->capabilities, cap);
break;
@@ -1107,6 +1415,31 @@ eis_device_add(struct eis_device *device)
if (rc < 0)
goto out;
}
+ if (eis_device_has_capability(device, EIS_DEVICE_CAP_GESTURES)) {
+ device->swipe = eis_gesture_swipe_new(device);
+ rc = eis_device_event_interface(device,
+ eis_gesture_swipe_get_id(device->swipe),
+ EIS_GESTURE_SWIPE_INTERFACE_NAME,
+ eis_gesture_swipe_get_version(device->swipe));
+ if (rc < 0)
+ goto out;
+
+ device->pinch = eis_gesture_pinch_new(device);
+ rc = eis_device_event_interface(device,
+ eis_gesture_pinch_get_id(device->pinch),
+ EIS_GESTURE_PINCH_INTERFACE_NAME,
+ eis_gesture_pinch_get_version(device->pinch));
+ if (rc < 0)
+ goto out;
+
+ device->hold = eis_gesture_hold_new(device);
+ rc = eis_device_event_interface(device,
+ eis_gesture_hold_get_id(device->hold),
+ EIS_GESTURE_HOLD_INTERFACE_NAME,
+ eis_gesture_hold_get_version(device->hold));
+ if (rc < 0)
+ goto out;
+ }
if (eis_device_has_capability(device, EIS_DEVICE_CAP_TEXT)) {
device->text = eis_text_new(device);
rc = eis_device_event_interface(device,
@@ -1175,6 +1508,20 @@ eis_device_remove(struct eis_device *device)
eis_keyboard_event_destroyed(device->keyboard, eis_client_get_next_serial(client));
eis_keyboard_unref(steal(&device->keyboard));
}
+ if (device->swipe) {
+ eis_gesture_swipe_event_destroyed(device->swipe,
+ eis_client_get_next_serial(client));
+ eis_gesture_swipe_unref(steal(&device->swipe));
+ }
+ if (device->pinch) {
+ eis_gesture_pinch_event_destroyed(device->pinch,
+ eis_client_get_next_serial(client));
+ eis_gesture_pinch_unref(steal(&device->pinch));
+ }
+ if (device->hold) {
+ eis_gesture_hold_event_destroyed(device->hold, eis_client_get_next_serial(client));
+ eis_gesture_hold_unref(steal(&device->hold));
+ }
if (device->text) {
eis_text_event_destroyed(device->text, eis_client_get_next_serial(client));
eis_text_unref(steal(&device->text));
@@ -1205,6 +1552,7 @@ eis_device_has_capability(struct eis_device *device, enum eis_device_capability
case EIS_DEVICE_CAP_TOUCH:
case EIS_DEVICE_CAP_BUTTON:
case EIS_DEVICE_CAP_SCROLL:
+ case EIS_DEVICE_CAP_GESTURES:
case EIS_DEVICE_CAP_TEXT:
return mask_all(device->capabilities, cap);
}
@@ -1721,6 +2069,9 @@ eis_device_pause(struct eis_device *device)
for (size_t i = 0; i < ARRAY_LENGTH(device->touch_state.down); i++) {
device->touch_state.down[i] = UINT64_MAX;
}
+ device->gesture_state.swipe.is_active = false;
+ device->gesture_state.pinch.is_active = false;
+ device->gesture_state.hold.is_active = false;
}
_public_ void
@@ -1762,3 +2113,90 @@ eis_device_keyboard_send_xkb_modifiers(struct eis_device *device,
latched,
group);
}
+
+_public_ void
+eis_device_swipe_cancel(struct eis_device *device)
+{
+ if (!eis_device_has_capability(device, EIS_DEVICE_CAP_GESTURES)) {
+ log_bug_client(eis_device_get_context(device),
+ "%s: device is not a gesture device",
+ __func__);
+ return;
+ }
+
+ if (!device->swipe)
+ return;
+
+ if (!device->gesture_state.swipe.is_active)
+ return;
+
+ device->gesture_state.swipe.is_active = false;
+ struct eis_client *client = eis_device_get_client(device);
+ eis_gesture_swipe_event_cancelled(device->swipe, eis_client_get_next_serial(client));
+}
+
+_public_ void
+eis_device_pinch_cancel(struct eis_device *device)
+{
+ if (!eis_device_has_capability(device, EIS_DEVICE_CAP_GESTURES)) {
+ log_bug_client(eis_device_get_context(device),
+ "%s: device is not a gesture device",
+ __func__);
+ return;
+ }
+
+ if (!device->pinch)
+ return;
+
+ if (!device->gesture_state.pinch.is_active)
+ return;
+
+ device->gesture_state.pinch.is_active = false;
+ struct eis_client *client = eis_device_get_client(device);
+ eis_gesture_pinch_event_cancelled(device->pinch, eis_client_get_next_serial(client));
+}
+
+_public_ void
+eis_device_hold_cancel(struct eis_device *device)
+{
+ if (!eis_device_has_capability(device, EIS_DEVICE_CAP_GESTURES)) {
+ log_bug_client(eis_device_get_context(device),
+ "%s: device is not a gesture device",
+ __func__);
+ return;
+ }
+
+ if (!device->hold)
+ return;
+
+ if (!device->gesture_state.hold.is_active)
+ return;
+
+ device->gesture_state.hold.is_active = false;
+ struct eis_client *client = eis_device_get_client(device);
+ eis_gesture_hold_event_cancelled(device->hold, eis_client_get_next_serial(client));
+}
+
+_public_ struct eis_swipe *
+eis_device_swipe_new(struct eis_device *device, uint32_t nfingers)
+{
+ struct eis_swipe *swipe = eis_swipe_new(device, nfingers);
+
+ return swipe;
+}
+
+_public_ struct eis_pinch *
+eis_device_pinch_new(struct eis_device *device, uint32_t nfingers)
+{
+ struct eis_pinch *pinch = eis_pinch_new(device, nfingers);
+
+ return pinch;
+}
+
+_public_ struct eis_hold *
+eis_device_hold_new(struct eis_device *device, uint32_t nfingers)
+{
+ struct eis_hold *hold = eis_hold_new(device, nfingers);
+
+ return hold;
+}
diff --git a/src/libeis-device.h b/src/libeis-device.h
index 1de5c54..cc1a603 100644
--- a/src/libeis-device.h
+++ b/src/libeis-device.h
@@ -58,6 +58,9 @@ struct eis_device {
struct eis_button *button;
struct eis_keyboard *keyboard;
struct eis_touchscreen *touchscreen;
+ struct eis_gesture_swipe *swipe;
+ struct eis_gesture_pinch *pinch;
+ struct eis_gesture_hold *hold;
struct eis_text *text;
char *name;
@@ -89,6 +92,33 @@ struct eis_device {
struct {
uint64_t down[EIS_MAX_TOUCHES]; /* touch id */
} touch_state;
+
+ struct {
+ struct {
+ /* receiver */
+ bool is_active;
+ uint32_t nfingers;
+
+ /* sender */
+ struct eis_swipe *current_swipe; /* no ref! */
+ } swipe;
+ struct {
+ /* receiver */
+ bool is_active;
+ uint32_t nfingers;
+
+ /* sender */
+ struct eis_pinch *current_pinch; /* no ref! */
+ } pinch;
+ struct {
+ /* receiver */
+ bool is_active;
+ uint32_t nfingers;
+
+ /* sender */
+ struct eis_hold *current_hold; /* no ref! */
+ } hold;
+ } gesture_state;
};
struct eis_touch {
@@ -133,6 +163,15 @@ OBJECT_DECLARE_GETTER(eis_device, scroll_interface, const struct eis_scroll_inte
OBJECT_DECLARE_GETTER(eis_device, button_interface, const struct eis_button_interface *);
OBJECT_DECLARE_GETTER(eis_device, keyboard_interface, const struct eis_keyboard_interface *);
OBJECT_DECLARE_GETTER(eis_device, touchscreen_interface, const struct eis_touchscreen_interface *);
+OBJECT_DECLARE_GETTER(eis_device,
+ gesture_swipe_interface,
+ const struct eis_gesture_swipe_interface *);
+OBJECT_DECLARE_GETTER(eis_device,
+ gesture_pinch_interface,
+ const struct eis_gesture_pinch_interface *);
+OBJECT_DECLARE_GETTER(eis_device,
+ gesture_hold_interface,
+ const struct eis_gesture_hold_interface *);
OBJECT_DECLARE_GETTER(eis_device, text_interface, const struct eis_text_interface *);
static inline bool
diff --git a/src/libeis-event.c b/src/libeis-event.c
index 4f7f18a..c7732dd 100644
--- a/src/libeis-event.c
+++ b/src/libeis-event.c
@@ -58,6 +58,14 @@ eis_event_destroy(struct eis_event *event)
case EIS_EVENT_TOUCH_MOTION:
case EIS_EVENT_TOUCH_UP:
case EIS_EVENT_FRAME:
+ case EIS_EVENT_SWIPE_BEGIN:
+ case EIS_EVENT_SWIPE_UPDATE:
+ case EIS_EVENT_SWIPE_END:
+ case EIS_EVENT_PINCH_BEGIN:
+ case EIS_EVENT_PINCH_UPDATE:
+ case EIS_EVENT_PINCH_END:
+ case EIS_EVENT_HOLD_BEGIN:
+ case EIS_EVENT_HOLD_END:
case EIS_EVENT_TEXT_KEYSYM:
handled = true;
break;
@@ -220,6 +228,7 @@ eis_event_seat_has_capability(struct eis_event *event, enum eis_device_capabilit
case EIS_DEVICE_CAP_TOUCH:
case EIS_DEVICE_CAP_BUTTON:
case EIS_DEVICE_CAP_SCROLL:
+ case EIS_DEVICE_CAP_GESTURES:
case EIS_DEVICE_CAP_TEXT:
return mask_all(event->bind.capabilities, cap);
}
@@ -436,6 +445,110 @@ eis_event_touch_get_is_cancel(struct eis_event *event)
return event->touch.is_cancel;
}
+_public_ uint32_t
+eis_event_swipe_get_finger_count(struct eis_event *event)
+{
+ require_event_type(event,
+ 0,
+ EIS_EVENT_SWIPE_BEGIN,
+ EIS_EVENT_SWIPE_UPDATE,
+ EIS_EVENT_SWIPE_END);
+
+ return event->gestures.nfingers;
+}
+
+_public_ double
+eis_event_swipe_get_dx(struct eis_event *event)
+{
+ require_event_type(event, 0.0, EIS_EVENT_SWIPE_UPDATE);
+
+ return event->gestures.dx;
+}
+
+_public_ double
+eis_event_swipe_get_dy(struct eis_event *event)
+{
+ require_event_type(event, 0.0, EIS_EVENT_SWIPE_UPDATE);
+
+ return event->gestures.dy;
+}
+
+_public_ bool
+eis_event_swipe_get_is_cancelled(struct eis_event *event)
+{
+ require_event_type(event, false, EIS_EVENT_SWIPE_END);
+
+ return event->gestures.is_cancelled;
+}
+
+_public_ uint32_t
+eis_event_pinch_get_finger_count(struct eis_event *event)
+{
+ require_event_type(event,
+ 0,
+ EIS_EVENT_PINCH_BEGIN,
+ EIS_EVENT_PINCH_UPDATE,
+ EIS_EVENT_PINCH_END);
+
+ return event->gestures.nfingers;
+}
+
+_public_ double
+eis_event_pinch_get_dx(struct eis_event *event)
+{
+ require_event_type(event, 0.0, EIS_EVENT_PINCH_UPDATE);
+
+ return event->gestures.dx;
+}
+
+_public_ double
+eis_event_pinch_get_dy(struct eis_event *event)
+{
+ require_event_type(event, 0.0, EIS_EVENT_PINCH_UPDATE);
+
+ return event->gestures.dy;
+}
+
+_public_ double
+eis_event_pinch_get_rotation(struct eis_event *event)
+{
+ require_event_type(event, 0.0, EIS_EVENT_PINCH_UPDATE);
+
+ return event->gestures.degrees;
+}
+
+_public_ double
+eis_event_pinch_get_scale(struct eis_event *event)
+{
+ require_event_type(event, 0.0, EIS_EVENT_PINCH_UPDATE);
+
+ return event->gestures.scale;
+}
+
+_public_ bool
+eis_event_pinch_get_is_cancelled(struct eis_event *event)
+{
+ require_event_type(event, false, EIS_EVENT_PINCH_END);
+
+ return event->gestures.is_cancelled;
+}
+
+_public_ uint32_t
+eis_event_hold_get_finger_count(struct eis_event *event)
+{
+ require_event_type(event, 0, EIS_EVENT_HOLD_BEGIN, EIS_EVENT_HOLD_END);
+
+ return event->gestures.nfingers;
+}
+
+_public_ bool
+eis_event_hold_get_is_cancelled(struct eis_event *event)
+{
+ require_event_type(event, false, EIS_EVENT_HOLD_END);
+
+ return event->gestures.is_cancelled;
+}
+
_public_ uint32_t
eis_event_text_get_keysym(struct eis_event *event)
{
diff --git a/src/libeis-event.h b/src/libeis-event.h
index 04c347c..b346833 100644
--- a/src/libeis-event.h
+++ b/src/libeis-event.h
@@ -75,6 +75,13 @@ struct eis_event {
struct {
struct eis_callback *callback;
} sync;
+ struct {
+ uint32_t nfingers;
+ double dx, dy; /* relative motion */
+ double degrees;
+ double scale;
+ bool is_cancelled;
+ } gestures;
};
};
diff --git a/src/libeis-gestures.c b/src/libeis-gestures.c
new file mode 100644
index 0000000..040d180
--- /dev/null
+++ b/src/libeis-gestures.c
@@ -0,0 +1,824 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2024 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include
+#include
+
+#include "util-bits.h"
+#include "util-io.h"
+#include "util-macros.h"
+#include "util-mem.h"
+#include "util-strings.h"
+#include "util-version.h"
+
+#include "eis-proto.h"
+#include "libeis-client.h"
+#include "libeis-gestures.h"
+#include "libeis-private.h"
+
+static void
+eis_gesture_swipe_destroy(struct eis_gesture_swipe *swipe)
+{
+ struct eis_client *client = eis_gesture_swipe_get_client(swipe);
+ eis_client_unregister_object(client, &swipe->proto_object);
+}
+
+OBJECT_IMPLEMENT_REF(eis_gesture_swipe);
+OBJECT_IMPLEMENT_UNREF_CLEANUP(eis_gesture_swipe);
+
+static OBJECT_IMPLEMENT_CREATE(eis_gesture_swipe);
+static OBJECT_IMPLEMENT_PARENT(eis_gesture_swipe, eis_device);
+OBJECT_IMPLEMENT_GETTER_AS_REF(eis_gesture_swipe, proto_object, const struct brei_object *);
+
+uint32_t
+eis_gesture_swipe_get_version(struct eis_gesture_swipe *swipe)
+{
+ return swipe->proto_object.version;
+}
+
+object_id_t
+eis_gesture_swipe_get_id(struct eis_gesture_swipe *swipe)
+{
+ return swipe->proto_object.id;
+}
+
+struct eis_device *
+eis_gesture_swipe_get_device(struct eis_gesture_swipe *swipe)
+{
+ return eis_gesture_swipe_parent(swipe);
+}
+
+struct eis_client *
+eis_gesture_swipe_get_client(struct eis_gesture_swipe *swipe)
+{
+ return eis_device_get_client(eis_gesture_swipe_get_device(swipe));
+}
+
+struct eis *
+eis_gesture_swipe_get_context(struct eis_gesture_swipe *swipe)
+{
+ return eis_device_get_context(eis_gesture_swipe_get_device(swipe));
+}
+
+const struct eis_gesture_swipe_interface *
+eis_gesture_swipe_get_interface(struct eis_gesture_swipe *swipe)
+{
+ struct eis_device *device = eis_gesture_swipe_get_device(swipe);
+ return eis_device_get_gesture_swipe_interface(device);
+}
+
+struct eis_gesture_swipe *
+eis_gesture_swipe_new(struct eis_device *device)
+{
+ struct eis_gesture_swipe *swipe = eis_gesture_swipe_create(&device->object);
+ struct eis_client *client = eis_device_get_client(device);
+
+ swipe->proto_object.id = eis_client_get_new_id(client);
+ swipe->proto_object.implementation = swipe;
+ swipe->proto_object.interface = &eis_gesture_swipe_proto_interface;
+ swipe->proto_object.version = client->interface_versions.ei_gesture_swipe;
+ eis_client_register_object(client, &swipe->proto_object);
+
+ return swipe; /* ref owned by caller */
+}
+
+static void
+eis_gesture_pinch_destroy(struct eis_gesture_pinch *pinch)
+{
+ struct eis_client *client = eis_gesture_pinch_get_client(pinch);
+ eis_client_unregister_object(client, &pinch->proto_object);
+}
+
+OBJECT_IMPLEMENT_REF(eis_gesture_pinch);
+OBJECT_IMPLEMENT_UNREF_CLEANUP(eis_gesture_pinch);
+
+static OBJECT_IMPLEMENT_CREATE(eis_gesture_pinch);
+static OBJECT_IMPLEMENT_PARENT(eis_gesture_pinch, eis_device);
+OBJECT_IMPLEMENT_GETTER_AS_REF(eis_gesture_pinch, proto_object, const struct brei_object *);
+
+uint32_t
+eis_gesture_pinch_get_version(struct eis_gesture_pinch *pinch)
+{
+ return pinch->proto_object.version;
+}
+
+object_id_t
+eis_gesture_pinch_get_id(struct eis_gesture_pinch *pinch)
+{
+ return pinch->proto_object.id;
+}
+
+struct eis_device *
+eis_gesture_pinch_get_device(struct eis_gesture_pinch *pinch)
+{
+ return eis_gesture_pinch_parent(pinch);
+}
+
+struct eis_client *
+eis_gesture_pinch_get_client(struct eis_gesture_pinch *pinch)
+{
+ return eis_device_get_client(eis_gesture_pinch_get_device(pinch));
+}
+
+struct eis *
+eis_gesture_pinch_get_context(struct eis_gesture_pinch *pinch)
+{
+ return eis_device_get_context(eis_gesture_pinch_get_device(pinch));
+}
+
+const struct eis_gesture_pinch_interface *
+eis_gesture_pinch_get_interface(struct eis_gesture_pinch *pinch)
+{
+ struct eis_device *device = eis_gesture_pinch_get_device(pinch);
+ return eis_device_get_gesture_pinch_interface(device);
+}
+
+struct eis_gesture_pinch *
+eis_gesture_pinch_new(struct eis_device *device)
+{
+ struct eis_gesture_pinch *pinch = eis_gesture_pinch_create(&device->object);
+ struct eis_client *client = eis_device_get_client(device);
+
+ pinch->proto_object.id = eis_client_get_new_id(client);
+ pinch->proto_object.implementation = pinch;
+ pinch->proto_object.interface = &eis_gesture_pinch_proto_interface;
+ pinch->proto_object.version = client->interface_versions.ei_gesture_pinch;
+ eis_client_register_object(client, &pinch->proto_object);
+
+ return pinch; /* ref owned by caller */
+}
+
+static void
+eis_gesture_hold_destroy(struct eis_gesture_hold *hold)
+{
+ struct eis_client *client = eis_gesture_hold_get_client(hold);
+ eis_client_unregister_object(client, &hold->proto_object);
+}
+
+OBJECT_IMPLEMENT_REF(eis_gesture_hold);
+OBJECT_IMPLEMENT_UNREF_CLEANUP(eis_gesture_hold);
+
+static OBJECT_IMPLEMENT_CREATE(eis_gesture_hold);
+static OBJECT_IMPLEMENT_PARENT(eis_gesture_hold, eis_device);
+OBJECT_IMPLEMENT_GETTER_AS_REF(eis_gesture_hold, proto_object, const struct brei_object *);
+
+uint32_t
+eis_gesture_hold_get_version(struct eis_gesture_hold *hold)
+{
+ return hold->proto_object.version;
+}
+
+object_id_t
+eis_gesture_hold_get_id(struct eis_gesture_hold *hold)
+{
+ return hold->proto_object.id;
+}
+
+struct eis_device *
+eis_gesture_hold_get_device(struct eis_gesture_hold *hold)
+{
+ return eis_gesture_hold_parent(hold);
+}
+
+struct eis_client *
+eis_gesture_hold_get_client(struct eis_gesture_hold *hold)
+{
+ return eis_device_get_client(eis_gesture_hold_get_device(hold));
+}
+
+struct eis *
+eis_gesture_hold_get_context(struct eis_gesture_hold *hold)
+{
+ return eis_device_get_context(eis_gesture_hold_get_device(hold));
+}
+
+const struct eis_gesture_hold_interface *
+eis_gesture_hold_get_interface(struct eis_gesture_hold *hold)
+{
+ struct eis_device *device = eis_gesture_hold_get_device(hold);
+ return eis_device_get_gesture_hold_interface(device);
+}
+
+struct eis_gesture_hold *
+eis_gesture_hold_new(struct eis_device *device)
+{
+ struct eis_gesture_hold *hold = eis_gesture_hold_create(&device->object);
+ struct eis_client *client = eis_device_get_client(device);
+
+ hold->proto_object.id = eis_client_get_new_id(client);
+ hold->proto_object.implementation = hold;
+ hold->proto_object.interface = &eis_gesture_hold_proto_interface;
+ hold->proto_object.version = client->interface_versions.ei_gesture_hold;
+ eis_client_register_object(client, &hold->proto_object);
+
+ return hold; /* ref owned by caller */
+}
+
+static void
+eis_gesture_destroy(struct eis_gesture *gesture)
+{
+ eis_device_unref(gesture->device);
+}
+
+static OBJECT_IMPLEMENT_INIT(eis_gesture);
+OBJECT_IMPLEMENT_REF(eis_gesture);
+OBJECT_IMPLEMENT_UNREF(eis_gesture);
+
+struct eis_device *
+eis_gesture_get_device(struct eis_gesture *gesture)
+{
+ return gesture->device;
+}
+
+struct eis *
+eis_gesture_get_context(struct eis_gesture *gesture)
+{
+ return eis_device_get_context(eis_gesture_get_device(gesture));
+}
+
+static void
+eis_gesture_init(struct eis_gesture *gesture, struct eis_device *device, uint32_t nfingers)
+{
+ /* Not using the device as parent object because we need a ref
+ * to it */
+ eis_gesture_init_object(gesture, NULL);
+
+ gesture->state = EIS_GESTURE_STATE_NEW;
+ gesture->device = eis_device_ref(device);
+ gesture->nfingers = nfingers;
+}
+
+struct eis_swipe *
+eis_swipe_new(struct eis_device *device, uint32_t nfingers)
+{
+ if (!eis_device_has_capability(device, EIS_DEVICE_CAP_GESTURES)) {
+ log_bug_client(eis_device_get_context(device),
+ "%s: device is not a gesture device",
+ __func__);
+ return NULL;
+ }
+
+ if (nfingers == 0) {
+ log_bug_client(eis_device_get_context(device),
+ "zero-finger gestures are not possible");
+ return NULL;
+ }
+
+ if (!device->swipe)
+ return NULL;
+
+ struct eis_swipe *swipe = xalloc(sizeof *swipe);
+ eis_gesture_init(&swipe->base, device, nfingers);
+
+ return swipe;
+}
+
+_public_ struct eis_swipe *
+eis_swipe_ref(struct eis_swipe *swipe)
+{
+ return (struct eis_swipe *)eis_gesture_ref(&swipe->base);
+}
+
+_public_ struct eis_swipe *
+eis_swipe_unref(struct eis_swipe *swipe)
+{
+ if (!swipe)
+ return NULL;
+
+ struct eis_device *device = eis_gesture_get_device(&swipe->base);
+
+ if (swipe == device->gesture_state.swipe.current_swipe) {
+ if (swipe->base.state == EIS_GESTURE_STATE_BEGUN &&
+ device->state == EIS_DEVICE_STATE_EMULATING) {
+ eis_swipe_cancel(swipe);
+ }
+ }
+
+ return (struct eis_swipe *)eis_gesture_unref(&swipe->base);
+}
+
+struct eis *
+eis_swipe_get_context(struct eis_swipe *swipe)
+{
+ return eis_gesture_get_context(&swipe->base);
+}
+
+_public_ void
+eis_swipe_begin(struct eis_swipe *swipe)
+{
+ struct eis_device *device = swipe->base.device;
+ struct eis_client *client = eis_device_get_client(device);
+
+ if (swipe->base.state != EIS_GESTURE_STATE_NEW) {
+ log_bug_client(eis_gesture_get_context(&swipe->base), "gesture cannot begin twice");
+ return;
+ }
+
+ if (device->gesture_state.swipe.current_swipe) {
+ log_bug_client(
+ eis_gesture_get_context(&swipe->base),
+ "a different swipe gesture is already in process, ignoring this one");
+ return;
+ }
+
+ if (device->state != EIS_DEVICE_STATE_EMULATING) {
+ log_bug_client(eis_device_get_context(device),
+ "%s: device is not emulating",
+ __func__);
+ return;
+ }
+
+ if (client->state == EIS_CLIENT_STATE_NEW || client->state == EIS_CLIENT_STATE_DISCONNECTED)
+ return;
+
+ if (!device->swipe)
+ return;
+
+ if (swipe->base.nfingers == 0) {
+ log_bug_client(eis_device_get_context(device),
+ "zero-finger gestures are invalid, ignoring gesture");
+ return;
+ }
+
+ device->gesture_state.swipe.current_swipe = swipe;
+
+ device->send_frame_event = true;
+
+ swipe->base.state = EIS_GESTURE_STATE_BEGUN;
+ int rc = eis_gesture_swipe_event_begin(device->swipe, swipe->base.nfingers);
+ if (rc)
+ eis_client_disconnect(client);
+}
+
+_public_ void
+eis_swipe_update(struct eis_swipe *swipe, double x, double y)
+{
+ struct eis_device *device = swipe->base.device;
+ struct eis_client *client = eis_device_get_client(device);
+
+ if (swipe->base.state != EIS_GESTURE_STATE_BEGUN) {
+ log_bug_client(eis_gesture_get_context(&swipe->base),
+ "gesture in invalid state for update");
+ return;
+ }
+
+ if (device->gesture_state.swipe.current_swipe != swipe) {
+ log_bug_client(
+ eis_gesture_get_context(&swipe->base),
+ "a different swipe gesture is already in process, ignoring this one");
+ return;
+ }
+
+ /* FIXME: what do we do with a gesture when the device stops emulating? */
+ if (device->state != EIS_DEVICE_STATE_EMULATING) {
+ log_bug_client(eis_device_get_context(device),
+ "%s: device is not emulating",
+ __func__);
+ return;
+ }
+
+ if (client->state == EIS_CLIENT_STATE_NEW || client->state == EIS_CLIENT_STATE_DISCONNECTED)
+ return;
+
+ if (!device->swipe)
+ return;
+
+ device->send_frame_event = true;
+
+ int rc = eis_gesture_swipe_event_update(device->swipe, x, y);
+ if (rc)
+ eis_client_disconnect(client);
+}
+
+static void
+eis_swipe_end_cancel(struct eis_swipe *swipe, bool is_cancel)
+{
+ struct eis_device *device = swipe->base.device;
+ struct eis_client *client = eis_device_get_client(device);
+
+ if (swipe->base.state != EIS_GESTURE_STATE_BEGUN) {
+ log_bug_client(eis_gesture_get_context(&swipe->base),
+ "gesture in invalid state for end/cancel");
+ return;
+ }
+
+ if (device->gesture_state.swipe.current_swipe != swipe) {
+ log_bug_client(
+ eis_gesture_get_context(&swipe->base),
+ "a different swipe gesture is already in process, ignoring this one");
+ return;
+ }
+
+ /* FIXME: what do we do with a gesture when the device stops emulating? */
+ if (device->state != EIS_DEVICE_STATE_EMULATING) {
+ log_bug_client(eis_device_get_context(device),
+ "%s: device is not emulating",
+ __func__);
+ return;
+ }
+
+ if (client->state == EIS_CLIENT_STATE_NEW || client->state == EIS_CLIENT_STATE_DISCONNECTED)
+ return;
+
+ if (!device->swipe)
+ return;
+
+ swipe->base.state = EIS_GESTURE_STATE_ENDED;
+ device->gesture_state.swipe.current_swipe = NULL;
+
+ device->send_frame_event = true;
+
+ int rc = eis_gesture_swipe_event_end(device->swipe, is_cancel);
+ if (rc)
+ eis_client_disconnect(client);
+}
+
+_public_ void
+eis_swipe_end(struct eis_swipe *swipe)
+{
+ eis_swipe_end_cancel(swipe, false);
+}
+
+_public_ void
+eis_swipe_cancel(struct eis_swipe *swipe)
+{
+ eis_swipe_end_cancel(swipe, true);
+}
+
+struct eis_pinch *
+eis_pinch_new(struct eis_device *device, uint32_t nfingers)
+{
+ if (!eis_device_has_capability(device, EIS_DEVICE_CAP_GESTURES)) {
+ log_bug_client(eis_device_get_context(device),
+ "%s: device is not a gesture device",
+ __func__);
+ return NULL;
+ }
+
+ if (nfingers < 2) {
+ log_bug_client(eis_device_get_context(device),
+ "pinch gestures require at least 2 fingers");
+ return NULL;
+ }
+
+ if (!device->pinch)
+ return NULL;
+
+ /* Not using the device as parent object because we need a ref
+ * to it */
+ struct eis_pinch *pinch = xalloc(sizeof *pinch);
+ eis_gesture_init(&pinch->base, device, nfingers);
+
+ return pinch;
+}
+
+_public_ struct eis_pinch *
+eis_pinch_ref(struct eis_pinch *pinch)
+{
+ return (struct eis_pinch *)eis_gesture_ref(&pinch->base);
+}
+
+_public_ struct eis_pinch *
+eis_pinch_unref(struct eis_pinch *pinch)
+{
+ if (!pinch)
+ return NULL;
+
+ struct eis_device *device = eis_gesture_get_device(&pinch->base);
+
+ if (pinch == device->gesture_state.pinch.current_pinch) {
+ if (pinch->base.state == EIS_GESTURE_STATE_BEGUN &&
+ device->state == EIS_DEVICE_STATE_EMULATING) {
+ eis_pinch_cancel(pinch);
+ }
+ }
+
+ return (struct eis_pinch *)eis_gesture_unref(&pinch->base);
+}
+
+struct eis *
+eis_pinch_get_context(struct eis_pinch *pinch)
+{
+ return eis_gesture_get_context(&pinch->base);
+}
+
+_public_ void
+eis_pinch_begin(struct eis_pinch *pinch)
+{
+ struct eis_device *device = pinch->base.device;
+ struct eis_client *client = eis_device_get_client(device);
+
+ if (pinch->base.state != EIS_GESTURE_STATE_NEW) {
+ log_bug_client(eis_gesture_get_context(&pinch->base), "gesture cannot begin twice");
+ return;
+ }
+
+ if (device->gesture_state.pinch.current_pinch) {
+ log_bug_client(
+ eis_gesture_get_context(&pinch->base),
+ "a different pinch gesture is already in process, ignoring this one");
+ return;
+ }
+
+ if (device->state != EIS_DEVICE_STATE_EMULATING) {
+ log_bug_client(eis_device_get_context(device),
+ "%s: device is not emulating",
+ __func__);
+ return;
+ }
+
+ if (client->state == EIS_CLIENT_STATE_NEW || client->state == EIS_CLIENT_STATE_DISCONNECTED)
+ return;
+
+ if (!device->pinch)
+ return;
+
+ if (pinch->base.nfingers == 0) {
+ log_bug_client(eis_device_get_context(device),
+ "zero-finger gestures are invalid, ignoring gesture");
+ return;
+ }
+
+ device->gesture_state.pinch.current_pinch = pinch;
+
+ device->send_frame_event = true;
+
+ pinch->base.state = EIS_GESTURE_STATE_BEGUN;
+ int rc = eis_gesture_pinch_event_begin(device->pinch, pinch->base.nfingers);
+ if (rc)
+ eis_client_disconnect(client);
+}
+
+_public_ void
+eis_pinch_update(struct eis_pinch *pinch, double x, double y, double scale, double rotation)
+{
+ struct eis_device *device = pinch->base.device;
+ struct eis_client *client = eis_device_get_client(device);
+
+ if (pinch->base.state != EIS_GESTURE_STATE_BEGUN) {
+ log_bug_client(eis_gesture_get_context(&pinch->base),
+ "gesture in invalid state for update");
+ return;
+ }
+
+ if (device->gesture_state.pinch.current_pinch != pinch) {
+ log_bug_client(
+ eis_gesture_get_context(&pinch->base),
+ "a different pinch gesture is already in process, ignoring this one");
+ return;
+ }
+
+ /* FIXME: what do we do with a gesture when the device stops emulating? */
+ if (device->state != EIS_DEVICE_STATE_EMULATING) {
+ log_bug_client(eis_device_get_context(device),
+ "%s: device is not emulating",
+ __func__);
+ return;
+ }
+
+ if (client->state == EIS_CLIENT_STATE_NEW || client->state == EIS_CLIENT_STATE_DISCONNECTED)
+ return;
+
+ if (!device->pinch)
+ return;
+
+ device->send_frame_event = true;
+
+ int rc = eis_gesture_pinch_event_update(device->pinch, x, y, scale, rotation);
+ if (rc)
+ eis_client_disconnect(client);
+}
+
+static void
+eis_pinch_end_cancel(struct eis_pinch *pinch, bool is_cancel)
+{
+ struct eis_device *device = pinch->base.device;
+ struct eis_client *client = eis_device_get_client(device);
+
+ if (pinch->base.state != EIS_GESTURE_STATE_BEGUN) {
+ log_bug_client(eis_gesture_get_context(&pinch->base),
+ "gesture in invalid state for end/cancel");
+ return;
+ }
+
+ if (device->gesture_state.pinch.current_pinch != pinch) {
+ log_bug_client(
+ eis_gesture_get_context(&pinch->base),
+ "a different pinch gesture is already in process, ignoring this one");
+ return;
+ }
+
+ if (device->state != EIS_DEVICE_STATE_EMULATING) {
+ log_bug_client(eis_device_get_context(device),
+ "%s: device is not emulating",
+ __func__);
+ return;
+ }
+
+ if (client->state == EIS_CLIENT_STATE_NEW || client->state == EIS_CLIENT_STATE_DISCONNECTED)
+ return;
+
+ if (!device->pinch)
+ return;
+
+ pinch->base.state = EIS_GESTURE_STATE_ENDED;
+ device->gesture_state.pinch.current_pinch = NULL;
+
+ device->send_frame_event = true;
+
+ int rc = eis_gesture_pinch_event_end(device->pinch, is_cancel);
+ if (rc)
+ eis_client_disconnect(client);
+}
+
+_public_ void
+eis_pinch_end(struct eis_pinch *pinch)
+{
+ eis_pinch_end_cancel(pinch, false);
+}
+
+_public_ void
+eis_pinch_cancel(struct eis_pinch *pinch)
+{
+ eis_pinch_end_cancel(pinch, true);
+}
+
+_public_ void
+eis_hold_begin(struct eis_hold *hold)
+{
+ struct eis_device *device = hold->base.device;
+ struct eis_client *client = eis_device_get_client(device);
+
+ if (hold->base.state != EIS_GESTURE_STATE_NEW) {
+ log_bug_client(eis_gesture_get_context(&hold->base), "gesture cannot begin twice");
+ return;
+ }
+
+ if (device->gesture_state.hold.current_hold) {
+ log_bug_client(eis_gesture_get_context(&hold->base),
+ "a different hold gesture is already in process, ignoring this one");
+ return;
+ }
+
+ if (device->state != EIS_DEVICE_STATE_EMULATING) {
+ log_bug_client(eis_device_get_context(device),
+ "%s: device is not emulating",
+ __func__);
+ return;
+ }
+
+ if (client->state == EIS_CLIENT_STATE_NEW || client->state == EIS_CLIENT_STATE_DISCONNECTED)
+ return;
+
+ if (!device->hold)
+ return;
+
+ if (hold->base.nfingers == 0) {
+ log_bug_client(eis_device_get_context(device),
+ "zero-finger gestures are invalid, ignoring gesture");
+ return;
+ }
+
+ device->gesture_state.hold.current_hold = hold;
+
+ device->send_frame_event = true;
+
+ hold->base.state = EIS_GESTURE_STATE_BEGUN;
+ int rc = eis_gesture_hold_event_begin(device->hold, hold->base.nfingers);
+ if (rc)
+ eis_client_disconnect(client);
+}
+
+struct eis_hold *
+eis_hold_new(struct eis_device *device, uint32_t nfingers)
+{
+ if (!eis_device_has_capability(device, EIS_DEVICE_CAP_GESTURES)) {
+ log_bug_client(eis_device_get_context(device),
+ "%s: device is not a gesture device",
+ __func__);
+ return NULL;
+ }
+
+ if (nfingers == 0) {
+ log_bug_client(eis_device_get_context(device),
+ "zero-finger gestures are not possible");
+ return NULL;
+ }
+
+ if (!device->hold)
+ return NULL;
+
+ /* Not using the device as parent object because we need a ref
+ * to it */
+ struct eis_hold *hold = xalloc(sizeof *hold);
+ eis_gesture_init(&hold->base, device, nfingers);
+
+ return hold;
+}
+
+_public_ struct eis_hold *
+eis_hold_ref(struct eis_hold *hold)
+{
+ return (struct eis_hold *)eis_gesture_ref(&hold->base);
+}
+
+_public_ struct eis_hold *
+eis_hold_unref(struct eis_hold *hold)
+{
+ if (!hold)
+ return NULL;
+
+ struct eis_device *device = eis_gesture_get_device(&hold->base);
+
+ if (hold == device->gesture_state.hold.current_hold) {
+ if (hold->base.state == EIS_GESTURE_STATE_BEGUN &&
+ device->state == EIS_DEVICE_STATE_EMULATING) {
+ eis_hold_cancel(hold);
+ }
+ }
+
+ return (struct eis_hold *)eis_gesture_unref(&hold->base);
+}
+
+struct eis *
+eis_hold_get_context(struct eis_hold *hold)
+{
+ return eis_gesture_get_context(&hold->base);
+}
+
+static void
+eis_hold_end_cancel(struct eis_hold *hold, bool is_cancel)
+{
+ struct eis_device *device = hold->base.device;
+ struct eis_client *client = eis_device_get_client(device);
+
+ if (hold->base.state != EIS_GESTURE_STATE_BEGUN) {
+ log_bug_client(eis_gesture_get_context(&hold->base),
+ "gesture in invalid state for end/cancel");
+ return;
+ }
+
+ if (device->gesture_state.hold.current_hold != hold) {
+ log_bug_client(eis_gesture_get_context(&hold->base),
+ "a different hold gesture is already in process, ignoring this one");
+ return;
+ }
+
+ /* FIXME: what do we do with a gesture when the device stops emulating? */
+ if (device->state != EIS_DEVICE_STATE_EMULATING) {
+ log_bug_client(eis_device_get_context(device),
+ "%s: device is not emulating",
+ __func__);
+ return;
+ }
+
+ if (client->state == EIS_CLIENT_STATE_NEW || client->state == EIS_CLIENT_STATE_DISCONNECTED)
+ return;
+
+ if (!device->hold)
+ return;
+
+ hold->base.state = EIS_GESTURE_STATE_ENDED;
+ device->gesture_state.hold.current_hold = NULL;
+
+ device->send_frame_event = true;
+
+ int rc = eis_gesture_hold_event_end(device->hold, is_cancel);
+ if (rc)
+ eis_client_disconnect(client);
+}
+
+_public_ void
+eis_hold_end(struct eis_hold *hold)
+{
+ eis_hold_end_cancel(hold, false);
+}
+
+_public_ void
+eis_hold_cancel(struct eis_hold *hold)
+{
+ eis_hold_end_cancel(hold, true);
+}
diff --git a/src/libeis-gestures.h b/src/libeis-gestures.h
new file mode 100644
index 0000000..82e39e0
--- /dev/null
+++ b/src/libeis-gestures.h
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2024 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include "util-list.h"
+#include "util-object.h"
+
+#include "brei-shared.h"
+
+struct eis;
+struct eis_device;
+
+/* This is the protocol-only object, not exposed in the API. See eis_swipe() for
+ * the C API object */
+struct eis_gesture_swipe {
+ struct object object;
+ struct brei_object proto_object;
+};
+
+OBJECT_DECLARE_GETTER(eis_gesture_swipe, id, object_id_t);
+OBJECT_DECLARE_GETTER(eis_gesture_swipe, version, uint32_t);
+OBJECT_DECLARE_GETTER(eis_gesture_swipe, context, struct eis *);
+OBJECT_DECLARE_GETTER(eis_gesture_swipe, client, struct eis_client *);
+OBJECT_DECLARE_GETTER(eis_gesture_swipe, device, struct eis_device *);
+OBJECT_DECLARE_GETTER(eis_gesture_swipe, proto_object, const struct brei_object *);
+OBJECT_DECLARE_GETTER(eis_gesture_swipe, interface, const struct eis_gesture_swipe_interface *);
+OBJECT_DECLARE_REF(eis_gesture_swipe);
+OBJECT_DECLARE_UNREF(eis_gesture_swipe);
+
+struct eis_gesture_swipe *
+eis_gesture_swipe_new(struct eis_device *device);
+
+/* This is the protocol-only object, not exposed in the API. See eis_pinch() for
+ * the C API object */
+struct eis_gesture_pinch {
+ struct object object;
+ struct brei_object proto_object;
+};
+
+OBJECT_DECLARE_GETTER(eis_gesture_pinch, id, object_id_t);
+OBJECT_DECLARE_GETTER(eis_gesture_pinch, version, uint32_t);
+OBJECT_DECLARE_GETTER(eis_gesture_pinch, context, struct eis *);
+OBJECT_DECLARE_GETTER(eis_gesture_pinch, client, struct eis_client *);
+OBJECT_DECLARE_GETTER(eis_gesture_pinch, device, struct eis_device *);
+OBJECT_DECLARE_GETTER(eis_gesture_pinch, proto_object, const struct brei_object *);
+OBJECT_DECLARE_GETTER(eis_gesture_pinch, interface, const struct eis_gesture_pinch_interface *);
+OBJECT_DECLARE_REF(eis_gesture_pinch);
+OBJECT_DECLARE_UNREF(eis_gesture_pinch);
+
+struct eis_gesture_pinch *
+eis_gesture_pinch_new(struct eis_device *device);
+
+/* This is the protocol-only object, not exposed in the API. See eis_hold() for
+ * the C API object */
+struct eis_gesture_hold {
+ struct object object;
+ struct brei_object proto_object;
+};
+
+OBJECT_DECLARE_GETTER(eis_gesture_hold, id, object_id_t);
+OBJECT_DECLARE_GETTER(eis_gesture_hold, version, uint32_t);
+OBJECT_DECLARE_GETTER(eis_gesture_hold, context, struct eis *);
+OBJECT_DECLARE_GETTER(eis_gesture_hold, client, struct eis_client *);
+OBJECT_DECLARE_GETTER(eis_gesture_hold, device, struct eis_device *);
+OBJECT_DECLARE_GETTER(eis_gesture_hold, proto_object, const struct brei_object *);
+OBJECT_DECLARE_GETTER(eis_gesture_hold, interface, const struct eis_gesture_hold_interface *);
+OBJECT_DECLARE_REF(eis_gesture_hold);
+OBJECT_DECLARE_UNREF(eis_gesture_hold);
+
+struct eis_gesture_hold *
+eis_gesture_hold_new(struct eis_device *device);
+
+enum eis_gesture_state { EIS_GESTURE_STATE_NEW, EIS_GESTURE_STATE_BEGUN, EIS_GESTURE_STATE_ENDED };
+
+/* This is the parent object for all the C API gestures */
+struct eis_gesture {
+ struct object object;
+ struct eis_device *device;
+ enum eis_gesture_state state;
+ uint32_t nfingers;
+};
+
+OBJECT_DECLARE_REF(eis_gesture);
+OBJECT_DECLARE_UNREF(eis_gesture);
+OBJECT_DECLARE_GETTER(eis_gesture, context, struct eis *);
+OBJECT_DECLARE_GETTER(eis_gesture, device, struct eis_device *);
+
+struct eis_swipe {
+ struct eis_gesture base;
+};
+
+struct eis_pinch {
+ struct eis_gesture base;
+};
+
+struct eis_hold {
+ struct eis_gesture base;
+};
+
+struct eis_swipe *
+eis_swipe_new(struct eis_device *device, uint32_t nfingers);
+OBJECT_DECLARE_GETTER(eis_swipe, context, struct eis *);
+
+struct eis_pinch *
+eis_pinch_new(struct eis_device *device, uint32_t nfingers);
+OBJECT_DECLARE_GETTER(eis_pinch, context, struct eis *);
+
+struct eis_hold *
+eis_hold_new(struct eis_device *device, uint32_t nfingers);
+OBJECT_DECLARE_GETTER(eis_hold, context, struct eis *);
diff --git a/src/libeis-handshake.c b/src/libeis-handshake.c
index 9a72442..3a8af53 100644
--- a/src/libeis-handshake.c
+++ b/src/libeis-handshake.c
@@ -136,6 +136,9 @@ client_msg_finish(struct eis_handshake *setup)
SEND_INTERFACE_VERSION(EIS_SCROLL, ei_scroll);
SEND_INTERFACE_VERSION(EIS_KEYBOARD, ei_keyboard);
SEND_INTERFACE_VERSION(EIS_TOUCHSCREEN, ei_touchscreen);
+ SEND_INTERFACE_VERSION(EIS_GESTURE_SWIPE, ei_gesture_swipe);
+ SEND_INTERFACE_VERSION(EIS_GESTURE_PINCH, ei_gesture_pinch);
+ SEND_INTERFACE_VERSION(EIS_GESTURE_HOLD, ei_gesture_hold);
#undef SEND_INTERFACE_VERSION
@@ -237,6 +240,9 @@ client_msg_interface_version(struct eis_handshake *setup, const char *name, uint
VERSION_ENTRY(ei_scroll),
VERSION_ENTRY(ei_keyboard),
VERSION_ENTRY(ei_touchscreen),
+ VERSION_ENTRY(ei_gesture_swipe),
+ VERSION_ENTRY(ei_gesture_pinch),
+ VERSION_ENTRY(ei_gesture_hold),
VERSION_ENTRY(ei_text),
#undef VERSION_ENTRY
};
diff --git a/src/libeis-private.h b/src/libeis-private.h
index 36b8ce6..44061c0 100644
--- a/src/libeis-private.h
+++ b/src/libeis-private.h
@@ -164,6 +164,34 @@ eis_queue_text_utf8_event(struct eis_device *device, const char *utf8);
void
eis_sync_event_send_done(struct eis_event *e);
+void
+eis_queue_swipe_begin_event(struct eis_device *device, uint32_t nfingers);
+
+void
+eis_queue_swipe_update_event(struct eis_device *device, double dx, double dy);
+
+void
+eis_queue_swipe_end_event(struct eis_device *device, bool is_cancelled);
+
+void
+eis_queue_pinch_begin_event(struct eis_device *device, uint32_t nfingers);
+
+void
+eis_queue_pinch_update_event(struct eis_device *device,
+ double dx,
+ double dy,
+ double scale,
+ double rotation);
+
+void
+eis_queue_pinch_end_event(struct eis_device *device, bool is_cancelled);
+
+void
+eis_queue_hold_begin_event(struct eis_device *device, uint32_t nfingers);
+
+void
+eis_queue_hold_end_event(struct eis_device *device, bool is_cancelled);
+
_printf_(6, 7) void
eis_log_msg(struct eis *eis,
enum eis_log_priority priority,
diff --git a/src/libeis-seat.c b/src/libeis-seat.c
index 03d0534..e1b42b8 100644
--- a/src/libeis-seat.c
+++ b/src/libeis-seat.c
@@ -103,6 +103,12 @@ convert_capabilities(uint64_t caps)
capabilities |= EIS_DEVICE_CAP_BUTTON;
if (caps & bit(EIS_SCROLL_INTERFACE_INDEX))
capabilities |= EIS_DEVICE_CAP_SCROLL;
+ if (caps & bit(EIS_GESTURE_SWIPE_INTERFACE_INDEX))
+ capabilities |= EIS_DEVICE_CAP_GESTURES;
+ if (caps & bit(EIS_GESTURE_PINCH_INTERFACE_INDEX))
+ capabilities |= EIS_DEVICE_CAP_GESTURES;
+ if (caps & bit(EIS_GESTURE_HOLD_INTERFACE_INDEX))
+ capabilities |= EIS_DEVICE_CAP_GESTURES;
if (caps & bit(EIS_TEXT_INTERFACE_INDEX))
capabilities |= EIS_DEVICE_CAP_TEXT;
@@ -242,6 +248,27 @@ eis_seat_add(struct eis_seat *seat)
mask_add(seat->capabilities.proto_mask, mask);
}
+ if (seat->capabilities.c_mask & EIS_DEVICE_CAP_GESTURES &&
+ client->interface_versions.ei_gesture_swipe > 0) {
+ uint64_t mask = bit(EIS_GESTURE_SWIPE_INTERFACE_INDEX);
+ eis_seat_event_capability(seat, mask, EIS_GESTURE_SWIPE_INTERFACE_NAME);
+ mask_add(seat->capabilities.proto_mask, mask);
+ }
+
+ if (seat->capabilities.c_mask & EIS_DEVICE_CAP_GESTURES &&
+ client->interface_versions.ei_gesture_pinch > 0) {
+ uint64_t mask = bit(EIS_GESTURE_PINCH_INTERFACE_INDEX);
+ eis_seat_event_capability(seat, mask, EIS_GESTURE_PINCH_INTERFACE_NAME);
+ mask_add(seat->capabilities.proto_mask, mask);
+ }
+
+ if (seat->capabilities.c_mask & EIS_DEVICE_CAP_GESTURES &&
+ client->interface_versions.ei_gesture_hold > 0) {
+ uint64_t mask = bit(EIS_GESTURE_HOLD_INTERFACE_INDEX);
+ eis_seat_event_capability(seat, mask, EIS_GESTURE_HOLD_INTERFACE_NAME);
+ mask_add(seat->capabilities.proto_mask, mask);
+ }
+
if (seat->capabilities.c_mask & EIS_DEVICE_CAP_TEXT &&
client->interface_versions.ei_text > 0) {
uint64_t mask = bit(EIS_TEXT_INTERFACE_INDEX);
@@ -341,6 +368,7 @@ eis_seat_configure_capability(struct eis_seat *seat, enum eis_device_capability
case EIS_DEVICE_CAP_TOUCH:
case EIS_DEVICE_CAP_BUTTON:
case EIS_DEVICE_CAP_SCROLL:
+ case EIS_DEVICE_CAP_GESTURES:
case EIS_DEVICE_CAP_TEXT:
mask_add(seat->capabilities.c_mask, cap);
break;
@@ -357,6 +385,7 @@ eis_seat_has_capability(struct eis_seat *seat, enum eis_device_capability cap)
case EIS_DEVICE_CAP_TOUCH:
case EIS_DEVICE_CAP_BUTTON:
case EIS_DEVICE_CAP_SCROLL:
+ case EIS_DEVICE_CAP_GESTURES:
case EIS_DEVICE_CAP_TEXT:
return mask_all(seat->capabilities.c_mask, cap);
}
diff --git a/src/libeis.c b/src/libeis.c
index e9fb811..1508e02 100644
--- a/src/libeis.c
+++ b/src/libeis.c
@@ -153,6 +153,14 @@ eis_event_type_to_string(enum eis_event_type type)
CASE_RETURN_STRING(EIS_EVENT_TOUCH_UP);
CASE_RETURN_STRING(EIS_EVENT_TOUCH_MOTION);
CASE_RETURN_STRING(EIS_EVENT_FRAME);
+ CASE_RETURN_STRING(EIS_EVENT_SWIPE_BEGIN);
+ CASE_RETURN_STRING(EIS_EVENT_SWIPE_UPDATE);
+ CASE_RETURN_STRING(EIS_EVENT_SWIPE_END);
+ CASE_RETURN_STRING(EIS_EVENT_PINCH_BEGIN);
+ CASE_RETURN_STRING(EIS_EVENT_PINCH_UPDATE);
+ CASE_RETURN_STRING(EIS_EVENT_PINCH_END);
+ CASE_RETURN_STRING(EIS_EVENT_HOLD_BEGIN);
+ CASE_RETURN_STRING(EIS_EVENT_HOLD_END);
CASE_RETURN_STRING(EIS_EVENT_TEXT_KEYSYM);
CASE_RETURN_STRING(EIS_EVENT_TEXT_UTF8);
}
@@ -175,6 +183,14 @@ update_event_timestamp(struct eis_event *event, uint64_t time)
case EIS_EVENT_TOUCH_DOWN:
case EIS_EVENT_TOUCH_UP:
case EIS_EVENT_TOUCH_MOTION:
+ case EIS_EVENT_SWIPE_BEGIN:
+ case EIS_EVENT_SWIPE_UPDATE:
+ case EIS_EVENT_SWIPE_END:
+ case EIS_EVENT_PINCH_BEGIN:
+ case EIS_EVENT_PINCH_UPDATE:
+ case EIS_EVENT_PINCH_END:
+ case EIS_EVENT_HOLD_BEGIN:
+ case EIS_EVENT_HOLD_END:
case EIS_EVENT_TEXT_KEYSYM:
case EIS_EVENT_TEXT_UTF8:
if (event->timestamp != 0) {
@@ -213,6 +229,14 @@ eis_queue_event(struct eis_event *event)
case EIS_EVENT_TOUCH_DOWN:
case EIS_EVENT_TOUCH_UP:
case EIS_EVENT_TOUCH_MOTION:
+ case EIS_EVENT_SWIPE_BEGIN:
+ case EIS_EVENT_SWIPE_UPDATE:
+ case EIS_EVENT_SWIPE_END:
+ case EIS_EVENT_PINCH_BEGIN:
+ case EIS_EVENT_PINCH_UPDATE:
+ case EIS_EVENT_PINCH_END:
+ case EIS_EVENT_HOLD_BEGIN:
+ case EIS_EVENT_HOLD_END:
case EIS_EVENT_TEXT_KEYSYM:
case EIS_EVENT_TEXT_UTF8:
prefix = "pending ";
@@ -484,6 +508,106 @@ eis_queue_touch_cancel_event(struct eis_device *device, uint32_t touchid)
eis_queue_event(e);
}
+void
+eis_queue_swipe_begin_event(struct eis_device *device, uint32_t nfingers)
+{
+ struct eis_event *e = eis_event_new_for_device(device);
+
+ e->type = EIS_EVENT_SWIPE_BEGIN;
+ e->gestures.nfingers = nfingers;
+
+ eis_queue_event(e);
+}
+
+void
+eis_queue_swipe_update_event(struct eis_device *device, double dx, double dy)
+{
+ struct eis_event *e = eis_event_new_for_device(device);
+
+ e->type = EIS_EVENT_SWIPE_UPDATE;
+ e->gestures.nfingers = device->gesture_state.swipe.nfingers;
+ e->gestures.dx = dx;
+ e->gestures.dy = dy;
+
+ eis_queue_event(e);
+}
+
+void
+eis_queue_swipe_end_event(struct eis_device *device, bool is_cancelled)
+{
+ struct eis_event *e = eis_event_new_for_device(device);
+
+ e->type = EIS_EVENT_SWIPE_END;
+ e->gestures.nfingers = device->gesture_state.swipe.nfingers;
+ e->gestures.is_cancelled = is_cancelled;
+
+ eis_queue_event(e);
+}
+
+void
+eis_queue_pinch_begin_event(struct eis_device *device, uint32_t nfingers)
+{
+ struct eis_event *e = eis_event_new_for_device(device);
+
+ e->type = EIS_EVENT_PINCH_BEGIN;
+ e->gestures.nfingers = nfingers;
+
+ eis_queue_event(e);
+}
+
+void
+eis_queue_pinch_update_event(struct eis_device *device,
+ double dx,
+ double dy,
+ double scale,
+ double rotation)
+{
+ struct eis_event *e = eis_event_new_for_device(device);
+ e->type = EIS_EVENT_PINCH_UPDATE;
+ e->gestures.nfingers = device->gesture_state.pinch.nfingers;
+ e->gestures.scale = scale;
+ e->gestures.degrees = rotation;
+ e->gestures.dx = dx;
+ e->gestures.dy = dy;
+
+ eis_queue_event(e);
+}
+
+void
+eis_queue_pinch_end_event(struct eis_device *device, bool is_cancelled)
+{
+ struct eis_event *e = eis_event_new_for_device(device);
+
+ e->type = EIS_EVENT_PINCH_END;
+ e->gestures.nfingers = device->gesture_state.pinch.nfingers;
+ e->gestures.is_cancelled = is_cancelled;
+
+ eis_queue_event(e);
+}
+
+void
+eis_queue_hold_begin_event(struct eis_device *device, uint32_t nfingers)
+{
+ struct eis_event *e = eis_event_new_for_device(device);
+
+ e->type = EIS_EVENT_HOLD_BEGIN;
+ e->gestures.nfingers = nfingers;
+
+ eis_queue_event(e);
+}
+
+void
+eis_queue_hold_end_event(struct eis_device *device, bool is_cancelled)
+{
+ struct eis_event *e = eis_event_new_for_device(device);
+
+ e->type = EIS_EVENT_HOLD_END;
+ e->gestures.nfingers = device->gesture_state.hold.nfingers;
+ e->gestures.is_cancelled = is_cancelled;
+
+ eis_queue_event(e);
+}
+
void
eis_queue_text_keysym_event(struct eis_device *device, uint32_t keysym, bool is_press)
{
diff --git a/src/libeis.h b/src/libeis.h
index d9db61c..e7382b7 100644
--- a/src/libeis.h
+++ b/src/libeis.h
@@ -146,6 +146,18 @@ struct eis_keymap;
* @struct eis_touch
*/
struct eis_touch;
+/**
+ * @struct eis_swipe
+ */
+struct eis_swipe;
+/**
+ * @struct eis_pinch
+ */
+struct eis_pinch;
+/**
+ * @struct eis_hold
+ */
+struct eis_hold;
/**
* @struct eis_ping
@@ -225,7 +237,11 @@ enum eis_device_capability {
/**
* @since 1.6
*/
- EIS_DEVICE_CAP_TEXT = (1 << 6),
+ EIS_DEVICE_CAP_GESTURES = (1 << 6),
+ /**
+ * @since 1.6
+ */
+ EIS_DEVICE_CAP_TEXT = (1 << 7),
};
/**
@@ -405,12 +421,97 @@ enum eis_event_type {
*/
EIS_EVENT_TOUCH_MOTION,
+ /**
+ * Event for a swipe begin. Only one swipe gesture may be active
+ * at any time.
+ *
+ * @note This event is only generated on a sender eis context.
+ * See eis_device_swipe_new() and eis_swipe_begin() for the receiver context API.
+ *
+ * @since 1.6
+ */
+ EIS_EVENT_SWIPE_BEGIN = 900,
+ /**
+ * Event for an update to the current ongoing swipe gesture.
+ *
+ * This library accumulates all updates to the gesture within the same
+ * device frame into one gesture event.
+ *
+ * @note This event is only generated on a sender eis context.
+ * See eis_device_swipe_new() and eis_swipe_update() for the receiver context API.
+ *
+ * @since 1.6
+ */
+ EIS_EVENT_SWIPE_UPDATE,
+ /**
+ * Event for the logical end to the current ongoing swipe gesture.
+ *
+ * @note This event is only generated on a sender eis context.
+ * See eis_device_swipe_new() and eis_swipe_end() for the receiver context API.
+ *
+ * @since 1.6
+ */
+ EIS_EVENT_SWIPE_END,
+
+ /**
+ * Event for a pinch begin. Only one pinch gesture may be active
+ * at any time.
+ *
+ * @note This event is only generated on a sender eis context.
+ * See eis_device_pinch_new() and eis_pinch_begin() for the receiver context API.
+ *
+ * @since 1.6
+ */
+ EIS_EVENT_PINCH_BEGIN = 910,
+ /**
+ * Event for an update to the current ongoing pinch gesture.
+ *
+ * This library accumulates all updates to the gesture within the same
+ * device frame into one gesture event, e.g. where the gestures moves
+ * and rotates only one update event is generated.
+ *
+ * @note This event is only generated on a sender eis context.
+ * See eis_device_pinch_new() and eis_pinch_update() for the receiver context API.
+ *
+ * @since 1.6
+ */
+ EIS_EVENT_PINCH_UPDATE,
+ /**
+ * Event for the logical end to the current ongoing pinch gesture.
+ *
+ * @note This event is only generated on a sender eis context.
+ * See eis_device_pinch_new() and eis_pinch_end() for the receiver context API.
+ *
+ * @since 1.6
+ */
+ EIS_EVENT_PINCH_END,
+
+ /**
+ * Event for a hold begin. Only one hold gesture may be active
+ * at any time.
+ *
+ * @note This event is only generated on a sender eis context.
+ * See eis_device_hold_new() and eis_hold_begin() for the receiver context API.
+ *
+ * @since 1.6
+ */
+ EIS_EVENT_HOLD_BEGIN = 920,
+ /**
+ * Event for the logical end to the current ongoing hold gesture.
+ *
+ * @note This event is only generated on a sender eis context.
+ * See eis_device_hold_new() and eis_hold_end() for the receiver context API.
+ *
+ * @since 1.6
+ */
+ EIS_EVENT_HOLD_END,
+
/**
* A key sym logical press or release event
*
* @since 1.6
*/
- EIS_EVENT_TEXT_KEYSYM = 900,
+ EIS_EVENT_TEXT_KEYSYM = 1000,
/**
* A UTF-8 text event
@@ -1372,7 +1473,8 @@ eis_device_remove(struct eis_device *device);
* - any buttons or keys logically down are released
* - any modifiers logically down are released
* - any touches logically down are released
- * No events will be sent for these releases back to a neutral state.
+ * - any gestures logically active are ended
+ * No events will be sent for these state changes back to a neutral state.
*
* @param device A connected device
*/
@@ -1724,6 +1826,363 @@ eis_touch_get_user_data(struct eis_touch *touch);
struct eis_device *
eis_touch_get_device(struct eis_touch *touch);
+/**
+ * @ingroup libeis-sender
+ *
+ * Cancel any ongoing swipe gesture on a device with the @ref EIS_DEVICE_CAP_GESTURES
+ * capability. This notifies the client of the gesture cancelation and the client
+ * may no longer send @ref EIS_EVENT_SWIPE_UPDATE or
+ * @ref EIS_EVENT_SWIPE_END for the current gestures.
+ *
+ * Due to the asynchronous nature of the protocol, events of type
+ * @ref EIS_EVENT_SWIPE_UPDATE and/or
+ * @ref EIS_EVENT_SWIPE_END may have already been sent by the client. An EIS
+ * implementation must ignore those events.
+ *
+ * This method is only available on a sender eis context.
+ *
+ * If no swipe gesture is currently in progress, this function does nothing.
+ *
+ * @since 1.6
+ */
+void
+eis_device_swipe_cancel(struct eis_device *device);
+
+/**
+ * @ingroup libeis-sender
+ *
+ * Cancel any ongoing pinch gesture on a device with the @ref EIS_DEVICE_CAP_GESTURES
+ * capability. This notifies the client of the gesture cancelation and the client
+ * may no longer send @ref EIS_EVENT_PINCH_UPDATE or
+ * @ref EIS_EVENT_PINCH_END for the current gestures.
+ *
+ * Due to the asynchronous nature of the protocol, events of type
+ * @ref EIS_EVENT_PINCH_UPDATE and/or
+ * @ref EIS_EVENT_PINCH_END may have already been sent by the client. An EIS
+ * implementation must ignore those events.
+ *
+ * This method is only available on a sender eis context.
+ *
+ * If no pinch gesture is currently in progress, this function does nothing.
+ *
+ * @since 1.6
+ */
+void
+eis_device_pinch_cancel(struct eis_device *device);
+
+/**
+ * @ingroup libeis-sender
+ *
+ * Cancel any ongoing hold gesture on a device with the @ref EIS_DEVICE_CAP_GESTURES
+ * capability. This notifies the client of the gesture cancelation and the client
+ * may no longer send @ref EIS_EVENT_HOLD_END for the current gestures.
+ *
+ * Due to the asynchronous nature of the protocol, events of type
+ * @ref EIS_EVENT_HOLD_END may have already been sent by the client. An EIS
+ * implementation must ignore those events.
+ *
+ * This method is only available on a sender eis context.
+ *
+ * If no hold gesture is currently in progress, this function does nothing.
+ *
+ * @since 1.6
+ */
+void
+eis_device_hold_cancel(struct eis_device *device);
+
+/**
+ * @ingroup libeis-receiver
+ *
+ * Initiate a new swipe gesture on a device with the @ref EIS_DEVICE_CAP_GESTURES
+ * capability. This gesture does not immediately send events, use
+ * eis_swipe_begin(), eis_swipe_update() and eis_swipe_end().
+ *
+ * The returned gesture has a refcount of at least 1, use eis_swipe_unref() to
+ * release resources associated with this gesture
+ *
+ * This method is only available on an eis receiver context.
+ *
+ * If the underlying device does not support swipe gestures, this function
+ * returns NULL.
+ *
+ * @param device A device with @ref EIS_DEVICE_CAP_GESTURES capability
+ * @param nfingers The number of fingers, must be greater than 0
+ * @return A new swipe gesture or NULL on error
+ *
+ * @since 1.6
+ */
+struct eis_swipe *
+eis_device_swipe_new(struct eis_device *device, uint32_t nfingers);
+
+/**
+ * @ingroup libeis-receiver
+ *
+ * Starts the swipe gesture.
+ *
+ * This function can only be called once on an eis_swipe object. Further
+ * calls to eis_swipe_begin() on the same object are silently ignored.
+ *
+ * @param swipe A newly created swipe gesture
+ *
+ * @since 1.6
+ */
+void
+eis_swipe_begin(struct eis_swipe *swipe);
+
+/**
+ * @ingroup libeis-receiver
+ *
+ * Update the swipe gestures's position by the given relative delta.
+ *
+ * @since 1.6
+ */
+void
+eis_swipe_update(struct eis_swipe *swipe, double x, double y);
+
+/**
+ * @ingroup libeis-receiver
+ *
+ * Release this swipe. After this call, the swipe event becomes inert and
+ * no longer responds to either eis_swipe_begin(),
+ * eis_swipe_update(), eis_swipe_cancel() or
+ * eis_swipe_end() and the caller should call eis_swipe_unref().
+ *
+ * @since 1.6
+ */
+void
+eis_swipe_end(struct eis_swipe *swipe);
+
+/**
+ * @ingroup libeis-receiver
+ *
+ * Cancel this swipe. After this call, the swipe event becomes inert and
+ * no longer responds to either eis_swipe_begin(),
+ * eis_swipe_update(), eis_swipe_cancel() or
+ * eis_swipe_end() and the caller should call eis_swipe_unref().
+ *
+ * @since 1.6
+ */
+void
+eis_swipe_cancel(struct eis_swipe *swipe);
+
+/**
+ * @ingroup libeis-receiver
+ *
+ * Increase the refcount of this struct by one. Use eis_swipe_unref() to
+ * decrease the refcount.
+ *
+ * @return the argument passed into the function
+ *
+ * @since 1.6
+ */
+struct eis_swipe *
+eis_swipe_ref(struct eis_swipe *swipe);
+
+/**
+ * @ingroup libeis-receiver
+ *
+ * Decrease the refcount of this struct by one. When the refcount reaches
+ * zero, all allocated resources for this struct are released.
+ *
+ * @return always NULL
+ *
+ * @since 1.6
+ */
+struct eis_swipe *
+eis_swipe_unref(struct eis_swipe *swipe);
+
+/**
+ * @ingroup libeis-receiver
+ *
+ * Initiate a new pinch gesture on a device with the @ref EIS_DEVICE_CAP_GESTURES
+ * capability. This gesture does not immediately send events, use
+ * eis_pinch_begin(), eis_pinch_update() and eis_pinch_end().
+ *
+ * The returned gesture has a refcount of at least 1, use eis_pinch_unref() to
+ * release resources associated with this gesture
+ *
+ * This method is only available on an eis receiver context.
+ *
+ * If the underlying device does not support pinch gestures, this function
+ * returns NULL.
+ *
+ * @param device A device with @ref EIS_DEVICE_CAP_GESTURES capability
+ * @param nfingers The number of fingers, must be at least 2
+ * @return A new pinch gesture or NULL on error
+ *
+ * @since 1.6
+ */
+struct eis_pinch *
+eis_device_pinch_new(struct eis_device *device, uint32_t nfingers);
+
+/**
+ * @ingroup libeis-receiver
+ *
+ * Starts the pinch gesture.
+ *
+ * This function can only be called once on an eis_pinch object. Further
+ * calls to eis_pinch_begin() on the same object are silently ignored.
+ *
+ * @param pinch A newly created pinch gesture
+ *
+ * @since 1.6
+ */
+void
+eis_pinch_begin(struct eis_pinch *pinch);
+
+/**
+ * @ingroup libeis-receiver
+ *
+ * Update the pinch gestures's position by the given relative delta.
+ *
+ * @since 1.6
+ */
+void
+eis_pinch_update(struct eis_pinch *pinch, double x, double y, double scale, double rotation);
+
+/**
+ * @ingroup libeis-receiver
+ *
+ * Release this pinch. After this call, the pinch event becomes inert and
+ * no longer responds to either eis_pinch_begin(),
+ * eis_pinch_update(), eis_pinch_cancel() or
+ * eis_pinch_end() and the caller should call eis_pinch_unref().
+ *
+ * @since 1.6
+ */
+void
+eis_pinch_end(struct eis_pinch *pinch);
+
+/**
+ * @ingroup libeis-receiver
+ *
+ * Cancel this pinch. After this call, the pinch event becomes inert and
+ * no longer responds to either eis_pinch_begin(),
+ * eis_pinch_update(), eis_pinch_cancel() or
+ * eis_pinch_end() and the caller should call eis_pinch_unref().
+ *
+ * @since 1.6
+ */
+void
+eis_pinch_cancel(struct eis_pinch *pinch);
+
+/**
+ * @ingroup libeis-receiver
+ *
+ * Increase the refcount of this struct by one. Use eis_pinch_unref() to
+ * decrease the refcount.
+ *
+ * @return the argument passed into the function
+ *
+ * @since 1.6
+ */
+struct eis_pinch *
+eis_pinch_ref(struct eis_pinch *pinch);
+
+/**
+ * @ingroup libeis-receiver
+ *
+ * Decrease the refcount of this struct by one. When the refcount reaches
+ * zero, all allocated resources for this struct are released.
+ *
+ * @return always NULL
+ *
+ * @since 1.6
+ */
+struct eis_pinch *
+eis_pinch_unref(struct eis_pinch *pinch);
+
+/**
+ * @ingroup libeis-receiver
+ *
+ * Initiate a new hold gesture on a device with the @ref EIS_DEVICE_CAP_GESTURES
+ * capability. This gesture does not immediately send events, use
+ * eis_hold_begin() and eis_hold_end().
+ *
+ * The returned gesture has a refcount of at least 1, use eis_hold_unref() to
+ * release resources associated with this gesture
+ *
+ * This method is only available on an eis receiver context.
+ *
+ * If the underlying device does not support hold gestures, this function
+ * returns NULL.
+ *
+ * @param device A device with @ref EIS_DEVICE_CAP_GESTURES capability
+ * @param nfingers The number of fingers, must be greater than 0
+ * @return A new hold gesture or NULL on error
+ *
+ * @since 1.6
+ */
+struct eis_hold *
+eis_device_hold_new(struct eis_device *device, uint32_t nfingers);
+
+/**
+ * @ingroup libeis-receiver
+ *
+ * Starts the hold gesture.
+ *
+ * This function can only be called once on an eis_hold object. Further
+ * calls to eis_hold_begin() on the same object are silently ignored.
+ *
+ * @param hold A newly created hold gesture
+ *
+ * @since 1.6
+ */
+void
+eis_hold_begin(struct eis_hold *hold);
+
+/**
+ * @ingroup libeis-receiver
+ *
+ * Release this hold. After this call, the hold event becomes inert and
+ * no longer responds to either eis_hold_begin(),
+ * eis_hold_cancel() or
+ * eis_hold_end() and the caller should call eis_hold_unref().
+ *
+ * @since 1.6
+ */
+void
+eis_hold_end(struct eis_hold *hold);
+
+/**
+ * @ingroup libeis-receiver
+ *
+ * Cancel this hold. After this call, the hold event becomes inert and
+ * no longer responds to either eis_hold_begin(),
+ * eis_hold_cancel() or
+ * eis_hold_end() and the caller should call eis_hold_unref().
+ *
+ * @since 1.6
+ */
+void
+eis_hold_cancel(struct eis_hold *hold);
+
+/**
+ * @ingroup libeis-receiver
+ *
+ * Increase the refcount of this struct by one. Use eis_hold_unref() to
+ * decrease the refcount.
+ *
+ * @return the argument passed into the function
+ *
+ * @since 1.6
+ */
+struct eis_hold *
+eis_hold_ref(struct eis_hold *hold);
+
+/**
+ * @ingroup libeis-receiver
+ *
+ * Decrease the refcount of this struct by one. When the refcount reaches
+ * zero, all allocated resources for this struct are released.
+ *
+ * @return always NULL
+ *
+ * @since 1.6
+ */
+struct eis_hold *
+eis_hold_unref(struct eis_hold *hold);
+
/**
* @ingroup libeis-sender
*
@@ -1915,6 +2374,145 @@ eis_event_touch_get_y(struct eis_event *event);
bool
eis_event_touch_get_is_cancel(struct eis_event *event);
+/**
+ * @ingroup libeis-sender
+ *
+ * For an event of type @ref EIS_EVENT_SWIPE_BEGIN,
+ * @ref EIS_EVENT_SWIPE_UPDATE or @ref EIS_EVENT_SWIPE_END
+ * return the finger count for this gesture.
+ *
+ * @since 1.6
+ */
+uint32_t
+eis_event_swipe_get_finger_count(struct eis_event *event);
+
+/**
+ * @ingroup libeis-sender
+ *
+ * For an event of type @ref EIS_EVENT_SWIPE_UPDATE
+ * return the relative x movement of the logical center
+ * of the gesture in logical pixels or mm, depending on the device type.
+ *
+ * @since 1.6
+ */
+double
+eis_event_swipe_get_dx(struct eis_event *event);
+
+/**
+ * @ingroup libeis-sender
+ *
+ * For an event of type @ref EIS_EVENT_SWIPE_UPDATE
+ * return the relative y movement of the logical center
+ * of the gesture in logical pixels or mm, depending on the device type.
+ *
+ * @since 1.6
+ */
+double
+eis_event_swipe_get_dy(struct eis_event *event);
+
+/**
+ * @ingroup libeis-sender
+ *
+ * For an event of type @ref EIS_EVENT_SWIPE_END
+ * return true if the gesture was cancelled, false otherwise.
+ *
+ * @since 1.6
+ */
+bool
+eis_event_swipe_get_is_cancelled(struct eis_event *event);
+
+/**
+ * @ingroup libeis-sender
+ *
+ * For an event of type @ref EIS_EVENT_PINCH_BEGIN,
+ * @ref EIS_EVENT_PINCH_UPDATE or @ref EIS_EVENT_PINCH_END
+ * return the finger count for this gesture.
+ *
+ * @since 1.6
+ */
+uint32_t
+eis_event_pinch_get_finger_count(struct eis_event *event);
+
+/**
+ * @ingroup libeis-sender
+ *
+ * For an event of type @ref EIS_EVENT_PINCH_UPDATE
+ * return the relative x movement of the logical center
+ * of the gesture in logical pixels or mm, depending on the device type.
+ *
+ * @since 1.6
+ */
+double
+eis_event_pinch_get_dx(struct eis_event *event);
+
+/**
+ * @ingroup libeis-sender
+ *
+ * For an event of type @ref EIS_EVENT_PINCH_UPDATE
+ * return the relative y movement of the logical center
+ * of the gesture in logical pixels or mm, depending on the device type.
+ *
+ * @since 1.6
+ */
+double
+eis_event_pinch_get_dy(struct eis_event *event);
+
+/**
+ * @ingroup libeis-sender
+ *
+ * For an event of type @ref EIS_EVENT_PINCH_UPDATE
+ * return the relative angle of rotation in degrees clockwise.
+ *
+ * @since 1.6
+ */
+double
+eis_event_pinch_get_rotation(struct eis_event *event);
+
+/**
+ * @ingroup libeis-sender
+ *
+ * For an event of type @ref EIS_EVENT_PINCH_UPDATE
+ * return the normalized scale of the gesture.
+ *
+ * @since 1.6
+ */
+double
+eis_event_pinch_get_scale(struct eis_event *event);
+
+/**
+ * @ingroup libeis-sender
+ *
+ * For an event of type @ref EIS_EVENT_PINCH_END
+ * return true if the gesture was cancelled, false otherwise.
+ *
+ * @since 1.6
+ */
+bool
+eis_event_pinch_get_is_cancelled(struct eis_event *event);
+
+/**
+ * @ingroup libeis-sender
+ *
+ * For an event of type @ref EIS_EVENT_HOLD_BEGIN
+ * or @ref EIS_EVENT_HOLD_END
+ * return the finger count for this gesture.
+ *
+ * @since 1.6
+ */
+uint32_t
+eis_event_hold_get_finger_count(struct eis_event *event);
+
+/**
+ * @ingroup libeis-sender
+ *
+ * For an event of type @ref EIS_EVENT_HOLD_END
+ * return true if the gesture was cancelled, false otherwise.
+ *
+ * @since 1.6
+ */
+bool
+eis_event_hold_get_is_cancelled(struct eis_event *event);
+
/**
* @ingroup libeis-sender
*
diff --git a/src/meson.build b/src/meson.build
index 2da91ad..1abfa38 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -67,6 +67,7 @@ if build_libei
'libei-device.c',
'libei-event.c',
'libei-fd.c',
+ 'libei-gestures.c',
'libei-handshake.c',
'libei-keyboard.c',
'libei-log.c',
@@ -147,6 +148,7 @@ if build_libeis
'libeis-device.c',
'libeis-event.c',
'libeis-fd.c',
+ 'libeis-gestures.c',
'libeis-handshake.c',
'libeis-keyboard.c',
'libeis-log.c',
diff --git a/src/util-munit.h b/src/util-munit.h
index c5d9fad..8423b33 100644
--- a/src/util-munit.h
+++ b/src/util-munit.h
@@ -135,6 +135,12 @@ static MunitResult _func(const MunitParameter params[], void *user_data)
#define MUNIT_TEST_PARAM(name_) \
munit_parameters_get(params, name_)
+#define MUNIT_TEST_PARAM_BOOL(name_) \
+ streq(munit_parameters_get(params, name_), "true")
+
+#define MUNIT_TEST_PARAM_INT(name_) \
+ atoi(munit_parameters_get(params, name_))
+
/**
* Defines a struct global_setup_function in a custom ELF section that we can
* then find in munit_tests_run() to do setup based on argv and/or pass userdata
diff --git a/test/eierpecken.c b/test/eierpecken.c
index 45f3a4a..5178cb8 100644
--- a/test/eierpecken.c
+++ b/test/eierpecken.c
@@ -74,6 +74,7 @@ struct peck {
struct eis_device *eis_button;
struct eis_device *eis_scroll;
struct eis_device *eis_touch;
+ struct eis_device *eis_gestures;
struct ei_seat *ei_seat;
struct ei_device *ei_pointer;
@@ -82,6 +83,7 @@ struct peck {
struct ei_device *ei_button;
struct ei_device *ei_scroll;
struct ei_device *ei_touch;
+ struct ei_device *ei_gestures;
uint64_t now;
@@ -174,6 +176,7 @@ peck_destroy(struct peck *peck)
eis_device_unref(peck->eis_touch);
eis_device_unref(peck->eis_button);
eis_device_unref(peck->eis_scroll);
+ eis_device_unref(peck->eis_gestures);
eis_seat_unref(peck->eis_seat);
ei_device_unref(peck->ei_pointer);
@@ -182,6 +185,7 @@ peck_destroy(struct peck *peck)
ei_device_unref(peck->ei_touch);
ei_device_unref(peck->ei_button);
ei_device_unref(peck->ei_scroll);
+ ei_device_unref(peck->ei_gestures);
ei_seat_unref(peck->ei_seat);
ei_unref(peck->ei);
@@ -350,6 +354,13 @@ peck_eis_get_default_touch(struct peck *peck)
return peck->eis_touch;
}
+struct eis_device *
+peck_eis_get_default_gestures(struct peck *peck)
+{
+ munit_assert_ptr_not_null(peck->eis_gestures);
+ return peck->eis_gestures;
+}
+
struct ei_seat *
peck_ei_get_default_seat(struct peck *peck)
{
@@ -399,6 +410,13 @@ peck_ei_get_default_touch(struct peck *peck)
return peck->ei_touch;
}
+struct ei_device *
+peck_ei_get_default_gestures(struct peck *peck)
+{
+ munit_assert_ptr_not_null(peck->ei_gestures);
+ return peck->ei_gestures;
+}
+
/* Ensures that device frames in tests always have an ascending and fixed
* interval. Every time this is called it adds 10ms to the time offset.
*/
@@ -749,6 +767,7 @@ peck_enable_eis_behavior(struct peck *peck, enum peck_eis_behavior behavior)
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_POINTER_ABSOLUTE);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_KEYBOARD);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_TOUCH);
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_GESTURES);
break;
case PECK_EIS_BEHAVIOR_HANDLE_BIND_SEAT:
case PECK_EIS_BEHAVIOR_HANDLE_CLOSE_DEVICE:
@@ -760,6 +779,9 @@ peck_enable_eis_behavior(struct peck *peck, enum peck_eis_behavior behavior)
case PECK_EIS_BEHAVIOR_ADD_POINTER_ABSOLUTE:
case PECK_EIS_BEHAVIOR_ADD_KEYBOARD:
case PECK_EIS_BEHAVIOR_ADD_TOUCH:
+ case PECK_EIS_BEHAVIOR_ADD_GESTURES:
+ flag_set(peck->eis_behavior, behavior);
+ break;
case PECK_EIS_BEHAVIOR_HANDLE_DEVICE_READY:
flag_set(peck->eis_behavior, behavior);
break;
@@ -818,6 +840,7 @@ peck_disable_eis_behavior(struct peck *peck, enum peck_eis_behavior behavior)
case PECK_EIS_BEHAVIOR_ADD_POINTER_ABSOLUTE:
case PECK_EIS_BEHAVIOR_ADD_KEYBOARD:
case PECK_EIS_BEHAVIOR_ADD_TOUCH:
+ case PECK_EIS_BEHAVIOR_ADD_GESTURES:
case PECK_EIS_BEHAVIOR_HANDLE_DEVICE_READY:
case PECK_EIS_BEHAVIOR_REJECT_CLIENT:
case PECK_EIS_BEHAVIOR_ACCEPT_CLIENT:
@@ -852,6 +875,7 @@ peck_enable_ei_behavior(struct peck *peck, enum peck_ei_behavior behavior)
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH);
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_BUTTON);
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_SCROLL);
+ peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_GESTURES);
break;
case PECK_EI_BEHAVIOR_AUTOSTART:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER:
@@ -860,6 +884,7 @@ peck_enable_ei_behavior(struct peck *peck, enum peck_ei_behavior behavior)
case PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_BUTTON:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_SCROLL:
+ case PECK_EI_BEHAVIOR_HANDLE_ADDED_GESTURES:
case PECK_EI_BEHAVIOR_HANDLE_FRAME:
case PECK_EI_BEHAVIOR_HANDLE_SYNC:
case PECK_EI_BEHAVIOR_HANDLE_RESUMED:
@@ -901,6 +926,7 @@ peck_disable_ei_behavior(struct peck *peck, enum peck_ei_behavior behavior)
case PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_BUTTON:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_SCROLL:
+ case PECK_EI_BEHAVIOR_HANDLE_ADDED_GESTURES:
case PECK_EI_BEHAVIOR_HANDLE_FRAME:
case PECK_EI_BEHAVIOR_HANDLE_SYNC:
case PECK_EI_BEHAVIOR_HANDLE_RESUMED:
@@ -921,6 +947,7 @@ peck_create_eis_seat(struct peck *peck, struct eis_client *client)
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_TOUCH);
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_BUTTON);
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_SCROLL);
+ eis_seat_configure_capability(seat, EIS_DEVICE_CAP_GESTURES);
log_debug(peck, "EIS adding seat: '%s'\n", eis_seat_get_name(seat));
eis_seat_add(seat);
@@ -1007,6 +1034,23 @@ peck_eis_create_touch(struct peck *peck, struct eis_seat *seat, const char *name
return device;
}
+static inline struct eis_device *
+peck_eis_create_gestures(struct peck *peck, struct eis_seat *seat, const char *name)
+{
+ struct eis_device *device = eis_seat_new_device(seat);
+ _unref_(eis_region) *region = eis_device_new_region(device);
+
+ eis_region_set_offset(region, 0, 0);
+ eis_region_set_size(region, 1920, 1080);
+
+ eis_device_configure_name(device, name);
+ eis_device_configure_capability(device, EIS_DEVICE_CAP_GESTURES);
+ eis_region_add(region);
+ eis_device_add(device);
+
+ return device;
+}
+
static inline void
peck_handle_eis_seat_bind(struct peck *peck, struct eis_event *e)
{
@@ -1098,13 +1142,35 @@ peck_handle_eis_seat_bind(struct peck *peck, struct eis_event *e)
}
}
+ if (eis_event_seat_has_capability(e, EIS_DEVICE_CAP_GESTURES)) {
+ if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_ADD_GESTURES) &&
+ !peck->eis_gestures) {
+ log_debug(peck, "EIS creating default gesture\n");
+ _unref_(eis_device) *gestures =
+ peck_eis_create_gestures(peck, seat, "default gesture");
+
+ if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_RESUME_DEVICE))
+ eis_device_resume(gestures);
+
+ peck->eis_gestures = eis_device_ref(gestures);
+ }
+ } else {
+ if (peck->eis_gestures) {
+ log_debug(peck, "EIS removing default gesture\n");
+
+ _unref_(eis_device) *gestures = steal(&peck->eis_gestures);
+ eis_device_remove(gestures);
+ }
+ }
+
/* Removing all caps means removing the seat */
if (!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_POINTER) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_POINTER_ABSOLUTE) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_KEYBOARD) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_BUTTON) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_SCROLL) &&
- !eis_event_seat_has_capability(e, EIS_DEVICE_CAP_TOUCH))
+ !eis_event_seat_has_capability(e, EIS_DEVICE_CAP_TOUCH) &&
+ !eis_event_seat_has_capability(e, EIS_DEVICE_CAP_GESTURES))
eis_seat_remove(seat);
}
@@ -1124,6 +1190,8 @@ peck_eis_device_remove(struct peck *peck, struct eis_device *device)
peck->eis_button = eis_device_unref(device);
if (device == peck->eis_scroll)
peck->eis_scroll = eis_device_unref(device);
+ if (device == peck->eis_gestures)
+ peck->eis_gestures = eis_device_unref(device);
}
bool
@@ -1205,6 +1273,14 @@ _peck_dispatch_eis(struct peck *peck, int lineno)
case EIS_EVENT_TOUCH_DOWN:
case EIS_EVENT_TOUCH_UP:
case EIS_EVENT_TOUCH_MOTION:
+ case EIS_EVENT_SWIPE_BEGIN:
+ case EIS_EVENT_SWIPE_UPDATE:
+ case EIS_EVENT_SWIPE_END:
+ case EIS_EVENT_PINCH_BEGIN:
+ case EIS_EVENT_PINCH_UPDATE:
+ case EIS_EVENT_PINCH_END:
+ case EIS_EVENT_HOLD_BEGIN:
+ case EIS_EVENT_HOLD_END:
case EIS_EVENT_TEXT_KEYSYM:
case EIS_EVENT_TEXT_UTF8:
peck->eis_needs_frame = true;
@@ -1291,6 +1367,10 @@ peck_check_ei_added(struct peck *peck, struct ei_event *e)
flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_ADDED_SCROLL))
return tristate_yes;
+ if (ei_device_has_capability(device, EI_DEVICE_CAP_GESTURES) &&
+ flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_ADDED_GESTURES))
+ return tristate_yes;
+
return tristate_unset;
}
@@ -1367,10 +1447,22 @@ _peck_dispatch_ei(struct peck *peck, int lineno)
case EI_EVENT_TOUCH_DOWN:
case EI_EVENT_TOUCH_UP:
case EI_EVENT_TOUCH_MOTION:
+ case EI_EVENT_SWIPE_BEGIN:
+ case EI_EVENT_SWIPE_UPDATE:
+ case EI_EVENT_SWIPE_END:
+ case EI_EVENT_PINCH_BEGIN:
+ case EI_EVENT_PINCH_UPDATE:
+ case EI_EVENT_PINCH_END:
+ case EI_EVENT_HOLD_BEGIN:
+ case EI_EVENT_HOLD_END:
case EI_EVENT_TEXT_KEYSYM:
case EI_EVENT_TEXT_UTF8:
peck->ei_needs_frame = true;
break;
+ case EI_EVENT_SWIPE_CANCELLED:
+ case EI_EVENT_PINCH_CANCELLED:
+ case EI_EVENT_HOLD_CANCELLED:
+ break;
}
log_debug(peck,
@@ -1405,6 +1497,7 @@ _peck_dispatch_ei(struct peck *peck, int lineno)
EI_DEVICE_CAP_TOUCH,
EI_DEVICE_CAP_BUTTON,
EI_DEVICE_CAP_SCROLL,
+ EI_DEVICE_CAP_GESTURES,
NULL);
break;
}
@@ -1428,6 +1521,9 @@ _peck_dispatch_ei(struct peck *peck, int lineno)
if (!peck->ei_scroll &&
ei_device_has_capability(device, EI_DEVICE_CAP_SCROLL))
peck->ei_scroll = ei_device_ref(device);
+ if (!peck->ei_gestures &&
+ ei_device_has_capability(device, EI_DEVICE_CAP_GESTURES))
+ peck->ei_gestures = ei_device_ref(device);
break;
}
case EI_EVENT_DEVICE_RESUMED:
@@ -1688,6 +1784,70 @@ _peck_ei_touch_event(struct ei *ei, enum ei_event_type type, double x, double y,
return event;
}
+struct ei_event *
+_peck_ei_swipe_event(struct ei *ei, enum ei_event_type type, int lineno)
+{
+ assert(type == EI_EVENT_SWIPE_BEGIN || type == EI_EVENT_SWIPE_UPDATE ||
+ type == EI_EVENT_SWIPE_END);
+
+ struct ei_event *event = _peck_ei_next_event(ei, type, lineno);
+
+ return event;
+}
+
+struct ei_event *
+_peck_ei_pinch_event(struct ei *ei, enum ei_event_type type, int lineno)
+{
+ assert(type == EI_EVENT_PINCH_BEGIN || type == EI_EVENT_PINCH_UPDATE ||
+ type == EI_EVENT_PINCH_END);
+
+ struct ei_event *event = _peck_ei_next_event(ei, type, lineno);
+
+ return event;
+}
+
+struct ei_event *
+_peck_ei_hold_event(struct ei *ei, enum ei_event_type type, int lineno)
+{
+ assert(type == EI_EVENT_HOLD_BEGIN || type == EI_EVENT_HOLD_END);
+
+ struct ei_event *event = _peck_ei_next_event(ei, type, lineno);
+
+ return event;
+}
+
+struct eis_event *
+_peck_eis_swipe_event(struct eis *eis, enum eis_event_type type, int lineno)
+{
+ assert(type == EIS_EVENT_SWIPE_BEGIN || type == EIS_EVENT_SWIPE_UPDATE ||
+ type == EIS_EVENT_SWIPE_END);
+
+ struct eis_event *event = _peck_eis_next_event(eis, type, lineno);
+
+ return event;
+}
+
+struct eis_event *
+_peck_eis_pinch_event(struct eis *eis, enum eis_event_type type, int lineno)
+{
+ assert(type == EIS_EVENT_PINCH_BEGIN || type == EIS_EVENT_PINCH_UPDATE ||
+ type == EIS_EVENT_PINCH_END);
+
+ struct eis_event *event = _peck_eis_next_event(eis, type, lineno);
+
+ return event;
+}
+
+struct eis_event *
+_peck_eis_hold_event(struct eis *eis, enum eis_event_type type, int lineno)
+{
+ assert(type == EIS_EVENT_HOLD_BEGIN || type == EIS_EVENT_HOLD_END);
+
+ struct eis_event *event = _peck_eis_next_event(eis, type, lineno);
+
+ return event;
+}
+
const char *
peck_ei_event_name(struct ei_event *e)
{
@@ -1726,6 +1886,17 @@ peck_ei_event_type_name(enum ei_event_type type)
CASE_STRING(TOUCH_DOWN);
CASE_STRING(TOUCH_UP);
CASE_STRING(TOUCH_MOTION);
+ CASE_STRING(SWIPE_BEGIN);
+ CASE_STRING(SWIPE_UPDATE);
+ CASE_STRING(SWIPE_END);
+ CASE_STRING(SWIPE_CANCELLED);
+ CASE_STRING(PINCH_BEGIN);
+ CASE_STRING(PINCH_UPDATE);
+ CASE_STRING(PINCH_END);
+ CASE_STRING(PINCH_CANCELLED);
+ CASE_STRING(HOLD_BEGIN);
+ CASE_STRING(HOLD_END);
+ CASE_STRING(HOLD_CANCELLED);
CASE_STRING(TEXT_KEYSYM);
CASE_STRING(TEXT_UTF8);
}
@@ -1767,6 +1938,14 @@ peck_eis_event_type_name(enum eis_event_type type)
CASE_STRING(TOUCH_DOWN);
CASE_STRING(TOUCH_UP);
CASE_STRING(TOUCH_MOTION);
+ CASE_STRING(SWIPE_BEGIN);
+ CASE_STRING(SWIPE_UPDATE);
+ CASE_STRING(SWIPE_END);
+ CASE_STRING(PINCH_BEGIN);
+ CASE_STRING(PINCH_UPDATE);
+ CASE_STRING(PINCH_END);
+ CASE_STRING(HOLD_BEGIN);
+ CASE_STRING(HOLD_END);
CASE_STRING(FRAME);
CASE_STRING(TEXT_KEYSYM);
CASE_STRING(TEXT_UTF8);
diff --git a/test/eierpecken.h b/test/eierpecken.h
index e2af837..2be58d2 100644
--- a/test/eierpecken.h
+++ b/test/eierpecken.h
@@ -106,6 +106,7 @@ enum peck_eis_behavior {
PECK_EIS_BEHAVIOR_ADD_POINTER_ABSOLUTE,
PECK_EIS_BEHAVIOR_ADD_KEYBOARD,
PECK_EIS_BEHAVIOR_ADD_TOUCH,
+ PECK_EIS_BEHAVIOR_ADD_GESTURES,
PECK_EIS_BEHAVIOR_RESUME_DEVICE,
PECK_EIS_BEHAVIOR_SUSPEND_DEVICE,
@@ -137,6 +138,7 @@ enum peck_ei_behavior {
PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH,
PECK_EI_BEHAVIOR_HANDLE_ADDED_BUTTON,
PECK_EI_BEHAVIOR_HANDLE_ADDED_SCROLL,
+ PECK_EI_BEHAVIOR_HANDLE_ADDED_GESTURES,
PECK_EI_BEHAVIOR_HANDLE_RESUMED,
PECK_EI_BEHAVIOR_HANDLE_PAUSED,
@@ -246,6 +248,9 @@ peck_eis_get_default_pointer_absolute(struct peck *peck);
struct eis_device *
peck_eis_get_default_touch(struct peck *peck);
+struct eis_device *
+peck_eis_get_default_gestures(struct peck *peck);
+
struct eis_device *
peck_eis_get_default_button(struct peck *peck);
@@ -273,6 +278,9 @@ peck_ei_get_default_pointer_absolute(struct peck *peck);
struct ei_device *
peck_ei_get_default_touch(struct peck *peck);
+struct ei_device *
+peck_ei_get_default_gestures(struct peck *peck);
+
uint64_t
peck_ei_now(struct peck *peck);
@@ -375,6 +383,62 @@ _peck_eis_touch_event(struct eis *eis, enum eis_event_type type, double x, doubl
struct ei_event *
_peck_ei_touch_event(struct ei *ei, enum ei_event_type type, double x, double y, int lineno);
+#define peck_eis_swipe_begin(_ei) \
+ _peck_eis_swipe_event(_ei, EIS_EVENT_SWIPE_BEGIN, __LINE__)
+#define peck_eis_swipe_update(_ei) \
+ _peck_eis_swipe_event(_ei, EIS_EVENT_SWIPE_UPDATE, __LINE__)
+#define peck_eis_swipe_end(_ei) \
+ _peck_eis_swipe_event(_ei, EIS_EVENT_SWIPE_END, __LINE__)
+
+struct eis_event *
+_peck_eis_swipe_event(struct eis *eis, enum eis_event_type type, int lineno);
+
+#define peck_eis_pinch_begin(_ei) \
+ _peck_eis_pinch_event(_ei, EIS_EVENT_PINCH_BEGIN, __LINE__)
+#define peck_eis_pinch_update(_ei) \
+ _peck_eis_pinch_event(_ei, EIS_EVENT_PINCH_UPDATE, __LINE__)
+#define peck_eis_pinch_end(_ei) \
+ _peck_eis_pinch_event(_ei, EIS_EVENT_PINCH_END, __LINE__)
+
+struct eis_event *
+_peck_eis_pinch_event(struct eis *eis, enum eis_event_type type, int lineno);
+
+#define peck_eis_hold_begin(_ei) \
+ _peck_eis_hold_event(_ei, EIS_EVENT_HOLD_BEGIN, __LINE__)
+#define peck_eis_hold_end(_ei) \
+ _peck_eis_hold_event(_ei, EIS_EVENT_HOLD_END, __LINE__)
+
+struct eis_event *
+_peck_eis_hold_event(struct eis *eis, enum eis_event_type type, int lineno);
+
+#define peck_ei_swipe_begin(_ei) \
+ _peck_ei_swipe_event(_ei, EI_EVENT_SWIPE_BEGIN, __LINE__)
+#define peck_ei_swipe_update(_ei) \
+ _peck_ei_swipe_event(_ei, EI_EVENT_SWIPE_UPDATE, __LINE__)
+#define peck_ei_swipe_end(_ei) \
+ _peck_ei_swipe_event(_ei, EI_EVENT_SWIPE_END, __LINE__)
+
+struct ei_event *
+_peck_ei_swipe_event(struct ei *ei, enum ei_event_type type, int lineno);
+
+#define peck_ei_pinch_begin(_ei) \
+ _peck_ei_pinch_event(_ei, EI_EVENT_PINCH_BEGIN, __LINE__)
+#define peck_ei_pinch_update(_ei) \
+ _peck_ei_pinch_event(_ei, EI_EVENT_PINCH_UPDATE, __LINE__)
+#define peck_ei_pinch_end(_ei) \
+ _peck_ei_pinch_event(_ei, EI_EVENT_PINCH_END, __LINE__)
+
+struct ei_event *
+_peck_ei_pinch_event(struct ei *ei, enum ei_event_type type, int lineno);
+
+#define peck_ei_hold_begin(_ei) \
+ _peck_ei_hold_event(_ei, EI_EVENT_HOLD_BEGIN, __LINE__)
+#define peck_ei_hold_end(_ei) \
+ _peck_ei_hold_event(_ei, EI_EVENT_HOLD_END, __LINE__)
+
+struct ei_event *
+_peck_ei_hold_event(struct ei *ei, enum ei_event_type type, int lineno);
+
const char *
peck_ei_event_name(struct ei_event *e);
@@ -398,6 +462,9 @@ DEFINE_UNREF_CLEANUP_FUNC(ei_keymap);
DEFINE_UNREF_CLEANUP_FUNC(ei_seat);
DEFINE_UNREF_CLEANUP_FUNC(ei_region);
DEFINE_UNREF_CLEANUP_FUNC(ei_ping);
+DEFINE_UNREF_CLEANUP_FUNC(ei_swipe);
+DEFINE_UNREF_CLEANUP_FUNC(ei_pinch);
+DEFINE_UNREF_CLEANUP_FUNC(ei_hold);
DEFINE_UNREF_CLEANUP_FUNC(eis);
DEFINE_UNREF_CLEANUP_FUNC(eis_client);
@@ -408,6 +475,9 @@ DEFINE_UNREF_CLEANUP_FUNC(eis_keymap);
DEFINE_UNREF_CLEANUP_FUNC(eis_seat);
DEFINE_UNREF_CLEANUP_FUNC(eis_region);
DEFINE_UNREF_CLEANUP_FUNC(eis_ping);
+DEFINE_UNREF_CLEANUP_FUNC(eis_swipe);
+DEFINE_UNREF_CLEANUP_FUNC(eis_pinch);
+DEFINE_UNREF_CLEANUP_FUNC(eis_hold);
/* Macros intended just for readability to make it more obvious which part
of a test handles server vs client */
diff --git a/test/test-ei-device.c b/test/test-ei-device.c
index 079c93f..e5f3768 100644
--- a/test/test-ei-device.c
+++ b/test/test-ei-device.c
@@ -67,11 +67,14 @@ MUNIT_TEST(test_ei_device_basics)
munit_assert_false(
eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER_ABSOLUTE));
munit_assert_false(eis_device_has_capability(device, EIS_DEVICE_CAP_TOUCH));
+ munit_assert_false(eis_device_has_capability(device, EIS_DEVICE_CAP_GESTURES));
eis_device_configure_capability(device, EIS_DEVICE_CAP_POINTER);
munit_assert_true(eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER));
eis_device_configure_capability(device, EIS_DEVICE_CAP_KEYBOARD);
munit_assert_true(eis_device_has_capability(device, EIS_DEVICE_CAP_KEYBOARD));
+ eis_device_configure_capability(device, EIS_DEVICE_CAP_GESTURES);
+ munit_assert_true(eis_device_has_capability(device, EIS_DEVICE_CAP_GESTURES));
eis_device_add(device);
@@ -100,6 +103,7 @@ MUNIT_TEST(test_ei_device_basics)
munit_assert_false(
ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE));
munit_assert_false(ei_device_has_capability(device, EI_DEVICE_CAP_TOUCH));
+ munit_assert_true(ei_device_has_capability(device, EI_DEVICE_CAP_GESTURES));
}
return MUNIT_OK;
@@ -1940,6 +1944,176 @@ MUNIT_TEST(test_ei_device_remove_no_stop_emulating_event)
return MUNIT_OK;
}
+MUNIT_TEST_WITH_PARAMS(test_ei_device_gesture_swipe,
+ "@cancel",
+ "true",
+ "false",
+ "@nfingers",
+ "1",
+ "2",
+ "3",
+ "4")
+{
+ _unref_(peck) *peck = peck_new();
+ bool cancel = MUNIT_TEST_PARAM_BOOL("@cancel");
+ int nfingers = MUNIT_TEST_PARAM_INT("@nfingers");
+
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL);
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_GESTURES);
+ peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES);
+ peck_dispatch_until_stable(peck);
+
+ with_client(peck) {
+ struct ei_device *device = peck_ei_get_default_gestures(peck);
+ _unref_(ei_swipe) *swipe = ei_device_swipe_new(device, nfingers);
+ ei_swipe_begin(swipe);
+ ei_device_frame(device, peck_ei_now(peck));
+
+ ei_swipe_update(swipe, 1.1, 2.2);
+ ei_device_frame(device, peck_ei_now(peck));
+
+ if (cancel)
+ ei_swipe_cancel(swipe);
+ else
+ ei_swipe_end(swipe);
+ ei_device_frame(device, peck_ei_now(peck));
+ }
+
+ peck_dispatch_until_stable(peck);
+
+ with_server(peck) {
+ _unref_(eis_event) *begin = peck_eis_swipe_begin(eis);
+ munit_assert_int(eis_event_swipe_get_finger_count(begin), ==, nfingers);
+
+ _unref_(eis_event) *update = peck_eis_swipe_update(eis);
+ munit_assert_int(eis_event_swipe_get_finger_count(update), ==, nfingers);
+ munit_assert_float(eis_event_swipe_get_dx(update), ==, 1.1);
+ munit_assert_float(eis_event_swipe_get_dy(update), ==, 2.2);
+
+ _unref_(eis_event) *end = peck_eis_swipe_end(eis);
+ munit_assert_int(eis_event_swipe_get_finger_count(end), ==, nfingers);
+ if (cancel)
+ munit_assert_true(eis_event_swipe_get_is_cancelled(end));
+ else
+ munit_assert_false(eis_event_swipe_get_is_cancelled(end));
+
+ peck_assert_no_eis_events(eis);
+ }
+
+ return MUNIT_OK;
+}
+
+MUNIT_TEST_WITH_PARAMS(test_ei_device_gesture_pinch,
+ "@cancel",
+ "true",
+ "false",
+ "@nfingers",
+ "2",
+ "3",
+ "4")
+{
+ _unref_(peck) *peck = peck_new();
+ bool cancel = MUNIT_TEST_PARAM_BOOL("@cancel");
+ int nfingers = MUNIT_TEST_PARAM_INT("@nfingers");
+
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL);
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_GESTURES);
+ peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES);
+ peck_dispatch_until_stable(peck);
+
+ with_client(peck) {
+ struct ei_device *device = peck_ei_get_default_gestures(peck);
+ _unref_(ei_pinch) *pinch = ei_device_pinch_new(device, nfingers);
+ ei_pinch_begin(pinch);
+ ei_device_frame(device, peck_ei_now(peck));
+
+ ei_pinch_update(pinch, 1.1, 2.2, 3.3, 4.4);
+ ei_device_frame(device, peck_ei_now(peck));
+
+ if (cancel)
+ ei_pinch_cancel(pinch);
+ else
+ ei_pinch_end(pinch);
+ ei_device_frame(device, peck_ei_now(peck));
+ }
+
+ peck_dispatch_until_stable(peck);
+
+ with_server(peck) {
+ _unref_(eis_event) *begin = peck_eis_pinch_begin(eis);
+ munit_assert_int(eis_event_pinch_get_finger_count(begin), ==, nfingers);
+
+ _unref_(eis_event) *update = peck_eis_pinch_update(eis);
+ munit_assert_int(eis_event_pinch_get_finger_count(update), ==, nfingers);
+ munit_assert_float(eis_event_pinch_get_dx(update), ==, 1.1);
+ munit_assert_float(eis_event_pinch_get_dy(update), ==, 2.2);
+ munit_assert_float(eis_event_pinch_get_scale(update), ==, 3.3);
+ munit_assert_float(eis_event_pinch_get_rotation(update), ==, 4.4);
+
+ _unref_(eis_event) *end = peck_eis_pinch_end(eis);
+ munit_assert_int(eis_event_pinch_get_finger_count(end), ==, nfingers);
+ if (cancel)
+ munit_assert_true(eis_event_pinch_get_is_cancelled(end));
+ else
+ munit_assert_false(eis_event_pinch_get_is_cancelled(end));
+
+ peck_assert_no_eis_events(eis);
+ }
+
+ return MUNIT_OK;
+}
+
+MUNIT_TEST_WITH_PARAMS(test_ei_device_gesture_hold,
+ "@cancel",
+ "true",
+ "false",
+ "@nfingers",
+ "1",
+ "2",
+ "3",
+ "4")
+{
+ _unref_(peck) *peck = peck_new();
+ bool cancel = MUNIT_TEST_PARAM_BOOL("@cancel");
+ int nfingers = MUNIT_TEST_PARAM_INT("@nfingers");
+
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL);
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_GESTURES);
+ peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES);
+ peck_dispatch_until_stable(peck);
+
+ with_client(peck) {
+ struct ei_device *device = peck_ei_get_default_gestures(peck);
+ _unref_(ei_hold) *hold = ei_device_hold_new(device, nfingers);
+ ei_hold_begin(hold);
+ ei_device_frame(device, peck_ei_now(peck));
+
+ if (cancel)
+ ei_hold_cancel(hold);
+ else
+ ei_hold_end(hold);
+ ei_device_frame(device, peck_ei_now(peck));
+ }
+
+ peck_dispatch_until_stable(peck);
+
+ with_server(peck) {
+ _unref_(eis_event) *begin = peck_eis_hold_begin(eis);
+ munit_assert_int(eis_event_hold_get_finger_count(begin), ==, nfingers);
+
+ _unref_(eis_event) *end = peck_eis_hold_end(eis);
+ munit_assert_int(eis_event_hold_get_finger_count(end), ==, nfingers);
+ if (cancel)
+ munit_assert_true(eis_event_hold_get_is_cancelled(end));
+ else
+ munit_assert_false(eis_event_hold_get_is_cancelled(end));
+
+ peck_assert_no_eis_events(eis);
+ }
+
+ return MUNIT_OK;
+}
+
MUNIT_TEST(test_passive_ei_device_start_stop_emulating)
{
_unref_(peck) *peck = peck_new_context("mode", PECK_EI_RECEIVER);
@@ -3006,3 +3180,759 @@ MUNIT_TEST(test_passive_ei_flush_frame)
return MUNIT_OK;
}
+
+MUNIT_TEST_WITH_PARAMS(test_passive_ei_device_gesture_swipe,
+ "@cancel",
+ "true",
+ "false",
+ "@nfingers",
+ "1",
+ "2",
+ "3",
+ "4")
+{
+ _unref_(peck) *peck = peck_new_context("mode", PECK_EI_RECEIVER);
+ bool cancel = MUNIT_TEST_PARAM_BOOL("@cancel");
+ int nfingers = MUNIT_TEST_PARAM_INT("@nfingers");
+
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL);
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_GESTURES);
+ peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES);
+ peck_dispatch_until_stable(peck);
+
+ uint32_t sequence = 123;
+
+ with_server(peck) {
+ struct eis_device *device = peck_eis_get_default_gestures(peck);
+
+ eis_device_start_emulating(device, sequence);
+
+ _unref_(eis_swipe) *swipe = eis_device_swipe_new(device, nfingers);
+ eis_swipe_begin(swipe);
+ eis_device_frame(device, peck_eis_now(peck));
+
+ eis_swipe_update(swipe, 1.1, 2.2);
+ eis_device_frame(device, peck_eis_now(peck));
+
+ if (cancel)
+ eis_swipe_cancel(swipe);
+ else
+ eis_swipe_end(swipe);
+ eis_device_frame(device, peck_eis_now(peck));
+ }
+
+ peck_dispatch_until_stable(peck);
+
+ with_client(peck) {
+ _unref_(ei_event) *start = peck_ei_next_event(ei, EI_EVENT_DEVICE_START_EMULATING);
+
+ _unref_(ei_event) *begin = peck_ei_swipe_begin(ei);
+ munit_assert_int(ei_event_swipe_get_finger_count(begin), ==, nfingers);
+
+ _unref_(ei_event) *update = peck_ei_swipe_update(ei);
+ munit_assert_int(ei_event_swipe_get_finger_count(update), ==, nfingers);
+ munit_assert_float(ei_event_swipe_get_dx(update), ==, 1.1);
+ munit_assert_float(ei_event_swipe_get_dy(update), ==, 2.2);
+
+ _unref_(ei_event) *end = peck_ei_swipe_end(ei);
+ munit_assert_int(ei_event_swipe_get_finger_count(end), ==, nfingers);
+ if (cancel)
+ munit_assert_true(ei_event_swipe_get_is_cancelled(end));
+ else
+ munit_assert_false(ei_event_swipe_get_is_cancelled(end));
+ peck_assert_no_ei_events(ei);
+ }
+
+ return MUNIT_OK;
+}
+
+MUNIT_TEST_WITH_PARAMS(test_passive_ei_device_gesture_pinch,
+ "@cancel",
+ "true",
+ "false",
+ "@nfingers",
+ "2",
+ "3",
+ "4")
+{
+ _unref_(peck) *peck = peck_new_context("mode", PECK_EI_RECEIVER);
+ bool cancel = MUNIT_TEST_PARAM_BOOL("@cancel");
+ int nfingers = MUNIT_TEST_PARAM_INT("@nfingers");
+
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL);
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_GESTURES);
+ peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES);
+ peck_dispatch_until_stable(peck);
+
+ uint32_t sequence = 123;
+
+ with_server(peck) {
+ struct eis_device *device = peck_eis_get_default_gestures(peck);
+
+ eis_device_start_emulating(device, sequence);
+
+ _unref_(eis_pinch) *pinch = eis_device_pinch_new(device, nfingers);
+ eis_pinch_begin(pinch);
+ eis_device_frame(device, peck_eis_now(peck));
+
+ eis_pinch_update(pinch, 1.1, 2.2, 3.3, 4.4);
+ eis_device_frame(device, peck_eis_now(peck));
+
+ if (cancel)
+ eis_pinch_cancel(pinch);
+ else
+ eis_pinch_end(pinch);
+ eis_device_frame(device, peck_eis_now(peck));
+ }
+
+ peck_dispatch_until_stable(peck);
+
+ with_client(peck) {
+ _unref_(ei_event) *start = peck_ei_next_event(ei, EI_EVENT_DEVICE_START_EMULATING);
+
+ _unref_(ei_event) *begin = peck_ei_pinch_begin(ei);
+ munit_assert_int(ei_event_pinch_get_finger_count(begin), ==, nfingers);
+
+ _unref_(ei_event) *update = peck_ei_pinch_update(ei);
+ munit_assert_int(ei_event_pinch_get_finger_count(update), ==, nfingers);
+ munit_assert_float(ei_event_pinch_get_dx(update), ==, 1.1);
+ munit_assert_float(ei_event_pinch_get_dy(update), ==, 2.2);
+ munit_assert_float(ei_event_pinch_get_scale(update), ==, 3.3);
+ munit_assert_float(ei_event_pinch_get_rotation(update), ==, 4.4);
+
+ _unref_(ei_event) *end = peck_ei_pinch_end(ei);
+ munit_assert_int(ei_event_pinch_get_finger_count(end), ==, nfingers);
+ if (cancel)
+ munit_assert_true(ei_event_pinch_get_is_cancelled(end));
+ else
+ munit_assert_false(ei_event_pinch_get_is_cancelled(end));
+
+ peck_assert_no_ei_events(ei);
+ }
+
+ return MUNIT_OK;
+}
+
+MUNIT_TEST_WITH_PARAMS(test_passive_ei_device_gesture_hold,
+ "@cancel",
+ "true",
+ "false",
+ "@nfingers",
+ "1",
+ "2",
+ "3",
+ "4")
+{
+ _unref_(peck) *peck = peck_new_context("mode", PECK_EI_RECEIVER);
+ bool cancel = MUNIT_TEST_PARAM_BOOL("@cancel");
+ int nfingers = MUNIT_TEST_PARAM_INT("@nfingers");
+
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL);
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_GESTURES);
+ peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES);
+
+ peck_dispatch_until_stable(peck);
+
+ uint32_t sequence = 123;
+ with_server(peck) {
+ struct eis_device *device = peck_eis_get_default_gestures(peck);
+
+ eis_device_start_emulating(device, sequence);
+
+ _unref_(eis_hold) *hold = eis_device_hold_new(device, nfingers);
+ eis_hold_begin(hold);
+ eis_device_frame(device, peck_eis_now(peck));
+
+ if (cancel)
+ eis_hold_cancel(hold);
+ else
+ eis_hold_end(hold);
+ eis_device_frame(device, peck_eis_now(peck));
+ }
+
+ peck_dispatch_until_stable(peck);
+
+ with_client(peck) {
+ _unref_(ei_event) *start = peck_ei_next_event(ei, EI_EVENT_DEVICE_START_EMULATING);
+
+ _unref_(ei_event) *begin = peck_ei_hold_begin(ei);
+ munit_assert_int(ei_event_hold_get_finger_count(begin), ==, nfingers);
+
+ _unref_(ei_event) *end = peck_ei_hold_end(ei);
+ munit_assert_int(ei_event_hold_get_finger_count(end), ==, nfingers);
+ if (cancel)
+ munit_assert_true(ei_event_hold_get_is_cancelled(end));
+ else
+ munit_assert_false(ei_event_hold_get_is_cancelled(end));
+
+ peck_assert_no_ei_events(ei);
+ }
+
+ return MUNIT_OK;
+}
+
+enum gesture_type { SWIPE, PINCH, HOLD };
+
+static inline enum gesture_type
+gesture_type_from_string(const char *string)
+{
+ if (streq(string, "swipe"))
+ return SWIPE;
+ else if (streq(string, "pinch"))
+ return PINCH;
+ else if (streq(string, "hold"))
+ return HOLD;
+ abort();
+}
+
+MUNIT_TEST_WITH_PARAMS(test_eis_device_gesture_cancel_noop, "@gesture", "swipe", "pinch", "hold")
+{
+ enum gesture_type gesture = gesture_type_from_string(MUNIT_TEST_PARAM("@gesture"));
+ _unref_(peck) *peck = peck_new();
+
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL);
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_GESTURES);
+ peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES);
+ peck_dispatch_until_stable(peck);
+
+ /* Test 1: Canceling when no gesture is active - should be a noop */
+ with_server(peck) {
+ struct eis_device *device = peck_eis_get_default_gestures(peck);
+
+ switch (gesture) {
+ case SWIPE:
+ eis_device_swipe_cancel(device);
+ break;
+ case PINCH:
+ eis_device_pinch_cancel(device);
+ break;
+ case HOLD:
+ eis_device_hold_cancel(device);
+ break;
+ }
+ }
+
+ peck_dispatch_until_stable(peck);
+
+ with_client(peck) {
+ /* Should have no events since no gesture was active */
+ peck_assert_no_ei_events(ei);
+ }
+
+ /* Test 2: Canceling after gesture has ended - should be a noop */
+ int nfingers = 2;
+
+ with_client(peck) {
+ struct ei_device *device = peck_ei_get_default_gestures(peck);
+
+ switch (gesture) {
+ case SWIPE: {
+ _unref_(ei_swipe) *swipe = ei_device_swipe_new(device, nfingers);
+ ei_swipe_begin(swipe);
+ ei_device_frame(device, peck_ei_now(peck));
+ ei_swipe_end(swipe);
+ ei_device_frame(device, peck_ei_now(peck));
+ break;
+ }
+ case PINCH: {
+ _unref_(ei_pinch) *pinch = ei_device_pinch_new(device, nfingers);
+ ei_pinch_begin(pinch);
+ ei_device_frame(device, peck_ei_now(peck));
+ ei_pinch_end(pinch);
+ ei_device_frame(device, peck_ei_now(peck));
+ break;
+ }
+ case HOLD: {
+ _unref_(ei_hold) *hold = ei_device_hold_new(device, nfingers);
+ ei_hold_begin(hold);
+ ei_device_frame(device, peck_ei_now(peck));
+ ei_hold_end(hold);
+ ei_device_frame(device, peck_ei_now(peck));
+ break;
+ }
+ }
+ }
+
+ peck_dispatch_until_stable(peck);
+
+ with_server(peck) {
+ struct eis_device *device = peck_eis_get_default_gestures(peck);
+
+ switch (gesture) {
+ case SWIPE: {
+ _unref_(eis_event) *begin = peck_eis_swipe_begin(eis);
+ _unref_(eis_event) *end = peck_eis_swipe_end(eis);
+
+ /* Cancel after end - should be a noop */
+ eis_device_swipe_cancel(device);
+ break;
+ }
+ case PINCH: {
+ _unref_(eis_event) *begin = peck_eis_pinch_begin(eis);
+ _unref_(eis_event) *end = peck_eis_pinch_end(eis);
+
+ eis_device_pinch_cancel(device);
+ break;
+ }
+ case HOLD: {
+ _unref_(eis_event) *begin = peck_eis_hold_begin(eis);
+ _unref_(eis_event) *end = peck_eis_hold_end(eis);
+
+ eis_device_hold_cancel(device);
+ break;
+ }
+ }
+ }
+
+ peck_dispatch_until_stable(peck);
+
+ with_client(peck) {
+ /* No cancel event should be sent */
+ peck_assert_no_ei_events(ei);
+ }
+
+ return MUNIT_OK;
+}
+
+MUNIT_TEST_WITH_PARAMS(test_eis_device_gesture_double_cancel, "@gesture", "swipe", "pinch", "hold")
+{
+ enum gesture_type gesture = gesture_type_from_string(MUNIT_TEST_PARAM("@gesture"));
+ _unref_(peck) *peck = peck_new();
+
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL);
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_GESTURES);
+ peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES);
+ peck_dispatch_until_stable(peck);
+
+ int nfingers = 3;
+
+ _unref_(ei_swipe) *swipe = NULL;
+ _unref_(ei_pinch) *pinch = NULL;
+ _unref_(ei_hold) *hold = NULL;
+
+ with_client(peck) {
+ struct ei_device *device = peck_ei_get_default_gestures(peck);
+
+ switch (gesture) {
+ case SWIPE: {
+ swipe = ei_device_swipe_new(device, nfingers);
+ ei_swipe_begin(swipe);
+ ei_device_frame(device, peck_ei_now(peck));
+
+ ei_swipe_update(swipe, 1.1, 2.2);
+ ei_device_frame(device, peck_ei_now(peck));
+ break;
+ }
+ case PINCH: {
+ pinch = ei_device_pinch_new(device, nfingers);
+ ei_pinch_begin(pinch);
+ ei_device_frame(device, peck_ei_now(peck));
+
+ ei_pinch_update(pinch, 1.1, 2.2, 3.3, 4.4);
+ ei_device_frame(device, peck_ei_now(peck));
+ break;
+ }
+ case HOLD: {
+ hold = ei_device_hold_new(device, nfingers);
+ ei_hold_begin(hold);
+ ei_device_frame(device, peck_ei_now(peck));
+ break;
+ }
+ }
+ }
+
+ peck_dispatch_until_stable(peck);
+
+ with_server(peck) {
+ struct eis_device *device = peck_eis_get_default_gestures(peck);
+ _unref_(eis_event) *begin = NULL;
+ _unref_(eis_event) *update = NULL;
+
+ switch (gesture) {
+ case SWIPE: {
+ begin = peck_eis_swipe_begin(eis);
+ update = peck_eis_swipe_update(eis);
+ eis_device_swipe_cancel(device);
+ eis_device_swipe_cancel(device); /* should be a noop */
+ break;
+ }
+ case PINCH: {
+ begin = peck_eis_pinch_begin(eis);
+ update = peck_eis_pinch_update(eis);
+
+ eis_device_pinch_cancel(device);
+ eis_device_pinch_cancel(device); /* should be noop */
+ break;
+ }
+ case HOLD: {
+ begin = peck_eis_hold_begin(eis);
+
+ eis_device_hold_cancel(device);
+ eis_device_hold_cancel(device); /* should be noop */
+ break;
+ }
+ }
+ }
+
+ peck_dispatch_ei(peck);
+
+ with_client(peck) {
+ _unref_(ei_event) *canceled = NULL;
+ switch (gesture) {
+ case SWIPE:
+ canceled = peck_ei_next_event(ei, EI_EVENT_SWIPE_CANCELLED);
+ break;
+ case PINCH:
+ canceled = peck_ei_next_event(ei, EI_EVENT_PINCH_CANCELLED);
+ break;
+ case HOLD:
+ canceled = peck_ei_next_event(ei, EI_EVENT_HOLD_CANCELLED);
+ break;
+ }
+
+ /* Should not receive duplicate cancel event */
+ peck_assert_no_ei_events(ei);
+ }
+ return MUNIT_OK;
+}
+
+MUNIT_TEST(test_eis_device_gesture_cancel_on_non_gesture_device)
+{
+ _unref_(peck) *peck = peck_new();
+
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL);
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_POINTER);
+ peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES);
+ peck_dispatch_until_stable(peck);
+
+ /* Enable log capture to verify error messages */
+ peck_eis_enable_log_capture(peck);
+
+ with_server(peck) {
+ struct eis_device *device = peck_eis_get_default_pointer(peck);
+
+ /* These should log errors but not crash */
+ with_nonfatal_eis_bug(peck)
+ {
+ eis_device_swipe_cancel(device);
+ eis_device_pinch_cancel(device);
+ eis_device_hold_cancel(device);
+ }
+ }
+
+ /* Verify error was logged */
+ with_server(peck) {
+ char **logs = peck_eis_get_log_capture(peck, EIS_LOG_PRIORITY_ERROR);
+ int count = 0;
+ for (char **log = logs; *log; log++) {
+ if (strstr(*log, "device is not a gesture device"))
+ count++;
+ }
+ /* Should have 3 error messages, one for each cancel call */
+ munit_assert_int(count, ==, 3);
+ }
+
+ peck_eis_disable_log_capture(peck);
+
+ return MUNIT_OK;
+}
+
+MUNIT_TEST_WITH_PARAMS(test_ei_device_gesture_no_second_gesture,
+ "@gesture",
+ "swipe",
+ "pinch",
+ "hold")
+{
+ const char *gesture_param = MUNIT_TEST_PARAM("@gesture");
+ enum gesture_type gesture = gesture_type_from_string(gesture_param);
+ _unref_(peck) *peck = peck_new();
+
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL);
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_GESTURES);
+ peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES);
+ peck_dispatch_until_stable(peck);
+
+ peck_ei_enable_log_capture(peck);
+
+ int nfingers = 3;
+
+ with_client(peck) {
+ struct ei_device *device = peck_ei_get_default_gestures(peck);
+
+ switch (gesture) {
+ case SWIPE: {
+ _unref_(ei_swipe) *swipe1 = ei_device_swipe_new(device, nfingers);
+ _unref_(ei_swipe) *swipe2 = ei_device_swipe_new(device, nfingers);
+ ei_swipe_begin(swipe1);
+ ei_device_frame(device, peck_ei_now(peck));
+
+ ei_swipe_update(swipe1, 1.1, 2.2);
+ ei_device_frame(device, peck_ei_now(peck));
+
+ with_nonfatal_ei_bug(peck)
+ {
+ ei_swipe_begin(swipe2);
+ ei_device_frame(device, peck_ei_now(peck));
+ ei_swipe_update(swipe2, 3.3, 4.4);
+ ei_device_frame(device, peck_ei_now(peck));
+ }
+ break;
+ }
+ case PINCH: {
+ _unref_(ei_pinch) *pinch1 = ei_device_pinch_new(device, nfingers);
+ _unref_(ei_pinch) *pinch2 = ei_device_pinch_new(device, nfingers);
+ ei_pinch_begin(pinch1);
+ ei_device_frame(device, peck_ei_now(peck));
+
+ ei_pinch_update(pinch1, 1.1, 2.2, 3.3, 4.4);
+ ei_device_frame(device, peck_ei_now(peck));
+
+ with_nonfatal_ei_bug(peck)
+ {
+ ei_pinch_begin(pinch2);
+ ei_device_frame(device, peck_ei_now(peck));
+
+ ei_pinch_update(pinch2, 1.1, 2.2, 3.3, 4.4);
+ ei_device_frame(device, peck_ei_now(peck));
+ }
+ break;
+ }
+ case HOLD: {
+ _unref_(ei_hold) *hold1 = ei_device_hold_new(device, nfingers);
+ _unref_(ei_hold) *hold2 = ei_device_hold_new(device, nfingers);
+ ei_hold_begin(hold1);
+ ei_device_frame(device, peck_ei_now(peck));
+ with_nonfatal_ei_bug(peck)
+ {
+ ei_hold_begin(hold2);
+ ei_device_frame(device, peck_ei_now(peck));
+ }
+ break;
+ }
+ }
+
+ char **logs = peck_ei_get_log_capture(peck, EI_LOG_PRIORITY_ERROR);
+ int count = 0;
+ _cleanup_free_ char *message =
+ xaprintf("a different %s gesture is already in process", gesture_param);
+ for (char **log = logs; *log; log++) {
+ if (strstr(*log, message))
+ count++;
+ }
+ /* Should have 1 error message for the rejected second gesture begin */
+ munit_assert_int(count, ==, 1);
+ }
+
+ peck_dispatch_until_stable(peck);
+
+ return MUNIT_OK;
+}
+
+MUNIT_TEST_WITH_PARAMS(test_ei_device_gesture_unref_cancels, "@gesture", "swipe", "pinch", "hold")
+{
+ enum gesture_type gesture = gesture_type_from_string(MUNIT_TEST_PARAM("@gesture"));
+ _unref_(peck) *peck = peck_new();
+ const int nfingers = 2;
+
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL);
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_GESTURES);
+ peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES);
+ peck_dispatch_until_stable(peck);
+
+ with_client(peck) {
+ struct ei_device *device = peck_ei_get_default_gestures(peck);
+
+ switch (gesture) {
+ case SWIPE: {
+ struct ei_swipe *swipe = ei_device_swipe_new(device, nfingers);
+ ei_swipe_begin(swipe);
+ ei_device_frame(device, peck_ei_now(peck));
+
+ ei_swipe_update(swipe, 1.1, 2.2);
+ ei_device_frame(device, peck_ei_now(peck));
+
+ /* Unref while gesture is still active - should cancel */
+ ei_swipe_unref(swipe);
+ ei_device_frame(device, peck_ei_now(peck));
+ break;
+ }
+ case PINCH: {
+ struct ei_pinch *pinch = ei_device_pinch_new(device, nfingers);
+ ei_pinch_begin(pinch);
+ ei_device_frame(device, peck_ei_now(peck));
+
+ ei_pinch_update(pinch, 1.1, 2.2, 3.3, 4.4);
+ ei_device_frame(device, peck_ei_now(peck));
+
+ /* Unref while gesture is still active - should cancel */
+ ei_pinch_unref(pinch);
+ ei_device_frame(device, peck_ei_now(peck));
+ break;
+ }
+ case HOLD: {
+ struct ei_hold *hold = ei_device_hold_new(device, nfingers);
+ ei_hold_begin(hold);
+ ei_device_frame(device, peck_ei_now(peck));
+
+ /* Unref while gesture is still active - should cancel */
+ ei_hold_unref(hold);
+ ei_device_frame(device, peck_ei_now(peck));
+ break;
+ }
+ }
+ }
+
+ peck_dispatch_until_stable(peck);
+
+ with_server(peck) {
+ switch (gesture) {
+ case SWIPE: {
+ _unref_(eis_event) *begin = peck_eis_swipe_begin(eis);
+ munit_assert_int(eis_event_swipe_get_finger_count(begin), ==, nfingers);
+
+ _unref_(eis_event) *update = peck_eis_swipe_update(eis);
+ munit_assert_int(eis_event_swipe_get_finger_count(update), ==, nfingers);
+
+ _unref_(eis_event) *end = peck_eis_swipe_end(eis);
+ munit_assert_int(eis_event_swipe_get_finger_count(end), ==, nfingers);
+ munit_assert_true(eis_event_swipe_get_is_cancelled(end));
+ break;
+ }
+ case PINCH: {
+ _unref_(eis_event) *begin = peck_eis_pinch_begin(eis);
+ munit_assert_int(eis_event_pinch_get_finger_count(begin), ==, nfingers);
+
+ _unref_(eis_event) *update = peck_eis_pinch_update(eis);
+ munit_assert_int(eis_event_pinch_get_finger_count(update), ==, nfingers);
+
+ _unref_(eis_event) *end = peck_eis_pinch_end(eis);
+ munit_assert_int(eis_event_pinch_get_finger_count(end), ==, nfingers);
+ munit_assert_true(eis_event_pinch_get_is_cancelled(end));
+ break;
+ }
+ case HOLD: {
+ _unref_(eis_event) *begin = peck_eis_hold_begin(eis);
+ munit_assert_int(eis_event_hold_get_finger_count(begin), ==, nfingers);
+
+ _unref_(eis_event) *end = peck_eis_hold_end(eis);
+ munit_assert_int(eis_event_hold_get_finger_count(end), ==, nfingers);
+ munit_assert_true(eis_event_hold_get_is_cancelled(end));
+ break;
+ }
+ }
+
+ peck_assert_no_eis_events(eis);
+ }
+
+ return MUNIT_OK;
+}
+
+MUNIT_TEST_WITH_PARAMS(test_passive_ei_device_gesture_unref_cancels,
+ "@gesture",
+ "swipe",
+ "pinch",
+ "hold")
+{
+ enum gesture_type gesture = gesture_type_from_string(MUNIT_TEST_PARAM("@gesture"));
+ _unref_(peck) *peck = peck_new_context("mode", PECK_EI_RECEIVER);
+ const int nfingers = 2;
+
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL);
+ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_GESTURES);
+ peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES);
+ peck_dispatch_until_stable(peck);
+
+ uint32_t sequence = 123;
+
+ with_server(peck) {
+ struct eis_device *device = peck_eis_get_default_gestures(peck);
+
+ eis_device_start_emulating(device, sequence);
+
+ switch (gesture) {
+ case SWIPE: {
+ struct eis_swipe *swipe = eis_device_swipe_new(device, nfingers);
+ eis_swipe_begin(swipe);
+ eis_device_frame(device, peck_eis_now(peck));
+
+ eis_swipe_update(swipe, 1.1, 2.2);
+ eis_device_frame(device, peck_eis_now(peck));
+
+ /* Unref while gesture is still active - should cancel */
+ eis_swipe_unref(swipe);
+ eis_device_frame(device, peck_eis_now(peck));
+ break;
+ }
+ case PINCH: {
+ struct eis_pinch *pinch = eis_device_pinch_new(device, nfingers);
+ eis_pinch_begin(pinch);
+ eis_device_frame(device, peck_eis_now(peck));
+
+ eis_pinch_update(pinch, 1.1, 2.2, 3.3, 4.4);
+ eis_device_frame(device, peck_eis_now(peck));
+
+ /* Unref while gesture is still active - should cancel */
+ eis_pinch_unref(pinch);
+ eis_device_frame(device, peck_eis_now(peck));
+ break;
+ }
+ case HOLD: {
+ struct eis_hold *hold = eis_device_hold_new(device, nfingers);
+ eis_hold_begin(hold);
+ eis_device_frame(device, peck_eis_now(peck));
+
+ /* Unref while gesture is still active - should cancel */
+ eis_hold_unref(hold);
+ eis_device_frame(device, peck_eis_now(peck));
+ break;
+ }
+ }
+ }
+
+ peck_dispatch_until_stable(peck);
+
+ with_client(peck) {
+ _unref_(ei_event) *start = peck_ei_next_event(ei, EI_EVENT_DEVICE_START_EMULATING);
+ munit_assert_uint(ei_event_emulating_get_sequence(start), ==, sequence);
+
+ switch (gesture) {
+ case SWIPE: {
+ _unref_(ei_event) *begin = peck_ei_swipe_begin(ei);
+ munit_assert_int(ei_event_swipe_get_finger_count(begin), ==, nfingers);
+
+ _unref_(ei_event) *update = peck_ei_swipe_update(ei);
+ munit_assert_int(ei_event_swipe_get_finger_count(update), ==, nfingers);
+
+ _unref_(ei_event) *end = peck_ei_swipe_end(ei);
+ munit_assert_int(ei_event_swipe_get_finger_count(end), ==, nfingers);
+ munit_assert_true(ei_event_swipe_get_is_cancelled(end));
+ break;
+ }
+ case PINCH: {
+ _unref_(ei_event) *begin = peck_ei_pinch_begin(ei);
+ munit_assert_int(ei_event_pinch_get_finger_count(begin), ==, nfingers);
+
+ _unref_(ei_event) *update = peck_ei_pinch_update(ei);
+ munit_assert_int(ei_event_pinch_get_finger_count(update), ==, nfingers);
+
+ _unref_(ei_event) *end = peck_ei_pinch_end(ei);
+ munit_assert_int(ei_event_pinch_get_finger_count(end), ==, nfingers);
+ munit_assert_true(ei_event_pinch_get_is_cancelled(end));
+ break;
+ }
+ case HOLD: {
+ _unref_(ei_event) *begin = peck_ei_hold_begin(ei);
+ munit_assert_int(ei_event_hold_get_finger_count(begin), ==, nfingers);
+
+ _unref_(ei_event) *end = peck_ei_hold_end(ei);
+ munit_assert_int(ei_event_hold_get_finger_count(end), ==, nfingers);
+ munit_assert_true(ei_event_hold_get_is_cancelled(end));
+ break;
+ }
+ }
+
+ peck_assert_no_ei_events(ei);
+ }
+
+ return MUNIT_OK;
+}
diff --git a/tools/ei-debug-events.c b/tools/ei-debug-events.c
index be809c6..beb0ca5 100644
--- a/tools/ei-debug-events.c
+++ b/tools/ei-debug-events.c
@@ -332,6 +332,72 @@ print_pong_event(struct ei_event *event)
printf(" id: %#" PRIx64 "\n", ei_ping_get_id(ping));
}
+static void
+print_swipe_event(struct ei_event *event)
+{
+ print_device(event);
+
+ printf(" fingers: %u\n", ei_event_swipe_get_finger_count(event));
+ if (ei_event_get_type(event) == EI_EVENT_SWIPE_UPDATE)
+ printf(" delta: [%.2f, %.2f]\n",
+ ei_event_swipe_get_dx(event),
+ ei_event_swipe_get_dy(event));
+ if (ei_event_get_type(event) == EI_EVENT_SWIPE_END)
+ printf(" cancelled: %s\n",
+ ei_event_swipe_get_is_cancelled(event) ? "true" : "false");
+}
+
+static void
+print_pinch_event(struct ei_event *event)
+{
+ print_device(event);
+
+ printf(" fingers: %u\n", ei_event_pinch_get_finger_count(event));
+ if (ei_event_get_type(event) == EI_EVENT_PINCH_UPDATE) {
+ printf(" delta: [%.2f, %.2f]\n",
+ ei_event_pinch_get_dx(event),
+ ei_event_pinch_get_dy(event));
+ printf(" scale: %.2f\n", ei_event_pinch_get_scale(event));
+ printf(" rotation: %.2f\n", ei_event_pinch_get_rotation(event));
+ }
+ if (ei_event_get_type(event) == EI_EVENT_PINCH_END)
+ printf(" cancelled: %s\n",
+ ei_event_pinch_get_is_cancelled(event) ? "true" : "false");
+}
+
+static void
+print_hold_event(struct ei_event *event)
+{
+ print_device(event);
+
+ printf(" fingers: %u\n", ei_event_hold_get_finger_count(event));
+ if (ei_event_get_type(event) == EI_EVENT_HOLD_END)
+ printf(" cancelled: %s\n",
+ ei_event_hold_get_is_cancelled(event) ? "true" : "false");
+}
+
+static void
+print_gesture_cancel_event(struct ei_event *event)
+{
+ print_device(event);
+ const char *type = NULL;
+ switch (ei_event_get_type(event)) {
+ case EI_EVENT_PINCH_CANCELLED:
+ type = "pinch";
+ break;
+ case EI_EVENT_SWIPE_CANCELLED:
+ type = "swipe";
+ break;
+ case EI_EVENT_HOLD_CANCELLED:
+ type = "hold";
+ break;
+ default:
+ abort();
+ break;
+ }
+ printf(" %s cancelled\n", type);
+}
+
int
main(int argc, char **argv)
{
@@ -502,6 +568,25 @@ main(int argc, char **argv)
break;
case EI_EVENT_SYNC:
break;
+ case EI_EVENT_SWIPE_BEGIN:
+ case EI_EVENT_SWIPE_UPDATE:
+ case EI_EVENT_SWIPE_END:
+ print_swipe_event(e);
+ break;
+ case EI_EVENT_PINCH_BEGIN:
+ case EI_EVENT_PINCH_UPDATE:
+ case EI_EVENT_PINCH_END:
+ print_pinch_event(e);
+ break;
+ case EI_EVENT_HOLD_BEGIN:
+ case EI_EVENT_HOLD_END:
+ print_hold_event(e);
+ break;
+ case EI_EVENT_PINCH_CANCELLED:
+ case EI_EVENT_SWIPE_CANCELLED:
+ case EI_EVENT_HOLD_CANCELLED:
+ print_gesture_cancel_event(e);
+ break;
}
}
}
diff --git a/tools/ei-demo-client.c b/tools/ei-demo-client.c
index b12de52..d289831 100644
--- a/tools/ei-demo-client.c
+++ b/tools/ei-demo-client.c
@@ -305,6 +305,7 @@ main(int argc, char **argv)
_unref_(ei_device) *kbd = NULL;
_unref_(ei_device) *abs = NULL;
_unref_(ei_device) *touch = NULL;
+ _unref_(ei_device) *gestures = NULL;
_unref_(ei_device) *text = NULL;
bool stop = false;
@@ -312,6 +313,7 @@ main(int argc, char **argv)
bool have_kbd = false;
bool have_abs = false;
bool have_touch = false;
+ bool have_gestures = false;
bool have_text = false;
struct ei_seat *default_seat = NULL;
@@ -351,6 +353,7 @@ main(int argc, char **argv)
EI_DEVICE_CAP_TOUCH,
EI_DEVICE_CAP_BUTTON,
EI_DEVICE_CAP_SCROLL,
+ EI_DEVICE_CAP_GESTURES,
EI_DEVICE_CAP_TEXT,
NULL);
break;
@@ -388,6 +391,12 @@ main(int argc, char **argv)
touch = ei_device_ref(device);
handle_regions(device);
}
+ if (ei_device_has_capability(device, EI_DEVICE_CAP_GESTURES)) {
+ colorprint("New gesture device: %s\n",
+ ei_device_get_name(device));
+ gestures = ei_device_ref(device);
+ handle_regions(device);
+ }
if (ei_device_has_capability(device, EI_DEVICE_CAP_TEXT)) {
colorprint("New text device: %s\n",
ei_device_get_name(device));
@@ -419,6 +428,12 @@ main(int argc, char **argv)
colorprint("Touch device was resumed\n");
have_touch = true;
}
+ if (ei_event_get_device(e) == gestures) {
+ if (!receiver)
+ ei_device_start_emulating(gestures, ++sequence);
+ colorprint("Gesture device was resumed\n");
+ have_gestures = true;
+ }
if (ei_event_get_device(e) == text) {
if (!receiver)
ei_device_start_emulating(text, ++sequence);
@@ -526,6 +541,50 @@ main(int argc, char **argv)
case EI_EVENT_SYNC: {
colorprint("sync\n");
} break;
+ case EI_EVENT_SWIPE_BEGIN: {
+ colorprint("swipe begin: %ufg\n",
+ ei_event_swipe_get_finger_count(e));
+ } break;
+ case EI_EVENT_SWIPE_UPDATE: {
+ colorprint("swipe update %.2f/%.2f\n",
+ ei_event_swipe_get_dx(e),
+ ei_event_swipe_get_dy(e));
+ } break;
+ case EI_EVENT_SWIPE_END: {
+ colorprint("swipe end, cancelled: %s\n",
+ ei_event_swipe_get_is_cancelled(e) ? "true" : "false");
+ } break;
+ case EI_EVENT_PINCH_BEGIN: {
+ colorprint("pinch begin: %ufg\n",
+ ei_event_pinch_get_finger_count(e));
+ } break;
+ case EI_EVENT_PINCH_UPDATE: {
+ colorprint("pinch update %.2f/%.2f rotation: %.2f scale %.2f\n",
+ ei_event_pinch_get_dx(e),
+ ei_event_pinch_get_dy(e),
+ ei_event_pinch_get_rotation(e),
+ ei_event_pinch_get_scale(e));
+ } break;
+ case EI_EVENT_PINCH_END: {
+ colorprint("pinch end, cancelled: %s\n",
+ ei_event_pinch_get_is_cancelled(e) ? "true" : "false");
+ } break;
+ case EI_EVENT_HOLD_BEGIN: {
+ colorprint("hold begin: %ufg\n", ei_event_hold_get_finger_count(e));
+ } break;
+ case EI_EVENT_HOLD_END: {
+ colorprint("hold end, cancelled: %s\n",
+ ei_event_hold_get_is_cancelled(e) ? "true" : "false");
+ } break;
+ case EI_EVENT_SWIPE_CANCELLED: {
+ colorprint("swipe cancelled by server\n");
+ } break;
+ case EI_EVENT_PINCH_CANCELLED: {
+ colorprint("pinch cancelled by server\n");
+ } break;
+ case EI_EVENT_HOLD_CANCELLED: {
+ colorprint("hold cancelled by server\n");
+ } break;
default: {
colorprint("ERROR: Unhandled event type %u (%s)\n",
type,
@@ -607,6 +666,80 @@ main(int argc, char **argv)
}
}
+ if (have_gestures) {
+ const int nfingers = 3;
+ static int counter = 0;
+ static double scale = 1.0;
+ static struct ei_swipe *swipe = NULL;
+ static struct ei_pinch *pinch = NULL;
+ static struct ei_hold *hold = NULL;
+
+ switch (counter++ % 8) {
+ case 0:
+ swipe = ei_device_swipe_new(gestures, nfingers);
+ if (swipe) {
+ colorprint("sending swipe begin event\n");
+ ei_swipe_begin(swipe);
+ ei_device_frame(gestures, now);
+ }
+ break;
+ case 1:
+ if (swipe) {
+ colorprint("sending swipe update event\n");
+ ei_swipe_update(swipe, 1.5, 2.3);
+ ei_device_frame(gestures, now);
+ }
+ break;
+ case 2:
+ if (swipe) {
+ colorprint("sending swipe cancel event\n");
+ ei_swipe_cancel(swipe);
+ ei_device_frame(gestures, now);
+ swipe = ei_swipe_unref(swipe);
+ }
+ break;
+ case 3:
+ pinch = ei_device_pinch_new(gestures, nfingers);
+ if (pinch) {
+ colorprint("sending pinch begin event\n");
+ ei_pinch_begin(pinch);
+ ei_device_frame(gestures, now);
+ }
+ break;
+ case 4:
+ if (pinch) {
+ colorprint("sending pinch update event\n");
+ ei_pinch_update(pinch, 1.5, 2.3, scale += 0.2, 1.7);
+ ei_device_frame(gestures, now);
+ }
+ break;
+ case 5:
+ if (pinch) {
+ colorprint("sending pinch cancel event\n");
+ ei_pinch_cancel(pinch);
+ ei_device_frame(gestures, now);
+ pinch = ei_pinch_unref(pinch);
+ }
+ break;
+ case 6:
+ hold = ei_device_hold_new(gestures, nfingers);
+ if (hold) {
+ colorprint("sending hold begin event\n");
+ ei_hold_begin(hold);
+ ei_device_frame(gestures, now);
+ }
+ break;
+ case 7:
+ if (hold) {
+ colorprint("sending hold cancel event\n");
+ ei_hold_cancel(hold);
+ ei_device_frame(gestures, now);
+ hold = ei_hold_unref(hold);
+ }
+ break;
+ }
+ }
+
if (have_text) {
static int key = 0;
colorprint("sending text event\n");
@@ -635,6 +768,8 @@ main(int argc, char **argv)
ei_device_close(abs);
if (touch)
ei_device_close(touch);
+ if (gestures)
+ ei_device_close(gestures);
if (text)
ei_device_close(text);
if (default_seat) {
diff --git a/tools/eis-demo-server.c b/tools/eis-demo-server.c
index 9cf04ad..afdf445 100644
--- a/tools/eis-demo-server.c
+++ b/tools/eis-demo-server.c
@@ -50,6 +50,8 @@
#include
#include
+#include "libeis.h"
+
#if HAVE_LIBXKBCOMMON
#include
#endif
@@ -151,6 +153,10 @@ eis_demo_client_destroy(struct eis_demo_client *democlient)
eis_device_unref(democlient->abs);
eis_device_unref(democlient->kbd);
eis_device_unref(democlient->touchscreen);
+ eis_device_unref(democlient->gestures);
+ eis_swipe_unref(democlient->swipe);
+ eis_pinch_unref(democlient->pinch);
+ eis_hold_unref(democlient->hold);
eis_device_unref(democlient->text);
}
@@ -349,6 +355,17 @@ add_device(struct eis_demo_server *server,
case EIS_DEVICE_CAP_SCROLL:
/* Mixed in with pointer/abs - good enough for a demo server */
break;
+ case EIS_DEVICE_CAP_GESTURES: {
+ struct eis_device *gestures = eis_seat_new_device(seat);
+ eis_device_configure_name(gestures, "test gestures");
+ eis_device_configure_capability(gestures, EIS_DEVICE_CAP_GESTURES);
+ colorprint("Creating gestures device %s for %s\n",
+ eis_device_get_name(gestures),
+ eis_client_get_name(client));
+ eis_device_add(gestures);
+ device = steal(&gestures);
+ break;
+ }
case EIS_DEVICE_CAP_TEXT: {
struct eis_device *text = eis_seat_new_device(seat);
eis_device_configure_name(text, "test text device");
@@ -409,6 +426,7 @@ eis_demo_server_printf_handle_event(struct eis_demo_server *server, struct eis_e
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_TOUCH);
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_BUTTON);
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_SCROLL);
+ eis_seat_configure_capability(seat, EIS_DEVICE_CAP_GESTURES);
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_TEXT);
eis_seat_add(seat);
/* Note: we don't have a ref to this seat ourselves anywhere */
@@ -479,6 +497,17 @@ eis_demo_server_printf_handle_event(struct eis_demo_server *server, struct eis_e
}
}
+ if (eis_event_seat_has_capability(e, EIS_DEVICE_CAP_GESTURES)) {
+ if (!democlient->gestures)
+ democlient->gestures =
+ add_device(server, client, seat, EIS_DEVICE_CAP_GESTURES);
+ } else {
+ if (democlient->gestures) {
+ eis_device_remove(democlient->gestures);
+ democlient->gestures = eis_device_unref(democlient->gestures);
+ }
+ }
+
if (eis_event_seat_has_capability(e, EIS_DEVICE_CAP_TEXT)) {
if (!democlient->text)
democlient->text =
@@ -496,6 +525,7 @@ eis_demo_server_printf_handle_event(struct eis_demo_server *server, struct eis_e
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_POINTER_ABSOLUTE) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_KEYBOARD) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_TOUCH) &&
+ !eis_event_seat_has_capability(e, EIS_DEVICE_CAP_GESTURES) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_TEXT))
eis_seat_remove(seat);
@@ -532,6 +562,9 @@ eis_demo_server_printf_handle_event(struct eis_demo_server *server, struct eis_e
if (democlient->touchscreen == device)
democlient->touchscreen = NULL;
+ if (democlient->gestures == device)
+ democlient->gestures = NULL;
+
if (democlient->text == device)
democlient->text = NULL;
@@ -586,6 +619,39 @@ eis_demo_server_printf_handle_event(struct eis_demo_server *server, struct eis_e
case EIS_EVENT_TOUCH_UP: {
colorprint("touch up %u\n", eis_event_touch_get_id(e));
} break;
+ case EIS_EVENT_SWIPE_BEGIN: {
+ colorprint("swipe begin: %ufg\n", eis_event_swipe_get_finger_count(e));
+ } break;
+ case EIS_EVENT_SWIPE_UPDATE: {
+ colorprint("swipe update %.2f/%.2f\n",
+ eis_event_swipe_get_dx(e),
+ eis_event_swipe_get_dy(e));
+ } break;
+ case EIS_EVENT_SWIPE_END: {
+ colorprint("swipe end, cancelled: %s\n",
+ eis_event_swipe_get_is_cancelled(e) ? "true" : "false");
+ } break;
+ case EIS_EVENT_PINCH_BEGIN: {
+ colorprint("pinch begin: %ufg\n", eis_event_pinch_get_finger_count(e));
+ } break;
+ case EIS_EVENT_PINCH_UPDATE: {
+ colorprint("pinch update %.2f/%.2f rotation: %.2f scale %.2f\n",
+ eis_event_pinch_get_dx(e),
+ eis_event_pinch_get_dy(e),
+ eis_event_pinch_get_rotation(e),
+ eis_event_pinch_get_scale(e));
+ } break;
+ case EIS_EVENT_PINCH_END: {
+ colorprint("pinch end, cancelled: %s\n",
+ eis_event_pinch_get_is_cancelled(e) ? "true" : "false");
+ } break;
+ case EIS_EVENT_HOLD_BEGIN: {
+ colorprint("hold begin: %ufg\n", eis_event_hold_get_finger_count(e));
+ } break;
+ case EIS_EVENT_HOLD_END: {
+ colorprint("hold end, cancelled: %s\n",
+ eis_event_hold_get_is_cancelled(e) ? "true" : "false");
+ } break;
case EIS_EVENT_TEXT_KEYSYM: {
char buf[128] = { 0 };
uint32_t keysym = eis_event_text_get_keysym(e);
@@ -789,6 +855,7 @@ main(int argc, char **argv)
struct eis_device *kbd = democlient->kbd;
struct eis_device *abs = democlient->abs;
struct eis_device *touchscreen = democlient->touchscreen;
+ struct eis_device *gestures = democlient->gestures;
struct eis_device *text = democlient->text;
if (ptr) {
colorprint("sending motion event\n");
@@ -862,6 +929,86 @@ main(int argc, char **argv)
break;
}
}
+ if (gestures) {
+ const int nfingers = 3;
+ static int counter = 0;
+ static double scale = 1.0;
+ struct eis_swipe *swipe = democlient->swipe;
+ struct eis_pinch *pinch = democlient->pinch;
+ struct eis_hold *hold = democlient->hold;
+
+ switch (counter++ % 8) {
+ case 0:
+ swipe = eis_device_swipe_new(gestures, nfingers);
+ if (swipe) {
+ colorprint("sending swipe begin event\n");
+ eis_swipe_begin(swipe);
+ eis_device_frame(gestures, now);
+ democlient->swipe = swipe;
+ }
+ break;
+ case 1:
+ if (swipe) {
+ colorprint("sending swipe update event\n");
+ eis_swipe_update(swipe, 1.5, 2.3);
+ eis_device_frame(gestures, now);
+ }
+ break;
+ case 2:
+ if (swipe) {
+ colorprint("sending swipe cancel event\n");
+ eis_swipe_cancel(swipe);
+ eis_device_frame(gestures, now);
+ democlient->swipe = eis_swipe_unref(swipe);
+ }
+ break;
+ case 3:
+ pinch = eis_device_pinch_new(gestures, nfingers);
+ if (pinch) {
+ colorprint("sending pinch begin event\n");
+ eis_pinch_begin(pinch);
+ eis_device_frame(gestures, now);
+ democlient->pinch = pinch;
+ }
+ break;
+ case 4:
+ if (pinch) {
+ colorprint("sending pinch update event\n");
+ eis_pinch_update(pinch,
+ 1.5,
+ 2.3,
+ scale += 0.2,
+ 1.7);
+ eis_device_frame(gestures, now);
+ }
+ break;
+ case 5:
+ if (pinch) {
+ colorprint("sending pinch cancel event\n");
+ eis_pinch_cancel(pinch);
+ eis_device_frame(gestures, now);
+ democlient->pinch = eis_pinch_unref(pinch);
+ }
+ break;
+ case 6:
+ hold = eis_device_hold_new(gestures, nfingers);
+ if (hold) {
+ colorprint("sending hold begin event\n");
+ eis_hold_begin(hold);
+ eis_device_frame(gestures, now);
+ democlient->hold = hold;
+ }
+ break;
+ case 7:
+ if (hold) {
+ colorprint("sending hold cancel event\n");
+ eis_hold_cancel(hold);
+ eis_device_frame(gestures, now);
+ democlient->hold = eis_hold_unref(hold);
+ }
+ break;
+ }
+ }
if (text) {
static int key = 0;
diff --git a/tools/eis-demo-server.h b/tools/eis-demo-server.h
index 20057ea..2fcfb03 100644
--- a/tools/eis-demo-server.h
+++ b/tools/eis-demo-server.h
@@ -40,7 +40,11 @@ struct eis_demo_client {
struct eis_device *kbd;
struct eis_device *abs;
struct eis_device *touchscreen;
+ struct eis_device *gestures;
struct eis_touch *touch;
+ struct eis_swipe *swipe;
+ struct eis_pinch *pinch;
+ struct eis_hold *hold;
struct eis_device *text;
};