[Wayland] Implement the cursor-shape-v1 protocol

Related #106229. The cursor-shape protocol allows us to not have to deal with cursor theming and instead depend on the
compositor for it.
This still does not quite solve the issue when the compositor doesn't implement the protocol
(or running under the x11 backend) but for gnome/kde and a few more this should resolve things.
This commit is contained in:
ArchercatNEO 2025-05-10 18:08:19 +01:00
parent 19bb18716e
commit 3cd7b5b9a8
5 changed files with 253 additions and 1 deletions

View file

@ -46,6 +46,14 @@ env.NoCache(
"protocol/viewporter.gen.h", "#thirdparty/wayland-protocols/stable/viewporter/viewporter.xml"
),
env.WAYLAND_API_CODE("protocol/viewporter.gen.c", "#thirdparty/wayland-protocols/stable/viewporter/viewporter.xml"),
env.WAYLAND_API_HEADER(
"protocol/cursor_shape.gen.h",
"#thirdparty/wayland-protocols/staging/cursor-shape/cursor-shape-v1.xml",
),
env.WAYLAND_API_CODE(
"protocol/cursor_shape.gen.c",
"#thirdparty/wayland-protocols/staging/cursor-shape/cursor-shape-v1.xml",
),
env.WAYLAND_API_HEADER(
"protocol/fractional_scale.gen.h",
"#thirdparty/wayland-protocols/staging/fractional-scale/fractional-scale-v1.xml",
@ -158,6 +166,7 @@ env.NoCache(
source_files = [
"protocol/wayland.gen.c",
"protocol/viewporter.gen.c",
"protocol/cursor_shape.gen.c",
"protocol/fractional_scale.gen.c",
"protocol/xdg_shell.gen.c",
"protocol/xdg_system_bell.gen.c",

View file

@ -547,6 +547,12 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
registry->wp_viewporter_name = name;
}
if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {
registry->wp_cursor_shape_manager = (struct wp_cursor_shape_manager_v1 *)wl_registry_bind(wl_registry, name, &wp_cursor_shape_manager_v1_interface, 1);
registry->wp_cursor_shape_manager_name = name;
return;
}
if (strcmp(interface, wp_fractional_scale_manager_v1_interface.name) == 0) {
registry->wp_fractional_scale_manager = (struct wp_fractional_scale_manager_v1 *)wl_registry_bind(wl_registry, name, &wp_fractional_scale_manager_v1_interface, 1);
registry->wp_fractional_scale_manager_name = name;
@ -753,6 +759,25 @@ void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry
return;
}
if (name == registry->wp_cursor_shape_manager_name) {
if (registry->wp_cursor_shape_manager) {
wp_cursor_shape_manager_v1_destroy(registry->wp_cursor_shape_manager);
registry->wp_cursor_shape_manager = nullptr;
}
registry->wp_cursor_shape_manager_name = 0;
for (struct wl_seat *wl_seat : registry->wl_seats) {
SeatState *ss = wl_seat_get_seat_state(wl_seat);
ERR_FAIL_NULL(ss);
if (ss->wp_cursor_shape_device) {
wp_cursor_shape_device_v1_destroy(ss->wp_cursor_shape_device);
ss->wp_cursor_shape_device = nullptr;
}
}
}
if (name == registry->wp_fractional_scale_manager_name) {
for (KeyValue<DisplayServer::WindowID, WindowState> &pair : registry->wayland_thread->windows) {
WindowState ws = pair.value;
@ -1452,6 +1477,10 @@ void WaylandThread::_wl_seat_on_capabilities(void *data, struct wl_seat *wl_seat
ss->wl_pointer = wl_seat_get_pointer(wl_seat);
wl_pointer_add_listener(ss->wl_pointer, &wl_pointer_listener, ss);
if (ss->registry->wp_cursor_shape_manager) {
ss->wp_cursor_shape_device = wp_cursor_shape_manager_v1_get_pointer(ss->registry->wp_cursor_shape_manager, ss->wl_pointer);
}
if (ss->registry->wp_relative_pointer_manager) {
ss->wp_relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(ss->registry->wp_relative_pointer_manager, ss->wl_pointer);
zwp_relative_pointer_v1_add_listener(ss->wp_relative_pointer, &wp_relative_pointer_listener, ss);
@ -1483,6 +1512,11 @@ void WaylandThread::_wl_seat_on_capabilities(void *data, struct wl_seat *wl_seat
ss->wl_pointer = nullptr;
}
if (ss->wp_cursor_shape_device) {
wp_cursor_shape_device_v1_destroy(ss->wp_cursor_shape_device);
ss->wp_cursor_shape_device = nullptr;
}
if (ss->wp_relative_pointer) {
zwp_relative_pointer_v1_destroy(ss->wp_relative_pointer);
ss->wp_relative_pointer = nullptr;
@ -3328,6 +3362,12 @@ void WaylandThread::seat_state_update_cursor(SeatState *p_ss) {
// We can't really reasonably scale custom cursors, so we'll let the
// compositor do it for us (badly).
scale = 1;
} else if (thread->registry.wp_cursor_shape_manager) {
wp_cursor_shape_device_v1_shape wp_shape = thread->standard_cursors[shape];
wp_cursor_shape_device_v1_set_shape(p_ss->wp_cursor_shape_device, p_ss->pointer_enter_serial, wp_shape);
// We should avoid calling the `wl_pointer_set_cursor` at the end of this method.
return;
} else {
struct wl_cursor *wl_cursor = thread->wl_cursors[shape];
@ -4890,6 +4930,10 @@ void WaylandThread::destroy() {
wl_data_device_destroy(ss->wl_data_device);
}
if (ss->wp_cursor_shape_device) {
wp_cursor_shape_device_v1_destroy(ss->wp_cursor_shape_device);
}
if (ss->wp_relative_pointer) {
zwp_relative_pointer_v1_destroy(ss->wp_relative_pointer);
}
@ -4957,6 +5001,10 @@ void WaylandThread::destroy() {
zxdg_decoration_manager_v1_destroy(registry.xdg_decoration_manager);
}
if (registry.wp_cursor_shape_manager) {
wp_cursor_shape_manager_v1_destroy(registry.wp_cursor_shape_manager);
}
if (registry.wp_fractional_scale_manager) {
wp_fractional_scale_manager_v1_destroy(registry.wp_fractional_scale_manager);
}

View file

@ -51,10 +51,11 @@
// These must go after the Wayland client include to work properly.
#include "wayland/protocol/idle_inhibit.gen.h"
#include "wayland/protocol/primary_selection.gen.h"
// These three protocol headers name wl_pointer method arguments as `pointer`,
// These four protocol headers name wl_pointer method arguments as `pointer`,
// which is the same name as X11's pointer typedef. This trips some very
// annoying shadowing warnings. A `#define` works around this issue.
#define pointer wl_pointer
#include "wayland/protocol/cursor_shape.gen.h"
#include "wayland/protocol/pointer_constraints.gen.h"
#include "wayland/protocol/pointer_gestures.gen.h"
#include "wayland/protocol/relative_pointer.gen.h"
@ -187,6 +188,9 @@ public:
struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager = nullptr;
uint32_t wp_fractional_scale_manager_name = 0;
struct wp_cursor_shape_manager_v1 *wp_cursor_shape_manager = nullptr;
uint32_t wp_cursor_shape_manager_name = 0;
struct zxdg_decoration_manager_v1 *xdg_decoration_manager = nullptr;
uint32_t xdg_decoration_manager_name = 0;
@ -416,6 +420,8 @@ public:
uint32_t pointer_enter_serial = 0;
struct wp_cursor_shape_device_v1 *wp_cursor_shape_device = nullptr;
struct zwp_relative_pointer_v1 *wp_relative_pointer = nullptr;
struct zwp_locked_pointer_v1 *wp_locked_pointer = nullptr;
struct zwp_confined_pointer_v1 *wp_confined_pointer = nullptr;
@ -547,9 +553,32 @@ private:
// especially as usually screen scales don't change continuously.
int cursor_scale = 1;
// Use cursor-shape-v1 protocol if the compositor supports it.
wp_cursor_shape_device_v1_shape standard_cursors[DisplayServer::CURSOR_MAX] = {
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT, //CURSOR_ARROW
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT, //CURSOR_IBEAM
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER, //CURSOR_POINTING_HAND
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR, //CURSOR_CROSS
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_WAIT, //CURSOR_WAIT
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_PROGRESS, //CURSOR_BUSY
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRAB, //CURSOR_DRAG
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRABBING, //CURSOR_CAN_DROP
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NO_DROP, //CURSOR_FORBIDDEN
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NS_RESIZE, //CURSOR_VSIZE
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_EW_RESIZE, //CURSOR_HSIZE
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NESW_RESIZE, //CURSOR_BDIAGSIZE
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NWSE_RESIZE, //CURSOR_FDIAGSIZE
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE, //CURSOR_MOVE
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ROW_RESIZE, //CURSOR_VSPLIT
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_COL_RESIZE, //CURSOR_HSPLIT
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_HELP, //CURSOR_HELP
};
// Fallback to reading $XCURSOR and system themes if the compositor does not.
struct wl_cursor_theme *wl_cursor_theme = nullptr;
struct wl_cursor *wl_cursors[DisplayServer::CURSOR_MAX] = {};
// User-defined cursor overrides. Take precedence over standard and wl cursors.
HashMap<DisplayServer::CursorShape, CustomCursor> custom_cursors;
DisplayServer::CursorShape cursor_shape = DisplayServer::CURSOR_ARROW;