From 90d2b1b980970307311fc1a84081306f56d968e1 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 22 Sep 2020 13:17:54 +1000 Subject: [PATCH] Add support for absolute pointer motion events Signed-off-by: Peter Hutterer --- proto/ei.proto | 15 +++++++--- src/libei-device.c | 23 +++++++++++++++ src/libei-private.h | 4 +++ src/libei-proto.c | 18 ++++++++++++ src/libei-proto.h | 4 +++ src/libei.c | 21 ++++++++++++++ src/libei.h | 1 + src/libeis-client.c | 20 +++++++++++++ src/libeis-device.c | 19 +++++++++++++ src/libeis-event.c | 27 ++++++++++++++++++ src/libeis-private.h | 9 ++++++ src/libeis-proto.c | 11 +++++++ src/libeis-proto.h | 6 ++++ src/libeis.c | 11 +++++++ test/eierpecken.c | 8 ++++++ test/eierpecken.h | 1 + test/test-ei.c | 68 ++++++++++++++++++++++++++++++++++++++++++++ 17 files changed, 262 insertions(+), 4 deletions(-) diff --git a/proto/ei.proto b/proto/ei.proto index b84ca3c..204ac05 100644 --- a/proto/ei.proto +++ b/proto/ei.proto @@ -103,6 +103,12 @@ message PointerRelative { double y = 3; } +message PointerAbsolute { + uint32 deviceid = 1; + double x = 2; + double y = 3; +} + message PointerButton { uint32 deviceid = 1; uint32 button = 2; @@ -122,10 +128,11 @@ message ClientMessage { AddDevice add_device = 3; RemoveDevice remove_device = 4; PointerRelative rel = 5; - PointerButton button = 6; - KeyboardKey key = 7; - ConfigureName configure_name = 8; - ConfigureCapabilities configure_caps = 9; + PointerAbsolute abs = 6; + PointerButton button = 7; + KeyboardKey key = 8; + ConfigureName configure_name = 9; + ConfigureCapabilities configure_caps = 10; } } diff --git a/src/libei-device.c b/src/libei-device.c index 87074ef..6820737 100644 --- a/src/libei-device.c +++ b/src/libei-device.c @@ -211,6 +211,12 @@ ei_device_set_keymap(struct ei_device *device, _public_ void ei_device_add(struct ei_device *device) { + if (ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE) && + (device->abs.dim.width == 0 || device->abs.dim.height == 0)) + log_bug(ei_device_get_context(device), + "device %s is missing an abs pointer range\n", + device->name); + ei_add_device(device); ei_device_set_state(device, EI_DEVICE_STATE_CONNECTING); } @@ -337,6 +343,23 @@ ei_device_pointer_motion(struct ei_device *device, ei_pointer_rel(device, x, y); } +_public_ void +ei_device_pointer_motion_absolute(struct ei_device *device, + double x, double y) +{ + if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE)) + return; + + if (device->state != EI_DEVICE_STATE_RESUMED) + return; + + if (x < 0 || x >= device->abs.dim.width || + y < 0 || y >= device->abs.dim.height) + return; + + ei_pointer_abs(device, x, y); +} + _public_ void ei_device_pointer_button(struct ei_device *device, uint32_t button, bool is_press) diff --git a/src/libei-private.h b/src/libei-private.h index bf4aa8e..f7c1c77 100644 --- a/src/libei-private.h +++ b/src/libei-private.h @@ -120,6 +120,10 @@ int ei_pointer_rel(struct ei_device *device, double x, double y); +int +ei_pointer_abs(struct ei_device *device, + double x, double y); + int ei_pointer_button(struct ei_device *device, uint32_t button, bool is_press); diff --git a/src/libei-proto.c b/src/libei-proto.c index 5302268..5d18488 100644 --- a/src/libei-proto.c +++ b/src/libei-proto.c @@ -146,6 +146,7 @@ log_wire_message(struct ei *ei, const ClientMessage *msg, int error) MSG_STRING_CASE(ADD_DEVICE); MSG_STRING_CASE(REMOVE_DEVICE); MSG_STRING_CASE(REL); + MSG_STRING_CASE(ABS); MSG_STRING_CASE(BUTTON); MSG_STRING_CASE(KEY); MSG_STRING_CASE(CONFIGURE_NAME); @@ -277,6 +278,23 @@ ei_proto_send_rel(struct ei *ei, struct ei_device *device, return ei_proto_send_msg(ei, &msg); } +int +ei_proto_send_abs(struct ei *ei, struct ei_device *device, + double x, double y) +{ + ClientMessage msg = CLIENT_MESSAGE__INIT; + PointerAbsolute abs = POINTER_ABSOLUTE__INIT; + + abs.deviceid = device->id; + abs.x = x; + abs.y = y; + + msg.abs = &abs; + msg.msg_case = CLIENT_MESSAGE__MSG_ABS; + + return ei_proto_send_msg(ei, &msg); +} + int ei_proto_send_button(struct ei *ei, struct ei_device *device, uint32_t b, bool is_press) diff --git a/src/libei-proto.h b/src/libei-proto.h index 2841377..b13a147 100644 --- a/src/libei-proto.h +++ b/src/libei-proto.h @@ -109,6 +109,10 @@ int ei_proto_send_rel(struct ei *ei, struct ei_device *device, double x, double y); +int +ei_proto_send_abs(struct ei *ei, struct ei_device *device, + double x, double y); + int ei_proto_send_button(struct ei *ei, struct ei_device *device, uint32_t b, bool is_press); diff --git a/src/libei.c b/src/libei.c index 2bc656f..c9482ad 100644 --- a/src/libei.c +++ b/src/libei.c @@ -269,6 +269,17 @@ connection_send_rel(struct ei *ei, struct ei_device *device, return ei_proto_send_rel(ei, device, x, y); } +static int +connection_send_abs(struct ei *ei, struct ei_device *device, + double x, double y) +{ + if (ei->state == EI_STATE_NEW || + ei->state == EI_STATE_DISCONNECTED) + return 0; + + return ei_proto_send_abs(ei, device, x, y); +} + static int connection_send_button(struct ei *ei, struct ei_device *device, uint32_t b, bool is_press) @@ -460,6 +471,16 @@ ei_pointer_rel(struct ei_device *device, double x, double y) return rc; } +int +ei_pointer_abs(struct ei_device *device, double x, double y) +{ + struct ei *ei = ei_device_get_context(device); + int rc = connection_send_abs(ei, device, x, y); + if (rc) + ei_disconnect(ei); + return rc; +} + int ei_pointer_button(struct ei_device *device, uint32_t button, bool is_press) { diff --git a/src/libei.h b/src/libei.h index f3903c8..c3bac08 100644 --- a/src/libei.h +++ b/src/libei.h @@ -572,6 +572,7 @@ ei_device_pointer_motion(struct ei_device *device, double x, double y); * The required conditions are: * - 0 <= x < width * - 0 <= y < height + * If these conditions are not met, the event is silently discarded. * * @param x The x position in logical pixels * @param y The y position in logical pixels diff --git a/src/libeis-client.c b/src/libeis-client.c index 3d71e56..210545a 100644 --- a/src/libeis-client.c +++ b/src/libeis-client.c @@ -228,6 +228,20 @@ client_pointer_rel(struct eis_client *client, uint32_t deviceid, return -EINVAL; } +static int +client_pointer_abs(struct eis_client *client, uint32_t deviceid, + double x, double y) +{ + struct eis_device *device; + + list_for_each(device, &client->devices, link) { + if (device->id == deviceid) { + return eis_device_pointer_abs(device, x, y); + } + } + return -EINVAL; +} + static int client_pointer_button(struct eis_client *client, uint32_t deviceid, uint32_t button, bool state) @@ -312,6 +326,7 @@ client_new_handle_msg(struct eis_client *client, struct message *msg) case MESSAGE_ADD_DEVICE: case MESSAGE_REMOVE_DEVICE: case MESSAGE_POINTER_REL: + case MESSAGE_POINTER_ABS: case MESSAGE_POINTER_BUTTON: case MESSAGE_KEYBOARD_KEY: rc = -EPROTO; @@ -344,6 +359,7 @@ client_connecting_handle_msg(struct eis_client *client, const struct message *ms case MESSAGE_ADD_DEVICE: case MESSAGE_REMOVE_DEVICE: case MESSAGE_POINTER_REL: + case MESSAGE_POINTER_ABS: case MESSAGE_POINTER_BUTTON: case MESSAGE_KEYBOARD_KEY: rc = -EPROTO; @@ -391,6 +407,10 @@ client_connected_handle_msg(struct eis_client *client, rc = client_pointer_rel(client, msg->pointer_rel.deviceid, msg->pointer_rel.x, msg->pointer_rel.y); break; + case MESSAGE_POINTER_ABS: + rc = client_pointer_abs(client, msg->pointer_abs.deviceid, + msg->pointer_abs.x, msg->pointer_abs.y); + break; case MESSAGE_POINTER_BUTTON: rc = client_pointer_button(client, msg->pointer_button.deviceid, msg->pointer_button.button, diff --git a/src/libeis-device.c b/src/libeis-device.c index da09a77..0eda071 100644 --- a/src/libeis-device.c +++ b/src/libeis-device.c @@ -220,6 +220,25 @@ eis_device_pointer_rel(struct eis_device *device, return 0; } +int +eis_device_pointer_abs(struct eis_device *device, + double x, double y) +{ + if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER_ABSOLUTE)) + return -EINVAL; + + if (device->state != EIS_DEVICE_STATE_RESUMED) + return -EINVAL; + + if (x < 0 || x >= device->abs.dim.width || + y < 0 || y >= device->abs.dim.height) + return -EINVAL; + + eis_queue_pointer_abs_event(device, x, y); + + return 0; +} + int eis_device_pointer_button(struct eis_device *device, uint32_t button, bool is_press) diff --git a/src/libeis-event.c b/src/libeis-event.c index cd48b2c..2fa17cf 100644 --- a/src/libeis-event.c +++ b/src/libeis-event.c @@ -39,6 +39,7 @@ eis_event_destroy(struct eis_event *event) case EIS_EVENT_DEVICE_ADDED: case EIS_EVENT_POINTER_BUTTON: case EIS_EVENT_POINTER_MOTION: + case EIS_EVENT_POINTER_MOTION_ABSOLUTE: case EIS_EVENT_KEYBOARD_KEY: break; case EIS_EVENT_DEVICE_REMOVED: @@ -160,6 +161,32 @@ eis_event_pointer_get_dy(struct eis_event *event) return event->pointer.dy; } +_public_ double +eis_event_pointer_get_absolute_x(struct eis_event *event) +{ + require_event_type(event, 0.0, + EIS_EVENT_POINTER_MOTION, + EIS_EVENT_POINTER_MOTION_ABSOLUTE, + EIS_EVENT_POINTER_BUTTON, + EIS_EVENT_POINTER_SCROLL, + EIS_EVENT_POINTER_SCROLL_DISCRETE); + + return event->pointer.absx; +} + +_public_ double +eis_event_pointer_get_absolute_y(struct eis_event *event) +{ + require_event_type(event, 0.0, + EIS_EVENT_POINTER_MOTION, + EIS_EVENT_POINTER_MOTION_ABSOLUTE, + EIS_EVENT_POINTER_BUTTON, + EIS_EVENT_POINTER_SCROLL, + EIS_EVENT_POINTER_SCROLL_DISCRETE); + + return event->pointer.absy; +} + _public_ uint32_t eis_event_pointer_get_button(struct eis_event *event) { diff --git a/src/libeis-private.h b/src/libeis-private.h index b437087..bcf143a 100644 --- a/src/libeis-private.h +++ b/src/libeis-private.h @@ -121,6 +121,7 @@ struct eis_event { union { struct { double dx, dy; /* relative motion */ + double absx, absy; /* absolute motion */ uint32_t button; bool button_is_press; } pointer; @@ -187,6 +188,10 @@ int eis_device_pointer_rel(struct eis_device *device, double x, double y); +int +eis_device_pointer_abs(struct eis_device *device, + double x, double y); + int eis_device_pointer_button(struct eis_device *device, uint32_t button, bool state); @@ -222,6 +227,10 @@ eis_queue_removed_event(struct eis_device *device); void eis_queue_pointer_rel_event(struct eis_device *device, double x, double y); +void +eis_queue_pointer_abs_event(struct eis_device *device, + double x, double y); + void eis_queue_pointer_button_event(struct eis_device *device, uint32_t button, bool is_press); diff --git a/src/libeis-proto.c b/src/libeis-proto.c index 70380cd..8195465 100644 --- a/src/libeis-proto.c +++ b/src/libeis-proto.c @@ -271,6 +271,17 @@ eis_proto_parse_message(struct brei_message *bmsg, size_t *consumed) }; } break; + case CLIENT_MESSAGE__MSG_ABS: + { + PointerAbsolute *a = proto->abs; + *msg = (struct message) { + .type = MESSAGE_POINTER_ABS, + .pointer_abs.deviceid = a->deviceid, + .pointer_abs.x = a->x, + .pointer_abs.y = a->y, + }; + } + break; case CLIENT_MESSAGE__MSG_BUTTON: { PointerButton *b = proto->button; diff --git a/src/libeis-proto.h b/src/libeis-proto.h index 5b02af1..c303397 100644 --- a/src/libeis-proto.h +++ b/src/libeis-proto.h @@ -40,6 +40,7 @@ enum message_type { MESSAGE_ADD_DEVICE, MESSAGE_REMOVE_DEVICE, MESSAGE_POINTER_REL, + MESSAGE_POINTER_ABS, MESSAGE_POINTER_BUTTON, MESSAGE_KEYBOARD_KEY, @@ -75,6 +76,11 @@ struct message { double x; double y; } pointer_rel; + struct message_pointer_abs { + uint32_t deviceid; + double x; + double y; + } pointer_abs; struct message_pointer_button { uint32_t deviceid; uint32_t button; diff --git a/src/libeis.c b/src/libeis.c index 6ba76c7..580ce1b 100644 --- a/src/libeis.c +++ b/src/libeis.c @@ -175,6 +175,17 @@ eis_queue_pointer_rel_event(struct eis_device *device, eis_queue_event(e); } +void +eis_queue_pointer_abs_event(struct eis_device *device, + double x, double y) +{ + struct eis_event *e = eis_event_new_for_device(device); + e->type = EIS_EVENT_POINTER_MOTION_ABSOLUTE; + e->pointer.absx = x; + e->pointer.absy = y; + eis_queue_event(e); +} + void eis_queue_pointer_button_event(struct eis_device *device, uint32_t button, bool is_press) diff --git a/test/eierpecken.c b/test/eierpecken.c index c94133d..de40d32 100644 --- a/test/eierpecken.c +++ b/test/eierpecken.c @@ -213,11 +213,13 @@ peck_enable_eis_behavior(struct peck *peck, enum peck_eis_behavior behavior) break; case PECK_EIS_BEHAVIOR_ACCEPT_DEVICE: peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_POINTER); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_POINTER_ABSOLUTE); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_KEYBOARD); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_TOUCH); break; case PECK_EIS_BEHAVIOR_REJECT_DEVICE: peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DROP_POINTER); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DROP_POINTER_ABSOLUTE); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DROP_KEYBOARD); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DROP_TOUCH); break; @@ -272,10 +274,12 @@ peck_enable_ei_behavior(struct peck *peck, enum peck_ei_behavior behavior) break; case PECK_EI_BEHAVIOR_HANDLE_ADDED: peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER); + peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER_ABSOLUTE); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_KEYBOARD); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH); break; case PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER: + case PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER_ABSOLUTE: case PECK_EI_BEHAVIOR_HANDLE_ADDED_KEYBOARD: case PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH: flag_set(peck->ei_behavior, behavior); @@ -427,6 +431,10 @@ peck_check_ei_added(struct peck *peck, struct ei_event *e) flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER)) return tristate_yes; + if (ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE) && + flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER_ABSOLUTE)) + return tristate_yes; + if (ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD) && flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_ADDED_KEYBOARD)) return tristate_yes; diff --git a/test/eierpecken.h b/test/eierpecken.h index 692621c..aef00dd 100644 --- a/test/eierpecken.h +++ b/test/eierpecken.h @@ -90,6 +90,7 @@ enum peck_ei_behavior { PECK_EI_BEHAVIOR_AUTODEVICES, PECK_EI_BEHAVIOR_HANDLE_ADDED, PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER, + PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER_ABSOLUTE, PECK_EI_BEHAVIOR_HANDLE_ADDED_KEYBOARD, PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH, diff --git a/test/test-ei.c b/test/test-ei.c index 1b0741c..498569a 100644 --- a/test/test-ei.c +++ b/test/test-ei.c @@ -663,6 +663,74 @@ MUNIT_TEST(test_ei_device_pointer_rel) return MUNIT_OK; } +MUNIT_TEST(test_ei_device_pointer_abs) +{ + _cleanup_peck_ struct peck *peck = peck_new(); + _cleanup_ei_device_ struct ei_device *device = NULL; + + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); + peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES); + peck_dispatch_until_stable(peck); + + with_client(peck) { + device = ei_device_new(ei); + munit_assert_not_null(device); + ei_device_configure_name(device, __func__); + + ei_device_configure_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE); + ei_device_pointer_configure_range(device, 1920, 1200); + ei_device_add(device); + } + + peck_dispatch_until_stable(peck); + + with_client(peck) { + for (int i = 0; i < 10; i++) + ei_device_pointer_motion_absolute(device, 1 * i , 2 + i); + } + + peck_dispatch_until_stable(peck); + + with_server(peck) { + for (int i = 0; i < 10; i++) { + _cleanup_eis_event_ struct eis_event *e = + peck_eis_next_event(eis, EIS_EVENT_POINTER_MOTION_ABSOLUTE); + munit_assert_double_equal(eis_event_pointer_get_absolute_x(e), 1.0 * i, 2 /* precision */); + munit_assert_double_equal(eis_event_pointer_get_absolute_y(e), 2.0 + i, 2 /* precision */); + } + } + + with_client(peck) { + /* outside of pointer range, expect to be discarded */ + ei_device_pointer_motion_absolute(device, 1920, 1200); + ei_device_pointer_motion_absolute(device, 2000, 1400); + } + + peck_dispatch_until_stable(peck); + + with_server(peck) { + peck_assert_no_eis_events(eis); + } + + with_client(peck) { + ei_device_remove(device); + /* absmotion after remove must not trigger an event */ + ei_device_pointer_motion_absolute(device, 100, 200); + } + + with_server(peck) { + /* Don't auto-handle the removed event */ + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_NONE); + peck_dispatch_eis(peck); + + _cleanup_eis_event_ struct eis_event *e = + peck_eis_next_event(eis, EIS_EVENT_DEVICE_REMOVED); + peck_assert_no_eis_events(eis); + } + + return MUNIT_OK; +} + int main(int argc, char **argv) {