/* SPDX-License-Identifier: MIT */ /* * Copyright © 2020 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 #include #include #include #include #include "util-io.h" #include "util-macros.h" #include "util-object.h" #include "util-sources.h" #include "util-strings.h" #include "util-time.h" #include "util-version.h" #include "libei.h" #include "libei-private.h" #include "brei-shared.h" #include "ei-proto.h" _Static_assert(sizeof(enum ei_device_capability) == sizeof(int), "Invalid enum size"); _Static_assert(sizeof(enum ei_keymap_type) == sizeof(int), "Invalid enum size"); _Static_assert(sizeof(enum ei_event_type) == sizeof(int), "Invalid enum size"); _Static_assert(sizeof(enum ei_log_priority) == sizeof(int), "Invalid enum size"); static struct ei_seat * ei_find_seat(struct ei *ei, uint32_t seatid) { struct ei_seat *seat; list_for_each(seat, &ei->seats, link) { if (ei_seat_get_id(seat) == seatid) return seat; } return NULL; } static struct ei_device * ei_find_device(struct ei *ei, uint32_t deviceid) { struct ei_seat *seat; list_for_each(seat, &ei->seats, link) { struct ei_device *device = ei_seat_find_device(seat, deviceid); if (device) return device; } return NULL; } static void ei_destroy(struct ei *ei) { ei_disconnect(ei); struct ei_event *e; while ((e = ei_get_event(ei)) != NULL) ei_event_unref(e); if (ei->backend_interface.destroy) ei->backend_interface.destroy(ei, ei->backend); ei->backend = NULL; ei_connection_unref(ei->connection); sink_unref(ei->sink); free(ei->name); } static OBJECT_IMPLEMENT_CREATE(ei); _public_ OBJECT_IMPLEMENT_REF(ei); _public_ OBJECT_IMPLEMENT_UNREF_CLEANUP(ei); _public_ OBJECT_IMPLEMENT_SETTER(ei, user_data, void *); _public_ OBJECT_IMPLEMENT_GETTER(ei, user_data, void *); OBJECT_IMPLEMENT_GETTER(ei, connection, struct ei_connection *); DEFINE_UNREF_CLEANUP_FUNC(ei_device); DEFINE_UNREF_CLEANUP_FUNC(ei_region); struct ei * ei_get_context(struct ei *ei) { return ei; /* for the protocol bindings */ } static struct ei * ei_create_context(bool is_sender, void *user_data) { _unref_(ei) *ei = ei_create(NULL); ei->state = EI_STATE_NEW; list_init(&ei->event_queue); list_init(&ei->seats); list_init(&ei->proto_objects); ei->connection = ei_connection_new(ei); ei->next_object_id = 1; ei_log_set_handler(ei, NULL); ei_log_set_priority(ei, EI_LOG_PRIORITY_INFO); ei->sink = sink_new(); if (!ei->sink) return NULL; ei->user_data = user_data; ei->backend = NULL; ei->is_sender = is_sender; return steal(&ei); } uint32_t ei_get_new_id(struct ei *ei) { static const int server_range = 0xff000000; return ei->next_object_id++ & ~server_range; } void ei_register_object(struct ei *ei, struct brei_object *object) { log_debug(ei, "registering %s v%u object %#x", object->interface->name, object->version, object->id); list_append(&ei->proto_objects, &object->link); } void ei_unregister_object(struct ei *ei, struct brei_object *object) { log_debug(ei, "deregistering %s v%u object %#x", object->interface->name, object->version, object->id); list_remove(&object->link); } _public_ bool ei_is_sender(struct ei *ei) { return ei->is_sender; } _public_ struct ei * ei_new(void *user_data) { return ei_new_sender(user_data); } _public_ struct ei * ei_new_sender(void *user_data) { return ei_create_context(true, user_data); } _public_ struct ei * ei_new_receiver(void *user_data) { return ei_create_context(false, user_data); } _public_ int ei_get_fd(struct ei *ei) { return sink_get_fd(ei->sink); } _public_ void ei_dispatch(struct ei *ei) { sink_dispatch(ei->sink); } static void update_event_timestamp(struct ei_event *event, uint64_t time) { switch (event->type) { 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: if (event->timestamp != 0) { log_bug(ei_event_get_context(event), "Unexpected timestamp for event of type: %s", ei_event_type_to_string(event->type)); return; } event->timestamp = time; break; default: log_bug(ei_event_get_context(event), "Unexpected event %s in pending queue event", ei_event_type_to_string(event->type)); return; } } static void queue_event(struct ei *ei, struct ei_event *event) { struct ei_device *device = ei_event_get_device(event); struct list *queue = &ei->event_queue; const char *prefix = ""; switch (event->type) { 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: prefix = "pending "; queue = &device->pending_event_queue; break; case EI_EVENT_FRAME: /* silently discard empty frames */ if (list_empty(&device->pending_event_queue)) return; struct ei_event *pending; list_for_each_safe(pending, &device->pending_event_queue, link) { update_event_timestamp(pending, event->timestamp); list_remove(&pending->link); list_append(&ei->event_queue, &pending->link); } break; default: if (device && !list_empty(&device->pending_event_queue)) ei_queue_frame_event(device, ei_now(ei)); break; } log_debug(ei, "queuing %sevent type %s (%u)", prefix, ei_event_type_to_string(event->type), event->type); list_append(queue, &event->link); } static void insert_event(struct ei *ei, struct ei_event *event) { log_debug(ei, "inserting event type %s (%u)", ei_event_type_to_string(event->type), event->type); list_insert(&ei->event_queue, &event->link); } static void queue_connect_event(struct ei *ei) { struct ei_event *e = ei_event_new(ei); e->type = EI_EVENT_CONNECT; queue_event(ei, e); } static void queue_disconnect_event(struct ei *ei) { struct ei_event *e = ei_event_new(ei); e->type = EI_EVENT_DISCONNECT; queue_event(ei, e); } static void queue_seat_added_event(struct ei_seat *seat) { struct ei *ei= ei_seat_get_context(seat); struct ei_event *e = ei_event_new(ei); e->type = EI_EVENT_SEAT_ADDED; e->seat = ei_seat_ref(seat); queue_event(ei, e); } static void queue_seat_removed_event(struct ei_seat *seat) { struct ei *ei= ei_seat_get_context(seat); struct ei_event *e = ei_event_new(ei); e->type = EI_EVENT_SEAT_REMOVED; e->seat = ei_seat_ref(seat); queue_event(ei, e); } void ei_queue_seat_removed_event(struct ei_seat *seat) { queue_seat_removed_event(seat); } static void queue_device_added_event(struct ei_device *device) { struct ei *ei= ei_device_get_context(device); struct ei_event *e = ei_event_new_for_device(device); e->type = EI_EVENT_DEVICE_ADDED; queue_event(ei, e); } static void queue_device_removed_event(struct ei_device *device) { struct ei *ei= ei_device_get_context(device); struct ei_event *e = ei_event_new_for_device(device); e->type = EI_EVENT_DEVICE_REMOVED; queue_event(ei, e); } static void insert_device_removed_event(struct ei_device *device) { struct ei *ei= ei_device_get_context(device); struct ei_event *e = ei_event_new_for_device(device); e->type = EI_EVENT_DEVICE_REMOVED; insert_event(ei, e); } static void queue_paused_event(struct ei_device *device) { struct ei *ei= ei_device_get_context(device); struct ei_event *e = ei_event_new_for_device(device); e->type = EI_EVENT_DEVICE_PAUSED; queue_event(ei, e); } static void queue_resumed_event(struct ei_device *device) { struct ei *ei= ei_device_get_context(device); struct ei_event *e = ei_event_new_for_device(device); e->type = EI_EVENT_DEVICE_RESUMED; queue_event(ei, e); } static void queue_keyboard_modifiers_event(struct ei_device *device, const struct ei_xkb_modifiers *mods) { struct ei *ei= ei_device_get_context(device); struct ei_event *e = ei_event_new_for_device(device); e->type = EI_EVENT_KEYBOARD_MODIFIERS; e->modifiers = *mods; queue_event(ei, e); } void ei_queue_frame_event(struct ei_device *device, uint64_t time) { struct ei_event *e = ei_event_new_for_device(device); e->type = EI_EVENT_FRAME; e->timestamp = time; queue_event(ei_device_get_context(device), e); } void ei_queue_device_start_emulating_event(struct ei_device *device, uint32_t sequence) { struct ei_event *e = ei_event_new_for_device(device); e->type = EI_EVENT_DEVICE_START_EMULATING; e->start_emulating.sequence = sequence; 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) { if (ei->state == EI_STATE_DISCONNECTED || ei->state == EI_STATE_DISCONNECTING) return; enum ei_state state = ei->state; /* We need the disconnecting state to be re-entrant ei_device_remove() may call ei_disconnect() on a socket error */ ei->state = EI_STATE_DISCONNECTING; struct ei_seat *seat; list_for_each_safe(seat, &ei->seats, link) { ei_seat_remove(seat); } if (state != EI_STATE_NEW) { ei_connection_request_disconnect(ei->connection); } queue_disconnect_event(ei); ei->state = EI_STATE_DISCONNECTED; if (ei->source) source_remove(ei->source); ei->source = source_unref(ei->source); } static int handle_msg_seat(struct ei_connection *connection, uint32_t seat_id, uint32_t version) { struct ei *ei = ei_connection_get_context(connection); struct ei_seat *seat = ei_seat_new(ei, seat_id, version); /* seats list owns the ref */ list_append(&ei->seats, &seat->link); return 0; } static int handle_msg_device_added(struct ei_connection *connection, uint32_t deviceid, uint32_t capabilities, const char *name, uint32_t seatid, uint32_t type, uint32_t width, uint32_t height) { struct ei *ei = ei_connection_get_context(connection); struct ei_seat *seat = ei_find_seat(ei, seatid); if (!seat) { log_bug(ei, "Invalid seat id %#x for device %s (%#x)", seatid, name, deviceid); return 0; } /* Wrong device id or a device already removed by the client but we * won't know which unless we keep some device ID table. Not worth * it, so just silently ignore */ if (ei_seat_find_device(seat, deviceid)) { log_error(ei, "Server sent duplicate device id %#x", deviceid); return -EINVAL; } switch (type) { case EI_DEVICE_TYPE_PHYSICAL: case EI_DEVICE_TYPE_VIRTUAL: break; default: log_error(ei, "Server sent invalid device type %u", type); return -EINVAL; } _unref_(ei_device) *device = ei_device_new(seat, deviceid); ei_device_set_type(device, type); if (type == EI_DEVICE_TYPE_PHYSICAL) ei_device_set_size(device, width, height); ei_device_set_name(device, name); ei_device_set_capabilities(device, capabilities); ei_device_added(device); log_debug(ei, "Added device %#x '%s' caps: %s%s%s%s seat: %s", deviceid, name, ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) ? "p" : "", ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE) ? "a" : "", ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD) ? "k" : "", ei_device_has_capability(device, EI_DEVICE_CAP_TOUCH) ? "t" : "", ei_seat_get_name(seat)); return 0; } static int handle_msg_device_keymap(struct ei_connection *connection, uint32_t deviceid, enum ei_keymap_type keymap_type, uint32_t keymap_sz, int keymap_fd) { struct ei *ei = ei_connection_get_context(connection); log_debug(ei, "Adding keymap for %#x", deviceid); struct ei_device *device = ei_find_device(ei, deviceid); if (!device) return 0; ei_device_set_keymap(device, keymap_type, keymap_fd, keymap_sz); return 0; } void ei_queue_device_removed_event(struct ei_device *device) { queue_device_removed_event(device); } void ei_insert_device_removed_event(struct ei_device *device) { insert_device_removed_event(device); } static int handle_msg_device_added_done(struct ei_connection *connection, uint32_t deviceid) { struct ei *ei = ei_connection_get_context(connection); log_debug(ei, "Done with device %#x", deviceid); struct ei_device *device = ei_find_device(ei, deviceid); if (!device) return 0; queue_device_added_event(device); ei_device_done(device); return 0; } static int handle_msg_device_region(struct ei_connection *connection, uint32_t deviceid, uint32_t x, uint32_t y, uint32_t w, uint32_t h, float scale) { struct ei *ei = ei_connection_get_context(connection); log_debug(ei, "Adding device region for %#x", deviceid); struct ei_device *device = ei_find_device(ei, deviceid); if (!device) return 0; _unref_(ei_region) *r = ei_region_new(); ei_region_set_offset(r, x, y); ei_region_set_size(r, w, h); ei_region_set_physical_scale(r, scale); ei_device_add_region(device, r); return 0; } static int handle_msg_keyboard_modifiers(struct ei_connection *connection, uint32_t deviceid, uint32_t depressed, uint32_t locked, uint32_t latched, uint32_t group) { struct ei *ei = ei_connection_get_context(connection); log_debug(ei, "Setting modifiers for %#x", deviceid); struct ei_device *device = ei_find_device(ei, deviceid); if (!device) return 0; if (!ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD)) { log_bug(ei,"Modifier event for non-keyboard"); return -EPROTO; } struct ei_xkb_modifiers mods = { .depressed = depressed, .latched = latched, .locked = locked, .group = group, }; queue_keyboard_modifiers_event(device, &mods); return 0; } static int handle_msg_device_removed(struct ei_connection *connection, uint32_t deviceid) { struct ei *ei = ei_connection_get_context(connection); log_debug(ei, "Removed device %#x", deviceid); struct ei_device *device = ei_find_device(ei, deviceid); if (!device) return 0; ei_device_removed_by_server(device); return 0; } static int handle_msg_device_resumed(struct ei_connection *connection, uint32_t deviceid) { struct ei *ei = ei_connection_get_context(connection); log_debug(ei, "Resumed device %#x", deviceid); struct ei_device *device = ei_find_device(ei, deviceid); if (device) { ei_device_resumed(device); queue_resumed_event(device); } return 0; } static int handle_msg_device_paused(struct ei_connection *connection, uint32_t deviceid) { struct ei *ei = ei_connection_get_context(connection); log_debug(ei, "Paused device %#x", deviceid); struct ei_device *device = ei_find_device(ei, deviceid); if (device) { ei_device_paused(device); queue_paused_event(device); } return 0; } int ei_send_close_device(struct ei_device *device) { struct ei *ei = ei_device_get_context(device); if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED) return 0; int rc = ei_connection_request_close_device(ei->connection, device->id); if (rc) ei_disconnect(ei); return rc; } int ei_send_start_emulating(struct ei_device *device, uint32_t sequence) { struct ei *ei = ei_device_get_context(device); if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED) return 0; int rc = ei_connection_request_start_emulating(ei->connection, device->id, sequence); if (rc) ei_disconnect(ei); return rc; } int ei_send_stop_emulating(struct ei_device *device) { struct ei *ei = ei_device_get_context(device); if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED) return 0; int rc = ei_connection_request_stop_emulating(ei->connection, device->id); if (rc) ei_disconnect(ei); return rc; } int ei_send_frame(struct ei_device *device, uint64_t time) { struct ei *ei = ei_device_get_context(device); if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED) return 0; if (!device->send_frame_event) return 0; device->send_frame_event = false; int rc = ei_connection_request_frame(ei->connection, device->id, us2ms(time), time % 1000); if (rc) ei_disconnect(ei); return rc; } int ei_send_pointer_rel(struct ei_device *device, double x, double y) { struct ei *ei = ei_device_get_context(device); if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED) return 0; device->send_frame_event = true; int rc = ei_connection_request_pointer_relative(ei->connection, device->id, x, y); if (rc) ei_disconnect(ei); return rc; } int ei_send_pointer_abs(struct ei_device *device, double x, double y) { struct ei *ei = ei_device_get_context(device); if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED) return 0; device->send_frame_event = true; int rc = ei_connection_request_pointer_absolute(ei->connection, device->id, x, y); if (rc) ei_disconnect(ei); return rc; } int ei_send_pointer_button(struct ei_device *device, uint32_t button, bool is_press) { struct ei *ei = ei_device_get_context(device); if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED) return 0; device->send_frame_event = true; int rc = ei_connection_request_pointer_button(ei->connection, device->id, button, is_press); if (rc) ei_disconnect(ei); return rc; } int ei_send_pointer_scroll(struct ei_device *device, double x, double y) { struct ei *ei = ei_device_get_context(device); if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED) return 0; device->send_frame_event = true; int rc = ei_connection_request_pointer_scroll(ei->connection, device->id, x, y); if (rc) ei_disconnect(ei); return rc; } int ei_send_pointer_scroll_stop(struct ei_device *device, double x, double y) { struct ei *ei = ei_device_get_context(device); if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED) return 0; device->send_frame_event = true; int rc = ei_connection_request_pointer_scroll_stop(ei->connection, device->id, x, y, false); if (rc) ei_disconnect(ei); return rc; } int ei_send_pointer_scroll_cancel(struct ei_device *device, double x, double y) { struct ei *ei = ei_device_get_context(device); if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED) return 0; device->send_frame_event = true; int rc = ei_connection_request_pointer_scroll_stop(ei->connection, device->id, x, y, true); if (rc) ei_disconnect(ei); return rc; } int ei_send_pointer_scroll_discrete(struct ei_device *device, int32_t x, int32_t y) { struct ei *ei = ei_device_get_context(device); if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED) return 0; device->send_frame_event = true; int rc = ei_connection_request_pointer_scroll_discrete(ei->connection, device->id, x, y); if (rc) ei_disconnect(ei); return rc; } int ei_send_keyboard_key(struct ei_device *device, uint32_t key, bool is_press) { struct ei *ei = ei_device_get_context(device); if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED) return 0; device->send_frame_event = true; int rc = ei_connection_request_keyboard_key(ei->connection, device->id, key, is_press); if (rc) ei_disconnect(ei); return rc; } int ei_send_touch_down(struct ei_device *device, uint32_t tid, double x, double y) { struct ei *ei = ei_device_get_context(device); if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED) return 0; device->send_frame_event = true; int rc = ei_connection_request_touch_down(ei->connection, device->id, tid, x, y); if (rc) ei_disconnect(ei); return rc; } int ei_send_touch_motion(struct ei_device *device, uint32_t tid, double x, double y) { struct ei *ei = ei_device_get_context(device); if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED) return 0; device->send_frame_event = true; int rc = ei_connection_request_touch_motion(ei->connection, device->id, tid, x, y); if (rc) ei_disconnect(ei); return rc; } int ei_send_touch_up(struct ei_device *device, uint32_t tid) { struct ei *ei = ei_device_get_context(device); if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED) return 0; device->send_frame_event = true; int rc = ei_connection_request_touch_up(ei->connection, device->id, tid); if (rc) ei_disconnect(ei); return rc; } _public_ struct ei_event* ei_get_event(struct ei *ei) { if (list_empty(&ei->event_queue)) return NULL; struct ei_event *e = list_first_entry(&ei->event_queue, e, link); list_remove(&e->link); return e; } _public_ struct ei_event* ei_peek_event(struct ei *ei) { if (list_empty(&ei->event_queue)) return NULL; struct ei_event *e = list_first_entry(&ei->event_queue, e, link); return ei_event_ref(e); } static int handle_msg_disconnected(struct ei_connection *connection, uint32_t reason, const char *explanation) { struct ei *ei = ei_connection_get_context(connection); if (reason == EI_CONNECTION_DISCONNECT_REASON_DISCONNECTED) log_info(ei, "Disconnected by EIS"); else log_info(ei, "Disconnected after error: %s", explanation); return -ECANCELED; } #define DISCONNECT_IF_SENDER_CONTEXT(ei_) do {\ if (ei_->is_sender) { \ log_bug_client(ei_, "Invalid event from receiver EIS context. Disconnecting"); \ return -ECANCELED; \ } \ } while(0) static int handle_msg_start_emulating(struct ei_connection *connection, uint32_t deviceid, uint32_t sequence) { struct ei *ei = ei_connection_get_context(connection); DISCONNECT_IF_SENDER_CONTEXT(ei); struct ei_device *device = ei_find_device(ei, deviceid); if (device) ei_device_event_start_emulating(device, sequence); return 0; } static int handle_msg_stop_emulating(struct ei_connection *connection, uint32_t deviceid) { struct ei *ei = ei_connection_get_context(connection); DISCONNECT_IF_SENDER_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_connection *connection, uint32_t deviceid, uint32_t time, uint32_t micros) { struct ei *ei = ei_connection_get_context(connection); DISCONNECT_IF_SENDER_CONTEXT(ei); struct ei_device *device = ei_find_device(ei, deviceid); if (device) return ei_device_event_frame(device, ms2us(time) + micros); return 0; } static int handle_msg_pointer_rel(struct ei_connection *connection, uint32_t deviceid, float x, float y) { struct ei *ei = ei_connection_get_context(connection); DISCONNECT_IF_SENDER_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_connection *connection, uint32_t deviceid, float x, float y) { struct ei *ei = ei_connection_get_context(connection); DISCONNECT_IF_SENDER_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_connection *connection, uint32_t deviceid, uint32_t button, uint32_t state) { struct ei *ei = ei_connection_get_context(connection); DISCONNECT_IF_SENDER_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_connection *connection, uint32_t deviceid, float x, float y) { struct ei *ei = ei_connection_get_context(connection); DISCONNECT_IF_SENDER_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_connection *connection, uint32_t deviceid, int32_t x, int32_t y) { struct ei *ei = ei_connection_get_context(connection); DISCONNECT_IF_SENDER_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_connection *connection, uint32_t deviceid, uint32_t x, uint32_t y, uint32_t is_cancel) { struct ei *ei = ei_connection_get_context(connection); DISCONNECT_IF_SENDER_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_connection *connection, uint32_t deviceid, uint32_t key, uint32_t state) { struct ei *ei = ei_connection_get_context(connection); DISCONNECT_IF_SENDER_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_connection *connection, uint32_t deviceid, uint32_t touchid, float x, float y) { struct ei *ei = ei_connection_get_context(connection); DISCONNECT_IF_SENDER_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_connection *connection, uint32_t deviceid, uint32_t touchid, float x, float y) { struct ei *ei = ei_connection_get_context(connection); DISCONNECT_IF_SENDER_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_connection *connection, uint32_t deviceid, uint32_t touchid) { struct ei *ei = ei_connection_get_context(connection); DISCONNECT_IF_SENDER_CONTEXT(ei); struct ei_device *device = ei_find_device(ei, deviceid); if (device) return ei_device_event_touch_up(device, touchid); return -EINVAL; } static void connected(struct ei_connection *connection, void *user_data) { struct ei *ei = ei_connection_get_context(connection); /* FIXME: the connected event *should* mean that the server * has accepted us but we don't currently have any event * for this to hook onto. So let's just assume "connected" means * the server has seen our messages. */ ei->state = EI_STATE_CONNECTED; queue_connect_event(ei); } static int handle_msg_connection_setup(struct ei_connection *connection, uint32_t new_id, uint32_t version) { struct ei *ei = ei_connection_get_context(connection); struct ei_connection_setup *setup = ei_connection_setup_new(ei, new_id, version); if (version >= EI_CONNECTION_SETUP_REQUEST_TYPE_SINCE_VERSION) ei_connection_setup_request_type(setup, ei->is_sender ? EI_CONNECTION_SETUP_CONTEXT_TYPE_SENDER : EI_CONNECTION_SETUP_CONTEXT_TYPE_RECEIVER); if (version >= EI_CONNECTION_SETUP_REQUEST_NAME_SINCE_VERSION) ei_connection_setup_request_name(setup, ei->name); if (version >= EI_CONNECTION_SETUP_REQUEST_INTERFACE_SINCE_VERSION) { ei_connection_setup_request_interface(setup, "ei_connection", VERSION_V(1)); ei_connection_setup_request_interface(setup, "ei_callback", VERSION_V(1)); ei_connection_setup_request_interface(setup, "ei_seat", VERSION_V(1)); } ei_connection_setup_request_done(setup); ei_connection_setup_unref(setup); ei->state = EI_STATE_CONNECTING; ei_connection_sync(ei->connection, connected, NULL); return 0; } static const struct ei_connection_interface intf_state_backend = { .connection_setup = handle_msg_connection_setup, /* Everything triggers -EPROTO */ }; static const struct ei_connection_interface intf_state_connecting = { .connection_setup = handle_msg_connection_setup, .disconnected = handle_msg_disconnected, }; static const struct ei_connection_interface intf_state_connected = { .connection_setup = NULL, /* EPROTO */ .disconnected = handle_msg_disconnected, .seat = handle_msg_seat, .device_added = handle_msg_device_added, .device_removed = handle_msg_device_removed, .device_resumed = handle_msg_device_resumed, .device_paused = handle_msg_device_paused, .device_region = handle_msg_device_region, .device_keymap = handle_msg_device_keymap, .device_done = handle_msg_device_added_done, .keyboard_modifiers = handle_msg_keyboard_modifiers, /* events */ .start_emulating = handle_msg_start_emulating, .stop_emulating = handle_msg_stop_emulating, .pointer_relative = handle_msg_pointer_rel, .pointer_absolute = handle_msg_pointer_abs, .pointer_button = handle_msg_pointer_button, .pointer_scroll = handle_msg_pointer_scroll, .pointer_scroll_stop = handle_msg_pointer_scroll_stop, .pointer_scroll_discrete = handle_msg_pointer_scroll_discrete, .keyboard_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_connection_interface *interfaces[] = { [EI_STATE_NEW] = NULL, [EI_STATE_BACKEND] = &intf_state_backend, [EI_STATE_CONNECTING] = &intf_state_connecting, [EI_STATE_CONNECTED] = &intf_state_connected, [EI_STATE_DISCONNECTING] = NULL, [EI_STATE_DISCONNECTED] = NULL, }; const struct ei_connection_interface * ei_get_interface(struct ei *ei) { assert(ei->state < ARRAY_LENGTH(interfaces)); return interfaces[ei->state]; } static int lookup_object(uint32_t object_id, struct brei_object **object, void *userdata) { struct ei *ei = userdata; struct brei_object *obj; list_for_each(obj, &ei->proto_objects, link) { if (obj->id == object_id) { *object = obj; return 0; } } return -ENOENT; } static void connection_dispatch(struct source *source, void *userdata) { struct ei *ei = userdata; enum ei_state old_state = ei->state; int rc = brei_dispatch(source_get_fd(source), lookup_object, ei); if (rc < 0) { brei_drain_fd(source_get_fd(source)); ei_disconnect(ei); } static const char *states[] = { "NEW", "BACKEND", "CONNECTING", "CONNECTED", "DISCONNECTED", "DISCONNECTING", }; if (rc == -ECANCELED) log_info(ei, "Disconnected"); else if (rc) log_warn(ei, "Connection error: %s", strerror(-rc)); if (old_state != ei->state) log_debug(ei, "Connection dispatch: %s -> %s", states[old_state], states[ei->state]); } int ei_send_message(struct ei *ei, const struct brei_object *object, uint32_t opcode, const char *signature, size_t nargs, ...) { int fd = source_get_fd(ei->source); log_debug(ei, "sending: object %#x (%s@v%u:%s(%u)) signature '%s'", object->id, object->interface->name, object->interface->version, object->interface->requests[opcode].name, opcode, signature); va_list args; va_start(args, nargs); int rc = brei_send_message(fd, object->id, opcode, signature, nargs, args); va_end(args); return rc < 0 ? rc : 0; } int ei_set_socket(struct ei *ei, int fd) { struct source *source = source_new(fd, connection_dispatch, ei); int rc = sink_add_source(ei->sink, source); if (rc == 0) { ei->source = source_ref(source); ei->state = EI_STATE_BACKEND; /* The server SHOULD have already sent the connection setup, let's * process that. If not ready, it'll happen in the next dispatch. * * FIXME: this will block if O_NONBLOCK is missing */ ei_dispatch(ei); } source_unref(source); return rc < 0 ? rc : 0; } void ei_add_seat(struct ei_seat *seat) { queue_seat_added_event(seat); } _public_ void ei_configure_name(struct ei *ei, const char *name) { if (ei->state != EI_STATE_NEW) { log_bug_client(ei,"Client is already connected"); return; } if (strlen(name) > 1024) { log_bug_client(ei, "Client name too long"); return; } free(ei->name); ei->name = xstrdup(name); } _public_ uint64_t ei_now(struct ei *ei) { uint64_t ts = 0; int rc = now(&ts); if (rc < 0) { /* We should probably disconnect here but the chances of this * happening are so slim it's not worth worrying about. Plus, * if this fails we're likely to be inside eis_device_frame() * so we should flush a frame event before disconnecting and... */ log_error(ei, "clock_gettime failed: %s", strerror(-rc)); } return ts; } #ifdef _enable_tests_ #include "util-munit.h" MUNIT_TEST(test_init_unref) { struct ei *ei = ei_new(NULL); munit_assert_int(ei->state, ==, EI_STATE_NEW); munit_assert(list_empty(&ei->event_queue)); munit_assert(list_empty(&ei->seats)); munit_assert_not_null(ei->sink); struct ei *refd = ei_ref(ei); munit_assert_ptr_equal(ei, refd); munit_assert_int(ei->object.refcount, ==, 2); struct ei *unrefd = ei_unref(ei); munit_assert_null(unrefd); unrefd = ei_unref(ei); munit_assert_null(unrefd); return MUNIT_OK; } MUNIT_TEST(test_configure_name) { struct ei *ei = ei_new(NULL); ei_configure_name(ei, "foo"); munit_assert_string_equal(ei->name, "foo"); ei_configure_name(ei, "bar"); munit_assert_string_equal(ei->name, "bar"); /* ignore names that are too long */ char buf[1200] = {0}; memset(buf, 'a', sizeof(buf) - 1); ei_configure_name(ei, buf); munit_assert_string_equal(ei->name, "bar"); /* ignore names in all other states */ for (enum ei_state state = EI_STATE_NEW + 1; state <= EI_STATE_DISCONNECTED; state++) { ei->state = state; ei_configure_name(ei, "expect ignored"); munit_assert_string_equal(ei->name, "bar"); } ei_unref(ei); return MUNIT_OK; } #endif