diff --git a/include/wayland-internal.h b/include/wayland-internal.h index ec30b5f10..e48ebcce5 100644 --- a/include/wayland-internal.h +++ b/include/wayland-internal.h @@ -14,6 +14,7 @@ typedef enum { WAYLAND_GLOBAL_COMPOSITOR, WAYLAND_GLOBAL_SHM, WAYLAND_GLOBAL_LAYER_SHELL, + WAYLAND_GLOBAL_CURSOR_SHAPE, _WAYLAND_GLOBAL_SIZE, } wayland_global_name; @@ -40,6 +41,10 @@ typedef struct { uint32_t global_names[_WAYLAND_GLOBAL_SIZE]; struct wl_compositor *compositor; +#ifdef HAVE_WAYLAND_CURSOR_SHAPE + struct wp_cursor_shape_manager_v1 *cursor_shape_manager; +#endif + struct wl_data_device_manager *data_device_manager; struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager; @@ -50,7 +55,7 @@ typedef struct { size_t buffer_count; struct { char *theme_name; - char **name; + RofiCursorType type; struct wl_cursor_theme *theme; struct wl_cursor *cursor; struct wl_cursor_image *image; @@ -88,9 +93,13 @@ struct _wayland_seat { int32_t delay; } repeat; uint32_t serial; + uint32_t pointer_serial; struct wl_keyboard *keyboard; struct wl_pointer *pointer; +#ifdef HAVE_WAYLAND_CURSOR_SHAPE + struct wp_cursor_shape_device_v1 *cursor_shape_device; +#endif struct wl_data_device *data_device; struct zwp_primary_selection_device_v1 *primary_selection_device; diff --git a/include/wayland.h b/include/wayland.h index 4f78959a0..522757df6 100644 --- a/include/wayland.h +++ b/include/wayland.h @@ -44,4 +44,6 @@ gboolean display_get_surface_dimensions(int *width, int *height); void display_set_surface_dimensions(int width, int height, int x_margin, int y_margin, int loc); +void wayland_display_set_cursor_type(RofiCursorType type); + #endif diff --git a/meson.build b/meson.build index f7d3be0c4..1aaaa8781 100644 --- a/meson.build +++ b/meson.build @@ -293,6 +293,15 @@ if wayland_enabled 'protocols/wlr-foreign-toplevel-management-unstable-v1.xml', 'protocols/wlr-layer-shell-unstable-v1.xml', ) + + if wayland_protocols.version().version_compare('>=1.32') + add_project_arguments('-DHAVE_WAYLAND_CURSOR_SHAPE', language: 'c') + protocols += files( + wayland_sys_protocols_dir + '/staging/cursor-shape/cursor-shape-v1.xml', + wayland_sys_protocols_dir + '/unstable/tablet/tablet-unstable-v2.xml', + ) + endif + proto_srcs = [] proto_headers = [] foreach p : protocols diff --git a/source/wayland/display.c b/source/wayland/display.c index bd35566f3..6346231f3 100644 --- a/source/wayland/display.c +++ b/source/wayland/display.c @@ -60,6 +60,9 @@ #include "display.h" #include "wayland-internal.h" +#ifdef HAVE_WAYLAND_CURSOR_SHAPE +#include "cursor-shape-v1-protocol.h" +#endif #include "primary-selection-unstable-v1-protocol.h" #include "wlr-layer-shell-unstable-v1-protocol.h" @@ -319,6 +322,7 @@ static void wayland_frame_callback(void *data, struct wl_callback *callback, uint32_t timestamp) { if (wayland->frame_cb != NULL) { wl_callback_destroy(wayland->frame_cb); + wayland->frame_cb = NULL; rofi_view_frame_callback(); } if (wayland->surface != NULL) { @@ -621,13 +625,60 @@ static void wayland_pointer_send_events(wayland_seat *self) { rofi_view_maybe_update(state); } -static void wayland_pointer_enter(void *data, struct wl_pointer *pointer, - uint32_t serial, struct wl_surface *surface, - wl_fixed_t x, wl_fixed_t y) { - wayland_seat *self = data; +static struct wl_cursor * +rofi_cursor_type_to_wl_cursor(struct wl_cursor_theme *theme, + RofiCursorType type) { + static const char *const default_names[] = { + "default", "left_ptr", "top_left_arrow", "left-arrow", NULL}; + static const char *const pointer_names[] = {"pointer", "hand1", NULL}; + static const char *const text_names[] = {"text", "xterm", NULL}; - if (!wayland_cursor_reload_theme(wayland->scale)) + const char *const *name; + struct wl_cursor *cursor = NULL; + + switch (type) { + case ROFI_CURSOR_POINTER: + name = pointer_names; + break; + case ROFI_CURSOR_TEXT: + name = text_names; + break; + default: + name = default_names; + break; + } + for (; cursor == NULL && *name != NULL; ++name) { + cursor = wl_cursor_theme_get_cursor(theme, *name); + } + return cursor; +} + +#ifdef HAVE_WAYLAND_CURSOR_SHAPE +static enum wp_cursor_shape_device_v1_shape +rofi_cursor_type_to_wp_cursor_shape(RofiCursorType type) { + switch (type) { + case ROFI_CURSOR_POINTER: + return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER; + case ROFI_CURSOR_TEXT: + return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT; + default: + return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT; + } +} +#endif + +static void wayland_cursor_update_for_seat(wayland_seat *seat) { +#ifdef HAVE_WAYLAND_CURSOR_SHAPE + if (seat->cursor_shape_device != NULL) { + wp_cursor_shape_device_v1_set_shape( + seat->cursor_shape_device, seat->pointer_serial, + rofi_cursor_type_to_wp_cursor_shape(wayland->cursor.type)); return; + } else if (wayland->cursor.theme == NULL) { + // cursor-shape-v1 is available, but the seat haven't seen a pointer yet + return; + } +#endif if (wayland->cursor.surface == NULL) { wayland->cursor.surface = wl_compositor_create_surface(wayland->compositor); @@ -640,17 +691,74 @@ static void wayland_pointer_enter(void *data, struct wl_pointer *pointer, } wl_pointer_set_cursor( - self->pointer, serial, wayland->cursor.surface, + seat->pointer, seat->pointer_serial, wayland->cursor.surface, wayland->cursor.image->hotspot_x / wayland->cursor.scale, wayland->cursor.image->hotspot_y / wayland->cursor.scale); } +static void wayland_pointer_enter(void *data, struct wl_pointer *pointer, + uint32_t serial, struct wl_surface *surface, + wl_fixed_t x, wl_fixed_t y) { + wayland_seat *self = data; + + self->pointer_serial = serial; + +#ifdef HAVE_WAYLAND_CURSOR_SHAPE + if (wayland->cursor_shape_manager != NULL) { + if (self->cursor_shape_device == NULL) { + self->cursor_shape_device = wp_cursor_shape_manager_v1_get_pointer( + wayland->cursor_shape_manager, pointer); + } + } else +#endif + if (!wayland_cursor_reload_theme(wayland->scale)) { + return; + } + + wayland_cursor_update_for_seat(self); +} + +void wayland_display_set_cursor_type(RofiCursorType type) { + wayland_seat *seat; + GHashTableIter iter; + struct wl_cursor *cursor; + + if (wayland->cursor.type == type) { + return; + } + wayland->cursor.type = type; + +#ifdef HAVE_WAYLAND_CURSOR_SHAPE + if (wayland->cursor_shape_manager == NULL) +#endif + { + if (wayland->cursor.theme == NULL) { + return; + } + + cursor = rofi_cursor_type_to_wl_cursor(wayland->cursor.theme, type); + if (cursor == NULL) { + g_info("Failed to load cursor type %d", type); + return; + } + wayland->cursor.cursor = cursor; + } + + g_hash_table_iter_init(&iter, wayland->seats); + while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&seat)) { + if (seat->pointer != NULL) { + wayland_cursor_update_for_seat(seat); + } + } +} + static void wayland_pointer_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface) { wayland_seat *self = data; if (wayland->cursor.frame_cb != NULL) { wl_callback_destroy(wayland->cursor.frame_cb); + wayland->cursor.frame_cb = NULL; } } @@ -952,6 +1060,13 @@ static void wayland_pointer_release(wayland_seat *self) { return; } +#ifdef HAVE_WAYLAND_CURSOR_SHAPE + if (self->cursor_shape_device != NULL) { + wp_cursor_shape_device_v1_destroy(self->cursor_shape_device); + self->cursor_shape_device = NULL; + } +#endif + if (wl_pointer_get_version(self->pointer) >= WL_POINTER_RELEASE_SINCE_VERSION) { wl_pointer_release(self->pointer); @@ -1138,9 +1253,6 @@ static const struct wl_output_listener wayland_output_listener = { #endif }; -static const char *const wayland_cursor_names[] = { - "left_ptr", "default", "top_left_arrow", "left-arrow", NULL}; - static void wayland_registry_handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, @@ -1191,6 +1303,13 @@ static void wayland_registry_handle_global(void *data, wayland->primary_selection_device_manager = wl_registry_bind( registry, name, &zwp_primary_selection_device_manager_v1_interface, 1); } +#ifdef HAVE_WAYLAND_CURSOR_SHAPE + else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) { + wayland->global_names[WAYLAND_GLOBAL_CURSOR_SHAPE] = name; + wayland->cursor_shape_manager = wl_registry_bind( + registry, name, &wp_cursor_shape_manager_v1_interface, 1); + } +#endif } static void wayland_registry_handle_global_remove(void *data, @@ -1208,6 +1327,12 @@ static void wayland_registry_handle_global_remove(void *data, wl_compositor_destroy(wayland->compositor); wayland->compositor = NULL; break; + case WAYLAND_GLOBAL_CURSOR_SHAPE: +#ifdef HAVE_WAYLAND_CURSOR_SHAPE + wp_cursor_shape_manager_v1_destroy(wayland->cursor_shape_manager); + wayland->cursor_shape_manager = NULL; +#endif + break; case WAYLAND_GLOBAL_LAYER_SHELL: zwlr_layer_shell_v1_destroy(wayland->layer_shell); wayland->layer_shell = NULL; @@ -1225,8 +1350,8 @@ static void wayland_registry_handle_global_remove(void *data, ((wayland->compositor == NULL) || (wayland->shm == NULL))) { if (wayland->cursor.frame_cb != NULL) { wl_callback_destroy(wayland->cursor.frame_cb); + wayland->cursor.frame_cb = NULL; } - wayland->cursor.frame_cb = NULL; wl_surface_destroy(wayland->cursor.surface); wl_cursor_theme_destroy(wayland->cursor.theme); @@ -1335,12 +1460,8 @@ static gboolean wayland_cursor_reload_theme(guint scale) { wayland->cursor.theme = wl_cursor_theme_load(wayland->cursor.theme_name, cursor_size, wayland->shm); if (wayland->cursor.theme != NULL) { - const char *const *cname = (const char *const *)wayland->cursor.name; - for (cname = (cname != NULL) ? cname : wayland_cursor_names; - (wayland->cursor.cursor == NULL) && (*cname != NULL); ++cname) { - wayland->cursor.cursor = - wl_cursor_theme_get_cursor(wayland->cursor.theme, *cname); - } + wayland->cursor.cursor = rofi_cursor_type_to_wl_cursor( + wayland->cursor.theme, wayland->cursor.type); if (wayland->cursor.cursor == NULL) { wl_cursor_theme_destroy(wayland->cursor.theme); wayland->cursor.theme = NULL; @@ -1368,6 +1489,7 @@ static gboolean wayland_display_setup(GMainLoop *main_loop, wayland_error, NULL, NULL); wayland->buffer_count = 3; + wayland->cursor.type = ROFI_CURSOR_DEFAULT; wayland->scale = 1; wayland->outputs = g_hash_table_new(g_direct_hash, g_direct_equal); diff --git a/source/wayland/view.c b/source/wayland/view.c index bb23fc2ea..28410eef7 100644 --- a/source/wayland/view.c +++ b/source/wayland/view.c @@ -176,12 +176,6 @@ static void wayland_rofi_view_get_size(RofiViewState *state, gint *width, *height = state->height; } -static void wayland_rofi_view_set_cursor(RofiCursorType type) { - (void)type; - - // TODO -} - static void wayland_rofi_view_ping_mouse(RofiViewState *state) { (void)state; } static gboolean wayland_rofi_view_reload_idle(G_GNUC_UNUSED gpointer data) { @@ -479,7 +473,7 @@ static view_proxy view_ = { .calculate_window_height = wayland_rofi_view_calculate_window_height, .calculate_window_width = wayland_rofi_view_calculate_window_width, .window_update_size = wayland_rofi_view_window_update_size, - .set_cursor = wayland_rofi_view_set_cursor, + .set_cursor = wayland_display_set_cursor_type, .ping_mouse = wayland_rofi_view_ping_mouse, .cleanup = wayland_rofi_view_cleanup,