From 7a6443887bc2bc35bc7f0e7187101a209e79c553 Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Wed, 20 Apr 2022 10:11:12 +0900 Subject: [PATCH] Add ds_seat ds_seat abstracts wl_seat protocol of server implementation. Change-Id: Ic3dcf2c9c374af762c2fbeccbe0f2174b9b1be3d --- include/libds/interfaces/keyboard.h | 8 - include/libds/keyboard.h | 8 + include/libds/seat.h | 117 ++++++++ src/libds/meson.build | 8 + src/libds/seat.h | 135 ++++++++++ src/libds/seat/seat.c | 390 +++++++++++++++++++++++++++ src/libds/seat/seat_keyboard.c | 383 +++++++++++++++++++++++++++ src/libds/seat/seat_pointer.c | 512 ++++++++++++++++++++++++++++++++++++ src/libds/seat/seat_private.h | 172 ++++++++++++ src/libds/seat/seat_touch.c | 432 ++++++++++++++++++++++++++++++ src/libds/surface.h | 3 + src/libds/surface/surface.c | 6 + 12 files changed, 2166 insertions(+), 8 deletions(-) create mode 100644 include/libds/seat.h create mode 100644 src/libds/seat.h create mode 100644 src/libds/seat/seat.c create mode 100644 src/libds/seat/seat_keyboard.c create mode 100644 src/libds/seat/seat_pointer.c create mode 100644 src/libds/seat/seat_private.h create mode 100644 src/libds/seat/seat_touch.c diff --git a/include/libds/interfaces/keyboard.h b/include/libds/interfaces/keyboard.h index 3602fe5..85a20a5 100644 --- a/include/libds/interfaces/keyboard.h +++ b/include/libds/interfaces/keyboard.h @@ -23,14 +23,6 @@ struct ds_keyboard_interface void (*destroy)(struct ds_keyboard *keyboard); }; -struct ds_keyboard_modifiers -{ - xkb_mod_mask_t depressed; - xkb_mod_mask_t latched; - xkb_mod_mask_t locked; - xkb_mod_mask_t group; -}; - struct ds_keyboard { const struct ds_keyboard_interface *iface; diff --git a/include/libds/keyboard.h b/include/libds/keyboard.h index 2a515e3..80165e6 100644 --- a/include/libds/keyboard.h +++ b/include/libds/keyboard.h @@ -22,6 +22,14 @@ enum ds_keyboard_modifier { DS_MODIFIER_MOD5 = 1 << 7, }; +struct ds_keyboard_modifiers +{ + xkb_mod_mask_t depressed; + xkb_mod_mask_t latched; + xkb_mod_mask_t locked; + xkb_mod_mask_t group; +}; + struct ds_event_keyboard_key { uint32_t time_msec; diff --git a/include/libds/seat.h b/include/libds/seat.h new file mode 100644 index 0000000..dbae0ea --- /dev/null +++ b/include/libds/seat.h @@ -0,0 +1,117 @@ +#ifndef LIBDS_SEAT_H +#define LIBDS_SEAT_H + +#include +#include + +#include +#include +#include + +struct ds_seat; + +enum ds_axis_orientation +{ + DS_AXIS_ORIENTATION_VERTICAL, + DS_AXIS_ORIENTATION_HORIZONTAL, +}; + +enum ds_axis_source +{ + DS_AXIS_SOURCE_WHEEL, + DS_AXIS_SOURCE_FINGER, + DS_AXIS_SOURCE_CONTINUOUS, + DS_AXIS_SOURCE_WHEEL_TILT, +}; + +struct ds_event_seat_pointer_focus_change +{ + struct ds_seat *seat; + struct ds_surface *old_surface, *new_surface; + double sx, sy; +}; + +struct ds_event_seat_keyboard_focus_change +{ + struct ds_seat *seat; + struct ds_surface *old_surface, *new_surface; +}; + +struct ds_seat *ds_seat_create(struct wl_display *display, const char *name); + +void ds_seat_destroy(struct ds_seat *seat); + +void ds_seat_set_capabilities(struct ds_seat *seat, + enum wl_seat_capability capabilities); + +void ds_seat_set_name(struct ds_seat *seat, const char *name); + +void ds_seat_add_destroy_listener(struct ds_seat *seat, + struct wl_listener *listener); + +void ds_seat_pointer_notify_enter(struct ds_seat *seat, + struct ds_surface *surface, double sx, double sy); + +void ds_seat_pointer_notify_clear_focus(struct ds_seat *seat); + +void ds_seat_pointer_notify_motion(struct ds_seat *seat, uint32_t time_msec, + double sx, double sy); + +uint32_t ds_seat_pointer_notify_button(struct ds_seat *seat, + uint32_t time_msec, uint32_t button, enum ds_button_state state); + +void ds_seat_pointer_notify_axis(struct ds_seat *seat, uint32_t time_msec, + enum ds_axis_orientation orientation, double value, + int32_t value_discrete, enum ds_axis_source source); + +void ds_seat_pointer_notify_frame(struct ds_seat *seat); + +void ds_seat_pointer_add_grab_begin_listener(struct ds_seat *seat, + struct wl_listener *listener); + +void ds_seat_pointer_add_grab_end_listener(struct ds_seat *seat, + struct wl_listener *listener); + +void ds_seat_pointer_add_focus_change_listener(struct ds_seat *seat, + struct wl_listener *listener); + +void ds_seat_keyboard_notify_enter(struct ds_seat *seat, + struct ds_surface *surface, uint32_t keycodes[], size_t num_keycodes, + struct ds_keyboard_modifiers *modifiers); + +void ds_seat_keyboard_notify_clear_focus(struct ds_seat *seat); + +void ds_seat_keyboard_notify_modifiers(struct ds_seat *seat, + struct ds_keyboard_modifiers *modifiers); + +void ds_seat_keyboard_notify_key(struct ds_seat *seat, uint32_t time_msec, + uint32_t key, uint32_t state); + +void ds_seat_keyboard_add_grab_begin_listener(struct ds_seat *seat, + struct wl_listener *listener); + +void ds_seat_keyboard_add_grab_end_listener(struct ds_seat *seat, + struct wl_listener *listener); + +void ds_seat_keyboard_add_focus_change_listener(struct ds_seat *seat, + struct wl_listener *listener); + +void ds_seat_touch_end_grab_start_listener(struct ds_seat *seat, + struct wl_listener *listener); + +uint32_t ds_seat_touch_notify_down(struct ds_seat *seat, + struct ds_surface *surface, uint32_t time_msec, int32_t touch_id, + double sx, double sy); + +void ds_seat_touch_notify_up(struct ds_seat *seat, uint32_t time_msec, + int32_t touch_id); + +void ds_seat_touch_notify_motion(struct ds_seat *seat, uint32_t time_msec, + int32_t touch_id, double sx, double sy); + +void ds_seat_touch_notify_frame(struct ds_seat *seat); + +void ds_seat_touch_add_grab_start_listener(struct ds_seat *seat, + struct wl_listener *listener); + +#endif diff --git a/src/libds/meson.build b/src/libds/meson.build index be07132..43bdeae 100644 --- a/src/libds/meson.build +++ b/src/libds/meson.build @@ -22,6 +22,10 @@ libds_files = [ 'pointer.c', 'keyboard.c', 'touch.c', + 'seat/seat.c', + 'seat/seat_pointer.c', + 'seat/seat_keyboard.c', + 'seat/seat_touch.c', ] protocols = { @@ -57,6 +61,10 @@ libdrm = dependency('libdrm', required: true) xkbcommon = dependency('xkbcommon', required: true) rt = meson.get_compiler('c').find_library('rt') +if wayland_server.version().version_compare('>= 1.19') + cdata.set('HAVE_WL_SEAT_ERROR_MISSING_CAPABILITY', '1') +endif + libds_deps = [ math, wayland_server, diff --git a/src/libds/seat.h b/src/libds/seat.h new file mode 100644 index 0000000..6295647 --- /dev/null +++ b/src/libds/seat.h @@ -0,0 +1,135 @@ +#ifndef DS_SEAT_H +#define DS_SEAT_H + +#include "libds/keyboard.h" +#include "libds/seat.h" + +struct ds_seat_pointer_grab; + +struct ds_pointer_grab_interface +{ + void (*enter)(struct ds_seat_pointer_grab *grab, + struct ds_surface *surface, double sx, double sy); + void (*clear_focus)(struct ds_seat_pointer_grab *grab); + void (*motion)(struct ds_seat_pointer_grab *grab, uint32_t time_msec, + double sx, double sy); + uint32_t (*button)(struct ds_seat_pointer_grab *grab, uint32_t time_msec, + uint32_t button, enum ds_button_state state); + void (*axis)(struct ds_seat_pointer_grab *grab, uint32_t time_msec, + enum ds_axis_orientation orientation, double value, + int32_t value_discrete, enum ds_axis_source source); + void (*frame)(struct ds_seat_pointer_grab *grab); + void (*cancel)(struct ds_seat_pointer_grab *grab); +}; + +struct ds_seat_keyboard_grab; + +struct ds_keyboard_grab_interface +{ + void (*enter)(struct ds_seat_keyboard_grab *grab, + struct ds_surface *surface, uint32_t keycodes[], + size_t num_keycodes, struct ds_keyboard_modifiers *modifiers); + void (*clear_focus)(struct ds_seat_keyboard_grab *grab); + void (*key)(struct ds_seat_keyboard_grab *grab, uint32_t time_msec, + uint32_t key, uint32_t state); + void (*modifiers)(struct ds_seat_keyboard_grab *grab, + struct ds_keyboard_modifiers *modifiers); + void (*cancel)(struct ds_seat_keyboard_grab *grab); +}; + +struct ds_touch_point; + +struct ds_seat_touch_grab; + +struct ds_touch_grab_interface +{ + uint32_t (*down)(struct ds_seat_touch_grab *grab, uint32_t time_msec, + struct ds_touch_point *point); + void (*up)(struct ds_seat_touch_grab *grab, uint32_t time_msec, + struct ds_touch_point *point); + void (*motion)(struct ds_seat_touch_grab *grab, uint32_t time_msec, + struct ds_touch_point *point); + void (*enter)(struct ds_seat_touch_grab *grab, uint32_t time_msec, + struct ds_touch_point *point); + void (*frame)(struct ds_seat_touch_grab *grab); + void (*cancel)(struct ds_seat_touch_grab *grab); +}; + +struct ds_seat_pointer_grab +{ + const struct ds_pointer_grab_interface *iface; + struct ds_seat *seat; + void *data; +}; + +struct ds_seat_keyboard_grab +{ + const struct ds_keyboard_grab_interface *iface; + struct ds_seat *seat; + void *data; +}; + +struct ds_seat_touch_grab +{ + const struct ds_touch_grab_interface *iface; + struct ds_seat *seat; + void *data; +}; + +void ds_seat_pointer_start_grab(struct ds_seat *seat, + struct ds_seat_pointer_grab *grab); + +void ds_seat_pointer_end_grab(struct ds_seat *seat); + +void ds_seat_pointer_enter(struct ds_seat *seat, struct ds_surface *surface, + double sx, double sy); + +void ds_seat_pointer_clear_focus(struct ds_seat *seat); + +void ds_seat_pointer_send_motion(struct ds_seat *seat, uint32_t time_msec, + double sx, double sy); + +uint32_t ds_seat_pointer_send_button(struct ds_seat *seat, uint32_t time_msec, + uint32_t button, enum ds_button_state state); + +void ds_seat_pointer_send_axis(struct ds_seat *seat, uint32_t time_msec, + enum ds_axis_orientation orientation, double value, + int32_t value_discrete, enum ds_axis_source source); + +void ds_seat_pointer_send_frame(struct ds_seat *seat); + +void ds_seat_keyboard_start_grab(struct ds_seat *seat, + struct ds_seat_keyboard_grab *grab); + +void ds_seat_keyboard_end_grab(struct ds_seat *seat); + +void ds_seat_keyboard_enter(struct ds_seat *seat, struct ds_surface *surface, + uint32_t keycodes[], size_t num_keycodes, + struct ds_keyboard_modifiers *modifiers); + +void ds_seat_keyboard_clear_focus(struct ds_seat *seat); + +void ds_seat_keyboard_send_key(struct ds_seat *seat, uint32_t time_msec, + uint32_t key, uint32_t state); + +void ds_seat_keyboard_send_modifiers(struct ds_seat *seat, + struct ds_keyboard_modifiers *modifiers); + +void ds_seat_touch_start_grab(struct ds_seat *seat, + struct ds_seat_touch_grab *grab); + +void ds_seat_touch_end_grab(struct ds_seat *seat); + +uint32_t ds_seat_touch_send_down(struct ds_seat *seat, + struct ds_surface *surface, uint32_t time_msec, int32_t touch_id, + double sx, double sy); + +void ds_seat_touch_send_up(struct ds_seat *seat, uint32_t time_msec, + int32_t touch_id); + +void ds_seat_touch_send_motion(struct ds_seat *seat, uint32_t time_msec, + int32_t touch_id, double sx, double sy); + +void ds_seat_touch_send_frame(struct ds_seat *seat); + +#endif diff --git a/src/libds/seat/seat.c b/src/libds/seat/seat.c new file mode 100644 index 0000000..542186e --- /dev/null +++ b/src/libds/seat/seat.c @@ -0,0 +1,390 @@ +#include "config.h" + +#define _POSIX_C_SOURCE 200809L +#include +#include +#include + +#include "libds/log.h" +#include "seat_private.h" + +#define SEAT_VERSION 7 + +static void seat_handle_bind(struct wl_client *wl_client, void *data, + uint32_t version, uint32_t id); +static void seat_handle_display_destroy(struct wl_listener *listener, + void *data); +static void seat_destroy(struct ds_seat *seat); +static struct ds_seat_client *seat_client_create(struct ds_seat *seat, + struct wl_client *wl_client); +static void seat_client_destroy(struct ds_seat_client *seat_client); +static void +seat_client_send_capabilities(struct ds_seat_client *seat_client); +static void seat_client_send_name(struct ds_seat_client *seat_client); + +WL_EXPORT struct ds_seat * +ds_seat_create(struct wl_display *display, const char *name) +{ + struct ds_seat *seat; + + seat = calloc(1, sizeof *seat); + if (!seat) + return NULL; + + if (!seat_pointer_init(seat)) { + ds_err("Failed to initialize pointer for seat(%s)", name); + goto err_ptr; + } + + if (!seat_keyboard_init(seat)) { + ds_err("Failed to initialize keyboard for seat(%s)", name); + goto err_kbd; + } + + if (!seat_touch_init(seat)) { + ds_err("Failed to initialize touch for seat(%s)", name); + goto err_touch; + } + + seat->global = wl_global_create(display, &wl_seat_interface, + SEAT_VERSION, seat, seat_handle_bind); + if (!seat->global) { + ds_err("Failed to create wl_global for seat(%s)", name); + goto err_global; + } + + seat->display = display; + seat->name = strdup(name); + + wl_list_init(&seat->clients); + + wl_signal_init(&seat->events.destroy); + wl_signal_init(&seat->events.pointer_grab_begin); + wl_signal_init(&seat->events.pointer_grab_end); + wl_signal_init(&seat->events.keyboard_grab_begin); + wl_signal_init(&seat->events.keyboard_grab_end); + wl_signal_init(&seat->events.touch_grab_begin); + wl_signal_init(&seat->events.touch_grab_end); + + seat->display_destroy.notify = seat_handle_display_destroy; + wl_display_add_destroy_listener(display, &seat->display_destroy); + + return seat; + +err_global: + seat_touch_finish(seat); +err_touch: + seat_keyboard_finish(seat); +err_kbd: + seat_pointer_finish(seat); +err_ptr: + free(seat); + + return NULL; +} + +WL_EXPORT void +ds_seat_destroy(struct ds_seat *seat) +{ + seat_destroy(seat); +} + +WL_EXPORT void +ds_seat_set_capabilities(struct ds_seat *seat, + enum wl_seat_capability capabilities) +{ + struct ds_seat_client *seat_client; + + if (capabilities == seat->capabilities) + return; + + seat->capabilities = capabilities; + seat->accumulated_capabilities |= capabilities; + + wl_list_for_each(seat_client, &seat->clients, link) { + if (!(capabilities & WL_SEAT_CAPABILITY_POINTER)) { + seat_client_remove_all_pointer_resources(seat_client); + } + if (!(capabilities & WL_SEAT_CAPABILITY_KEYBOARD)) { + seat_client_remove_all_keyboard_resources(seat_client); + } + if (!(capabilities & WL_SEAT_CAPABILITY_TOUCH)) { + seat_client_remove_all_touch_resources(seat_client); + } + + seat_client_send_capabilities(seat_client); + } +} + +WL_EXPORT void +ds_seat_set_name(struct ds_seat *seat, const char *name) +{ + struct ds_seat_client *seat_client; + + free(seat->name); + seat->name = strdup(name); + + wl_list_for_each(seat_client, &seat->clients, link) { + seat_client_send_name(seat_client); + } +} + +WL_EXPORT void +ds_seat_add_destroy_listener(struct ds_seat *seat, + struct wl_listener *listener) +{ + wl_signal_add(&seat->events.destroy, listener); +} + +struct ds_seat_client * +seat_client_for_wl_client(struct ds_seat *seat, struct wl_client *wl_client) +{ + struct ds_seat_client *seat_client; + + wl_list_for_each(seat_client, &seat->clients, link) { + if (seat_client->wl_client == wl_client) + return seat_client; + } + + return NULL; +} + +static struct ds_seat_client * +ds_seat_client_get_or_create(struct ds_seat *seat, struct wl_client *wl_client) +{ + struct ds_seat_client *seat_client; + + seat_client = seat_client_for_wl_client(seat, wl_client); + if (!seat_client) { + seat_client = seat_client_create(seat, wl_client); + if (!seat_client) + return NULL; + + wl_list_insert(&seat->clients, &seat_client->link); + } + + return seat_client; +} + +static void +seat_handle_get_pointer(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t id) +{ + struct ds_seat_client *seat_client; + + seat_client = wl_resource_get_user_data(resource); + if (!seat_client) + return; + + if (!(seat_client->seat->accumulated_capabilities & + WL_SEAT_CAPABILITY_POINTER)) { +#ifdef HAVE_WL_SEAT_ERROR_MISSING_CAPABILITY + wl_resource_post_error(resource, WL_SEAT_ERROR_MISSING_CAPABILITY, + "wl_seat.get_pointer called when no " + "pointer capability has existed"); +#endif + return; + } + + seat_client_add_pointer_resource(seat_client, + wl_resource_get_version(resource), id); +} + +static void +seat_handle_get_keyboard(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t id) +{ + struct ds_seat_client *seat_client; + + seat_client = wl_resource_get_user_data(resource); + if (!seat_client) + return; + + if (!(seat_client->seat->accumulated_capabilities & + WL_SEAT_CAPABILITY_POINTER)) { +#ifdef HAVE_WL_SEAT_ERROR_MISSING_CAPABILITY + wl_resource_post_error(resource, WL_SEAT_ERROR_MISSING_CAPABILITY, + "wl_seat.get_pointer called when no " + "keyboard capability has existed"); +#endif + return; + } + + seat_client_add_keyboard_resource(seat_client, + wl_resource_get_version(resource), id); +} + +static void +seat_handle_get_touch(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t id) +{ + struct ds_seat_client *seat_client; + + seat_client = wl_resource_get_user_data(resource); + if (!seat_client) + return; + + if (!(seat_client->seat->accumulated_capabilities & + WL_SEAT_CAPABILITY_POINTER)) { +#ifdef HAVE_WL_SEAT_ERROR_MISSING_CAPABILITY + wl_resource_post_error(resource, WL_SEAT_ERROR_MISSING_CAPABILITY, + "wl_seat.get_pointer called when no " + "touch capability has existed"); +#endif + return; + } + + seat_client_add_touch_resource(seat_client, + wl_resource_get_version(resource), id); +} + +static void +seat_handle_release(struct wl_client *wl_client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct wl_seat_interface seat_impl = +{ + .get_pointer = seat_handle_get_pointer, + .get_keyboard = seat_handle_get_keyboard, + .get_touch = seat_handle_get_touch, + .release = seat_handle_release, +}; + +static void +seat_client_handle_resource_destroy(struct wl_resource *resource) +{ + struct ds_seat_client *seat_client; + + seat_client = wl_resource_get_user_data(resource); + if (!seat_client) + return; + + wl_list_remove(wl_resource_get_link(resource)); + if (!wl_list_empty(&seat_client->resources)) + return; + + seat_client_destroy(seat_client); +} + +static void +seat_handle_bind(struct wl_client *wl_client, void *data, uint32_t version, + uint32_t id) +{ + struct ds_seat *seat = data; + struct ds_seat_client *seat_client; + struct wl_resource *resource; + + resource = wl_resource_create(wl_client, &wl_seat_interface, version, id); + if (!resource) { + wl_client_post_no_memory(wl_client); + return; + } + + seat_client = ds_seat_client_get_or_create(seat, wl_client); + if (!seat_client) { + wl_resource_destroy(resource); + wl_client_post_no_memory(wl_client); + return; + } + + wl_resource_set_implementation(resource, &seat_impl, + seat_client, seat_client_handle_resource_destroy); + + wl_list_insert(&seat_client->resources, wl_resource_get_link(resource)); + + wl_seat_send_capabilities(resource, seat->capabilities); + + if (version >= WL_SEAT_NAME_SINCE_VERSION) + wl_seat_send_name(resource, seat->name); +} + +static void +seat_handle_display_destroy(struct wl_listener *listener, void *data) +{ + struct ds_seat *seat; + + seat = wl_container_of(listener, seat, display_destroy); + seat_destroy(seat); +} + +static void +seat_destroy(struct ds_seat *seat) +{ + struct ds_seat_client *seat_client, *tmp; + struct wl_resource *resource, *next; + + wl_signal_emit(&seat->events.destroy, seat); + + wl_list_remove(&seat->display_destroy.link); + + wl_list_for_each_safe(seat_client, tmp, &seat->clients, link) { + wl_resource_for_each_safe(resource, next, &seat_client->resources) { + wl_list_remove(wl_resource_get_link(resource)); + wl_resource_set_user_data(resource, NULL); + } + seat_client_destroy(seat_client); + } + + seat_pointer_finish(seat); + seat_keyboard_finish(seat); + seat_touch_finish(seat); + + wl_global_destroy(seat->global); + free(seat->name); + free(seat); +} + +static struct ds_seat_client * +seat_client_create(struct ds_seat *seat, struct wl_client *wl_client) +{ + struct ds_seat_client *seat_client; + + seat_client = calloc(1, sizeof *seat_client); + seat_client->seat = seat; + seat_client->wl_client = wl_client; + + wl_list_init(&seat_client->resources); + wl_list_init(&seat_client->pointers); + wl_list_init(&seat_client->keyboards); + wl_list_init(&seat_client->touches); + + wl_signal_init(&seat_client->events.destroy); + + return seat_client; +} + +static void +seat_client_destroy(struct ds_seat_client *seat_client) +{ + wl_signal_emit(&seat_client->events.destroy, seat_client); + + seat_client_remove_all_pointer_resources(seat_client); + seat_client_remove_all_keyboard_resources(seat_client); + seat_client_remove_all_touch_resources(seat_client); + + wl_list_remove(&seat_client->link); + + free(seat_client); +} + +static void +seat_client_send_capabilities(struct ds_seat_client *seat_client) +{ + struct wl_resource *resource; + + wl_resource_for_each(resource, &seat_client->resources) { + wl_seat_send_capabilities(resource, seat_client->seat->capabilities); + } +} + +static void +seat_client_send_name(struct ds_seat_client *seat_client) +{ + struct wl_resource *resource; + + wl_resource_for_each(resource, &seat_client->resources) { + wl_seat_send_name(resource, seat_client->seat->name); + } +} diff --git a/src/libds/seat/seat_keyboard.c b/src/libds/seat/seat_keyboard.c new file mode 100644 index 0000000..381e2d6 --- /dev/null +++ b/src/libds/seat/seat_keyboard.c @@ -0,0 +1,383 @@ +#include +#include +#include +#include + +#include "libds/log.h" +#include "seat_private.h" + +static const struct ds_keyboard_grab_interface default_keyboard_grab_iface; +static const struct wl_keyboard_interface keyboard_impl; + +static void +seat_client_send_keyboard_leave_raw(struct ds_seat_client *seat_client, + struct ds_surface *surface); +static void +seat_keyboard_handle_surface_destroy(struct wl_listener *listener, + void *data); +static void keyboard_handle_resource_destroy(struct wl_resource *resource); + +WL_EXPORT void +ds_seat_keyboard_notify_enter(struct ds_seat *seat, + struct ds_surface *surface, uint32_t keycodes[], size_t num_keycodes, + struct ds_keyboard_modifiers *modifiers) +{ + struct ds_seat_keyboard_grab *grab = seat->keyboard.grab; + + grab->iface->enter(grab, surface, keycodes, num_keycodes, modifiers); +} + +WL_EXPORT void +ds_seat_keyboard_notify_clear_focus(struct ds_seat *seat) +{ + struct ds_seat_keyboard_grab *grab = seat->keyboard.grab; + + grab->iface->clear_focus(grab); +} + +WL_EXPORT void +ds_seat_keyboard_notify_modifiers(struct ds_seat *seat, + struct ds_keyboard_modifiers *modifiers) +{ + struct ds_seat_keyboard_grab *grab = seat->keyboard.grab; + + clock_gettime(CLOCK_MONOTONIC, &seat->last_event); + grab->iface->modifiers(grab, modifiers); +} + +WL_EXPORT void +ds_seat_keyboard_notify_key(struct ds_seat *seat, uint32_t time_msec, + uint32_t key, uint32_t state) +{ + struct ds_seat_keyboard_grab *grab = seat->keyboard.grab; + + clock_gettime(CLOCK_MONOTONIC, &seat->last_event); + grab->iface->key(grab, time_msec, key, state); +} + +WL_EXPORT void +ds_seat_keyboard_add_grab_begin_listener(struct ds_seat *seat, + struct wl_listener *listener) +{ + wl_signal_add(&seat->events.keyboard_grab_begin, listener); +} + +WL_EXPORT void +ds_seat_keyboard_add_grab_end_listener(struct ds_seat *seat, + struct wl_listener *listener) +{ + wl_signal_add(&seat->events.keyboard_grab_end, listener); +} + +WL_EXPORT void +ds_seat_keyboard_add_focus_change_listener(struct ds_seat *seat, + struct wl_listener *listener) +{ + wl_signal_add(&seat->keyboard.events.focus_change, listener); +} + +void +ds_seat_keyboard_start_grab(struct ds_seat *seat, + struct ds_seat_keyboard_grab *grab) +{ + grab->seat = seat; + seat->keyboard.grab = grab; + + wl_signal_emit(&seat->events.keyboard_grab_begin, grab); +} + +void +ds_seat_keyboard_end_grab(struct ds_seat *seat) +{ + struct ds_seat_keyboard *keyboard = &seat->keyboard; + struct ds_seat_keyboard_grab *grab = keyboard->grab; + + if (grab != keyboard->default_grab) { + keyboard->grab = keyboard->default_grab; + wl_signal_emit(&seat->events.keyboard_grab_end, grab); + if (grab->iface->cancel) + grab->iface->cancel(grab); + } +} + +void +ds_seat_keyboard_enter(struct ds_seat *seat, struct ds_surface *surface, + uint32_t keycodes[], size_t num_keycodes, + struct ds_keyboard_modifiers *modifiers) +{ + struct ds_seat_keyboard *keyboard = &seat->keyboard; + struct ds_seat_client *seat_client = NULL, *focused_client; + struct ds_surface *focused_surface; + struct wl_client *wl_client; + struct wl_array keys; + struct wl_resource *resource; + uint32_t *p; + uint32_t serial; + + if (keyboard->focused_surface == surface) + return; + + if (surface) { + wl_client = + wl_resource_get_client(ds_surface_get_wl_resource(surface)); + seat_client = seat_client_for_wl_client(seat, wl_client); + } + + focused_client = keyboard->focused_client; + focused_surface = keyboard->focused_surface; + + if (focused_client != NULL && focused_surface != NULL) + seat_client_send_keyboard_leave_raw(focused_client, focused_surface); + + if (seat_client) { + wl_array_init(&keys); + + for (size_t i = 0; i < num_keycodes; i++) { + p = wl_array_add(&keys, sizeof(uint32_t)); + if (!p) { + ds_err("Cannot allocate memory, skipping keycode: %" PRIu32 + "\n", keycodes[i]); + continue; + } + *p = keycodes[i]; + } + + serial = wl_display_next_serial(seat->display); + + wl_resource_for_each(resource, &seat_client->keyboards) { + wl_keyboard_send_enter(resource, serial, + ds_surface_get_wl_resource(surface), &keys); + } + wl_array_release(&keys); + } + + wl_list_remove(&keyboard->surface_destroy.link); + wl_list_init(&keyboard->surface_destroy.link); + + if (surface) { + keyboard->surface_destroy.notify = + seat_keyboard_handle_surface_destroy; + ds_surface_add_destroy_listener(surface, &keyboard->surface_destroy); + } + + keyboard->focused_client = seat_client; + keyboard->focused_surface = surface; + + if (seat_client) { + ds_seat_keyboard_send_modifiers(seat, modifiers); + + // TODO handle selection + } + + struct ds_event_seat_keyboard_focus_change event = { + .seat = seat, + .old_surface = focused_surface, + .new_surface = surface, + }; + wl_signal_emit(&keyboard->events.focus_change, &event); +} + +void +ds_seat_keyboard_clear_focus(struct ds_seat *seat) +{ + ds_seat_keyboard_enter(seat, NULL, NULL, 0, NULL); +} + +void +ds_seat_keyboard_send_key(struct ds_seat *seat, uint32_t time_msec, + uint32_t key, uint32_t state) +{ + struct ds_seat_client *seat_client; + struct wl_resource *resource; + uint32_t serial; + + seat_client = seat->keyboard.focused_client; + if (!seat_client) + return; + + serial = wl_display_next_serial(seat->display); + wl_resource_for_each(resource, &seat_client->keyboards) + wl_keyboard_send_key(resource, serial, time_msec, key, state); +} + +void +ds_seat_keyboard_send_modifiers(struct ds_seat *seat, + struct ds_keyboard_modifiers *modifiers) +{ + struct ds_seat_keyboard *keyboard = &seat->keyboard; + struct ds_seat_client *seat_client = keyboard->focused_client; + struct wl_resource *resource; + uint32_t serial; + + if (!seat_client) + return; + + serial = wl_display_next_serial(seat->display); + wl_resource_for_each(resource, &seat_client->keyboards) { + if (!modifiers) { + wl_keyboard_send_modifiers(resource, serial, 0, 0, 0, 0); + } + else { + wl_keyboard_send_modifiers(resource, serial, + modifiers->depressed, modifiers->latched, + modifiers->locked, modifiers->group); + } + } +} + +bool +seat_keyboard_init(struct ds_seat *seat) +{ + struct ds_seat_keyboard *keyboard = &seat->keyboard; + struct ds_seat_keyboard_grab *grab; + + grab = calloc(1, sizeof *grab); + if (!grab) + return false; + + grab->iface = &default_keyboard_grab_iface; + grab->seat = seat; + + keyboard->default_grab = grab; + keyboard->grab = grab; + keyboard->seat = seat; + + wl_list_init(&keyboard->surface_destroy.link); + + wl_signal_init(&keyboard->events.focus_change); + + return true; +} + +void seat_keyboard_finish(struct ds_seat *seat) +{ + struct ds_seat_keyboard *keyboard = &seat->keyboard; + + wl_list_remove(&keyboard->surface_destroy.link); + free(keyboard->default_grab); +} + +void +seat_client_add_keyboard_resource(struct ds_seat_client *seat_client, + uint32_t version, uint32_t id) +{ + struct wl_resource *resource; + + resource = wl_resource_create(seat_client->wl_client, + &wl_keyboard_interface, version, id); + if (!resource) { + wl_client_post_no_memory(seat_client->wl_client); + return; + } + + wl_resource_set_implementation(resource, &keyboard_impl, seat_client, + keyboard_handle_resource_destroy); + + wl_list_insert(&seat_client->keyboards, wl_resource_get_link(resource)); + + if (!(seat_client->seat->capabilities & WL_SEAT_CAPABILITY_KEYBOARD)) { + wl_resource_set_user_data(resource, NULL); + return; + } +} + +void +seat_client_remove_all_keyboard_resources(struct ds_seat_client *seat_client) +{ + struct wl_resource *resource, *tmp; + + wl_resource_for_each_safe(resource, tmp, &seat_client->keyboards) { + wl_list_remove(wl_resource_get_link(resource)); + wl_resource_set_user_data(resource, NULL); + } +} + +static void +keyboard_handle_release(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct wl_keyboard_interface keyboard_impl = +{ + .release = keyboard_handle_release, +}; + +static void +keyboard_handle_resource_destroy(struct wl_resource *resource) +{ + if (!wl_resource_get_user_data(resource)) + return; + + wl_list_remove(wl_resource_get_link(resource)); +} + +static void +default_keyboard_grab_iface_enter(struct ds_seat_keyboard_grab *grab, + struct ds_surface *surface, uint32_t keycodes[], + size_t num_keycodes, struct ds_keyboard_modifiers *modifiers) +{ + ds_seat_keyboard_enter(grab->seat, surface, keycodes, num_keycodes, + modifiers); +} + +static void +default_keyboard_grab_iface_clear_focus(struct ds_seat_keyboard_grab *grab) +{ + ds_seat_keyboard_clear_focus(grab->seat); +} + +static void +default_keyboard_grab_iface_key(struct ds_seat_keyboard_grab *grab, + uint32_t time_msec, uint32_t key, uint32_t state) +{ + ds_seat_keyboard_send_key(grab->seat, time_msec, key, state); +} + +static void +default_modifiers_grab_iface_key(struct ds_seat_keyboard_grab *grab, + struct ds_keyboard_modifiers *modifiers) +{ + ds_seat_keyboard_send_modifiers(grab->seat, modifiers); +} + +static void +default_cancel_grab_iface_key(struct ds_seat_keyboard_grab *grab) +{ + // cannot be cancelled +} + +static const struct ds_keyboard_grab_interface default_keyboard_grab_iface = { + .enter = default_keyboard_grab_iface_enter, + .clear_focus = default_keyboard_grab_iface_clear_focus, + .key = default_keyboard_grab_iface_key, + .modifiers = default_modifiers_grab_iface_key, + .cancel = default_cancel_grab_iface_key, +}; + +static void +seat_client_send_keyboard_leave_raw(struct ds_seat_client *seat_client, + struct ds_surface *surface) +{ + struct wl_resource *resource; + uint32_t serial; + + serial = wl_display_next_serial(seat_client->seat->display); + wl_resource_for_each(resource, &seat_client->keyboards) { + wl_keyboard_send_leave(resource, serial, + ds_surface_get_wl_resource(surface)); + } +} + +static void +seat_keyboard_handle_surface_destroy(struct wl_listener *listener, + void *data) +{ + struct ds_seat_keyboard *keyboard; + + keyboard = wl_container_of(listener, keyboard, surface_destroy); + + wl_list_remove(&keyboard->surface_destroy.link); + wl_list_init(&keyboard->surface_destroy.link); + ds_seat_keyboard_clear_focus(keyboard->seat); +} diff --git a/src/libds/seat/seat_pointer.c b/src/libds/seat/seat_pointer.c new file mode 100644 index 0000000..2616756 --- /dev/null +++ b/src/libds/seat/seat_pointer.c @@ -0,0 +1,512 @@ +#include +#include +#include +#include + +#include "seat_private.h" + +static const struct ds_pointer_grab_interface default_pointer_grab_iface; +static const struct wl_pointer_interface pointer_impl; + +static void seat_pointer_warp(struct ds_seat *seat, double sx, double sy); +static void +seat_client_send_pointer_leave_raw(struct ds_seat_client *seat_client, + struct ds_surface *surface); +static void +seat_pointer_handle_surface_destroy(struct wl_listener *listener, + void *data); +static void pointer_handle_resource_destroy(struct wl_resource *resource); +static void pointer_send_frame(struct wl_resource *resource); + +WL_EXPORT void +ds_seat_pointer_notify_enter(struct ds_seat *seat, struct ds_surface *surface, + double sx, double sy) +{ + struct ds_seat_pointer_grab *grab = seat->pointer.grab; + + assert(surface); + grab->iface->enter(grab, surface, sx, sy); +} + +WL_EXPORT void +ds_seat_pointer_notify_clear_focus(struct ds_seat *seat) +{ + struct ds_seat_pointer_grab *grab = seat->pointer.grab; + + grab->iface->clear_focus(grab); +} + +WL_EXPORT void +ds_seat_pointer_notify_motion(struct ds_seat *seat, uint32_t time_msec, + double sx, double sy) +{ + struct ds_seat_pointer_grab *grab = seat->pointer.grab; + + grab->iface->motion(grab, time_msec, sx, sy); +} + +WL_EXPORT uint32_t +ds_seat_pointer_notify_button(struct ds_seat *seat, uint32_t time_msec, + uint32_t button, enum ds_button_state state) +{ + struct ds_seat_pointer *pointer = &seat->pointer; + struct ds_seat_pointer_grab *grab = pointer->grab; + uint32_t serial; + + if (state == DS_BUTTON_PRESSED) { + if (pointer->button_count == 0) { + pointer->grab_button = button; + pointer->grab_time = time_msec; + } + // TODO need a set struct for assigning pointer->buttons + } + else { + // TODO + } + + serial = grab->iface->button(grab, time_msec, button, state); + if (serial && pointer->button_count == 1 && + state == DS_BUTTON_PRESSED) + pointer->grab_serial = serial; + + return serial; +} + +WL_EXPORT void +ds_seat_pointer_notify_axis(struct ds_seat *seat, uint32_t time_msec, + enum ds_axis_orientation orientation, double value, + int32_t value_discrete, enum ds_axis_source source) +{ + struct ds_seat_pointer_grab *grab = seat->pointer.grab; + + clock_gettime(CLOCK_MONOTONIC, &seat->last_event); + grab->iface->axis(grab, time_msec, orientation, value, value_discrete, source); +} + +WL_EXPORT void +ds_seat_pointer_notify_frame(struct ds_seat *seat) +{ + struct ds_seat_pointer_grab *grab = seat->pointer.grab; + + clock_gettime(CLOCK_MONOTONIC, &seat->last_event); + if (grab->iface->frame) + grab->iface->frame(grab); +} + +WL_EXPORT void +ds_seat_pointer_add_grab_begin_listener(struct ds_seat *seat, + struct wl_listener *listener) +{ + wl_signal_add(&seat->events.pointer_grab_begin, listener); +} + +WL_EXPORT void +ds_seat_pointer_add_grab_end_listener(struct ds_seat *seat, + struct wl_listener *listener) +{ + wl_signal_add(&seat->events.pointer_grab_end, listener); +} + +WL_EXPORT void +ds_seat_pointer_add_focus_change_listener(struct ds_seat *seat, + struct wl_listener *listener) +{ + wl_signal_add(&seat->pointer.events.focus_change, listener); +} + +void +ds_seat_pointer_start_grab(struct ds_seat *seat, + struct ds_seat_pointer_grab *grab) +{ + grab->seat = seat; + seat->pointer.grab = grab; + wl_signal_emit(&seat->events.pointer_grab_begin, grab); +} + +void +ds_seat_pointer_end_grab(struct ds_seat *seat) +{ + struct ds_seat_pointer *pointer = &seat->pointer; + struct ds_seat_pointer_grab *grab = pointer->grab; + + if (grab != pointer->default_grab) { + pointer->grab = pointer->default_grab; + wl_signal_emit(&seat->events.pointer_grab_end, grab); + if (grab->iface->cancel) + grab->iface->cancel(grab); + } +} + +void +ds_seat_pointer_enter(struct ds_seat *seat, struct ds_surface *surface, + double sx, double sy) +{ + struct ds_seat_pointer *pointer = &seat->pointer; + struct ds_seat_client *seat_client = NULL, *focused_client; + struct ds_surface *focused_surface; + struct wl_client *wl_client; + struct wl_resource *resource; + uint32_t serial; + + if (pointer->focused_surface == surface) { + // this surface already got an enter notify + return; + } + + focused_client = pointer->focused_client; + focused_surface = pointer->focused_surface; + + if (focused_client != NULL && focused_surface != NULL) + seat_client_send_pointer_leave_raw(focused_client, focused_surface); + + if (surface) { + wl_client = + wl_resource_get_client(ds_surface_get_wl_resource(surface)); + seat_client = seat_client_for_wl_client(seat, wl_client); + } + + if (seat_client) { + serial = wl_display_next_serial(seat->display); + wl_resource_for_each(resource, &seat_client->pointers) { + wl_pointer_send_enter(resource, serial, + ds_surface_get_wl_resource(surface), + wl_fixed_from_double(sx), wl_fixed_from_double(sy)); + pointer_send_frame(resource); + } + } + + wl_list_remove(&pointer->surface_destroy.link); + wl_list_init(&pointer->surface_destroy.link); + + if (surface) { + pointer->surface_destroy.notify = + seat_pointer_handle_surface_destroy; + ds_surface_add_destroy_listener(surface, &pointer->surface_destroy); + } + + pointer->focused_client = seat_client; + pointer->focused_surface = surface; + if (surface) + seat_pointer_warp(seat, sx, sy); + else + seat_pointer_warp(seat, NAN, NAN); + + struct ds_event_seat_pointer_focus_change event = { + .seat = seat, + .new_surface = surface, + .old_surface = focused_surface, + .sx = sx, + .sy = sy, + }; + wl_signal_emit(&pointer->events.focus_change, &event); +} + +void +ds_seat_pointer_clear_focus(struct ds_seat *seat) +{ + ds_seat_pointer_enter(seat, NULL, 0, 0); +} + +void +ds_seat_pointer_send_motion(struct ds_seat *seat, uint32_t time_msec, + double sx, double sy) +{ + struct ds_seat_pointer *pointer = &seat->pointer; + struct ds_seat_client *seat_client = pointer->focused_client; + struct wl_resource *resource; + wl_fixed_t sx_fixed, sy_fixed; + + if (!seat_client) + return; + + sx_fixed = wl_fixed_from_double(sx); + sy_fixed = wl_fixed_from_double(sy); + if (wl_fixed_from_double(pointer->sx) != sx_fixed || + wl_fixed_from_double(pointer->sy) != sy_fixed) { + wl_resource_for_each(resource, &seat_client->pointers) + wl_pointer_send_motion(resource, time_msec, sx_fixed, sy_fixed); + } + + seat_pointer_warp(seat, sx, sy); +} + +uint32_t +ds_seat_pointer_send_button(struct ds_seat *seat, uint32_t time_msec, + uint32_t button, enum ds_button_state state) +{ + struct ds_seat_client *seat_client = seat->pointer.focused_client; + struct wl_resource *resource; + uint32_t serial; + + if (!seat_client) + return 0; + + serial = wl_display_next_serial(seat->display); + wl_resource_for_each(resource, &seat_client->pointers) + wl_pointer_send_button(resource, serial, time_msec, button, state); + + return serial; +} + +void +ds_seat_pointer_send_axis(struct ds_seat *seat, uint32_t time_msec, + enum ds_axis_orientation orientation, double value, + int32_t value_discrete, enum ds_axis_source source) +{ + struct ds_seat_pointer *pointer = &seat->pointer; + struct ds_seat_client *seat_client = pointer->focused_client; + struct wl_resource *resource; + uint32_t version; + bool send_source = false; + + if (!seat_client) + return; + + if (pointer->sent_axis_source) { + assert(pointer->cached_axis_source == source); + } + else { + pointer->sent_axis_source = true; + pointer->cached_axis_source = source; + send_source = true; + } + + wl_resource_for_each(resource, &seat_client->pointers) { + version = wl_resource_get_version(resource); + + if (send_source && version >= WL_POINTER_AXIS_SOURCE_SINCE_VERSION) + wl_pointer_send_axis_source(resource, source); + + if (value) { + if (value_discrete && + version >= WL_POINTER_AXIS_DISCRETE_SINCE_VERSION) { + wl_pointer_send_axis_discrete(resource, orientation, + value_discrete); + } + + wl_pointer_send_axis(resource, time_msec, orientation, + wl_fixed_from_double(value)); + } + else if (version >= WL_POINTER_AXIS_STOP_SINCE_VERSION) { + wl_pointer_send_axis_stop(resource, time_msec, orientation); + } + } +} + +void +ds_seat_pointer_send_frame(struct ds_seat *seat) +{ + struct ds_seat_pointer *pointer = &seat->pointer; + struct ds_seat_client *seat_client = pointer->focused_client; + struct wl_resource *resource; + + if (!seat_client) + return; + + pointer->sent_axis_source = false; + + wl_resource_for_each(resource, &seat_client->pointers) + pointer_send_frame(resource); +} + +bool +seat_pointer_init(struct ds_seat *seat) +{ + struct ds_seat_pointer *pointer = &seat->pointer; + struct ds_seat_pointer_grab *grab; + + grab = calloc(1, sizeof *grab); + if (!grab) + return false; + + grab->iface = &default_pointer_grab_iface; + grab->seat = seat; + + pointer->default_grab = grab; + pointer->grab = grab; + pointer->seat = seat; + + wl_list_init(&pointer->surface_destroy.link); + + wl_signal_init(&pointer->events.focus_change); + + return true; +} + +void +seat_pointer_finish(struct ds_seat *seat) +{ + struct ds_seat_pointer *pointer = &seat->pointer; + + wl_list_remove(&pointer->surface_destroy.link); + free(pointer->default_grab); +} + +void +seat_client_add_pointer_resource(struct ds_seat_client *seat_client, + uint32_t version, uint32_t id) +{ + struct wl_resource *resource; + + resource = wl_resource_create(seat_client->wl_client, + &wl_pointer_interface, version, id); + if (!resource) { + wl_client_post_no_memory(seat_client->wl_client); + return; + } + + wl_resource_set_implementation(resource, &pointer_impl, seat_client, + &pointer_handle_resource_destroy); + + if (!(seat_client->seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) { + wl_resource_set_user_data(resource, NULL); + return; + } + + wl_list_insert(&seat_client->pointers, wl_resource_get_link(resource)); +} + +void +seat_client_remove_all_pointer_resources(struct ds_seat_client *seat_client) +{ + struct wl_resource *resource, *tmp; + + wl_resource_for_each_safe(resource, tmp, &seat_client->pointers) { + wl_list_remove(wl_resource_get_link(resource)); + wl_resource_set_user_data(resource, NULL); + } +} + +static void +seat_pointer_warp(struct ds_seat *seat, double sx, double sy) +{ + seat->pointer.sx = sx; + seat->pointer.sy = sy; +} + +static void +seat_client_send_pointer_leave_raw(struct ds_seat_client *seat_client, + struct ds_surface *surface) +{ + struct wl_resource *resource; + uint32_t serial; + + serial = wl_display_next_serial(seat_client->seat->display); + wl_resource_for_each(resource, &seat_client->pointers) { + wl_pointer_send_leave(resource, serial, + ds_surface_get_wl_resource(surface)); + pointer_send_frame(resource); + } +} + +static void +seat_pointer_handle_surface_destroy(struct wl_listener *listener, void *data) +{ + struct ds_seat_pointer *pointer; + + pointer = wl_container_of(listener, pointer, surface_destroy); + wl_list_remove(&pointer->surface_destroy.link); + wl_list_init(&pointer->surface_destroy.link); + ds_seat_pointer_clear_focus(pointer->seat); +} + +static void +default_pointer_grab_iface_enter(struct ds_seat_pointer_grab *grab, + struct ds_surface *surface, double sx, double sy) +{ + ds_seat_pointer_enter(grab->seat, surface, sx, sy); +} + +static void +default_pointer_grab_iface_clear_focus(struct ds_seat_pointer_grab *grab) +{ + ds_seat_pointer_clear_focus(grab->seat); +} + +static void +default_pointer_grab_iface_motion(struct ds_seat_pointer_grab *grab, + uint32_t time_msec, double sx, double sy) +{ + ds_seat_pointer_send_motion(grab->seat, time_msec, sx, sy); +} + +static uint32_t +default_pointer_grab_iface_button(struct ds_seat_pointer_grab *grab, + uint32_t time_msec, uint32_t button, enum ds_button_state state) +{ + return ds_seat_pointer_send_button(grab->seat, time_msec, button, state); +} + +static void +default_pointer_grab_iface_axis(struct ds_seat_pointer_grab *grab, + uint32_t time_msec, enum ds_axis_orientation orientation, double value, + int32_t value_discrete, enum ds_axis_source source) +{ + ds_seat_pointer_send_axis(grab->seat, time_msec, orientation, value, + value_discrete, source); +} + +static void +default_pointer_grab_iface_frame(struct ds_seat_pointer_grab *grab) +{ + ds_seat_pointer_send_frame(grab->seat); +} + +static void +default_pointer_grab_iface_cancel(struct ds_seat_pointer_grab *grab) +{ + // cannot be cancelled +} + +static const struct ds_pointer_grab_interface default_pointer_grab_iface = { + .enter = default_pointer_grab_iface_enter, + .clear_focus = default_pointer_grab_iface_clear_focus, + .motion = default_pointer_grab_iface_motion, + .button = default_pointer_grab_iface_button, + .axis = default_pointer_grab_iface_axis, + .frame = default_pointer_grab_iface_frame, + .cancel = default_pointer_grab_iface_cancel, +}; + +static void +pointer_handle_set_cursor(struct wl_client *client, + struct wl_resource *pointer_resource, uint32_t serial, + struct wl_resource *surface_resource, + int32_t hotspot_x, int32_t hotspot_y) +{ + struct ds_seat_client *seat_client; + + seat_client = wl_resource_get_user_data(pointer_resource); + if (!seat_client) + return; + + // TODO +} + +static void +pointer_handle_release(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct wl_pointer_interface pointer_impl = +{ + .set_cursor = pointer_handle_set_cursor, + .release = pointer_handle_release, +}; + +static void pointer_handle_resource_destroy(struct wl_resource *resource) +{ + if (!wl_resource_get_user_data(resource)) + return; + + wl_list_remove(wl_resource_get_link(resource)); +} + +static void +pointer_send_frame(struct wl_resource *resource) +{ + if (wl_resource_get_version(resource) >= + WL_POINTER_FRAME_SINCE_VERSION) + wl_pointer_send_frame(resource); +} diff --git a/src/libds/seat/seat_private.h b/src/libds/seat/seat_private.h new file mode 100644 index 0000000..7a566bb --- /dev/null +++ b/src/libds/seat/seat_private.h @@ -0,0 +1,172 @@ +#ifndef DS_SEAT_PRIVATE_H +#define DS_SEAT_PRIVATE_H + +#include +#include + +#include "libds/seat.h" +#include "seat.h" +#include "surface.h" + +struct ds_seat_client +{ + struct ds_seat *seat; + struct wl_client *wl_client; + struct wl_list link; + + struct wl_list resources; // wl_seat + struct wl_list pointers; // wl_pointer + struct wl_list keyboards; // wl_keyboard + struct wl_list touches; // wl_touch + + struct { + struct wl_signal destroy; + } events; + + bool needs_touch_frame; +}; + +#define DS_POINTER_BUTTONS_CAP 16 + +struct ds_seat_pointer +{ + struct ds_seat *seat; + struct ds_seat_client *focused_client; + struct ds_surface *focused_surface; + double sx, sy; + + struct ds_seat_pointer_grab *grab; + struct ds_seat_pointer_grab *default_grab; + + bool sent_axis_source; + enum ds_axis_source cached_axis_source; + + uint32_t buttons[DS_POINTER_BUTTONS_CAP]; + size_t button_count; + uint32_t grab_button; + uint32_t grab_serial; + uint32_t grab_time; + + struct wl_listener surface_destroy; + + struct { + struct wl_signal focus_change; + } events; +}; + +struct ds_seat_keyboard +{ + struct ds_seat *seat; + + struct ds_seat_client *focused_client; + struct ds_surface *focused_surface; + + struct wl_listener surface_destroy; + + struct ds_seat_keyboard_grab *grab; + struct ds_seat_keyboard_grab *default_grab; + + struct { + struct wl_signal focus_change; + } events; +}; + +struct ds_touch_point +{ + int32_t touch_id; + struct ds_surface *surface; + struct ds_seat_client *seat_client; + + struct ds_seat_client *focused_client; + struct ds_surface *focused_surface; + double sx, sy; + + struct wl_listener surface_destroy; + struct wl_listener focused_surface_destroy; + struct wl_listener client_destroy; + + struct { + struct wl_signal destroy; + } events; + + struct wl_list link; +}; + +struct ds_seat_touch +{ + struct ds_seat *seat; + struct wl_list touch_points; // ds_touch_point::link + + uint32_t grab_serial; + uint32_t grab_id; + + struct ds_seat_touch_grab *grab; + struct ds_seat_touch_grab *default_grab; +}; + +struct ds_seat +{ + char *name; + enum wl_seat_capability capabilities; + enum wl_seat_capability accumulated_capabilities; + struct timespec last_event; + + struct wl_display *display; + struct wl_global *global; + + struct wl_list clients; // ds_seat_client::link + + struct ds_seat_pointer pointer; + struct ds_seat_keyboard keyboard; + struct ds_seat_touch touch; + + struct wl_listener display_destroy; + + struct { + struct wl_signal destroy; + + struct wl_signal pointer_grab_begin; + struct wl_signal pointer_grab_end; + + struct wl_signal keyboard_grab_begin; + struct wl_signal keyboard_grab_end; + + struct wl_signal touch_grab_begin; + struct wl_signal touch_grab_end; + } events; +}; + +struct ds_seat_client * +seat_client_for_wl_client(struct ds_seat *seat, struct wl_client *wl_client); + +bool seat_pointer_init(struct ds_seat *seat); + +void seat_pointer_finish(struct ds_seat *seat); + +void seat_client_add_pointer_resource(struct ds_seat_client *seat_client, + uint32_t version, uint32_t id); + +void +seat_client_remove_all_pointer_resources(struct ds_seat_client *seat_client); + +bool seat_keyboard_init(struct ds_seat *seat); + +void seat_keyboard_finish(struct ds_seat *seat); + +void seat_client_add_keyboard_resource(struct ds_seat_client *seat_client, + uint32_t version, uint32_t id); + +void +seat_client_remove_all_keyboard_resources(struct ds_seat_client *seat_client); + +bool seat_touch_init(struct ds_seat *seat); + +void seat_touch_finish(struct ds_seat *seat); + +void seat_client_add_touch_resource(struct ds_seat_client *seat_client, + uint32_t version, uint32_t id); + +void +seat_client_remove_all_touch_resources(struct ds_seat_client *seat_client); + +#endif diff --git a/src/libds/seat/seat_touch.c b/src/libds/seat/seat_touch.c new file mode 100644 index 0000000..8a02e94 --- /dev/null +++ b/src/libds/seat/seat_touch.c @@ -0,0 +1,432 @@ +#include +#include +#include + +#include "libds/log.h" +#include "seat_private.h" + +static const struct ds_touch_grab_interface default_touch_grab_iface; +static const struct wl_touch_interface touch_impl; + +static void touch_handle_resource_destroy(struct wl_resource *resource); +static struct ds_touch_point *touch_point_create(struct ds_seat *seat, + int32_t touch_id, struct ds_surface *surface, double sx, double sy); +static void touch_point_destroy(struct ds_touch_point *point); +static void touch_point_clear_focus(struct ds_touch_point *point); +static struct ds_touch_point *seat_find_touch_point(struct ds_seat *seat, + int32_t touch_id); +static int seat_touch_num_points(struct ds_seat *seat); + +WL_EXPORT uint32_t +ds_seat_touch_notify_down(struct ds_seat *seat, struct ds_surface *surface, + uint32_t time_msec, int32_t touch_id, double sx, double sy) +{ + struct ds_seat_touch_grab *grab = seat->touch.grab; + struct ds_touch_point *point; + uint32_t serial; + + // FIXME + // What if ds_touch_point is already exist associated with given touch_id? + point = touch_point_create(seat, touch_id, surface, sx, sy); + if (!point) { + ds_err("Could not create touch point"); + return 0; + } + + serial = grab->iface->down(grab, time_msec, point); + if (!serial) { + touch_point_destroy(point); + return 0; + } + + if (serial && seat_touch_num_points(seat) == 1) { + seat->touch.grab_serial = serial; + seat->touch.grab_id = touch_id; + } + + return serial; +} + +WL_EXPORT void +ds_seat_touch_notify_up(struct ds_seat *seat, uint32_t time_msec, + int32_t touch_id) +{ + struct ds_seat_touch_grab *grab = seat->touch.grab; + struct ds_touch_point *point; + + clock_gettime(CLOCK_MONOTONIC, &seat->last_event); + + point = seat_find_touch_point(seat, touch_id); + if (!point) + return; + + grab->iface->up(grab, time_msec, point); + + touch_point_destroy(point); +} + +WL_EXPORT void +ds_seat_touch_notify_motion(struct ds_seat *seat, uint32_t time_msec, + int32_t touch_id, double sx, double sy) +{ + struct ds_seat_touch_grab *grab = seat->touch.grab; + struct ds_touch_point *point; + + clock_gettime(CLOCK_MONOTONIC, &seat->last_event); + + point = seat_find_touch_point(seat, touch_id); + if (!point) + return; + + point->sx = sx; + point->sy = sy; + + grab->iface->motion(grab, time_msec, point); +} + +WL_EXPORT void +ds_seat_touch_notify_frame(struct ds_seat *seat) +{ + struct ds_seat_touch_grab *grab = seat->touch.grab; + + if (grab->iface->frame) + grab->iface->frame(grab); +} + +WL_EXPORT void +ds_seat_touch_add_grab_start_listener(struct ds_seat *seat, + struct wl_listener *listener) +{ + wl_signal_add(&seat->events.touch_grab_begin, listener); +} + +WL_EXPORT void +ds_seat_touch_end_grab_start_listener(struct ds_seat *seat, + struct wl_listener *listener) +{ + wl_signal_add(&seat->events.touch_grab_end, listener); +} + +uint32_t +ds_seat_touch_send_down(struct ds_seat *seat, struct ds_surface *surface, + uint32_t time_msec, int32_t touch_id, double sx, double sy) +{ + struct ds_touch_point *point; + struct wl_resource *resource; + uint32_t serial; + + point = seat_find_touch_point(seat, touch_id); + if (!point) { + ds_err("Got touch down for unknown touch point"); + return 0; + } + + serial = wl_display_next_serial(seat->display); + wl_resource_for_each(resource, &point->seat_client->touches) { + wl_touch_send_down(resource, serial, time_msec, + ds_surface_get_wl_resource(surface), touch_id, + wl_fixed_from_double(sx), wl_fixed_from_double(sy)); + } + + point->seat_client->needs_touch_frame = true; + + return serial; +} + +void +ds_seat_touch_send_up(struct ds_seat *seat, uint32_t time_msec, + int32_t touch_id) +{ + struct ds_touch_point *point; + struct wl_resource *resource; + uint32_t serial; + + point = seat_find_touch_point(seat, touch_id); + if (!point) { + ds_err("Got touch up for unknown touch point"); + return; + } + + serial = wl_display_next_serial(seat->display); + wl_resource_for_each(resource, &point->seat_client->touches) + wl_touch_send_up(resource, serial, time_msec, touch_id); + + point->seat_client->needs_touch_frame = true; +} + +void +ds_seat_touch_send_motion(struct ds_seat *seat, uint32_t time_msec, + int32_t touch_id, double sx, double sy) +{ + struct ds_touch_point *point; + struct wl_resource *resource; + + point = seat_find_touch_point(seat, touch_id); + if (!point) { + ds_err("Got touch motion for unknown touch point"); + return; + } + + wl_resource_for_each(resource, &point->seat_client->touches) { + wl_touch_send_motion(resource, time_msec, touch_id, + wl_fixed_from_double(sx), wl_fixed_from_double(sy)); + } + + point->seat_client->needs_touch_frame = true; +} + +void +ds_seat_touch_send_frame(struct ds_seat *seat) +{ + struct ds_seat_client *seat_client; + struct wl_resource *resource; + + wl_list_for_each(seat_client, &seat->clients, link) { + if (!seat_client->needs_touch_frame) + continue; + + wl_resource_for_each(resource, &seat_client->touches) + wl_touch_send_frame(resource); + + seat_client->needs_touch_frame = false; + } +} + +bool +seat_touch_init(struct ds_seat *seat) +{ + struct ds_seat_touch *touch = &seat->touch; + struct ds_seat_touch_grab *grab; + + grab = calloc(1, sizeof *grab); + if (!grab) + return false; + + grab->iface = &default_touch_grab_iface; + grab->seat = seat; + + touch->default_grab = grab; + touch->grab = grab; + touch->seat = seat; + + wl_list_init(&touch->touch_points); + + return true; +} + +void +seat_touch_finish(struct ds_seat *seat) +{ + struct ds_seat_touch *touch = &seat->touch; + struct ds_touch_point *point; + + wl_list_for_each(point, &touch->touch_points, link) + touch_point_clear_focus(point); + + free(touch->default_grab); +} + +void +seat_client_add_touch_resource(struct ds_seat_client *seat_client, + uint32_t version, uint32_t id) +{ + struct wl_resource *resource; + + resource = wl_resource_create(seat_client->wl_client, + &wl_keyboard_interface, version, id); + if (!resource) { + wl_client_post_no_memory(seat_client->wl_client); + return; + } + + wl_resource_set_implementation(resource, &touch_impl, seat_client, + touch_handle_resource_destroy); + + wl_list_insert(&seat_client->touches, wl_resource_get_link(resource)); + + if (!(seat_client->seat->capabilities & WL_SEAT_CAPABILITY_TOUCH)) + wl_resource_set_user_data(resource, NULL); +} + +void +seat_client_remove_all_touch_resources(struct ds_seat_client *seat_client) +{ + struct wl_resource *resource, *tmp; + + wl_resource_for_each_safe(resource, tmp, &seat_client->touches) { + wl_list_remove(wl_resource_get_link(resource)); + wl_resource_set_user_data(resource, NULL); + } +} + +static uint32_t +default_touch_grab_iface_down(struct ds_seat_touch_grab *grab, + uint32_t time_msec, struct ds_touch_point *point) +{ + return ds_seat_touch_send_down(grab->seat, point->surface, time_msec, + point->touch_id, point->sx, point->sy); +} + +static void +default_touch_grab_iface_up(struct ds_seat_touch_grab *grab, + uint32_t time_msec, struct ds_touch_point *point) +{ + ds_seat_touch_send_up(grab->seat, time_msec, point->touch_id); +} + +static void +default_touch_grab_iface_motion(struct ds_seat_touch_grab *grab, + uint32_t time_msec, struct ds_touch_point *point) +{ + if (!point->focused_surface || point->focused_surface == point->surface) { + ds_seat_touch_send_motion(grab->seat, time_msec, point->touch_id, + point->sx, point->sy); + } +} + +static void +default_touch_grab_iface_enter(struct ds_seat_touch_grab *grab, + uint32_t time_msec, struct ds_touch_point *point) +{ + // not handled by default +} + +static void +default_touch_grab_iface_frame(struct ds_seat_touch_grab *grab) +{ + ds_seat_touch_send_frame(grab->seat); +} + +static void +default_touch_grab_iface_cancel(struct ds_seat_touch_grab *grab) +{ + // cannot be cancelled +} + +static const struct ds_touch_grab_interface default_touch_grab_iface = { + .down = default_touch_grab_iface_down, + .up = default_touch_grab_iface_up, + .motion = default_touch_grab_iface_motion, + .enter = default_touch_grab_iface_enter, + .frame = default_touch_grab_iface_frame, + .cancel = default_touch_grab_iface_cancel, +}; + +static void +touch_handle_release(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct wl_touch_interface touch_impl = +{ + .release = touch_handle_release, +}; + +static void +touch_handle_resource_destroy(struct wl_resource *resource) +{ + if (!wl_resource_get_user_data(resource)) + return; + + wl_list_remove(wl_resource_get_link(resource)); +} + +static void +touch_point_handle_surface_destroy(struct wl_listener *listener, void *data) +{ + struct ds_touch_point *point; + + point = wl_container_of(listener, point, surface_destroy); + point->surface = NULL; + wl_list_remove(&point->surface_destroy.link); + wl_list_init(&point->surface_destroy.link); +} + +static void +touch_point_handle_client_destroy(struct wl_listener *listener, void *data) +{ + struct ds_touch_point *point; + + point = wl_container_of(listener, point, surface_destroy); + touch_point_destroy(point); +} + +static struct ds_touch_point * +touch_point_create(struct ds_seat *seat, int32_t touch_id, + struct ds_surface *surface, double sx, double sy) +{ + struct ds_touch_point *point; + struct ds_seat_client *seat_client; + struct wl_client *wl_client; + + wl_client = wl_resource_get_client(ds_surface_get_wl_resource(surface)); + seat_client = seat_client_for_wl_client(seat, wl_client); + if (!seat_client || wl_list_empty(&seat_client->touches)) + return NULL; + + point = calloc(1, sizeof *point); + if (!point) + return NULL; + + point->touch_id = touch_id; + point->surface = surface; + point->seat_client = seat_client; + point->sx = sx; + point->sy = sy; + + wl_signal_init(&point->events.destroy); + + point->surface_destroy.notify = touch_point_handle_surface_destroy; + ds_surface_add_destroy_listener(surface, &point->surface_destroy); + + point->client_destroy.notify = touch_point_handle_client_destroy; + wl_signal_add(&seat_client->events.destroy, &point->client_destroy); + + wl_list_insert(&seat->touch.touch_points, &point->link); + + return point; +} + +static void +touch_point_destroy(struct ds_touch_point *point) +{ + wl_signal_emit(&point->events.destroy, point); + + touch_point_clear_focus(point); + + wl_list_remove(&point->surface_destroy.link); + wl_list_remove(&point->client_destroy.link); + wl_list_remove(&point->link); + free(point); +} + +static void +touch_point_clear_focus(struct ds_touch_point *point) +{ + if (!point->focused_surface) + return; + + wl_list_remove(&point->focused_surface_destroy.link); + point->focused_client = NULL; + point->focused_surface = NULL; +} + +static struct ds_touch_point *seat_find_touch_point(struct ds_seat *seat, + int32_t touch_id) +{ + struct ds_touch_point *point; + + wl_list_for_each(point, &seat->touch.touch_points, link) { + if (point->touch_id == touch_id) + return point; + } + + return NULL; +} + +static int +seat_touch_num_points(struct ds_seat *seat) +{ + return wl_list_length(&seat->touch.touch_points); +} diff --git a/src/libds/surface.h b/src/libds/surface.h index 8f3ceac..008fb9c 100644 --- a/src/libds/surface.h +++ b/src/libds/surface.h @@ -46,4 +46,7 @@ ds_subsurface_create(struct wl_resource *subcomp_resource, struct ds_surface *surface, struct ds_surface *parent, uint32_t version, uint32_t id); +struct wl_resource * +ds_surface_get_wl_resource(struct ds_surface *surface); + #endif diff --git a/src/libds/surface/surface.c b/src/libds/surface/surface.c index 0333561..78dc41c 100644 --- a/src/libds/surface/surface.c +++ b/src/libds/surface/surface.c @@ -220,6 +220,12 @@ ds_surface_has_buffer(struct ds_surface *surface) return !!surface->buffer; } +struct wl_resource * +ds_surface_get_wl_resource(struct ds_surface *surface) +{ + return surface->resource; +} + static void surface_handle_destroy(struct wl_client *client, struct wl_resource *resource) { -- 2.7.4