diff --git a/proto/ei.proto b/proto/ei.proto index ee05de9..1bd7d08 100644 --- a/proto/ei.proto +++ b/proto/ei.proto @@ -60,6 +60,7 @@ message ConfigureCapabilities { message Connect { string name = 1; + bool is_active = 2; } message ConnectDone { @@ -195,6 +196,7 @@ message Connected { } message Disconnected { + bool is_active = 2; } message SeatAdded { @@ -256,6 +258,7 @@ message KeyboardModifiers { message ServerMessage { oneof msg { + /* Server setup and configuration */ Connected connected = 2; Disconnected disconnected = 3; SeatAdded seat_added = 4; @@ -269,6 +272,21 @@ message ServerMessage { DevicePaused device_paused = 12; KeyboardModifiers keyboard_modifiers = 13; Property property = 14; + + /* Events */ + StartEmulating start_emulating = 20; + StopEmulating stop_emulating = 21; + PointerRelative pointer_relative = 22; + PointerAbsolute pointer_absolute = 23; + PointerScroll pointer_scroll = 24; + PointerScrollDiscrete pointer_scroll_discrete = 25; + PointerScrollStop pointer_scroll_stop = 26; + PointerButton pointer_button = 27; + KeyboardKey keyboard_key = 28; + TouchDown touch_down = 29; + TouchMotion touch_motion = 30; + TouchUp touch_up = 31; + Frame frame = 32; } } diff --git a/src/libei-device.c b/src/libei-device.c index 7bfbde5..ca531e2 100644 --- a/src/libei-device.c +++ b/src/libei-device.c @@ -638,3 +638,265 @@ ei_device_frame(struct ei_device *device) ei_send_frame(device); } + +void +ei_device_event_start_emulating(struct ei_device *device) +{ + switch (device->state) { + case EI_DEVICE_STATE_DEAD: + case EI_DEVICE_STATE_NEW: + case EI_DEVICE_STATE_PAUSED: + case EI_DEVICE_STATE_EMULATING: + break; + case EI_DEVICE_STATE_RESUMED: + ei_queue_device_start_emulating_event(device); + device->state = EI_DEVICE_STATE_EMULATING; + break; + case EI_DEVICE_STATE_REMOVED_FROM_CLIENT: + case EI_DEVICE_STATE_REMOVED_FROM_SERVER: + break; + } +} + +void +ei_device_event_stop_emulating(struct ei_device *device) +{ + switch (device->state) { + case EI_DEVICE_STATE_DEAD: + case EI_DEVICE_STATE_NEW: + case EI_DEVICE_STATE_PAUSED: + case EI_DEVICE_STATE_RESUMED: + break; + case EI_DEVICE_STATE_EMULATING: + ei_queue_device_stop_emulating_event(device); + device->state = EI_DEVICE_STATE_RESUMED; + break; + case EI_DEVICE_STATE_REMOVED_FROM_CLIENT: + case EI_DEVICE_STATE_REMOVED_FROM_SERVER: + break; + } +} + +int +ei_device_event_frame(struct ei_device *device) +{ + if (device->state != EI_DEVICE_STATE_RESUMED) + return -EINVAL; + + ei_queue_frame_event(device); + + return 0; +} + +int +ei_device_event_pointer_rel(struct ei_device *device, double x, double y) +{ + if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER)) { + log_bug_client(ei_device_get_context(device), + "%s: device is not a pointer\n", __func__); + return -EINVAL; + } + + if (device->state != EI_DEVICE_STATE_EMULATING) + return -EINVAL; + + ei_queue_pointer_rel_event(device, x, y); + + return 0; +} + +static inline bool +ei_device_in_region(struct ei_device *device, double x, double y) +{ + struct ei_region *r; + + list_for_each(r, &device->regions, link) { + if (ei_region_contains(r, x, y)) + return true; + } + + return false; +} + +int +ei_device_event_pointer_abs(struct ei_device *device, + double x, double y) +{ + if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE)) { + log_bug_client(ei_device_get_context(device), + "%s: device is not an absolute pointer\n", __func__); + return -EINVAL; + } + + if (device->state != EI_DEVICE_STATE_EMULATING) + return -EINVAL; + + if (!ei_device_in_region(device, x, y)) + return -EINVAL; + + ei_queue_pointer_abs_event(device, x, y); + + return 0; +} + +int +ei_device_event_pointer_button(struct ei_device *device, + uint32_t button, bool is_press) +{ + if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER)) { + log_bug_client(ei_device_get_context(device), + "%s: device is not a pointer\n", __func__); + return -EINVAL; + } + + if (device->state != EI_DEVICE_STATE_EMULATING) + return -EINVAL; + + ei_queue_pointer_button_event(device, button, is_press); + + return 0; +} + +int +ei_device_event_pointer_scroll(struct ei_device *device, + double x, double y) +{ + if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) && + !ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE)) { + log_bug_client(ei_device_get_context(device), + "%s: device is not a (absolute) pointer\n", __func__); + return -EINVAL; + } + + if (device->state != EI_DEVICE_STATE_EMULATING) + return -EINVAL; + + ei_queue_pointer_scroll_event(device, x, y); + + return 0; +} + +int +ei_device_event_pointer_scroll_discrete(struct ei_device *device, + int32_t x, int32_t y) +{ + if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) && + !ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE)) { + log_bug_client(ei_device_get_context(device), + "%s: device is not a (absolute) pointer\n", __func__); + return -EINVAL; + } + + if (device->state != EI_DEVICE_STATE_EMULATING) + return -EINVAL; + + ei_queue_pointer_scroll_discrete_event(device, x, y); + + return 0; +} + +int +ei_device_event_pointer_scroll_stop(struct ei_device *device, bool x, bool y) +{ + if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) && + !ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE)) { + log_bug_client(ei_device_get_context(device), + "%s: device is not a (absolute) pointer\n", __func__); + return -EINVAL; + } + + if (device->state != EI_DEVICE_STATE_EMULATING) + return -EINVAL; + + ei_queue_pointer_scroll_stop_event(device, x, y); + + return 0; +} + +int +ei_device_event_pointer_scroll_cancel(struct ei_device *device, bool x, bool y) +{ + if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) && + !ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE)) { + log_bug_client(ei_device_get_context(device), + "%s: device is not a (absolute) pointer\n", __func__); + return -EINVAL; + } + + if (device->state != EI_DEVICE_STATE_EMULATING) + return -EINVAL; + + ei_queue_pointer_scroll_cancel_event(device, x, y); + + return 0; +} + +int +ei_device_event_keyboard_key(struct ei_device *device, + uint32_t key, bool is_press) +{ + if (!ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD)) { + log_bug_client(ei_device_get_context(device), + "%s: device is not a keyboard\n", __func__); + return -EINVAL; + } + + if (device->state != EI_DEVICE_STATE_EMULATING) + return -EINVAL; + + ei_queue_keyboard_key_event(device, key, is_press); + + return 0; +} + +int +ei_device_event_touch_down(struct ei_device *device, uint32_t touchid, double x, double y) +{ + if (!ei_device_has_capability(device, EI_DEVICE_CAP_TOUCH)) { + log_bug_client(ei_device_get_context(device), + "%s: device is not a touch device\n", __func__); + return -EINVAL; + } + + if (device->state != EI_DEVICE_STATE_EMULATING) + return -EINVAL; + + ei_queue_touch_down_event(device, touchid, x, y); + + return 0; +} + +int +ei_device_event_touch_motion(struct ei_device *device, uint32_t touchid, double x, double y) +{ + if (!ei_device_has_capability(device, EI_DEVICE_CAP_TOUCH)) { + log_bug_client(ei_device_get_context(device), + "%s: device is not a touch device\n", __func__); + return -EINVAL; + } + + if (device->state != EI_DEVICE_STATE_EMULATING) + return -EINVAL; + + ei_queue_touch_motion_event(device, touchid, x, y); + + return 0; +} + +int +ei_device_event_touch_up(struct ei_device *device, uint32_t touchid) +{ + if (!ei_device_has_capability(device, EI_DEVICE_CAP_TOUCH)) { + log_bug_client(ei_device_get_context(device), + "%s: device is not a touch device\n", __func__); + return -EINVAL; + } + + if (device->state != EI_DEVICE_STATE_EMULATING) + return -EINVAL; + + ei_queue_touch_up_event(device, touchid); + + return 0; +} + diff --git a/src/libei-event.c b/src/libei-event.c index 7b4f030..45f5d99 100644 --- a/src/libei-event.c +++ b/src/libei-event.c @@ -50,6 +50,20 @@ ei_event_type_to_string(enum ei_event_type type) CASE_RETURN_STRING(EI_EVENT_DEVICE_RESUMED); CASE_RETURN_STRING(EI_EVENT_KEYBOARD_MODIFIERS); CASE_RETURN_STRING(EI_EVENT_PROPERTY); + CASE_RETURN_STRING(EI_EVENT_FRAME); + CASE_RETURN_STRING(EI_EVENT_DEVICE_START_EMULATING); + CASE_RETURN_STRING(EI_EVENT_DEVICE_STOP_EMULATING); + CASE_RETURN_STRING(EI_EVENT_POINTER_MOTION); + CASE_RETURN_STRING(EI_EVENT_POINTER_MOTION_ABSOLUTE); + CASE_RETURN_STRING(EI_EVENT_POINTER_BUTTON); + CASE_RETURN_STRING(EI_EVENT_POINTER_SCROLL); + CASE_RETURN_STRING(EI_EVENT_POINTER_SCROLL_STOP); + CASE_RETURN_STRING(EI_EVENT_POINTER_SCROLL_CANCEL); + CASE_RETURN_STRING(EI_EVENT_POINTER_SCROLL_DISCRETE); + CASE_RETURN_STRING(EI_EVENT_KEYBOARD_KEY); + CASE_RETURN_STRING(EI_EVENT_TOUCH_DOWN); + CASE_RETURN_STRING(EI_EVENT_TOUCH_UP); + CASE_RETURN_STRING(EI_EVENT_TOUCH_MOTION); } assert(!"Unhandled event type"); @@ -73,6 +87,21 @@ ei_event_destroy(struct ei_event *event) free(event->prop.name); free(event->prop.value); break; + case EI_EVENT_FRAME: + case EI_EVENT_DEVICE_START_EMULATING: + case EI_EVENT_DEVICE_STOP_EMULATING: + case EI_EVENT_POINTER_MOTION: + case EI_EVENT_POINTER_MOTION_ABSOLUTE: + case EI_EVENT_POINTER_BUTTON: + case EI_EVENT_POINTER_SCROLL: + case EI_EVENT_POINTER_SCROLL_STOP: + case EI_EVENT_POINTER_SCROLL_CANCEL: + case EI_EVENT_POINTER_SCROLL_DISCRETE: + case EI_EVENT_KEYBOARD_KEY: + case EI_EVENT_TOUCH_DOWN: + case EI_EVENT_TOUCH_UP: + case EI_EVENT_TOUCH_MOTION: + break; } ei_device_unref(event->device); ei_seat_unref(event->seat); @@ -200,3 +229,196 @@ ei_event_property_get_permissions(struct ei_event *event) return event->prop.permissions; } + +_public_ double +ei_event_pointer_get_dx(struct ei_event *event) +{ + require_event_type(event, 0.0, + EI_EVENT_POINTER_MOTION, + EI_EVENT_POINTER_MOTION_ABSOLUTE, + EI_EVENT_POINTER_BUTTON, + EI_EVENT_POINTER_SCROLL, + EI_EVENT_POINTER_SCROLL_DISCRETE); + + return event->pointer.dx; +} + +_public_ double +ei_event_pointer_get_dy(struct ei_event *event) +{ + require_event_type(event, 0.0, + EI_EVENT_POINTER_MOTION, + EI_EVENT_POINTER_MOTION_ABSOLUTE, + EI_EVENT_POINTER_BUTTON, + EI_EVENT_POINTER_SCROLL, + EI_EVENT_POINTER_SCROLL_DISCRETE); + + return event->pointer.dy; +} + +_public_ double +ei_event_pointer_get_absolute_x(struct ei_event *event) +{ + require_event_type(event, 0.0, + EI_EVENT_POINTER_MOTION, + EI_EVENT_POINTER_MOTION_ABSOLUTE, + EI_EVENT_POINTER_BUTTON, + EI_EVENT_POINTER_SCROLL, + EI_EVENT_POINTER_SCROLL_DISCRETE); + + return event->pointer.absx; +} + +_public_ double +ei_event_pointer_get_absolute_y(struct ei_event *event) +{ + require_event_type(event, 0.0, + EI_EVENT_POINTER_MOTION, + EI_EVENT_POINTER_MOTION_ABSOLUTE, + EI_EVENT_POINTER_BUTTON, + EI_EVENT_POINTER_SCROLL, + EI_EVENT_POINTER_SCROLL_DISCRETE); + + return event->pointer.absy; +} + +_public_ uint32_t +ei_event_pointer_get_button(struct ei_event *event) +{ + require_event_type(event, 0, + EI_EVENT_POINTER_MOTION, + EI_EVENT_POINTER_MOTION_ABSOLUTE, + EI_EVENT_POINTER_BUTTON, + EI_EVENT_POINTER_SCROLL, + EI_EVENT_POINTER_SCROLL_DISCRETE); + + return event->pointer.button; +} + +_public_ bool +ei_event_pointer_get_button_is_press(struct ei_event *event) +{ + require_event_type(event, false, + EI_EVENT_POINTER_MOTION, + EI_EVENT_POINTER_MOTION_ABSOLUTE, + EI_EVENT_POINTER_BUTTON, + EI_EVENT_POINTER_SCROLL, + EI_EVENT_POINTER_SCROLL_DISCRETE); + + return event->pointer.button_is_press; +} + +_public_ double +ei_event_pointer_get_scroll_x(struct ei_event *event) +{ + require_event_type(event, 0, + EI_EVENT_POINTER_MOTION, + EI_EVENT_POINTER_MOTION_ABSOLUTE, + EI_EVENT_POINTER_BUTTON, + EI_EVENT_POINTER_SCROLL, + EI_EVENT_POINTER_SCROLL_DISCRETE); + return event->pointer.sx; +} + +_public_ double +ei_event_pointer_get_scroll_y(struct ei_event *event) +{ + require_event_type(event, 0, + EI_EVENT_POINTER_MOTION, + EI_EVENT_POINTER_MOTION_ABSOLUTE, + EI_EVENT_POINTER_BUTTON, + EI_EVENT_POINTER_SCROLL, + EI_EVENT_POINTER_SCROLL_DISCRETE); + return event->pointer.sy; +} + +_public_ int32_t +ei_event_pointer_get_scroll_discrete_x(struct ei_event *event) +{ + require_event_type(event, 0, + EI_EVENT_POINTER_MOTION, + EI_EVENT_POINTER_MOTION_ABSOLUTE, + EI_EVENT_POINTER_BUTTON, + EI_EVENT_POINTER_SCROLL, + EI_EVENT_POINTER_SCROLL_DISCRETE); + return event->pointer.sdx; +} + +_public_ int32_t +ei_event_pointer_get_scroll_discrete_y(struct ei_event *event) +{ + require_event_type(event, 0, + EI_EVENT_POINTER_MOTION, + EI_EVENT_POINTER_MOTION_ABSOLUTE, + EI_EVENT_POINTER_BUTTON, + EI_EVENT_POINTER_SCROLL, + EI_EVENT_POINTER_SCROLL_DISCRETE); + return event->pointer.sdy; +} + +_public_ bool +ei_event_pointer_get_scroll_stop_x(struct ei_event *event) +{ + require_event_type(event, 0, + EI_EVENT_POINTER_SCROLL_STOP, + EI_EVENT_POINTER_SCROLL_CANCEL); + return event->pointer.stop_x; +} + +_public_ bool +ei_event_pointer_get_scroll_stop_y(struct ei_event *event) +{ + require_event_type(event, 0, + EI_EVENT_POINTER_SCROLL_STOP, + EI_EVENT_POINTER_SCROLL_CANCEL); + return event->pointer.stop_y; +} + +_public_ uint32_t +ei_event_keyboard_get_key(struct ei_event *event) +{ + require_event_type(event, 0, + EI_EVENT_KEYBOARD_KEY); + + return event->keyboard.key; +} + +_public_ bool +ei_event_keyboard_get_key_is_press(struct ei_event *event) +{ + require_event_type(event, false, + EI_EVENT_KEYBOARD_KEY); + + return event->keyboard.key_is_press; +} + +_public_ uint32_t +ei_event_touch_get_id(struct ei_event *event) +{ + require_event_type(event, 0, + EI_EVENT_TOUCH_DOWN, + EI_EVENT_TOUCH_UP, + EI_EVENT_TOUCH_MOTION); + + return event->touch.touchid; +} + +_public_ double +ei_event_touch_get_x(struct ei_event *event) +{ + require_event_type(event, 0.0, + EI_EVENT_TOUCH_DOWN, + EI_EVENT_TOUCH_MOTION); + + return event->touch.x; +} + +_public_ double +ei_event_touch_get_y(struct ei_event *event) +{ + require_event_type(event, 0.0, + EI_EVENT_TOUCH_DOWN, + EI_EVENT_TOUCH_MOTION); + + return event->touch.y; +} diff --git a/src/libei-private.h b/src/libei-private.h index c62dc64..573784b 100644 --- a/src/libei-private.h +++ b/src/libei-private.h @@ -67,6 +67,8 @@ struct ei { } log; const struct ei_proto_requests *requests; + + bool is_active; }; enum ei_seat_state { @@ -183,6 +185,23 @@ struct ei_event { char *value; uint32_t permissions; } prop; + struct { + double dx, dy; /* relative motion */ + double absx, absy; /* absolute motion */ + double sx, sy; /* scroll */ + int32_t sdx, sdy; /* discrete scroll */ + bool stop_x, stop_y; /* scroll stop */ + uint32_t button; + bool button_is_press; + } pointer; + struct { + uint32_t key; + bool key_is_press; + } keyboard; + struct { + uint32_t touchid; + double x, y; + } touch; }; }; @@ -265,6 +284,50 @@ ei_insert_device_removed_event(struct ei_device *device); void ei_queue_seat_removed_event(struct ei_seat *seat); +void +ei_queue_device_start_emulating_event(struct ei_device *device); + +void +ei_queue_device_stop_emulating_event(struct ei_device *device); + +void +ei_queue_frame_event(struct ei_device *device); + +void +ei_queue_pointer_rel_event(struct ei_device *device, double x, double y); + +void +ei_queue_pointer_abs_event(struct ei_device *device, double x, double y); + +void +ei_queue_pointer_button_event(struct ei_device *device, uint32_t button, bool is_press); + +void +ei_queue_keyboard_key_event(struct ei_device *device, uint32_t key, bool is_press); + +void +ei_queue_pointer_scroll_event(struct ei_device *device, double x, double y); + +void +ei_queue_pointer_scroll_discrete_event(struct ei_device *device, int32_t x, int32_t y); + +void +ei_queue_pointer_scroll_stop_event(struct ei_device *device, bool x, bool y); + +void +ei_queue_pointer_scroll_cancel_event(struct ei_device *device, bool x, bool y); + +void +ei_queue_touch_down_event(struct ei_device *device, uint32_t touchid, + double x, double y); + +void +ei_queue_touch_motion_event(struct ei_device *device, uint32_t touchid, + double x, double y); + +void +ei_queue_touch_up_event(struct ei_device *device, uint32_t touchid); + struct ei_device * ei_device_new(struct ei_seat *seat, uint32_t deviceid); @@ -277,6 +340,53 @@ ei_device_done(struct ei_device *device); void ei_device_removed_by_server(struct ei_device *device); +int +ei_device_event_frame(struct ei_device *device); + +void +ei_device_event_start_emulating(struct ei_device *device); + +void +ei_device_event_stop_emulating(struct ei_device *device); + +int +ei_device_event_pointer_rel(struct ei_device *device, + double x, double y); + +int +ei_device_event_pointer_abs(struct ei_device *device, + double x, double y); + +int +ei_device_event_pointer_button(struct ei_device *device, + uint32_t button, bool is_press); + +int +ei_device_event_pointer_scroll(struct ei_device *device, + double x, double y); +int +ei_device_event_pointer_scroll_stop(struct ei_device *device, bool x, bool y); + +int +ei_device_event_pointer_scroll_cancel(struct ei_device *device, bool x, bool y); + +int +ei_device_event_pointer_scroll_discrete(struct ei_device *device, + int32_t x, int32_t y); + +int +ei_device_event_keyboard_key(struct ei_device *device, + uint32_t key, bool is_press); + +int +ei_device_event_touch_down(struct ei_device *device, uint32_t tid, + double x, double y); +int +ei_device_event_touch_motion(struct ei_device *device, uint32_t tid, + double x, double y); +int +ei_device_event_touch_up(struct ei_device *device, uint32_t tid); + int ei_send_pointer_rel(struct ei_device *device, double x, double y); diff --git a/src/libei-proto.c b/src/libei-proto.c index ac73d22..42bb8d0 100644 --- a/src/libei-proto.c +++ b/src/libei-proto.c @@ -64,7 +64,7 @@ ei_proto_handle_message(struct ei *ei, rc = call(connected, ei); break; case SERVER_MESSAGE__MSG_DISCONNECTED: - rc = call(disconnected, ei); + rc = call(disconnected, ei, proto->disconnected->is_active); break; case SERVER_MESSAGE__MSG_SEAT_ADDED: rc = call(seat_added, ei, @@ -132,6 +132,80 @@ ei_proto_handle_message(struct ei *ei, proto->property->value[0] ? proto->property->value : NULL, proto->property->permissions); break; + /* Events */ + case SERVER_MESSAGE__MSG_START_EMULATING: + rc = call(start_emulating, ei, + proto->start_emulating->deviceid); + break; + case SERVER_MESSAGE__MSG_STOP_EMULATING: + rc = call(stop_emulating, ei, + proto->stop_emulating->deviceid); + break; + case SERVER_MESSAGE__MSG_POINTER_RELATIVE: + rc = call(rel, ei, + proto->pointer_relative->deviceid, + proto->pointer_relative->x, + proto->pointer_relative->y); + break; + case SERVER_MESSAGE__MSG_POINTER_ABSOLUTE: + rc = call(abs, ei, + proto->pointer_absolute->deviceid, + proto->pointer_absolute->x, + proto->pointer_absolute->y); + break; + case SERVER_MESSAGE__MSG_POINTER_BUTTON: + rc = call(button, ei, + proto->pointer_button->deviceid, + proto->pointer_button->button, + proto->pointer_button->state); + break; + case SERVER_MESSAGE__MSG_POINTER_SCROLL: + rc = call(scroll, ei, + proto->pointer_scroll->deviceid, + proto->pointer_scroll->x, + proto->pointer_scroll->y); + break; + case SERVER_MESSAGE__MSG_POINTER_SCROLL_STOP: + rc = call(scroll_stop, ei, + proto->pointer_scroll_stop->deviceid, + proto->pointer_scroll_stop->x, + proto->pointer_scroll_stop->y, + proto->pointer_scroll_stop->is_cancel); + break; + case SERVER_MESSAGE__MSG_POINTER_SCROLL_DISCRETE: + rc = call(scroll_discrete, ei, + proto->pointer_scroll_discrete->deviceid, + proto->pointer_scroll_discrete->x, + proto->pointer_scroll_discrete->y); + break; + case SERVER_MESSAGE__MSG_KEYBOARD_KEY: + rc = call(key, ei, + proto->keyboard_key->deviceid, + proto->keyboard_key->key, + proto->keyboard_key->state); + break; + case SERVER_MESSAGE__MSG_TOUCH_DOWN: + rc = call(touch_down, ei, + proto->touch_down->deviceid, + proto->touch_down->touchid, + proto->touch_down->x, + proto->touch_down->y); + break; + case SERVER_MESSAGE__MSG_TOUCH_MOTION: + rc = call(touch_motion, ei, + proto->touch_motion->deviceid, + proto->touch_motion->touchid, + proto->touch_motion->x, + proto->touch_motion->y); + break; + case SERVER_MESSAGE__MSG_TOUCH_UP: + rc = call(touch_up, ei, + proto->touch_up->deviceid, + proto->touch_up->touchid); + break; + case SERVER_MESSAGE__MSG_FRAME: + rc = call(frame, ei, proto->frame->deviceid); + break; default: rc = -EBADMSG; break; @@ -240,6 +314,7 @@ ei_proto_send_connect(struct ei *ei) prepare_msg(CONNECT, Connect, connect); connect.name = ei->name; + connect.is_active = ei->is_active; return ei_proto_send_msg(ei, &msg); } diff --git a/src/libei-proto.h b/src/libei-proto.h index b68115c..c9cad93 100644 --- a/src/libei-proto.h +++ b/src/libei-proto.h @@ -35,7 +35,7 @@ /* callbacks invoked during ei_proto_parse_message() */ struct ei_proto_interface { int (*connected)(struct ei *ei); - int (*disconnected)(struct ei *ei); + int (*disconnected)(struct ei *ei, bool is_active); int (*seat_added)(struct ei *ei, uint32_t seatid, const char *name, uint32_t capabilities); int (*seat_removed)(struct ei *ei, uint32_t seatid); @@ -58,6 +58,23 @@ struct ei_proto_interface { int (*property)(struct ei *ei, const char *name, const char *value, uint32_t permissions); + + /* events */ + int (*start_emulating)(struct ei *ei, uint32_t deviceid); + int (*stop_emulating)(struct ei *ei, uint32_t deviceid); + int (*rel)(struct ei *ei, uint32_t deviceid, double x, double y); + int (*abs)(struct ei *ei, uint32_t deviceid, double x, double y); + int (*button)(struct ei *ei, uint32_t deviceid, uint32_t button, bool is_press); + int (*key)(struct ei *ei, uint32_t deviceid, uint32_t key, bool is_press); + int (*scroll)(struct ei *ei, uint32_t deviceid, double x, double y); + int (*scroll_stop)(struct ei *ei, uint32_t deviceid, bool x, bool y, bool is_cancel); + int (*scroll_discrete)(struct ei *ei, uint32_t deviceid, int32_t x, int32_t y); + int (*touch_down)(struct ei *ei, uint32_t deviceid, + uint32_t tid, double x, double y); + int (*touch_motion)(struct ei *ei, uint32_t deviceid, + uint32_t tid, double x, double y); + int (*touch_up)(struct ei *ei, uint32_t deviceid, uint32_t tid); + int (*frame) (struct ei *ei, uint32_t deviceid); }; struct ei_proto_requests { diff --git a/src/libei.c b/src/libei.c index 00acfd8..79de105 100644 --- a/src/libei.c +++ b/src/libei.c @@ -132,8 +132,8 @@ set_prop_type(struct ei *ei) ei_property_update(ei, "ei.connection.type", "socket", EI_PROPERTY_PERM_NONE); } -_public_ struct ei * -ei_new(void *user_data) +static struct ei * +ei_create_context(bool is_active, void *user_data) { _unref_(ei) *ei = ei_create(NULL); @@ -151,6 +151,7 @@ ei_new(void *user_data) ei->user_data = user_data; ei->backend = NULL; + ei->is_active = is_active; set_prop_pid(ei); set_prop_cmdline(ei); @@ -159,6 +160,24 @@ ei_new(void *user_data) return steal(&ei); } +_public_ struct ei * +ei_new(void *user_data) +{ + return ei_new_active(user_data); +} + +_public_ struct ei * +ei_new_active(void *user_data) +{ + return ei_create_context(true, user_data); +} + +_public_ struct ei * +ei_new_passive(void *user_data) +{ + return ei_create_context(false, user_data); +} + _public_ int ei_get_fd(struct ei *ei) { @@ -319,6 +338,177 @@ queue_property_event(struct ei *ei, const char *name, queue_event(ei, e); } +void +ei_queue_frame_event(struct ei_device *device) +{ + struct ei_event *e = ei_event_new_for_device(device); + + e->type = EI_EVENT_FRAME; + + queue_event(ei_device_get_context(device), e); +} + +void +ei_queue_device_start_emulating_event(struct ei_device *device) +{ + struct ei_event *e = ei_event_new_for_device(device); + + e->type = EI_EVENT_DEVICE_START_EMULATING; + + queue_event(ei_device_get_context(device), e); +} + +void +ei_queue_device_stop_emulating_event(struct ei_device *device) +{ + struct ei_event *e = ei_event_new_for_device(device); + + e->type = EI_EVENT_DEVICE_STOP_EMULATING; + + queue_event(ei_device_get_context(device), e); +} + +void +ei_queue_pointer_rel_event(struct ei_device *device, + double dx, double dy) +{ + struct ei_event *e = ei_event_new_for_device(device); + + e->type = EI_EVENT_POINTER_MOTION; + e->pointer.dx = dx; + e->pointer.dy = dy; + + queue_event(ei_device_get_context(device), e); +} + +void +ei_queue_pointer_abs_event(struct ei_device *device, + double x, double y) +{ + struct ei_event *e = ei_event_new_for_device(device); + + e->type = EI_EVENT_POINTER_MOTION_ABSOLUTE; + e->pointer.absx = x; + e->pointer.absy = y; + + queue_event(ei_device_get_context(device), e); +} + +void +ei_queue_pointer_button_event(struct ei_device *device, uint32_t button, + bool is_press) +{ + struct ei_event *e = ei_event_new_for_device(device); + + e->type = EI_EVENT_POINTER_BUTTON; + e->pointer.button = button; + e->pointer.button_is_press = is_press; + + queue_event(ei_device_get_context(device), e); +} + +void +ei_queue_pointer_scroll_event(struct ei_device *device, + double x, double y) +{ + struct ei_event *e = ei_event_new_for_device(device); + + e->type = EI_EVENT_POINTER_SCROLL; + e->pointer.sx = x; + e->pointer.sy = y; + + queue_event(ei_device_get_context(device), e); +} + +void +ei_queue_pointer_scroll_discrete_event(struct ei_device *device, + int32_t x, int32_t y) +{ + struct ei_event *e = ei_event_new_for_device(device); + + e->type = EI_EVENT_POINTER_SCROLL_DISCRETE; + e->pointer.sdx = x; + e->pointer.sdy = y; + + queue_event(ei_device_get_context(device), e); +} + +void +ei_queue_pointer_scroll_stop_event(struct ei_device *device, bool x, bool y) +{ + struct ei_event *e = ei_event_new_for_device(device); + + e->type = EI_EVENT_POINTER_SCROLL_STOP; + e->pointer.stop_x = x; + e->pointer.stop_y = y; + + queue_event(ei_device_get_context(device), e); +} + +void +ei_queue_pointer_scroll_cancel_event(struct ei_device *device, bool x, bool y) +{ + struct ei_event *e = ei_event_new_for_device(device); + + e->type = EI_EVENT_POINTER_SCROLL_CANCEL; + e->pointer.stop_x = x; + e->pointer.stop_y = y; + + queue_event(ei_device_get_context(device), e); +} + +void +ei_queue_keyboard_key_event(struct ei_device *device, uint32_t key, + bool is_press) +{ + struct ei_event *e = ei_event_new_for_device(device); + + e->type = EI_EVENT_KEYBOARD_KEY; + e->keyboard.key = key; + e->keyboard.key_is_press = is_press; + + queue_event(ei_device_get_context(device), e); +} + +void +ei_queue_touch_down_event(struct ei_device *device, uint32_t touchid, + double x, double y) +{ + struct ei_event *e = ei_event_new_for_device(device); + + e->type = EI_EVENT_TOUCH_DOWN; + e->touch.touchid = touchid, + e->touch.x = x; + e->touch.y = y; + + queue_event(ei_device_get_context(device), e); +} + +void +ei_queue_touch_motion_event(struct ei_device *device, uint32_t touchid, + double x, double y) +{ + struct ei_event *e = ei_event_new_for_device(device); + + e->type = EI_EVENT_TOUCH_MOTION; + e->touch.touchid = touchid, + e->touch.x = x; + e->touch.y = y; + + queue_event(ei_device_get_context(device), e); +} + +void +ei_queue_touch_up_event(struct ei_device *device, uint32_t touchid) +{ + struct ei_event *e = ei_event_new_for_device(device); + + e->type = EI_EVENT_TOUCH_UP; + e->touch.touchid = touchid, + + queue_event(ei_device_get_context(device), e); +} + void ei_disconnect(struct ei *ei) { @@ -838,10 +1028,204 @@ static int handle_msg_connected(struct ei *ei) { return 0; } -static int handle_msg_disconnected(struct ei *ei) { +static int handle_msg_disconnected(struct ei *ei, bool is_active) { + if (ei->is_active == is_active) { + log_bug_client(ei, "Unable to connect %s ei context to %s EIS implementation\n", + is_active ? "active" : "passive", + is_active ? "active" : "passive"); + } return -ECANCELED; } +#define DISCONNECT_IF_ACTIVE_CONTEXT(ei_) do {\ + if (ei_->is_active) { \ + log_bug_client(ei_, "Invalid event from passive EIS context. Disconnecting\n"); \ + return -ECANCELED; \ + } \ +} while(0) + +static int +handle_msg_start_emulating(struct ei *ei, uint32_t deviceid) +{ + DISCONNECT_IF_ACTIVE_CONTEXT(ei); + + struct ei_device *device = ei_find_device(ei, deviceid); + + if (device) + ei_device_event_start_emulating(device); + + return 0; +} + +static int +handle_msg_stop_emulating(struct ei *ei, uint32_t deviceid) +{ + DISCONNECT_IF_ACTIVE_CONTEXT(ei); + + struct ei_device *device = ei_find_device(ei, deviceid); + + if (device) + ei_device_event_stop_emulating(device); + + return 0; +} + +static int +handle_msg_frame(struct ei *ei, uint32_t deviceid) +{ + DISCONNECT_IF_ACTIVE_CONTEXT(ei); + + struct ei_device *device = ei_find_device(ei, deviceid); + + if (device) + return ei_device_event_frame(device); + + return 0; +} + +static int +handle_msg_pointer_rel(struct ei *ei, uint32_t deviceid, + double x, double y) +{ + DISCONNECT_IF_ACTIVE_CONTEXT(ei); + + struct ei_device *device = ei_find_device(ei, deviceid); + + if (device) + return ei_device_event_pointer_rel(device, x, y); + + return -EINVAL; +} + +static int +handle_msg_pointer_abs(struct ei *ei, uint32_t deviceid, + double x, double y) +{ + DISCONNECT_IF_ACTIVE_CONTEXT(ei); + + struct ei_device *device = ei_find_device(ei, deviceid); + + if (device) + return ei_device_event_pointer_abs(device, x, y); + + return -EINVAL; +} + +static int +handle_msg_pointer_button(struct ei *ei, uint32_t deviceid, + uint32_t button, bool state) +{ + DISCONNECT_IF_ACTIVE_CONTEXT(ei); + + struct ei_device *device = ei_find_device(ei, deviceid); + + if (device) + return ei_device_event_pointer_button(device, button, state); + + return -EINVAL; +} + +static int +handle_msg_pointer_scroll(struct ei *ei, uint32_t deviceid, + double x, double y) +{ + DISCONNECT_IF_ACTIVE_CONTEXT(ei); + + struct ei_device *device = ei_find_device(ei, deviceid); + + if (device) + return ei_device_event_pointer_scroll(device, x, y); + + return -EINVAL; +} + +static int +handle_msg_pointer_scroll_discrete(struct ei *ei, uint32_t deviceid, + int32_t x, int32_t y) +{ + DISCONNECT_IF_ACTIVE_CONTEXT(ei); + + struct ei_device *device = ei_find_device(ei, deviceid); + + if (device) + return ei_device_event_pointer_scroll_discrete(device, x, y); + + return -EINVAL; +} + +static int +handle_msg_pointer_scroll_stop(struct ei *ei, uint32_t deviceid, + bool x, bool y, bool is_cancel) +{ + DISCONNECT_IF_ACTIVE_CONTEXT(ei); + + struct ei_device *device = ei_find_device(ei, deviceid); + + if (device) { + if (is_cancel) + return ei_device_event_pointer_scroll_cancel(device, x, y); + else + return ei_device_event_pointer_scroll_stop(device, x, y); + } + + return -EINVAL; +} + +static int +handle_msg_keyboard_key(struct ei *ei, uint32_t deviceid, + uint32_t key, bool state) +{ + DISCONNECT_IF_ACTIVE_CONTEXT(ei); + + struct ei_device *device = ei_find_device(ei, deviceid); + + if (device) + return ei_device_event_keyboard_key(device, key, state); + + return -EINVAL; +} + +static int +handle_msg_touch_down(struct ei *ei, uint32_t deviceid, + uint32_t touchid, double x, double y) +{ + DISCONNECT_IF_ACTIVE_CONTEXT(ei); + + struct ei_device *device = ei_find_device(ei, deviceid); + + if (device) + return ei_device_event_touch_down(device, touchid, x, y); + + return -EINVAL; +} + +static int +handle_msg_touch_motion(struct ei *ei, uint32_t deviceid, + uint32_t touchid, double x, double y) +{ + DISCONNECT_IF_ACTIVE_CONTEXT(ei); + + struct ei_device *device = ei_find_device(ei, deviceid); + + if (device) + return ei_device_event_touch_motion(device, touchid, x, y); + + return -EINVAL; +} + +static int +handle_msg_touch_up(struct ei *ei, uint32_t deviceid, uint32_t touchid) +{ + DISCONNECT_IF_ACTIVE_CONTEXT(ei); + + struct ei_device *device = ei_find_device(ei, deviceid); + + if (device) + return ei_device_event_touch_up(device, touchid); + + return -EINVAL; +} + static const struct ei_proto_interface intf_state_backend = { /* Everything triggers -EPROTO */ .connected = NULL, @@ -865,6 +1249,21 @@ static const struct ei_proto_interface intf_state_connected = { .device_done = handle_msg_device_added_done, .keyboard_modifiers = handle_msg_keyboard_modifiers, .property = handle_msg_property, + + /* events */ + .start_emulating = handle_msg_start_emulating, + .stop_emulating = handle_msg_stop_emulating, + .rel = handle_msg_pointer_rel, + .abs = handle_msg_pointer_abs, + .button = handle_msg_pointer_button, + .scroll = handle_msg_pointer_scroll, + .scroll_stop = handle_msg_pointer_scroll_stop, + .scroll_discrete = handle_msg_pointer_scroll_discrete, + .key = handle_msg_keyboard_key, + .touch_down = handle_msg_touch_down, + .touch_motion = handle_msg_touch_motion, + .touch_up = handle_msg_touch_up, + .frame = handle_msg_frame, }; static const struct ei_proto_interface *interfaces[] = { diff --git a/src/libei.h b/src/libei.h index c3a6090..5b25402 100644 --- a/src/libei.h +++ b/src/libei.h @@ -38,6 +38,12 @@ extern "C" { * libei is the client-side module. This API should be used by processes * that need to emulate devices. * + * libei clients come in "active" and "passive" modes, depending on whether + * the client sends or receives events. A libeis context however is both + * active and passive at the same time, it is up to the implementation to + * disconnect clients that it does not want to allow. See + * eis_client_is_active() for details. + * * @{ */ @@ -278,11 +284,65 @@ enum ei_event_type { * This event may arrive while a device is paused. */ EI_EVENT_KEYBOARD_MODIFIERS, + + /** + * "Hardware" frame event. This event **must** be sent by the server + * and notifies the client that the previous set of events belong to + * the same logical hardware event. + * + * These events are only generated on a passive ei context. + * + * This event is most commonly used to implement multitouch (multiple + * touches may update within the same hardware scanout cycle). + */ + EI_EVENT_FRAME = 100, + + /** + * The server is about to send events for a device. This event should + * be used by the client to clear the logical state of the emulated + * devices and/or provide UI to the user. + * + * These events are only generated on a passive ei context. + * + * Note that a server start multiple emulating sequences + * simultaneously, depending on the devices available. + * For example, in a synergy-like situation, the server + * may start sending pointer and keyboard once the remote device + * logically entered the screen. + */ + EI_EVENT_DEVICE_START_EMULATING = 200, + EI_EVENT_DEVICE_STOP_EMULATING, + + /* These events are only generated on a passive ei context. */ + EI_EVENT_POINTER_MOTION = 300, + EI_EVENT_POINTER_MOTION_ABSOLUTE, + EI_EVENT_POINTER_BUTTON, + EI_EVENT_POINTER_SCROLL, + EI_EVENT_POINTER_SCROLL_STOP, + EI_EVENT_POINTER_SCROLL_CANCEL, + EI_EVENT_POINTER_SCROLL_DISCRETE, + + EI_EVENT_KEYBOARD_KEY = 400, + + EI_EVENT_TOUCH_DOWN = 500, + EI_EVENT_TOUCH_UP, + EI_EVENT_TOUCH_MOTION, }; /** - * Create a new ei context. The context is refcounted and must be released - * with ei_unref(). + * This is an alias for @ref ei_new_active. + */ +struct ei * +ei_new(void *user_data); + +/** + * Create a new active ei context. The context is refcounted and must be + * released with ei_unref(). + * + * An active ei context sends events to the EIS implementation but cannot + * receive events. An active ei context can only connect to a passive EIS + * implementation, connecting to an active EIS implementation will immediately + * disconnect the client. * * A context supports exactly one backend, set up with one of * ei_setup_backend_socket() or ei_setup_backend_fd(). @@ -295,7 +355,29 @@ enum ei_event_type { * @see ei_setup_backend_socket */ struct ei * -ei_new(void *user_data); +ei_new_active(void *user_data); + +/** + * Create a new passive ei context. The context is refcounted and must be + * released with ei_unref(). + * + * A passive ei context receives events from the EIS implementation but cannot + * send events. A passive ei context can only connect to an active EIS + * implementation, connecting to a passive EIS implementation will immediately + * disconnect the client. + * + * A context supports exactly one backend, set up with one of + * ei_setup_backend_socket() or ei_setup_backend_fd(). + * + * @param user_data An opaque pointer to be returned with ei_get_user_data() + * + * @see ei_set_user_data + * @see ei_get_user_data + * @see ei_setup_backend_fd + * @see ei_setup_backend_socket + */ +struct ei * +ei_new_passive(void *user_data); enum ei_log_priority { EI_LOG_PRIORITY_DEBUG = 10, @@ -1298,6 +1380,131 @@ ei_event_property_get_value(struct ei_event *event); uint32_t ei_event_property_get_permissions(struct ei_event *event); +/** + * For an event of type @ref EI_EVENT_POINTER_MOTION return the relative x + * movement in logical pixels. + */ +double +ei_event_pointer_get_dx(struct ei_event *event); + +/** + * For an event of type @ref EI_EVENT_POINTER_MOTION return the relative y +uint32_* movement in logical pixels. + */ +double +ei_event_pointer_get_dy(struct ei_event *event); + +/** + * For an event of type @ref EI_EVENT_POINTER_MOTION_ABSOLUTE return the x + * position in logical pixels. + */ +double +ei_event_pointer_get_absolute_x(struct ei_event *event); + +/** + * For an event of type @ref EI_EVENT_POINTER_MOTION_ABSOLUTE return the y + * position in logical pixels. + */ +double +ei_event_pointer_get_absolute_y(struct ei_event *event); + +/** + * For an event of type @ref EI_EVENT_POINTER_BUTTON return the button + * code as defined in linux/input-event-codes.h + */ +uint32_t +ei_event_pointer_get_button(struct ei_event *event); + +/** + * For an event of type @ref EI_EVENT_POINTER_BUTTON return true if the + * event is a button press, false for a release. + */ +bool +ei_event_pointer_get_button_is_press(struct ei_event *event); + +/** + * For an event of type @ref EI_EVENT_POINTER_SCROLL return the x scroll + * distance in logical pixels. + */ +double +ei_event_pointer_get_scroll_x(struct ei_event *event); + +/** + * For an event of type @ref EI_EVENT_POINTER_SCROLL return the y scroll + * distance in logical pixels. + */ +double +ei_event_pointer_get_scroll_y(struct ei_event *event); + +/** + * For an event of type @ref EI_EVENT_POINTER_SCROLL_CANCEL return whether the + * x axis has cancelled scrolling. + */ +bool +ei_event_pointer_get_scroll_stop_x(struct ei_event *event); + +/** + * For an event of type @ref EI_EVENT_POINTER_SCROLL_STOP return whether the + * y axis has stopped scrolling. + */ +bool +ei_event_pointer_get_scroll_stop_y(struct ei_event *event); + +/** + * For an event of type @ref EI_EVENT_POINTER_SCROLL_DISCRETE return the x + * scroll distance in fractions or multiples of 120. + */ +int32_t +ei_event_pointer_get_scroll_discrete_x(struct ei_event *event); + +/** + * For an event of type @ref EI_EVENT_POINTER_SCROLL_DISCRETE return the y + * scroll distance in fractions or multiples of 120. + */ +int32_t +ei_event_pointer_get_scroll_discrete_y(struct ei_event *event); + +/** + * For an event of type @ref EI_EVENT_KEYBOARD_KEY return the key code (as + * defined in include/linux/input-event-codes.h). + */ +uint32_t +ei_event_keyboard_get_key(struct ei_event *event); + +/** + * For an event of type @ref EI_EVENT_KEYBOARD_KEY return true if the + * event is a key down, false for a release. + */ +bool +ei_event_keyboard_get_key_is_press(struct ei_event *event); + +/** + * For an event of type @ref EI_EVENT_TOUCH_DOWN, @ref + * EI_EVENT_TOUCH_MOTION, or @ref EI_EVENT_TOUCH_UP, return the tracking + * ID of the touch. + * + * The tracking ID is a unique identifier for a touch and is valid from + * touch down through to touch up but may be re-used in the future. + * The tracking ID is randomly assigned to a touch, a client + * must not expect any specific value. + */ +uint32_t +ei_event_touch_get_id(struct ei_event *event); + +/** + * For an event of type @ref EI_EVENT_TOUCH_DOWN, or @ref + * EI_EVENT_TOUCH_MOTION, return the x coordinate of the touch. + */ +double +ei_event_touch_get_x(struct ei_event *event); + +/** + * For an event of type @ref EI_EVENT_TOUCH_DOWN, or @ref + * EI_EVENT_TOUCH_MOTION, return the y coordinate of the touch. + */ +double +ei_event_touch_get_y(struct ei_event *event); + /** * @} */ diff --git a/src/libeis-client.c b/src/libeis-client.c index bb538bb..85376a7 100644 --- a/src/libeis-client.c +++ b/src/libeis-client.c @@ -79,6 +79,12 @@ eis_client_get_context(struct eis_client *client) return eis_client_parent(client); } +_public_ bool +eis_client_is_active(struct eis_client *client) +{ + return client->is_active; +} + static struct eis_device * eis_client_find_device(struct eis_client *client, uint32_t deviceid) { @@ -268,9 +274,19 @@ client_msg_unbind_seat(struct eis_client *client, uint32_t seatid) return seat ? 0 : -EINVAL; } +#define DISCONNECT_IF_ACTIVE_CONTEXT(client_) do { \ + struct eis *_ctx = eis_client_get_context(client_); \ + if (_ctx->is_active) { \ + log_bug_client(_ctx, "Invalid event from passive ei context. Disconnecting client\n"); \ + return -EINVAL; \ + } \ +} while(0) + static int client_msg_start_emulating(struct eis_client *client, uint32_t deviceid) { + DISCONNECT_IF_ACTIVE_CONTEXT(client); + struct eis_device *device = eis_client_find_device(client, deviceid); if (device) @@ -282,6 +298,8 @@ client_msg_start_emulating(struct eis_client *client, uint32_t deviceid) static int client_msg_stop_emulating(struct eis_client *client, uint32_t deviceid) { + DISCONNECT_IF_ACTIVE_CONTEXT(client); + struct eis_device *device = eis_client_find_device(client, deviceid); if (device) @@ -293,6 +311,8 @@ client_msg_stop_emulating(struct eis_client *client, uint32_t deviceid) static int client_msg_frame(struct eis_client *client, uint32_t deviceid) { + DISCONNECT_IF_ACTIVE_CONTEXT(client); + struct eis_device *device = eis_client_find_device(client, deviceid); if (device) @@ -305,6 +325,8 @@ static int client_msg_pointer_rel(struct eis_client *client, uint32_t deviceid, double x, double y) { + DISCONNECT_IF_ACTIVE_CONTEXT(client); + struct eis_device *device = eis_client_find_device(client, deviceid); if (device) @@ -317,6 +339,8 @@ static int client_msg_pointer_abs(struct eis_client *client, uint32_t deviceid, double x, double y) { + DISCONNECT_IF_ACTIVE_CONTEXT(client); + struct eis_device *device = eis_client_find_device(client, deviceid); if (device) @@ -329,6 +353,8 @@ static int client_msg_pointer_button(struct eis_client *client, uint32_t deviceid, uint32_t button, bool state) { + DISCONNECT_IF_ACTIVE_CONTEXT(client); + struct eis_device *device = eis_client_find_device(client, deviceid); if (device) @@ -341,6 +367,8 @@ static int client_msg_pointer_scroll(struct eis_client *client, uint32_t deviceid, double x, double y) { + DISCONNECT_IF_ACTIVE_CONTEXT(client); + struct eis_device *device = eis_client_find_device(client, deviceid); if (device) @@ -353,6 +381,8 @@ static int client_msg_pointer_scroll_discrete(struct eis_client *client, uint32_t deviceid, int32_t x, int32_t y) { + DISCONNECT_IF_ACTIVE_CONTEXT(client); + struct eis_device *device = eis_client_find_device(client, deviceid); if (device) @@ -365,6 +395,8 @@ static int client_msg_pointer_scroll_stop(struct eis_client *client, uint32_t deviceid, bool x, bool y, bool is_cancel) { + DISCONNECT_IF_ACTIVE_CONTEXT(client); + struct eis_device *device = eis_client_find_device(client, deviceid); if (device) { @@ -381,6 +413,8 @@ static int client_msg_keyboard_key(struct eis_client *client, uint32_t deviceid, uint32_t key, bool state) { + DISCONNECT_IF_ACTIVE_CONTEXT(client); + struct eis_device *device = eis_client_find_device(client, deviceid); if (device) @@ -393,6 +427,8 @@ static int client_msg_touch_down(struct eis_client *client, uint32_t deviceid, uint32_t touchid, double x, double y) { + DISCONNECT_IF_ACTIVE_CONTEXT(client); + struct eis_device *device = eis_client_find_device(client, deviceid); if (device) @@ -405,6 +441,8 @@ static int client_msg_touch_motion(struct eis_client *client, uint32_t deviceid, uint32_t touchid, double x, double y) { + DISCONNECT_IF_ACTIVE_CONTEXT(client); + struct eis_device *device = eis_client_find_device(client, deviceid); if (device) @@ -416,6 +454,8 @@ client_msg_touch_motion(struct eis_client *client, uint32_t deviceid, static int client_msg_touch_up(struct eis_client *client, uint32_t deviceid, uint32_t touchid) { + DISCONNECT_IF_ACTIVE_CONTEXT(client); + struct eis_device *device = eis_client_find_device(client, deviceid); if (device) @@ -450,11 +490,13 @@ client_msg_configure_capabilities(struct eis_client *client, uint32_t allowed_ca } static int -client_msg_connect(struct eis_client *client, const char *name) +client_msg_connect(struct eis_client *client, const char *name, bool is_active) { if (client->name == NULL) client->name = xstrdup(name); + client->is_active = is_active; + return 0; } @@ -589,6 +631,7 @@ eis_client_new(struct eis *eis, int fd) static uint32_t client_id; struct eis_client *client = eis_client_create(&eis->object); + client->is_active = true; client->id = ++client_id; list_init(&client->seats); list_init(&client->seats_pending); diff --git a/src/libeis-device.c b/src/libeis-device.c index 11aad16..4ac1120 100644 --- a/src/libeis-device.c +++ b/src/libeis-device.c @@ -31,6 +31,7 @@ #include "util-io.h" #include "libeis-private.h" +#include "libeis-proto.h" _public_ OBJECT_IMPLEMENT_REF(eis_keymap); @@ -247,6 +248,9 @@ eis_device_remove(struct eis_device *device) if (device->state == EIS_DEVICE_STATE_DEAD) return; + if (device->state == EIS_DEVICE_STATE_EMULATING) + eis_device_stop_emulating(device); + device->state = EIS_DEVICE_STATE_DEAD; eis_client_remove_device(eis_device_get_client(device), device); list_remove(&device->link); @@ -267,6 +271,331 @@ eis_device_has_capability(struct eis_device *device, return false; } +#define handle_request_noargs(device_, func_) { \ + struct eis *eis = eis_device_get_context(device); \ + eis->requests->func_(device_, device->id); \ +} + +#define handle_request(device_, func_, ...) { \ + struct eis *eis = eis_device_get_context(device); \ + eis->requests->func_(device_, device->id, __VA_ARGS__); \ +} + +_public_ void +eis_device_start_emulating(struct eis_device *device) +{ + if (device->state != EIS_DEVICE_STATE_RESUMED) + return; + + device->state = EIS_DEVICE_STATE_EMULATING; + + handle_request_noargs(device, start_emulating); +} + +_public_ void +eis_device_stop_emulating(struct eis_device *device) +{ + if (device->state != EIS_DEVICE_STATE_EMULATING) + return; + + device->state = EIS_DEVICE_STATE_RESUMED; + + handle_request_noargs(device, stop_emulating); +} + +_public_ void +eis_device_pointer_motion(struct eis_device *device, + double x, double y) +{ + if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER)) { + log_bug_client(eis_device_get_context(device), + "%s: device is not a pointer\n", __func__); + return; + } + + if (device->state != EIS_DEVICE_STATE_EMULATING) + return; + + handle_request(device, rel, x, y); +} + +_public_ void +eis_device_pointer_motion_absolute(struct eis_device *device, + double x, double y) +{ + if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER_ABSOLUTE)) { + log_bug_client(eis_device_get_context(device), + "%s: device is not an absolute pointer\n", __func__); + return; + } + + if (device->state != EIS_DEVICE_STATE_EMULATING) + return; + + struct eis_region *r; + list_for_each(r, &device->regions, link) { + if (!eis_region_contains(r, x, y)) { + return; + } + } + + handle_request(device, abs, x, y); +} + +_public_ void +eis_device_pointer_button(struct eis_device *device, + uint32_t button, bool is_press) +{ + if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER)) { + log_bug_client(eis_device_get_context(device), + "%s: device is not a pointer\n", __func__); + return; + } + + if (device->state != EIS_DEVICE_STATE_EMULATING) + return; + + /* Ignore anything < BTN_MOUSE. Avoids the common error of sending + * numerical buttons instead of BTN_LEFT and friends. */ + if (button < 0x110) { + log_bug_client(eis_device_get_context(device), + "%s: button code must be one of BTN_*\n", __func__); + return; + } + + handle_request(device, button, button, is_press); +} + +static inline void +eis_device_resume_scrolling(struct eis_device *device, double x, double y) +{ + if (x) { + device->scroll.x_is_stopped = false; + device->scroll.x_is_cancelled = false; + } + if (y) { + device->scroll.y_is_stopped = false; + device->scroll.y_is_cancelled = false; + } +} + +_public_ void +eis_device_pointer_scroll(struct eis_device *device, + double x, double y) +{ + if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER) && + !eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER_ABSOLUTE)) { + log_bug_client(eis_device_get_context(device), + "%s: device is not a (absolute) pointer\n", __func__); + } + + if (device->state != EIS_DEVICE_STATE_EMULATING) + return; + + eis_device_resume_scrolling(device, x, y); + + handle_request(device, scroll, x, y); +} + +_public_ void +eis_device_pointer_scroll_stop(struct eis_device *device, bool x, bool y) +{ + if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER) && + !eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER_ABSOLUTE)) { + log_bug_client(eis_device_get_context(device), + "%s: device is not a (absolute) pointer\n", __func__); + } + if (device->state != EIS_DEVICE_STATE_EMULATING) + return; + + /* Filter out duplicate scroll stop requests */ + if (x && !device->scroll.x_is_stopped) + device->scroll.x_is_stopped = true; + else + x = false; + + if (y && !device->scroll.y_is_stopped) + device->scroll.y_is_stopped = true; + else + y = false; + + if (x || y) + handle_request(device, scroll_stop, x, y, false); +} + +_public_ void +eis_device_pointer_scroll_cancel(struct eis_device *device, bool x, bool y) +{ + if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER) && + !eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER_ABSOLUTE)) { + log_bug_client(eis_device_get_context(device), + "%s: device is not a (absolute) pointer\n", __func__); + } + if (device->state != EIS_DEVICE_STATE_EMULATING) + return; + + /* Filter out duplicate scroll cancelled requests */ + if (x && !device->scroll.x_is_cancelled) { + device->scroll.x_is_stopped = true; + device->scroll.x_is_cancelled = true; + } else { + x = false; + } + + if (y && !device->scroll.y_is_cancelled) { + device->scroll.y_is_stopped = true; + device->scroll.y_is_cancelled = true; + } else { + y = false; + } + + if (x || y) + handle_request(device, scroll_stop, x, y, true); +} + +_public_ void +eis_device_pointer_scroll_discrete(struct eis_device *device, + int32_t x, int32_t y) +{ + if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER) && + !eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER_ABSOLUTE)) { + log_bug_client(eis_device_get_context(device), + "%s: device is not a (absolute) pointer\n", __func__); + } + + if (device->state != EIS_DEVICE_STATE_EMULATING) + return; + + eis_device_resume_scrolling(device, x, y); + + handle_request(device, scroll_discrete, x, y); +} + +_public_ void +eis_device_keyboard_key(struct eis_device *device, + uint32_t key, bool is_press) +{ + if (!eis_device_has_capability(device, EIS_DEVICE_CAP_KEYBOARD)) { + log_bug_client(eis_device_get_context(device), + "%s: device is not a keyboard\n", __func__); + return; + } + + if (device->state != EIS_DEVICE_STATE_EMULATING) + return; + + handle_request(device, key, key, is_press); +} + + +_public_ +OBJECT_IMPLEMENT_REF(eis_touch); +_public_ +OBJECT_IMPLEMENT_UNREF_CLEANUP(eis_touch); +_public_ +OBJECT_IMPLEMENT_GETTER(eis_touch, device, struct eis_device*); +_public_ +OBJECT_IMPLEMENT_GETTER(eis_touch, user_data, void *); +_public_ +OBJECT_IMPLEMENT_SETTER(eis_touch, user_data, void *); + +static void +eis_touch_destroy(struct eis_touch *touch) +{ + eis_touch_up(touch); + eis_device_unref(touch->device); +} + +static +OBJECT_IMPLEMENT_CREATE(eis_touch); + +_public_ struct eis_touch * +eis_device_touch_new(struct eis_device *device) +{ + static uint32_t tracking_id = 0; + + /* Not using the device as parent object because we need a ref + * to it */ + struct eis_touch *touch = eis_touch_create(NULL); + + touch->device = eis_device_ref(device); + touch->state = TOUCH_IS_NEW; + touch->tracking_id = ++tracking_id; + + return touch; +} + +_public_ void +eis_touch_down(struct eis_touch *touch, double x, double y) +{ + struct eis_device *device = eis_touch_get_device(touch); + + if (touch->state != TOUCH_IS_NEW) { + log_bug_client(eis_device_get_context(device), + "%s: device is not a keyboard\n", __func__); + return; + } + + struct eis_region *r; + list_for_each(r, &device->regions, link) { + if (!eis_region_contains(r, x, y)) { + log_bug_client(eis_device_get_context(device), + "%s: invalid x/y coordinates\n", __func__); + touch->state = TOUCH_IS_UP; + return; + } + } + + touch->state = TOUCH_IS_DOWN; + + handle_request(device, touch_down, touch->tracking_id, x, y); +} + +_public_ void +eis_touch_motion(struct eis_touch *touch, double x, double y) +{ + if (touch->state != TOUCH_IS_DOWN) + return; + + struct eis_device *device = eis_touch_get_device(touch); + struct eis_region *r; + list_for_each(r, &device->regions, link) { + if (!eis_region_contains(r, x, y)) { + log_bug_client(eis_device_get_context(device), + "%s: invalid x/y coordinates\n", __func__); + eis_touch_up(touch); + return; + } + } + + handle_request(device, touch_motion, touch->tracking_id, x, y); +} + +_public_ void +eis_touch_up(struct eis_touch *touch) +{ + struct eis_device *device = eis_touch_get_device(touch); + + if (touch->state != TOUCH_IS_DOWN) { + log_bug_client(eis_device_get_context(device), + "%s: touch is not currently down\n", __func__); + return; + } + + touch->state = TOUCH_IS_UP; + + handle_request(device, touch_up, touch->tracking_id); +} + +_public_ void +eis_device_frame(struct eis_device *device) +{ + if (device->state != EIS_DEVICE_STATE_RESUMED) + return; + + handle_request_noargs(device, frame); +} + int eis_device_event_frame(struct eis_device *device) { diff --git a/src/libeis-private.h b/src/libeis-private.h index 47f06a4..ac7c9d4 100644 --- a/src/libeis-private.h +++ b/src/libeis-private.h @@ -54,6 +54,8 @@ struct eis { } log; const struct eis_proto_requests *requests; + + bool is_active; }; enum eis_client_state { @@ -71,7 +73,7 @@ struct eis_client { uint32_t id; enum eis_client_state state; char *name; - + bool is_active; struct list seats; struct list seats_pending; @@ -139,6 +141,26 @@ struct eis_device { struct list regions_new; /* not yet added */ struct eis_keymap *keymap; + + struct { + bool x_is_stopped, y_is_stopped; + bool x_is_cancelled, y_is_cancelled; + } scroll; + +}; + +struct eis_touch { + struct object object; + struct eis_device *device; + void *user_data; + uint32_t tracking_id; + enum { + TOUCH_IS_NEW, + TOUCH_IS_DOWN, + TOUCH_IS_UP, + } state; + + double x, y; }; struct eis_event { diff --git a/src/libeis-proto.c b/src/libeis-proto.c index 7dba18a..4c3a646 100644 --- a/src/libeis-proto.c +++ b/src/libeis-proto.c @@ -66,6 +66,20 @@ log_wire_message(struct eis *eis, const ServerMessage *msg) MSG_STRING_CASE(DEVICE_PAUSED); MSG_STRING_CASE(KEYBOARD_MODIFIERS); MSG_STRING_CASE(PROPERTY); + /* events */ + MSG_STRING_CASE(START_EMULATING); + MSG_STRING_CASE(STOP_EMULATING); + MSG_STRING_CASE(POINTER_RELATIVE); + MSG_STRING_CASE(POINTER_ABSOLUTE); + MSG_STRING_CASE(POINTER_BUTTON); + MSG_STRING_CASE(POINTER_SCROLL); + MSG_STRING_CASE(POINTER_SCROLL_STOP); + MSG_STRING_CASE(POINTER_SCROLL_DISCRETE); + MSG_STRING_CASE(KEYBOARD_KEY); + MSG_STRING_CASE(TOUCH_DOWN); + MSG_STRING_CASE(TOUCH_MOTION); + MSG_STRING_CASE(TOUCH_UP); + MSG_STRING_CASE(FRAME); break; } if (message == NULL) @@ -118,7 +132,10 @@ eis_proto_send_msg_with_fds(struct eis_client *client, const ServerMessage *msg, static int eis_proto_send_disconnected(struct eis_client *client) { + struct eis *eis = eis_client_get_context(client); + prepare_msg(DISCONNECTED, Disconnected, disconnected); + disconnected.is_active = eis->is_active; return eis_proto_send_msg(client, &msg); } @@ -271,6 +288,167 @@ eis_proto_send_property(struct eis_client *client, const char *name, return eis_proto_send_msg(client, &msg); } +static int +eis_proto_send_start_emulating(struct eis_device *device, uint32_t deviceid) +{ + prepare_msg(START_EMULATING, StartEmulating, start_emulating); + + start_emulating.deviceid = device->id; + + return eis_proto_send_msg(eis_device_get_client(device), &msg); +} + +static int +eis_proto_send_stop_emulating(struct eis_device *device, uint32_t deviceid) +{ + prepare_msg(STOP_EMULATING, StopEmulating, stop_emulating); + + stop_emulating.deviceid = device->id; + + return eis_proto_send_msg(eis_device_get_client(device), &msg); +} + +static int +eis_proto_send_rel(struct eis_device *device, uint32_t deviceid, double x, double y) +{ + prepare_msg(POINTER_RELATIVE, PointerRelative, pointer_relative); + + pointer_relative.deviceid = device->id; + pointer_relative.x = x; + pointer_relative.y = y; + + return eis_proto_send_msg(eis_device_get_client(device), &msg); +} + +static int +eis_proto_send_abs(struct eis_device *device, uint32_t deviceid, double x, double y) +{ + prepare_msg(POINTER_ABSOLUTE, PointerAbsolute, pointer_absolute); + + pointer_absolute.deviceid = device->id; + pointer_absolute.x = x; + pointer_absolute.y = y; + + return eis_proto_send_msg(eis_device_get_client(device), &msg); +} + +static int +eis_proto_send_button(struct eis_device *device, uint32_t deviceid, + uint32_t button, bool is_press) +{ + prepare_msg(POINTER_BUTTON, PointerButton, pointer_button); + + pointer_button.deviceid = device->id; + pointer_button.button = button; + pointer_button.state = is_press; + + return eis_proto_send_msg(eis_device_get_client(device), &msg); +} + +static int +eis_proto_send_key(struct eis_device *device, uint32_t deviceid, + uint32_t key, bool is_press) +{ + prepare_msg(KEYBOARD_KEY, KeyboardKey, keyboard_key); + + keyboard_key.deviceid = device->id; + keyboard_key.key = key; + keyboard_key.state = is_press; + + return eis_proto_send_msg(eis_device_get_client(device), &msg); +} + +static int +eis_proto_send_scroll(struct eis_device *device, uint32_t deviceid, + double x, double y) +{ + prepare_msg(POINTER_SCROLL, PointerScroll, pointer_scroll); + + pointer_scroll.deviceid = device->id; + pointer_scroll.x = x; + pointer_scroll.y = y; + + + return eis_proto_send_msg(eis_device_get_client(device), &msg); +} + +static int +eis_proto_send_scroll_stop(struct eis_device *device, uint32_t deviceid, + bool x, bool y, bool is_cancel) +{ + prepare_msg(POINTER_SCROLL_STOP, PointerScrollStop, pointer_scroll_stop); + + pointer_scroll_stop.deviceid = device->id; + pointer_scroll_stop.x = x; + pointer_scroll_stop.y = y; + pointer_scroll_stop.is_cancel = is_cancel; + + return eis_proto_send_msg(eis_device_get_client(device), &msg); +} + +static int +eis_proto_send_scroll_discrete(struct eis_device *device, uint32_t deviceid, + int32_t x, int32_t y) +{ + + prepare_msg(POINTER_SCROLL_DISCRETE, PointerScrollDiscrete, pointer_scroll_discrete); + + pointer_scroll_discrete.deviceid = device->id; + pointer_scroll_discrete.x = x; + pointer_scroll_discrete.y = y; + + return eis_proto_send_msg(eis_device_get_client(device), &msg); +} + +static int +eis_proto_send_touch_down(struct eis_device *device, uint32_t deviceid, + uint32_t tid, double x, double y) +{ + prepare_msg(TOUCH_DOWN, TouchDown, touch_down); + + touch_down.deviceid = device->id; + touch_down.touchid = tid; + touch_down.x = x; + touch_down.y = y; + + return eis_proto_send_msg(eis_device_get_client(device), &msg); +} + +static int +eis_proto_send_touch_motion(struct eis_device *device, uint32_t deviceid, + uint32_t tid, double x, double y) +{ + prepare_msg(TOUCH_MOTION, TouchMotion, touch_motion); + + touch_motion.deviceid = device->id; + touch_motion.touchid = tid; + touch_motion.x = x; + touch_motion.y = y; + + return eis_proto_send_msg(eis_device_get_client(device), &msg); +} + +static int +eis_proto_send_touch_up(struct eis_device *device, uint32_t deviceid, uint32_t tid) +{ + prepare_msg(TOUCH_UP, TouchUp, touch_up); + + touch_up.deviceid = device->id; + touch_up.touchid = tid; + + return eis_proto_send_msg(eis_device_get_client(device), &msg); +} + +static int +eis_proto_send_frame(struct eis_device *device, uint32_t deviceid) +{ + prepare_msg(FRAME, Frame, frame); + + frame.deviceid = device->id; + + return eis_proto_send_msg(eis_device_get_client(device), &msg); +} + static const struct eis_proto_requests requests = { .disconnected = eis_proto_send_disconnected, .connected = eis_proto_send_connected, @@ -285,6 +463,21 @@ static const struct eis_proto_requests requests = { .device_region = eis_proto_send_device_region, .keyboard_modifiers = eis_proto_send_keyboard_modifiers, .property = eis_proto_send_property, + + /* events */ + .start_emulating = eis_proto_send_start_emulating, + .stop_emulating = eis_proto_send_stop_emulating, + .rel = eis_proto_send_rel, + .abs = eis_proto_send_abs, + .button = eis_proto_send_button, + .scroll = eis_proto_send_scroll, + .scroll_stop = eis_proto_send_scroll_stop, + .scroll_discrete = eis_proto_send_scroll_discrete, + .key = eis_proto_send_key, + .touch_down = eis_proto_send_touch_down, + .touch_motion = eis_proto_send_touch_motion, + .touch_up = eis_proto_send_touch_up, + .frame = eis_proto_send_frame, }; const struct eis_proto_requests * @@ -318,7 +511,7 @@ eis_proto_handle_message(struct eis_client *client, int rc; switch (proto->msg_case) { case CLIENT_MESSAGE__MSG_CONNECT: - rc = call(connect, client, proto->connect->name); + rc = call(connect, client, proto->connect->name, proto->connect->is_active); break; case CLIENT_MESSAGE__MSG_CONNECT_DONE: rc = call(connect_done, client); diff --git a/src/libeis-proto.h b/src/libeis-proto.h index f4186cb..b989439 100644 --- a/src/libeis-proto.h +++ b/src/libeis-proto.h @@ -36,7 +36,7 @@ /* callbacks invoked during eis_proto_parse_message() */ struct eis_proto_interface { - int (*connect)(struct eis_client *client, const char *name); + int (*connect)(struct eis_client *client, const char *name, bool is_active); int (*connect_done)(struct eis_client *client); int (*disconnect)(struct eis_client *client); int (*bind_seat)(struct eis_client *client, uint32_t seatid, uint32_t capabilities); @@ -81,6 +81,23 @@ struct eis_proto_requests { const struct eis_xkb_modifiers *mods); int (*property)(struct eis_client *client, const char *name, const char *value, uint32_t permissions); + + /* events */ + int (*start_emulating)(struct eis_device *device, uint32_t deviceid); + int (*stop_emulating)(struct eis_device *device, uint32_t deviceid); + int (*rel)(struct eis_device *device, uint32_t deviceid, double x, double y); + int (*abs)(struct eis_device *device, uint32_t deviceid, double x, double y); + int (*button)(struct eis_device *device, uint32_t deviceid, uint32_t button, bool is_press); + int (*key)(struct eis_device *device, uint32_t deviceid, uint32_t key, bool is_press); + int (*scroll)(struct eis_device *device, uint32_t deviceid, double x, double y); + int (*scroll_stop)(struct eis_device *device, uint32_t deviceid, bool x, bool y, bool is_cancel); + int (*scroll_discrete)(struct eis_device *device, uint32_t deviceid, int32_t x, int32_t y); + int (*touch_down)(struct eis_device *device, uint32_t deviceid, + uint32_t tid, double x, double y); + int (*touch_motion)(struct eis_device *device, uint32_t deviceid, + uint32_t tid, double x, double y); + int (*touch_up)(struct eis_device *device, uint32_t deviceid, uint32_t tid); + int (*frame) (struct eis_device *device, uint32_t deviceid); }; int diff --git a/src/libeis.h b/src/libeis.h index 60c1219..a8e18ca 100644 --- a/src/libeis.h +++ b/src/libeis.h @@ -39,6 +39,18 @@ extern "C" { * libeis is the server-side module. This API should be used by processes * that have control over input devices, e.g. Wayland compositors. * + * libei clients come in "active" and "passive" modes, depending on whether + * the client sends or receives events. A libeis context however is both + * active and passive at the same time, it is up to the implementation to + * disconnect clients that it does not want to allow. See + * eis_client_is_active() for details. + * + * Note that usually the differentiation between active and passive client + * has an effect on the devices that should be sent to the client. Active + * clients typically expect devices representing the available screen area so + * they can control input, passive clients typically expect devices + * representing the physical input devices. + * * @{ */ @@ -48,6 +60,7 @@ struct eis_device; struct eis_seat; struct eis_event; struct eis_keymap; +struct eis_touch; /** * @struct eis_region @@ -144,6 +157,8 @@ enum eis_event_type { * and notifies the server that the previous set of events belong to * the same logical hardware event. * + * These events are only generated on a passive EIS context. + * * This event is most commonly used to implement multitouch (multiple * touches may update within the same hardware scanout cycle). */ @@ -154,7 +169,9 @@ enum eis_event_type { * be used by the server to clear the logical state of the emulated * devices and/or provide UI to the user. * - * Note that a client may need to start multiple emulating sequences + * These events are only generated on a passive EIS context. + * + * Note that a client start multiple emulating sequences * simultaneously, depending on the devices it received from the * server. For example, in a synergy-like situation, the client * may start emulating of pointer and keyboard once the remote device @@ -169,6 +186,8 @@ enum eis_event_type { */ EIS_EVENT_DEVICE_STOP_EMULATING, + /* These events are only generated on a passive EIS context */ + /** * A relative motion event with delta coordinates */ @@ -286,6 +305,14 @@ eis_get_user_data(struct eis *eis); void eis_set_user_data(struct eis *eis, void *user_data); +/** + * Returns true if the client is active, false otherwise. An active client may + * send events to the EIS implementation, a passive client expects to receive + * events from the EIS implementation. + */ +bool +eis_client_is_active(struct eis_client *client); + /** * See eis_client_property_set_with_permissions(), but the permissions are * left as-is. If the property does not exist, it is created with permissions @@ -842,6 +869,88 @@ eis_device_keyboard_send_xkb_modifiers(struct eis_device *device, uint32_t locked, uint32_t group); +/** see @ref ei_device_start_emulating */ +void +eis_device_start_emulating(struct eis_device *device); + +/** see @ref ei_device_stop_emulating */ +void +eis_device_stop_emulating(struct eis_device *device); + +/** see @ref ei_device_frame */ +void +eis_device_frame(struct eis_device *device); + +/** see @ref ei_device_pointer_motion */ +void +eis_device_pointer_motion(struct eis_device *device, double x, double y); + +/** see @ref ei_device_pointer_motion_absolute */ +void +eis_device_pointer_motion_absolute(struct eis_device *device, + double x, double y); + +/** see @ref ei_device_pointer_button */ +void +eis_device_pointer_button(struct eis_device *device, + uint32_t button, bool is_press); + +/** see @ref ei_device_pointer_scroll */ +void +eis_device_pointer_scroll(struct eis_device *device, double x, double y); + +/** see @ref ei_device_pointer_scroll_discrete */ +void +eis_device_pointer_scroll_discrete(struct eis_device *device, int32_t x, int32_t y); + +/** see @ref ei_device_pointer_scroll_stop */ +void +eis_device_pointer_scroll_stop(struct eis_device *device, bool stop_x, bool stop_y); + +/** see @ref ei_device_pointer_scroll_cancel */ +void +eis_device_pointer_scroll_cancel(struct eis_device *device, bool cancel_x, bool cancel_y); + +/** see @ref ei_device_keyboard_key */ +void +eis_device_keyboard_key(struct eis_device *device, uint32_t keycode, bool is_press); + +/** see @ref ei_device_touch_new */ +struct eis_touch * +eis_device_touch_new(struct eis_device *device); + +/** see @ref ei_touch_down */ +void +eis_touch_down(struct eis_touch *touch, double x, double y); + +/** see @ref ei_touch_motion */ +void +eis_touch_motion(struct eis_touch *touch, double x, double y); + +/** see @ref ei_touch_up */ +void +eis_touch_up(struct eis_touch *touch); + +/** see @ref ei_touch_ref */ +struct eis_touch * +eis_touch_ref(struct eis_touch *touch); + +/** see @ref ei_touch_unref */ +struct eis_touch * +eis_touch_unref(struct eis_touch *touch); + +/** see @ref ei_touch_set_user_data */ +void +eis_touch_set_user_data(struct eis_touch *touch, void *user_data); + +/** see @ref ei_touch_get_user_data */ +void * +eis_touch_get_user_data(struct eis_touch *touch); + +/** see @ref ei_touch_get_device */ +struct eis_device * +eis_touch_get_device(struct eis_touch *touch); + /** * For an event of type @ref EIS_EVENT_SEAT_BIND, return the capabilities * requested by the client. diff --git a/test/eierpecken.c b/test/eierpecken.c index f89926e..5391857 100644 --- a/test/eierpecken.c +++ b/test/eierpecken.c @@ -314,15 +314,17 @@ peck_log_handler(struct logger *logger, } struct peck * -peck_new(void) +peck_new_context(enum peck_ei_mode ei_mode) { struct peck *peck = peck_create(NULL); + assert(ei_mode == PECK_EI_PASSIVE || ei_mode == PECK_EI_ACTIVE); + int sv[2]; int rc = socketpair(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, sv); munit_assert_int(rc, ==, 0); - struct ei *ei = ei_new(peck); + struct ei *ei = ei_mode == PECK_EI_PASSIVE ? ei_new_passive(peck) : ei_new_active(peck); ei_set_user_data(ei, peck); ei_log_set_handler(ei, peck_ei_log_handler); ei_log_set_priority(ei, EI_LOG_PRIORITY_DEBUG); @@ -352,6 +354,12 @@ peck_new(void) return peck; } +struct peck * +peck_new(void) +{ + return peck_new_context(PECK_EI_ACTIVE); +} + void peck_enable_eis_behavior(struct peck *peck, enum peck_eis_behavior behavior) { @@ -1014,6 +1022,20 @@ peck_ei_event_type_name(enum ei_event_type type) CASE_STRING(DEVICE_RESUMED); CASE_STRING(KEYBOARD_MODIFIERS); CASE_STRING(PROPERTY); + CASE_STRING(FRAME); + CASE_STRING(DEVICE_START_EMULATING); + CASE_STRING(DEVICE_STOP_EMULATING); + CASE_STRING(POINTER_MOTION); + CASE_STRING(POINTER_MOTION_ABSOLUTE); + CASE_STRING(POINTER_BUTTON); + CASE_STRING(POINTER_SCROLL); + CASE_STRING(POINTER_SCROLL_STOP); + CASE_STRING(POINTER_SCROLL_CANCEL); + CASE_STRING(POINTER_SCROLL_DISCRETE); + CASE_STRING(KEYBOARD_KEY); + CASE_STRING(TOUCH_DOWN); + CASE_STRING(TOUCH_UP); + CASE_STRING(TOUCH_MOTION); } #undef CASE_STRING assert(!"Unhandled ei event type"); diff --git a/test/eierpecken.h b/test/eierpecken.h index e296a17..e7672dd 100644 --- a/test/eierpecken.h +++ b/test/eierpecken.h @@ -31,6 +31,11 @@ #include "util-mem.h" +enum peck_ei_mode { + PECK_EI_PASSIVE = 20, + PECK_EI_ACTIVE, +}; + /** * An enum to define basic server behavior in peck_dispatch_eis(). * Where a flag is **not** set for any specific behaviour, that event will @@ -134,6 +139,9 @@ struct peck; struct peck * peck_new(void); +struct peck * +peck_new_context(enum peck_ei_mode ei_mode); + void _peck_mark(struct peck *peck, const char *func, int line); /** Add debug marker to the log output */ #define peck_mark(peck_) _peck_mark(peck_, __func__, __LINE__) diff --git a/test/test-ei.c b/test/test-ei.c index ec5aa00..a646f17 100644 --- a/test/test-ei.c +++ b/test/test-ei.c @@ -860,6 +860,43 @@ MUNIT_TEST(test_ei_disconnect_after_unbind_after_received) return MUNIT_OK; } +MUNIT_TEST(test_client_is_active) +{ + _unref_(peck) *peck = peck_new_context(PECK_EI_ACTIVE); + + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_NONE); + peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_NONE); + peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTOCONNECT); + peck_dispatch_until_stable(peck); + + with_server(peck) { + _unref_(eis_event) *connect = peck_eis_next_event(eis, EIS_EVENT_CLIENT_CONNECT); + struct eis_client *client = eis_event_get_client(connect); + munit_assert_true(eis_client_is_active(client)); + } + + return MUNIT_OK; +} + +MUNIT_TEST(test_client_is_passive) +{ + _unref_(peck) *peck = peck_new_context(PECK_EI_PASSIVE); + + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_NONE); + peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_NONE); + peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTOCONNECT); + peck_dispatch_until_stable(peck); + + with_server(peck) { + _unref_(eis_event) *connect = peck_eis_next_event(eis, EIS_EVENT_CLIENT_CONNECT); + struct eis_client *client = eis_event_get_client(connect); + munit_assert_false(eis_client_is_active(client)); + } + + return MUNIT_OK; +} + + /* Emulates the XWayland behavior for calling * xdotool mousemove_relative -- -1 10 */