From c2e9693de559e23c530ad6cc364be78c4a8657e4 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Tue, 12 Mar 2024 19:49:36 -0400 Subject: [PATCH] wayland: Add cursor-shape-v1 protocol support --- src/video/wayland/SDL_waylandevents.c | 31 ++++- src/video/wayland/SDL_waylandevents_c.h | 2 + src/video/wayland/SDL_waylandmouse.c | 93 ++++++++++++++- src/video/wayland/SDL_waylandvideo.c | 72 +++++++----- src/video/wayland/SDL_waylandvideo.h | 1 + wayland-protocols/cursor-shape-v1.xml | 147 ++++++++++++++++++++++++ 6 files changed, 307 insertions(+), 39 deletions(-) create mode 100644 wayland-protocols/cursor-shape-v1.xml diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 488f0038d..f55c4eb5a 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -56,13 +56,14 @@ #define BTN_SIDE (0x113) #define BTN_EXTRA (0x114) #endif +#include "../../events/SDL_keysym_to_scancode_c.h" +#include "../../events/imKStoUCS.h" +#include #include #include -#include -#include #include -#include "../../events/imKStoUCS.h" -#include "../../events/SDL_keysym_to_scancode_c.h" +#include +#include "cursor-shape-v1-client-protocol.h" /* Weston uses a ratio of 10 units per scroll tick */ #define WAYLAND_WHEEL_AXIS_UNIT 10 @@ -245,6 +246,17 @@ void Wayland_RegisterTimestampListeners(struct SDL_WaylandInput *input) } } +void Wayland_CreateCursorShapeDevice(struct SDL_WaylandInput *input) +{ + SDL_VideoData *viddata = input->display; + + if (viddata->cursor_shape_manager) { + if (input->pointer && !input->cursor_shape) { + input->cursor_shape = wp_cursor_shape_manager_v1_get_pointer(viddata->cursor_shape_manager, input->pointer); + } + } +} + /* Returns SDL_TRUE if a key repeat event was due */ static SDL_bool keyboard_repeat_handle(SDL_WaylandKeyboardRepeat *repeat_info, Uint64 elapsed) { @@ -1696,10 +1708,17 @@ static void seat_handle_capabilities(void *data, struct wl_seat *seat, input->pointer = wl_seat_get_pointer(seat); SDL_memset(&input->pointer_curr_axis_info, 0, sizeof(input->pointer_curr_axis_info)); input->display->pointer = input->pointer; + + Wayland_CreateCursorShapeDevice(input); + wl_pointer_set_user_data(input->pointer, input); wl_pointer_add_listener(input->pointer, &pointer_listener, input); } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) { + if (input->cursor_shape) { + wp_cursor_shape_device_v1_destroy(input->cursor_shape); + input->cursor_shape = NULL; + } wl_pointer_destroy(input->pointer); input->pointer = NULL; input->display->pointer = NULL; @@ -3066,6 +3085,10 @@ void Wayland_display_destroy_input(SDL_VideoData *d) } } + if (input->cursor_shape) { + wp_cursor_shape_device_v1_destroy(input->cursor_shape); + } + if (input->pointer) { if (wl_pointer_get_version(input->pointer) >= WL_POINTER_RELEASE_SINCE_VERSION) { wl_pointer_release(input->pointer); diff --git a/src/video/wayland/SDL_waylandevents_c.h b/src/video/wayland/SDL_waylandevents_c.h index af317811f..f7ed50a8f 100644 --- a/src/video/wayland/SDL_waylandevents_c.h +++ b/src/video/wayland/SDL_waylandevents_c.h @@ -102,6 +102,7 @@ struct SDL_WaylandInput SDL_WaylandDataDevice *data_device; SDL_WaylandPrimarySelectionDevice *primary_selection_device; SDL_WaylandTextInput *text_input; + struct wp_cursor_shape_device_v1 *cursor_shape; struct zwp_relative_pointer_v1 *relative_pointer; struct zwp_input_timestamps_v1 *keyboard_timestamps; struct zwp_input_timestamps_v1 *pointer_timestamps; @@ -209,6 +210,7 @@ extern void Wayland_input_add_tablet(struct SDL_WaylandInput *input, struct SDL_ extern void Wayland_input_destroy_tablet(struct SDL_WaylandInput *input); extern void Wayland_RegisterTimestampListeners(struct SDL_WaylandInput *input); +extern void Wayland_CreateCursorShapeDevice(struct SDL_WaylandInput *input); /* The implicit grab serial needs to be updated on: * - Keyboard key down/up diff --git a/src/video/wayland/SDL_waylandmouse.c b/src/video/wayland/SDL_waylandmouse.c index 13b6e807c..93575a671 100644 --- a/src/video/wayland/SDL_waylandmouse.c +++ b/src/video/wayland/SDL_waylandmouse.c @@ -41,6 +41,8 @@ #include "wayland-cursor.h" #include "SDL_waylandmouse.h" +#include "cursor-shape-v1-client-protocol.h" + #include "../../SDL_hints_c.h" static SDL_Cursor *sys_cursors[SDL_HITTEST_RESIZE_LEFT + 1]; @@ -529,14 +531,19 @@ static SDL_Cursor *Wayland_CreateSystemCursor(SDL_SystemCursor id) SDL_free(cursor); return NULL; } + cursor->driverdata = (void *)cdata; - cdata->surface = wl_compositor_create_surface(data->compositor); - wl_surface_set_user_data(cdata->surface, NULL); - - /* Note that we can't actually set any other cursor properties, as this + /* The surface is only necessary if the cursor shape manager is not present. + * + * Note that we can't actually set any other cursor properties, as this * is output-specific. See wayland_get_system_cursor for the rest! */ + if (!data->cursor_shape_manager) { + cdata->surface = wl_compositor_create_surface(data->compositor); + wl_surface_set_user_data(cdata->surface, NULL); + } + cdata->system_cursor = id; } @@ -581,6 +588,79 @@ static void Wayland_FreeCursor(SDL_Cursor *cursor) SDL_free(cursor); } +static void Wayland_SetSystemCursorShape(struct SDL_WaylandInput *input, SDL_SystemCursor id) +{ + Uint32 shape; + + switch (id) { + case SDL_SYSTEM_CURSOR_ARROW: + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT; + break; + case SDL_SYSTEM_CURSOR_IBEAM: + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT; + break; + case SDL_SYSTEM_CURSOR_WAIT: + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_WAIT; + break; + case SDL_SYSTEM_CURSOR_CROSSHAIR: + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR; + break; + case SDL_SYSTEM_CURSOR_WAITARROW: + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_PROGRESS; + break; + case SDL_SYSTEM_CURSOR_SIZENWSE: + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NWSE_RESIZE; + break; + case SDL_SYSTEM_CURSOR_SIZENESW: + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NESW_RESIZE; + break; + case SDL_SYSTEM_CURSOR_SIZEWE: + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_EW_RESIZE; + break; + case SDL_SYSTEM_CURSOR_SIZENS: + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NS_RESIZE; + break; + case SDL_SYSTEM_CURSOR_SIZEALL: + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALL_SCROLL; + break; + case SDL_SYSTEM_CURSOR_NO: + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NOT_ALLOWED; + break; + case SDL_SYSTEM_CURSOR_HAND: + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER; + break; + case SDL_SYSTEM_CURSOR_WINDOW_TOPLEFT: + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NW_RESIZE; + break; + case SDL_SYSTEM_CURSOR_WINDOW_TOP: + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_N_RESIZE; + break; + case SDL_SYSTEM_CURSOR_WINDOW_TOPRIGHT: + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NE_RESIZE; + break; + case SDL_SYSTEM_CURSOR_WINDOW_RIGHT: + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_E_RESIZE; + break; + case SDL_SYSTEM_CURSOR_WINDOW_BOTTOMRIGHT: + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SE_RESIZE; + break; + case SDL_SYSTEM_CURSOR_WINDOW_BOTTOM: + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_S_RESIZE; + break; + case SDL_SYSTEM_CURSOR_WINDOW_BOTTOMLEFT: + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SW_RESIZE; + break; + case SDL_SYSTEM_CURSOR_WINDOW_LEFT: + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_W_RESIZE; + break; + default: + SDL_assert(0); /* Should never be here... */ + shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT; + } + + wp_cursor_shape_device_v1_set_shape(input->cursor_shape, input->pointer_enter_serial, shape); +} + static int Wayland_ShowCursor(SDL_Cursor *cursor) { SDL_VideoDevice *vd = SDL_GetVideoDevice(); @@ -598,7 +678,10 @@ static int Wayland_ShowCursor(SDL_Cursor *cursor) /* TODO: High-DPI custom cursors? -flibit */ if (!data->shm_data) { - if (!wayland_get_system_cursor(d, data, &scale)) { + if (input->cursor_shape) { + Wayland_SetSystemCursorShape(input, data->system_cursor); + return 0; + } else if (!wayland_get_system_cursor(d, data, &scale)) { return -1; } } diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index 090458503..8a3015832 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -23,41 +23,42 @@ #ifdef SDL_VIDEO_DRIVER_WAYLAND -#include "../../events/SDL_events_c.h" #include "../../core/linux/SDL_system_theme.h" +#include "../../events/SDL_events_c.h" -#include "SDL_waylandvideo.h" -#include "SDL_waylandevents_c.h" -#include "SDL_waylandwindow.h" -#include "SDL_waylandopengles.h" -#include "SDL_waylandmouse.h" -#include "SDL_waylandkeyboard.h" #include "SDL_waylandclipboard.h" -#include "SDL_waylandvulkan.h" +#include "SDL_waylandevents_c.h" +#include "SDL_waylandkeyboard.h" #include "SDL_waylandmessagebox.h" +#include "SDL_waylandmouse.h" +#include "SDL_waylandopengles.h" +#include "SDL_waylandvideo.h" +#include "SDL_waylandvulkan.h" +#include "SDL_waylandwindow.h" +#include #include #include -#include #include #include -#include "xdg-shell-client-protocol.h" -#include "xdg-decoration-unstable-v1-client-protocol.h" -#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h" -#include "idle-inhibit-unstable-v1-client-protocol.h" -#include "xdg-activation-v1-client-protocol.h" -#include "text-input-unstable-v3-client-protocol.h" -#include "tablet-unstable-v2-client-protocol.h" -#include "xdg-output-unstable-v1-client-protocol.h" -#include "viewporter-client-protocol.h" -#include "primary-selection-unstable-v1-client-protocol.h" +#include "cursor-shape-v1-client-protocol.h" #include "fractional-scale-v1-client-protocol.h" +#include "idle-inhibit-unstable-v1-client-protocol.h" #include "input-timestamps-unstable-v1-client-protocol.h" -#include "relative-pointer-unstable-v1-client-protocol.h" -#include "pointer-constraints-unstable-v1-client-protocol.h" #include "kde-output-order-v1-client-protocol.h" +#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h" +#include "pointer-constraints-unstable-v1-client-protocol.h" +#include "primary-selection-unstable-v1-client-protocol.h" +#include "relative-pointer-unstable-v1-client-protocol.h" +#include "tablet-unstable-v2-client-protocol.h" +#include "text-input-unstable-v3-client-protocol.h" +#include "viewporter-client-protocol.h" +#include "xdg-activation-v1-client-protocol.h" +#include "xdg-decoration-unstable-v1-client-protocol.h" +#include "xdg-output-unstable-v1-client-protocol.h" +#include "xdg-shell-client-protocol.h" #ifdef HAVE_LIBDECOR_H #include @@ -200,7 +201,8 @@ error: static void Wayland_FlushOutputOrder(SDL_VideoData *vid) { SDL_WaylandConnectorName *c, *tmp; - wl_list_for_each_safe (c, tmp, &vid->output_order, link) { + wl_list_for_each_safe(c, tmp, &vid->output_order, link) + { WAYLAND_wl_list_remove(&c->link); SDL_free(c); } @@ -796,7 +798,7 @@ static void display_handle_done(void *data, /* ...and the compositor scales the logical viewport... */ if (video->viewporter) { /* ...and viewports are supported, calculate the true scale of the output. */ - driverdata->scale_factor = (float) native_mode.w / (float)driverdata->screen_width; + driverdata->scale_factor = (float)native_mode.w / (float)driverdata->screen_width; } else { /* ...otherwise, the 'native' pixel values are a multiple of the logical screen size. */ driverdata->pixel_width = driverdata->screen_width * (int)driverdata->scale_factor; @@ -906,11 +908,11 @@ static void display_handle_description(void *data, struct wl_output *wl_output, } static const struct wl_output_listener output_listener = { - display_handle_geometry, /* Version 1 */ - display_handle_mode, /* Version 1 */ - display_handle_done, /* Version 2 */ - display_handle_scale, /* Version 2 */ - display_handle_name, /* Version 4 */ + display_handle_geometry, /* Version 1 */ + display_handle_mode, /* Version 1 */ + display_handle_done, /* Version 2 */ + display_handle_scale, /* Version 2 */ + display_handle_name, /* Version 4 */ display_handle_description /* Version 4 */ }; @@ -978,7 +980,7 @@ static void Wayland_FinalizeDisplays(SDL_VideoData *vid) SDL_DisplayData *d; Wayland_SortOutputs(vid); - wl_list_for_each(d, &vid->output_list, link) { + wl_list_for_each (d, &vid->output_list, link) { d->display = SDL_AddVideoDisplay(&d->placeholder, SDL_FALSE); SDL_free(d->placeholder.name); SDL_zero(d->placeholder); @@ -1072,6 +1074,11 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint if (d->input) { Wayland_RegisterTimestampListeners(d->input); } + } else if (SDL_strcmp(interface, "wp_cursor_shape_manager_v1") == 0) { + d->cursor_shape_manager = wl_registry_bind(d->registry, id, &wp_cursor_shape_manager_v1_interface, 1); + if (d->input) { + Wayland_CreateCursorShapeDevice(d->input); + } } else if (SDL_strcmp(interface, "kde_output_order_v1") == 0) { d->kde_output_order = wl_registry_bind(d->registry, id, &kde_output_order_v1_interface, 1); kde_output_order_v1_add_listener(d->kde_output_order, &kde_output_order_listener, d); @@ -1081,7 +1088,7 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint static void display_remove_global(void *data, struct wl_registry *registry, uint32_t id) { SDL_VideoData *d = data; - SDL_DisplayData *node; + SDL_DisplayData *node; /* We don't get an interface, just an ID, so assume it's a wl_output :shrug: */ wl_list_for_each (node, &d->output_list, link) { @@ -1320,6 +1327,11 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this) data->input_timestamps_manager = NULL; } + if (data->cursor_shape_manager) { + wp_cursor_shape_manager_v1_destroy(data->cursor_shape_manager); + data->cursor_shape_manager = NULL; + } + if (data->kde_output_order) { Wayland_FlushOutputOrder(data); kde_output_order_v1_destroy(data->kde_output_order); diff --git a/src/video/wayland/SDL_waylandvideo.h b/src/video/wayland/SDL_waylandvideo.h index 974d93efc..874a33ae8 100644 --- a/src/video/wayland/SDL_waylandvideo.h +++ b/src/video/wayland/SDL_waylandvideo.h @@ -67,6 +67,7 @@ struct SDL_VideoData } shell; struct zwp_relative_pointer_manager_v1 *relative_pointer_manager; struct zwp_pointer_constraints_v1 *pointer_constraints; + struct wp_cursor_shape_manager_v1 *cursor_shape_manager; struct wl_data_device_manager *data_device_manager; struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager; struct zxdg_decoration_manager_v1 *decoration_manager; diff --git a/wayland-protocols/cursor-shape-v1.xml b/wayland-protocols/cursor-shape-v1.xml new file mode 100644 index 000000000..56f6a1a65 --- /dev/null +++ b/wayland-protocols/cursor-shape-v1.xml @@ -0,0 +1,147 @@ + + + + Copyright 2018 The Chromium Authors + Copyright 2023 Simon Ser + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + + This global offers an alternative, optional way to set cursor images. This + new way uses enumerated cursors instead of a wl_surface like + wl_pointer.set_cursor does. + + Warning! The protocol described in this file is currently in the testing + phase. Backward compatible changes may be added together with the + corresponding interface version bump. Backward incompatible changes can + only be done by creating a new major version of the extension. + + + + + Destroy the cursor shape manager. + + + + + + Obtain a wp_cursor_shape_device_v1 for a wl_pointer object. + + + + + + + + Obtain a wp_cursor_shape_device_v1 for a zwp_tablet_tool_v2 object. + + + + + + + + + This interface advertises the list of supported cursor shapes for a + device, and allows clients to set the cursor shape. + + + + + This enum describes cursor shapes. + + The names are taken from the CSS W3C specification: + https://w3c.github.io/csswg-drafts/css-ui/#cursor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Destroy the cursor shape device. + + The device cursor shape remains unchanged. + + + + + + Sets the device cursor to the specified shape. The compositor will + change the cursor image based on the specified shape. + + The cursor actually changes only if the input device focus is one of + the requesting client's surfaces. If any, the previous cursor image + (surface or shape) is replaced. + + The "shape" argument must be a valid enum entry, otherwise the + invalid_shape protocol error is raised. + + This is similar to the wl_pointer.set_cursor and + zwp_tablet_tool_v2.set_cursor requests, but this request accepts a + shape instead of contents in the form of a surface. Clients can mix + set_cursor and set_shape requests. + + The serial parameter must match the latest wl_pointer.enter or + zwp_tablet_tool_v2.proximity_in serial number sent to the client. + Otherwise the request will be ignored. + + + + + +