From f4624065b9715e474d2aaaf82b5613a0165ff148 Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Thu, 7 Apr 2022 10:46:48 +0900 Subject: [PATCH] Add ds_input_device, and ds_pointer. This patch is just the beginning of ds_input_device. Currently, a ds_input_device is created only on the wayland backend, and it only supports a ds_pointer. Other devices like keyboard and touch, and other backends like libinput will be supported in future patches. Change-Id: I9efce1bea5fd362d5bcee86d92a81076b0be48bc --- include/libds/backend.h | 4 + include/libds/input_device.h | 31 +++ include/libds/interfaces/backend.h | 1 + include/libds/interfaces/input_device.h | 40 ++++ include/libds/interfaces/pointer.h | 30 +++ include/libds/pointer.h | 38 +++ src/examples/meson.build | 9 + src/examples/pointer-test.c | 278 ++++++++++++++++++++++ src/libds/backend.c | 8 + src/libds/backend/wayland/backend.c | 27 ++- src/libds/backend/wayland/backend.h | 61 +++++ src/libds/backend/wayland/meson.build | 1 + src/libds/backend/wayland/output.c | 28 +++ src/libds/backend/wayland/seat.c | 406 ++++++++++++++++++++++++++++++++ src/libds/input_device.c | 69 ++++++ src/libds/meson.build | 2 + src/libds/pointer.c | 55 +++++ 17 files changed, 1083 insertions(+), 5 deletions(-) create mode 100644 include/libds/input_device.h create mode 100644 include/libds/interfaces/input_device.h create mode 100644 include/libds/interfaces/pointer.h create mode 100644 include/libds/pointer.h create mode 100644 src/examples/pointer-test.c create mode 100644 src/libds/backend/wayland/seat.c create mode 100644 src/libds/input_device.c create mode 100644 src/libds/pointer.c diff --git a/include/libds/backend.h b/include/libds/backend.h index 6b587d2..d6a266f 100644 --- a/include/libds/backend.h +++ b/include/libds/backend.h @@ -31,6 +31,10 @@ void ds_backend_add_new_output_listener(struct ds_backend *backend, struct wl_listener *listener); +void +ds_backend_add_new_input_listener(struct ds_backend *backend, + struct wl_listener *listener); + #ifdef __cplusplus } #endif diff --git a/include/libds/input_device.h b/include/libds/input_device.h new file mode 100644 index 0000000..45e125b --- /dev/null +++ b/include/libds/input_device.h @@ -0,0 +1,31 @@ +#ifndef LIBDS_INPUT_DEVICE_H +#define LIBDS_INPUT_DEVICE_H + +struct ds_input_device; + +struct ds_pointer; + +enum ds_button_state +{ + DS_BUTTON_RELEASED, + DS_BUTTON_PRESSED, +}; + +enum ds_input_device_type +{ + DS_INPUT_DEVICE_POINTER, + DS_INPUT_DEVICE_KEYBOARD, + DS_INPUT_DEVICE_TOUCH, +}; + +enum ds_input_device_type +ds_input_device_get_type(struct ds_input_device *dev); + +struct ds_pointer * +ds_input_device_get_pointer(struct ds_input_device *dev); + +void +ds_input_device_add_destroy_listener(struct ds_input_device *dev, + struct wl_listener *listener); + +#endif diff --git a/include/libds/interfaces/backend.h b/include/libds/interfaces/backend.h index e960ac4..bad37a0 100644 --- a/include/libds/interfaces/backend.h +++ b/include/libds/interfaces/backend.h @@ -22,6 +22,7 @@ struct ds_backend { struct wl_signal destroy; struct wl_signal new_output; + struct wl_signal new_input; } events; bool started; diff --git a/include/libds/interfaces/input_device.h b/include/libds/interfaces/input_device.h new file mode 100644 index 0000000..f31594b --- /dev/null +++ b/include/libds/interfaces/input_device.h @@ -0,0 +1,40 @@ +#ifndef LIBDS_INTERFACES_INPUT_DEVICE_H +#define LIBDS_INTERFACES_INPUT_DEVICE_H + +#include +#include +#include + +struct ds_input_device_interface +{ + void (*destroy)(struct ds_input_device *dev); +}; + +struct ds_input_device +{ + const struct ds_input_device_interface *iface; + + char *name; + double width_mm, height_mm; + char *output_name; + + enum ds_input_device_type type; + union { + void *_device; + struct ds_pointer *pointer; + }; + + struct { + struct wl_signal destroy; + } events; + + struct wl_list link; +}; + +void ds_input_device_init(struct ds_input_device *dev, + enum ds_input_device_type type, + const struct ds_input_device_interface *iface, + const char *name, int vendor, int product); +void ds_input_device_destroy(struct ds_input_device *dev); + +#endif diff --git a/include/libds/interfaces/pointer.h b/include/libds/interfaces/pointer.h new file mode 100644 index 0000000..84b0e23 --- /dev/null +++ b/include/libds/interfaces/pointer.h @@ -0,0 +1,30 @@ +#ifndef LIBDS_INTERFACES_POINTER_H +#define LIBDS_INTERFACES_POINTER_H + +#include + +struct ds_pointer; + +struct ds_pointer_interface +{ + void (*destroy)(struct ds_pointer *pointer); +}; + +struct ds_pointer +{ + const struct ds_pointer_interface *iface; + + struct { + struct wl_signal motion; + struct wl_signal motion_absolute; + struct wl_signal button; + struct wl_signal frame; + } events; +}; + +void ds_pointer_init(struct ds_pointer *pointer, + const struct ds_pointer_interface *iface); + +void ds_pointer_destroy(struct ds_pointer *pointer); + +#endif diff --git a/include/libds/pointer.h b/include/libds/pointer.h new file mode 100644 index 0000000..30bfaca --- /dev/null +++ b/include/libds/pointer.h @@ -0,0 +1,38 @@ +#ifndef LIBDS_POINTER_H +#define LIBDS_POINTER_H + +#include +#include +#include + +struct ds_pointer; + +struct ds_event_pointer_motion_absolute +{ + struct ds_input_device *device; + uint32_t time_msec; + // From 0..1 + double x, y; +}; + +struct ds_event_pointer_button +{ + struct ds_input_device *device; + uint32_t time_msec; + uint32_t button; + enum ds_button_state state; +}; + +void ds_pointer_add_motion_listener(struct ds_pointer *pointer, + struct wl_listener *listener); + +void ds_pointer_add_motion_absolute_listener(struct ds_pointer *pointer, + struct wl_listener *listener); + +void ds_pointer_add_button_listener(struct ds_pointer *pointer, + struct wl_listener *listener); + +void ds_pointer_add_frame_listener(struct ds_pointer *pointer, + struct wl_listener *listener); + +#endif diff --git a/src/examples/meson.build b/src/examples/meson.build index ddb0792..f19f139 100644 --- a/src/examples/meson.build +++ b/src/examples/meson.build @@ -23,6 +23,15 @@ executable('tinyds', install : true ) +executable('pointer-test', + [ + 'pointer-test.c', + 'pixman-helper.c', + ], + dependencies: common_deps, + install_dir: libds_bindir, + install : true) + if get_option('tizen') common_deps += dep_libds_tizen diff --git a/src/examples/pointer-test.c b/src/examples/pointer-test.c new file mode 100644 index 0000000..d669a03 --- /dev/null +++ b/src/examples/pointer-test.c @@ -0,0 +1,278 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "pixman-helper.h" + +#define WIDTH 700 +#define HEIGHT 400 + +struct pointer_device { + struct ds_pointer *ds_pointer; + + struct wl_listener destroy; + struct wl_listener motion_absolute; + struct wl_listener button; + struct wl_listener frame; +}; + +struct output +{ + struct server *server; + + struct ds_output *ds_output; + struct ds_allocator *allocator; + struct ds_swapchain *swapchain; + + struct ds_buffer *front_buffer; + + struct wl_listener destroy; +}; + +struct server +{ + struct wl_display *display; + + struct output output; + + struct ds_backend *backend; + + struct wl_listener backend_destroy; + struct wl_listener new_input; +}; + +struct server _server; + +static struct ds_backend *create_backend_auto(struct wl_display *display); +static void handle_backend_destroy(struct wl_listener *listener, void *data); +static void handle_new_input(struct wl_listener *listener, void *data); +static void output_init(struct output *output, struct server *sever); +static void output_draw(struct output *output); + +int +main(void) +{ + struct server *server = &_server; + + ds_log_init(DS_DBG, NULL); + + server->display = wl_display_create(); + assert(server->display); + + server->backend = create_backend_auto(server->display); + assert(server->backend); + + server->backend_destroy.notify = handle_backend_destroy; + ds_backend_add_destroy_listener(server->backend, &server->backend_destroy); + + server->new_input.notify = handle_new_input; + ds_backend_add_new_input_listener(server->backend, &server->new_input); + + output_init(&server->output, server); + + ds_backend_start(server->backend); + + output_draw(&server->output); + + wl_display_run(server->display); + + wl_display_destroy(server->display); + + return 0; +} + +static struct ds_backend * +create_backend_auto(struct wl_display *display) +{ + struct ds_backend *backend = NULL; + char name[512]; + int i; + + for (i = 0; i < 5; i++) { + snprintf(name, sizeof name, "wayland-%d", i); + backend = ds_wl_backend_create(display, name); + if (backend) + break; + } + + return backend; +} + +static void +handle_backend_destroy(struct wl_listener *listener, void *data) +{ + struct server *server; + + server = wl_container_of(listener, server, backend_destroy); + + wl_list_remove(&server->backend_destroy.link); + wl_list_remove(&server->new_input.link); +} + +static char * +device_type_to_string(enum ds_input_device_type type) +{ + switch (type) { + case DS_INPUT_DEVICE_POINTER: + return "pointer"; + break; + case DS_INPUT_DEVICE_KEYBOARD: + return "keyboard"; + break; + case DS_INPUT_DEVICE_TOUCH: + return "touch"; + break; + default: + return "Unknown"; + } +} + +static void +pointer_handle_device_destroy(struct wl_listener *listener, void *data) +{ + struct pointer_device *pointer; + + pointer = wl_container_of(listener, pointer, destroy); + + wl_list_remove(&pointer->destroy.link); + wl_list_remove(&pointer->motion_absolute.link); + wl_list_remove(&pointer->button.link); + wl_list_remove(&pointer->frame.link); + + free(pointer); +} + +static void +pointer_handle_motion_absolute(struct wl_listener *listener, void *data) +{ + struct ds_event_pointer_motion_absolute *event = data; + + ds_inf("Pointer device(%p): motion absolute (%f, %f) time(%d ms)", + event->device, event->x, event->y, event->time_msec); +} + +static void +pointer_handle_button(struct wl_listener *listener, void *data) +{ + struct ds_event_pointer_button *event = data; + + ds_inf("Pointer Device(%p): button(%d) state(%d) time(%d ms)", + event->device, event->button, event->state, event->time_msec); +} + +static void +pointer_handle_frame(struct wl_listener *listener, void *data) +{ + ds_inf("Pointer device(%p): frame", data); +} + +static void +add_pointer(struct ds_input_device *dev) +{ + struct pointer_device *pointer; + + pointer = calloc(1, sizeof *pointer); + if (!pointer) + return; + + pointer->ds_pointer = ds_input_device_get_pointer(dev); + + pointer->destroy.notify = pointer_handle_device_destroy; + ds_input_device_add_destroy_listener(dev, &pointer->destroy); + + pointer->motion_absolute.notify = pointer_handle_motion_absolute; + ds_pointer_add_motion_absolute_listener(pointer->ds_pointer, + &pointer->motion_absolute); + + pointer->button.notify = pointer_handle_button; + ds_pointer_add_button_listener(pointer->ds_pointer, + &pointer->button); + + pointer->frame.notify = pointer_handle_frame; + ds_pointer_add_frame_listener(pointer->ds_pointer, + &pointer->frame); +} + +static void +handle_new_input(struct wl_listener *listener, void *data) +{ + struct ds_input_device *dev = data; + enum ds_input_device_type type; + + type = ds_input_device_get_type(dev); + + if (type != DS_INPUT_DEVICE_POINTER) + return; + + ds_inf("New pointer device(%p) type(%s)", dev, + device_type_to_string(type)); + + add_pointer(dev); +} + +static void +output_handle_destroy(struct wl_listener *listener, void *data) +{ + struct output *output; + + output = wl_container_of(listener, output, destroy); + + wl_list_remove(&output->destroy.link); + + ds_swapchain_destroy(output->swapchain); + ds_allocator_destroy(output->allocator); +} + +static void +output_init(struct output *output, struct server *server) +{ + output->server = server; + + output->ds_output = ds_wl_backend_create_output(server->backend); + assert(output->ds_output); + + output->destroy.notify = output_handle_destroy; + ds_output_add_destroy_listener(output->ds_output, &output->destroy); + + output->allocator = ds_shm_allocator_create(); + assert(output->allocator); + + output->swapchain = ds_swapchain_create(output->allocator, + WIDTH, HEIGHT, DRM_FORMAT_XRGB8888); + assert(output->swapchain); +} + +static void +output_draw(struct output *output) +{ + struct ds_buffer *buffer; + pixman_image_t *img; + + ds_dbg("Redraw output"); + + buffer = ds_swapchain_acquire(output->swapchain, NULL); + assert(buffer); + + img = pixman_image_from_buffer(buffer, DS_BUFFER_DATA_PTR_ACCESS_WRITE); + assert(img); + + pixman_image_fill_color(img, 80, 80, 80); + + pixman_image_unref(img); + + ds_output_attach_buffer(output->ds_output, buffer); + ds_output_commit(output->ds_output); + + if (output->front_buffer) + ds_buffer_unlock(output->front_buffer); + + output->front_buffer = buffer; +} diff --git a/src/libds/backend.c b/src/libds/backend.c index ee2a912..641374a 100644 --- a/src/libds/backend.c +++ b/src/libds/backend.c @@ -41,6 +41,13 @@ ds_backend_add_new_output_listener(struct ds_backend *backend, wl_signal_add(&backend->events.new_output, listener); } +WL_EXPORT void +ds_backend_add_new_input_listener(struct ds_backend *backend, + struct wl_listener *listener) +{ + wl_signal_add(&backend->events.new_input, listener); +} + void ds_backend_init(struct ds_backend *backend, const struct ds_backend_interface *iface) @@ -48,6 +55,7 @@ ds_backend_init(struct ds_backend *backend, backend->iface = iface; wl_signal_init(&backend->events.destroy); wl_signal_init(&backend->events.new_output); + wl_signal_init(&backend->events.new_input); } void diff --git a/src/libds/backend/wayland/backend.c b/src/libds/backend/wayland/backend.c index 778ca1c..d956aa0 100644 --- a/src/libds/backend/wayland/backend.c +++ b/src/libds/backend/wayland/backend.c @@ -14,8 +14,8 @@ static const struct ds_backend_interface wl_backend_interface; static void wl_backend_handle_display_destroy(struct wl_listener *listener, void *data); -static bool wl_backend_server_init(struct ds_wl_backend_server *server, - const char *name); +static bool wl_backend_server_init(struct ds_wl_backend *wl_backend, + struct ds_wl_backend_server *server, const char *name); static void wl_backend_server_finish(struct ds_wl_backend_server *server); static int wl_backend_handle_dispatch_events(int fd, uint32_t mask, void *data); @@ -38,8 +38,9 @@ ds_wl_backend_create(struct wl_display *display, const char *server_name) wl_backend->display = display; wl_list_init(&wl_backend->buffers); wl_list_init(&wl_backend->outputs); + wl_list_init(&wl_backend->seats); - if (!wl_backend_server_init(&wl_backend->server, server_name)) { + if (!wl_backend_server_init(wl_backend, &wl_backend->server, server_name)) { ds_err("Failed to initialize Wayland Server"); goto err_server; } @@ -48,7 +49,7 @@ ds_wl_backend_create(struct wl_display *display, const char *server_name) fd = wl_display_get_fd(wl_backend->server.display); wl_backend->server_event_source = - wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, + wl_event_loop_add_fd(loop, fd, WL_EVENT_WRITABLE | WL_EVENT_READABLE, wl_backend_handle_dispatch_events, wl_backend); if (!wl_backend->server_event_source) { ds_err("Failed to create event source"); @@ -84,6 +85,7 @@ wl_backend_destroy(struct ds_wl_backend *backend) { struct ds_wl_output *output, *tmp_output; struct ds_wl_buffer *buffer, *tmp_buffer; + struct ds_wl_seat *seat, *tmp_seat; ds_dbg("Destroy wayland backend(%p)", backend); @@ -93,6 +95,9 @@ wl_backend_destroy(struct ds_wl_backend *backend) wl_list_for_each_safe(buffer, tmp_buffer, &backend->buffers, link) destroy_wl_buffer(buffer); + wl_list_for_each_safe(seat, tmp_seat, &backend->seats, link) + destroy_wl_seat(seat); + ds_backend_finish(&backend->base); wl_list_remove(&backend->display_destroy.link); @@ -159,6 +164,7 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *iface, uint32_t version) { struct ds_wl_backend_server *server = data; + struct ds_wl_seat *seat; ds_log(DS_DBG, "Wayland global: %s v%d", iface, version); @@ -176,6 +182,15 @@ registry_handle_global(void *data, struct wl_registry *registry, server->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); wl_shm_add_listener(server->shm, &shm_listener, server); } + else if (strcmp(iface, wl_seat_interface.name) == 0) { + seat = create_wl_seat(server->backend, name, version); + if (!seat) { + ds_err("Could not create ds_wl_seat"); + return; + } + + wl_list_insert(&server->backend->seats, &seat->link); + } } static void @@ -192,8 +207,10 @@ static const struct wl_registry_listener registry_listener = }; static bool -wl_backend_server_init(struct ds_wl_backend_server *server, const char *name) +wl_backend_server_init(struct ds_wl_backend *wl_backend, struct ds_wl_backend_server *server, const char *name) { + server->backend = wl_backend; + server->display = wl_display_connect(name); if (!server->display) { ds_log_errno(DS_ERR, "Could not connect to display: name \"%s\"", name); diff --git a/src/libds/backend/wayland/backend.h b/src/libds/backend/wayland/backend.h index ab37ec0..12e594a 100644 --- a/src/libds/backend/wayland/backend.h +++ b/src/libds/backend/wayland/backend.h @@ -3,9 +3,12 @@ #include "libds/interfaces/backend.h" #include "libds/interfaces/output.h" +#include "libds/interfaces/input_device.h" +#include "libds/interfaces/pointer.h" struct ds_wl_backend_server { + struct ds_wl_backend *backend; struct wl_display *display; struct wl_registry *registry; struct wl_compositor *compositor; @@ -23,6 +26,7 @@ struct ds_wl_backend struct wl_list outputs; // ds_wl_output.link struct wl_list buffers; // ds_wl_buffer.link + struct wl_list seats; // ds_wl_seat.link struct wl_event_source *server_event_source; struct ds_wl_backend_server server; @@ -50,6 +54,53 @@ struct ds_wl_output struct wl_callback *frame_callback; struct wl_list link; + + struct { + struct ds_wl_pointer *pointer; + struct wl_surface *surface; + int32_t hotspot_x, hotspot_y; + uint32_t enter_serial; + } cursor; +}; + +struct ds_wl_seat +{ + struct ds_wl_backend *backend; + struct ds_wl_output *output; + + struct wl_seat *wl_seat; + char *name; + + struct ds_input_device *pointer_dev; + struct ds_input_device *keyboard_dev; + struct ds_input_device *touch_dev; + + struct wl_callback *initial_info_cb; + + struct wl_list link; // ds_wl_backend.seats + + enum wl_seat_capability caps; + + int version; + uint32_t enter_serial; + + bool initialized; +}; + +struct ds_wl_input_device +{ + struct ds_input_device base; + + struct ds_wl_backend *backend; + struct ds_wl_seat *seat; +}; + +struct ds_wl_pointer +{ + struct ds_pointer base; + + struct ds_wl_input_device *input_device; + struct wl_pointer *wl_pointer; }; struct ds_wl_backend * @@ -58,4 +109,14 @@ wl_backend_from_backend(struct ds_backend *backend); void destroy_wl_buffer(struct ds_wl_buffer *buffer); +struct ds_wl_seat *create_wl_seat(struct ds_wl_backend *backend, uint32_t id, + uint32_t available_version); + +void destroy_wl_seat(struct ds_wl_seat *seat); + +void output_enter_pointer(struct ds_wl_output *output, + struct ds_wl_pointer *pointer, uint32_t serial); + +void output_leave_pointer(struct ds_wl_output *output); + #endif diff --git a/src/libds/backend/wayland/meson.build b/src/libds/backend/wayland/meson.build index db68fe9..88574a4 100644 --- a/src/libds/backend/wayland/meson.build +++ b/src/libds/backend/wayland/meson.build @@ -1,6 +1,7 @@ libds_files += files( 'backend.c', 'output.c', + 'seat.c', ) protocols = { diff --git a/src/libds/backend/wayland/output.c b/src/libds/backend/wayland/output.c index 1966a97..36ea5f7 100644 --- a/src/libds/backend/wayland/output.c +++ b/src/libds/backend/wayland/output.c @@ -13,6 +13,8 @@ const struct ds_output_interface wl_output_iface; static const struct xdg_surface_listener wl_output_xdg_surface_listener; static const struct xdg_toplevel_listener wl_output_xdg_toplevel_listener; +static void output_update_cursor(struct ds_wl_output *output); + struct ds_output * ds_wl_backend_create_output(struct ds_backend *ds_backend) { @@ -90,6 +92,32 @@ destroy_wl_buffer(struct ds_wl_buffer *buffer) free(buffer); } +void +output_enter_pointer(struct ds_wl_output *output, + struct ds_wl_pointer *pointer, uint32_t serial) +{ + output->cursor.pointer = pointer; + output->cursor.enter_serial = serial; + + output_update_cursor(output); +} + +void +output_leave_pointer(struct ds_wl_output *output) +{ + output->cursor.pointer = NULL; + output->cursor.enter_serial = 0; +} + +static void output_update_cursor(struct ds_wl_output *output) +{ + struct ds_wl_pointer *pointer = output->cursor.pointer; + + wl_pointer_set_cursor(pointer->wl_pointer, output->cursor.enter_serial, + output->cursor.surface, output->cursor.hotspot_x, + output->cursor.hotspot_y); +} + static struct ds_wl_output * wl_output_from_output(struct ds_output *ds_output) { diff --git a/src/libds/backend/wayland/seat.c b/src/libds/backend/wayland/seat.c new file mode 100644 index 0000000..b799e66 --- /dev/null +++ b/src/libds/backend/wayland/seat.c @@ -0,0 +1,406 @@ +#include +#include +#include +#include +#include + +#include "libds/log.h" +#include "libds/pointer.h" + +#include "backend.h" + +#ifdef MIN +# undef MIN +#endif + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +static const struct wl_seat_listener seat_listener; +static const struct wl_callback_listener seat_callback_listener; + +static void seat_update_capabilities(struct ds_wl_seat *seat, + enum wl_seat_capability caps); +static struct ds_input_device * +create_wl_input_device(struct ds_wl_seat *seat, + enum ds_input_device_type type); +static struct ds_pointer *create_wl_pointer(struct ds_wl_seat *seat); + +struct ds_wl_seat * +create_wl_seat(struct ds_wl_backend *backend, uint32_t id, + uint32_t available_version) +{ + struct ds_wl_seat *seat; + + seat = calloc(1, sizeof *seat); + if (!seat) + return NULL; + + seat->backend = backend; + seat->version = MIN(available_version, 5); + seat->wl_seat = wl_registry_bind(backend->server.registry, id, + &wl_seat_interface, seat->version); + + wl_seat_add_listener(seat->wl_seat, &seat_listener, seat); + + seat->initial_info_cb = wl_display_sync(backend->server.display); + wl_callback_add_listener(seat->initial_info_cb, &seat_callback_listener, + seat); + + ds_dbg("wl_backend: Seat(%p) created", seat); + + return seat; +} + +void +destroy_wl_seat(struct ds_wl_seat *seat) +{ + ds_dbg("wl_backend: Seat(%p) destroy", seat); + + if (seat->pointer_dev) + ds_input_device_destroy(seat->pointer_dev); + + if (seat->keyboard_dev) + ds_input_device_destroy(seat->keyboard_dev); + + if (seat->touch_dev) + ds_input_device_destroy(seat->touch_dev); + + if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION) + wl_seat_release(seat->wl_seat); + else + wl_seat_destroy(seat->wl_seat); + + wl_list_remove(&seat->link); + + free(seat->name); + free(seat); +} + +static void +seat_handle_capabilities(void *data, struct wl_seat *wl_seat, + enum wl_seat_capability caps) +{ + struct ds_wl_seat *seat = data; + + if (seat->initialized) + seat_update_capabilities(seat, caps); + else + seat->caps = caps; + + ds_dbg("wl_backend: Seat(%p) capabilities(%d)", seat, caps); +} + +static void +seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) +{ + struct ds_wl_seat *seat = data; + + if (seat->name) + free(seat->name); + + ds_dbg("wl_backend: Seat(%p) name(%s)", seat, name); + + seat->name = strdup(name); +} + +static const struct wl_seat_listener seat_listener = { + .capabilities = seat_handle_capabilities, + .name = seat_handle_name, +}; + +static void +seat_add_callback_handle_done(void *data, struct wl_callback *callback, + uint32_t callback_data) +{ + struct ds_wl_seat *seat = data; + + wl_callback_destroy(seat->initial_info_cb); + seat->initial_info_cb = NULL; + seat->initialized = true; + + seat_update_capabilities(seat, seat->caps); +} + +static const struct wl_callback_listener seat_callback_listener = { + .done = seat_add_callback_handle_done, +}; + +static void +seat_update_capabilities(struct ds_wl_seat *seat, enum wl_seat_capability caps) +{ + if ((caps & WL_SEAT_CAPABILITY_POINTER) && seat->pointer_dev == NULL) { + ds_dbg("wl_backend: Seat(%p) offered pointer", seat); + + seat->pointer_dev = create_wl_input_device(seat, + DS_INPUT_DEVICE_POINTER); + seat->pointer_dev->pointer = create_wl_pointer(seat); + + wl_signal_emit(&seat->backend->base.events.new_input, + seat->pointer_dev); + } + else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && + seat->pointer_dev != NULL) { + ds_dbg("wl_backend: Seat(%p) dropped pointer", seat); + ds_input_device_destroy(seat->pointer_dev); + seat->pointer_dev = NULL; + } + + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && seat->keyboard_dev == NULL) { + ds_dbg("wl_backend: Seat(%p) offered keyboard", seat); + + seat->keyboard_dev = create_wl_input_device(seat, + DS_INPUT_DEVICE_KEYBOARD); + } + else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && + seat->keyboard_dev != NULL) { + ds_dbg("wl_backend: Seat(%p) dropped keyboard", seat); + ds_input_device_destroy(seat->keyboard_dev); + seat->keyboard_dev = NULL; + } + + if ((caps & WL_SEAT_CAPABILITY_TOUCH) && seat->touch_dev == NULL) { + ds_dbg("wl_backend: Seat(%p) offered touch", seat); + seat->touch_dev = create_wl_input_device(seat, + DS_INPUT_DEVICE_TOUCH); + } + else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && + seat->touch_dev != NULL) { + ds_dbg("wl_backend: Seat(%p) dropped touch", seat); + ds_input_device_destroy(seat->touch_dev); + seat->touch_dev = NULL; + } +} + +static const struct ds_input_device_interface input_device_iface; + +static bool +ds_input_device_is_wl(struct ds_input_device *ds_dev) +{ + return ds_dev->iface == &input_device_iface; +} + +static struct ds_wl_input_device * +get_wl_input_device_from_input_device(struct ds_input_device *ds_dev) +{ + assert(ds_input_device_is_wl(ds_dev)); + return (struct ds_wl_input_device *)ds_dev; +} + +static void +input_device_iface_destroy(struct ds_input_device *ds_dev) +{ + struct ds_wl_input_device *dev; + + dev = get_wl_input_device_from_input_device(ds_dev); + + free(dev); +} + +static const struct ds_input_device_interface input_device_iface = +{ + .destroy = input_device_iface_destroy, +}; + +static struct ds_input_device * +create_wl_input_device(struct ds_wl_seat *seat, + enum ds_input_device_type type) +{ + struct ds_wl_input_device *dev; + unsigned int vendor = 0, product = 0; + size_t name_size; + char *name; + + dev = calloc(1, sizeof *dev); + if (!dev) + return NULL; + + dev->backend = seat->backend; + dev->seat = seat; + + name_size = 8 + strlen(seat->name) + 1; + name = alloca(name_size); + (void) snprintf(name, name_size, "wayland-%s", seat->name); + + ds_input_device_init(&dev->base, type, &input_device_iface, name, vendor, + product); + + return &dev->base; +} + +static const struct ds_pointer_interface pointer_iface; + +static struct ds_wl_pointer * +get_wl_pointer_from_pointer(struct ds_pointer *ds_pointer) +{ + assert(ds_pointer->iface == &pointer_iface); + return (struct ds_wl_pointer *)ds_pointer; +} + +static void +pointer_iface_destroy(struct ds_pointer *ds_pointer) +{ + struct ds_wl_pointer *pointer; + + pointer = get_wl_pointer_from_pointer(ds_pointer); + + wl_pointer_release(pointer->wl_pointer); + + free(pointer); +} + +static const struct ds_pointer_interface pointer_iface = { + .destroy = pointer_iface_destroy, +}; + +static void +pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface, + wl_fixed_t sx, wl_fixed_t sy) +{ + struct ds_wl_seat *seat = data; + struct ds_wl_pointer *pointer; + + if (!surface) + return; + + ds_dbg("Enter pointer"); + + seat->output = wl_surface_get_user_data(surface); + seat->enter_serial = serial; + + pointer = get_wl_pointer_from_pointer(seat->pointer_dev->pointer); + output_enter_pointer(seat->output, pointer, seat->enter_serial); +} + +static void +pointer_handle_leave(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface) +{ + struct ds_wl_seat *seat = data; + struct ds_wl_output *output; + + if (!seat->output) + return; + + output = wl_surface_get_user_data(surface); + if (seat->output != output) + return; + + ds_dbg("Leave pointer"); + + output_leave_pointer(seat->output); + + seat->output = NULL; + seat->enter_serial = 0; +} + +static void +pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, + uint32_t time, wl_fixed_t sx, wl_fixed_t sy) +{ + struct ds_wl_seat *seat = data; + + if (!seat->output) + return; + + // FIXME take size size of a output into account + struct ds_event_pointer_motion_absolute event = { + .device = seat->pointer_dev, + .time_msec = time, + .x = wl_fixed_to_double(sx) / 700, + .y = wl_fixed_to_double(sy) / 400, + }; + + wl_signal_emit(&seat->pointer_dev->pointer->events.motion_absolute, + &event); +} + +static void +pointer_handle_button(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time, uint32_t button, uint32_t state) +{ + struct ds_wl_seat *seat = data; + + if (!seat->output) + return; + + struct ds_event_pointer_button event = { + .device = seat->pointer_dev, + .button = button, + .state = state, + .time_msec = time, + }; + + wl_signal_emit(&seat->pointer_dev->pointer->events.button, &event); +} + +static void +pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value) +{ + // TODO +} + +static void +pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) +{ + struct ds_wl_seat *seat = data; + + if (!seat->output) + return; + + wl_signal_emit(&seat->pointer_dev->pointer->events.frame, + seat->pointer_dev); +} + +static void +pointer_handle_axis_source(void *data, struct wl_pointer *wl_pointer, + uint32_t axis_source) +{ + // TODO +} + +static void +pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis) +{ + // TODO +} + +static void +pointer_handle_axis_discrete(void *data, struct wl_pointer *wl_pointer, + uint32_t axis, int32_t discrete) +{ + // TODO +} + +static const struct wl_pointer_listener wl_pointer_listener = { + .enter = pointer_handle_enter, + .leave = pointer_handle_leave, + .motion = pointer_handle_motion, + .button = pointer_handle_button, + .axis = pointer_handle_axis, + .frame = pointer_handle_frame, + .axis_source = pointer_handle_axis_source, + .axis_stop = pointer_handle_axis_stop, + .axis_discrete = pointer_handle_axis_discrete, +}; + +static struct ds_pointer * +create_wl_pointer(struct ds_wl_seat *seat) +{ + struct ds_wl_pointer *pointer; + + pointer = calloc(1, sizeof *pointer); + if (!pointer) { + ds_err("Could not allocate memory"); + return NULL; + } + + ds_pointer_init(&pointer->base, &pointer_iface); + + pointer->wl_pointer = wl_seat_get_pointer(seat->wl_seat); + wl_pointer_add_listener(pointer->wl_pointer, &wl_pointer_listener, seat); + + return &pointer->base; +} diff --git a/src/libds/input_device.c b/src/libds/input_device.c new file mode 100644 index 0000000..db84100 --- /dev/null +++ b/src/libds/input_device.c @@ -0,0 +1,69 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include + +#include "libds/log.h" +#include "libds/interfaces/input_device.h" +#include "libds/interfaces/pointer.h" + +WL_EXPORT enum ds_input_device_type +ds_input_device_get_type(struct ds_input_device *dev) +{ + return dev->type; +} + +WL_EXPORT struct ds_pointer * +ds_input_device_get_pointer(struct ds_input_device *dev) +{ + if (dev->type != DS_INPUT_DEVICE_POINTER) { + ds_err("Given ds_input_device is not a pointer device"); + return NULL; + } + + return dev->pointer; +} + +WL_EXPORT void +ds_input_device_add_destroy_listener(struct ds_input_device *dev, + struct wl_listener *listener) +{ + wl_signal_add(&dev->events.destroy, listener); +} + +void +ds_input_device_init(struct ds_input_device *dev, + enum ds_input_device_type type, + const struct ds_input_device_interface *iface, + const char *name, int vendor, int product) +{ + dev->type = type; + dev->iface = iface; + dev->name = strdup(name); + + wl_signal_init(&dev->events.destroy); +} + +void +ds_input_device_destroy(struct ds_input_device *dev) +{ + wl_signal_emit(&dev->events.destroy, dev); + + if (dev->_device) { + switch (dev->type) { + case DS_INPUT_DEVICE_POINTER: + ds_pointer_destroy(dev->pointer); + break; + default: + ds_err("Warning: leaking memory %p %p %d", + dev->_device, dev, dev->type); + break; + } + } + + free(dev->name); + if (dev->iface && dev->iface->destroy) + dev->iface->destroy(dev); + else + free(dev); +} diff --git a/src/libds/meson.build b/src/libds/meson.build index 50f7962..a08dfdc 100644 --- a/src/libds/meson.build +++ b/src/libds/meson.build @@ -18,6 +18,8 @@ libds_files = [ 'xdg_shell/xdg_toplevel.c', 'pixel_format.c', 'backend.c', + 'input_device.c', + 'pointer.c', ] protocols = { diff --git a/src/libds/pointer.c b/src/libds/pointer.c new file mode 100644 index 0000000..5c5cbda --- /dev/null +++ b/src/libds/pointer.c @@ -0,0 +1,55 @@ +#include +#include +#include "libds/interfaces/pointer.h" + +void +ds_pointer_init(struct ds_pointer *pointer, + const struct ds_pointer_interface *iface) +{ + pointer->iface = iface; + + wl_signal_init(&pointer->events.motion); + wl_signal_init(&pointer->events.motion_absolute); + wl_signal_init(&pointer->events.button); + wl_signal_init(&pointer->events.frame); +} + +void +ds_pointer_destroy(struct ds_pointer *pointer) +{ + if (!pointer) + return; + + if (pointer->iface && pointer->iface->destroy) + pointer->iface->destroy(pointer); + else + free(pointer); +} + +WL_EXPORT void +ds_pointer_add_motion_listener(struct ds_pointer *pointer, + struct wl_listener *listener) +{ + wl_signal_add(&pointer->events.motion, listener); +} + +WL_EXPORT void +ds_pointer_add_motion_absolute_listener(struct ds_pointer *pointer, + struct wl_listener *listener) +{ + wl_signal_add(&pointer->events.motion_absolute, listener); +} + +WL_EXPORT void +ds_pointer_add_button_listener(struct ds_pointer *pointer, + struct wl_listener *listener) +{ + wl_signal_add(&pointer->events.button, listener); +} + +WL_EXPORT void +ds_pointer_add_frame_listener(struct ds_pointer *pointer, + struct wl_listener *listener) +{ + wl_signal_add(&pointer->events.frame, listener); +} -- 2.7.4