summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com>2026-01-05 15:05:25 +0100
committerManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com>2026-01-05 15:05:25 +0100
commit522728de8cf1d4f24ab5d56ab400b8d6676228b4 (patch)
treebf7595024fdcb59e4f6db4bc9726d2ed02eef161
parent11325da073a78b5a012e64b9732e253b50289763 (diff)
Wayland: use modern approach to cursor shapes with the "Cursor shape" protocol.
This removes the need to guess names of files each theme gives to cursor shapes and makes linking with dbus superfluous when the compositor supports the new protocol. The old, surface-based approach to cursor shapes remains used for custom shapes.
-rw-r--r--CMake/options.cmake10
-rw-r--r--config.h.in9
-rw-r--r--src/CMakeLists.txt27
-rw-r--r--src/drivers/Wayland/Fl_Wayland_Screen_Driver.H6
-rw-r--r--src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx105
-rw-r--r--src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx13
6 files changed, 154 insertions, 16 deletions
diff --git a/CMake/options.cmake b/CMake/options.cmake
index 589550c73..937bc29f9 100644
--- a/CMake/options.cmake
+++ b/CMake/options.cmake
@@ -2,7 +2,7 @@
# Main CMakeLists.txt to build the FLTK project using CMake (www.cmake.org)
# Originally written by Michael Surette
#
-# Copyright 1998-2025 by Bill Spitzak and others.
+# Copyright 1998-2026 by Bill Spitzak and others.
#
# This library is free software. Distribution and use rights are outlined in
# the file "COPYING" which should have been included with this file. If this
@@ -318,6 +318,14 @@ if(UNIX)
else()
set(HAVE_XDG_DIALOG 0)
endif()
+ if(EXISTS ${PROTOCOLS}/staging/cursor-shape/cursor-shape-v1.xml AND
+ EXISTS ${PROTOCOLS}/stable/tablet/tablet-v2.xml)
+ set(HAVE_CURSOR_SHAPE 1)
+ message(STATUS "Found dev files for Wayland protocols 'Cursor shape' and 'Tablet'")
+ message(STATUS " ==> option FLTK_USE_DBUS can be turned OFF if 'Cursor shape'-enabled wayland compositor is used.")
+ else()
+ set(HAVE_CURSOR_SHAPE 0)
+ endif()
if(FLTK_BACKEND_X11)
include(FindX11)
endif()
diff --git a/config.h.in b/config.h.in
index b48e2cb43..8eebd16ea 100644
--- a/config.h.in
+++ b/config.h.in
@@ -3,7 +3,7 @@
* Configuration file for the Fast Light Tool Kit (FLTK). This file is used
* internally in the FLTK library and is not publicly available (not installed).
*
- * Copyright 1998-2025 by Bill Spitzak and others.
+ * Copyright 1998-2026 by Bill Spitzak and others.
*
* This library is free software. Distribution and use rights are outlined in
* the file "COPYING" which should have been included with this file. If this
@@ -349,3 +349,10 @@
*/
#cmakedefine01 HAVE_XDG_DIALOG
+
+
+/*
+ * Can we use the "Cursor shape" Wayland protocol?
+ */
+
+#cmakedefine01 HAVE_CURSOR_SHAPE
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b5f10cc0d..55f5166b2 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,7 +1,7 @@
#
# CMakeLists.txt to build the FLTK library using CMake (www.cmake.org)
#
-# Copyright 1998-2025 by Bill Spitzak and others.
+# Copyright 1998-2026 by Bill Spitzak and others.
#
# This library is free software. Distribution and use rights are outlined in
# the file "COPYING" which should have been included with this file. If this
@@ -835,6 +835,31 @@ if(UNIX AND FLTK_USE_WAYLAND)
list(APPEND SHARED_FILES "xdg-dialog-protocol.c")
endif()
+ if(HAVE_CURSOR_SHAPE)
+ set(INFILE ${PROTOCOLS}/staging/cursor-shape/cursor-shape-v1.xml)
+ add_custom_command(
+ OUTPUT cursor-shape-protocol.c cursor-shape-client-protocol.h
+ COMMAND wayland-scanner private-code ${INFILE} cursor-shape-protocol.c
+ COMMAND wayland-scanner client-header ${INFILE} cursor-shape-client-protocol.h
+ DEPENDS ${INFILE}
+ VERBATIM
+ )
+ list(APPEND STATIC_FILES "cursor-shape-protocol.c")
+ list(APPEND SHARED_FILES "cursor-shape-protocol.c")
+# File cursor-shape-protocol.c requires memory data allocated by the "Tablet" protocol
+# but that protocol's header file is not necessary for the "cursor shape" protocol itself.
+ set(INFILE ${PROTOCOLS}/stable/tablet/tablet-v2.xml)
+ add_custom_command(
+ OUTPUT tablet-protocol.c # tablet-client-protocol.h
+ COMMAND wayland-scanner private-code ${INFILE} tablet-protocol.c
+# COMMAND wayland-scanner client-header ${INFILE} tablet-client-protocol.h
+ DEPENDS ${INFILE}
+ VERBATIM
+ )
+ list(APPEND STATIC_FILES "tablet-protocol.c")
+ list(APPEND SHARED_FILES "tablet-protocol.c")
+ endif()
+
if(STOP_REQUIRED)
message(FATAL_ERROR "*** Terminating: one or more required file(s) were not found. ***")
endif()
diff --git a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.H b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.H
index 084e0c967..8ef7a1e10 100644
--- a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.H
+++ b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.H
@@ -2,7 +2,7 @@
// Definition of the Wayland Screen interface
// for the Fast Light Tool Kit (FLTK).
//
-// Copyright 2010-2024 by Bill Spitzak and others.
+// Copyright 2010-2026 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@@ -110,6 +110,10 @@ public:
#if HAVE_XDG_DIALOG
struct xdg_wm_dialog_v1 *xdg_wm_dialog;
#endif
+#if HAVE_CURSOR_SHAPE
+ struct wp_cursor_shape_manager_v1 *wp_cursor_shape_manager;
+ struct wp_cursor_shape_device_v1 *wp_cursor_shape_device;
+#endif
// constructor
Fl_Wayland_Screen_Driver();
diff --git a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx
index a0911cb51..ea4c8f97c 100644
--- a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx
+++ b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx
@@ -1,7 +1,7 @@
//
// Implementation of Wayland Screen interface
//
-// Copyright 1998-2025 by Bill Spitzak and others.
+// Copyright 1998-2026 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@@ -39,6 +39,9 @@
#if HAVE_XDG_DIALOG
# include "xdg-dialog-client-protocol.h"
#endif
+#if HAVE_CURSOR_SHAPE
+# include "cursor-shape-client-protocol.h"
+#endif
#include <assert.h>
#include <sys/mman.h>
#include <poll.h>
@@ -127,7 +130,7 @@ void Fl_Wayland_Screen_Driver::do_set_cursor(
struct wl_buffer *buffer;
const int scale = seat->pointer_scale;
- if (!seat->cursor_theme || !seat->wl_pointer)
+ if ((!seat->cursor_theme && !wl_cursor) || !seat->wl_pointer)
return;
if (!wl_cursor) wl_cursor = seat->default_cursor;
@@ -226,7 +229,48 @@ static void pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t se
// use custom cursor if present
struct wl_cursor *cursor =
fl_wl_xid(win)->custom_cursor ? fl_wl_xid(win)->custom_cursor->wl_cursor : NULL;
- Fl_Wayland_Screen_Driver::do_set_cursor(seat, cursor);
+#if HAVE_CURSOR_SHAPE
+ static struct cursor_shape_struct {
+ Fl_Cursor c;
+ int shape;
+ } cursor_shape_array[] = {
+ {FL_CURSOR_ARROW, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT },
+ {FL_CURSOR_CROSS, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR },
+ {FL_CURSOR_WAIT, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_WAIT },
+ {FL_CURSOR_INSERT, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT },
+ {FL_CURSOR_HAND, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRAB },
+ {FL_CURSOR_HELP, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_HELP },
+ {FL_CURSOR_MOVE, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE },
+ {FL_CURSOR_N, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_N_RESIZE },
+ {FL_CURSOR_E, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_E_RESIZE },
+ {FL_CURSOR_W, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_W_RESIZE },
+ {FL_CURSOR_S, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_S_RESIZE },
+ {FL_CURSOR_NS, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NS_RESIZE },
+ {FL_CURSOR_WE, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_EW_RESIZE },
+ {FL_CURSOR_SW, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SW_RESIZE },
+ {FL_CURSOR_SE, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SE_RESIZE },
+ {FL_CURSOR_NE, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NE_RESIZE },
+ {FL_CURSOR_NW, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NW_RESIZE },
+ {FL_CURSOR_NESW, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NESW_RESIZE },
+ {FL_CURSOR_NWSE, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NWSE_RESIZE }
+ };
+
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ if (scr_driver->wp_cursor_shape_device && !cursor) {
+ int shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT;
+ for (int i = 0; i < sizeof(cursor_shape_array)/sizeof(cursor_shape_struct); i++) {
+ if (cursor_shape_array[i].c == Fl_Wayland_Window_Driver::driver(win)->standard_cursor()) {
+ shape = cursor_shape_array[i].shape;
+ break;
+ }
+ }
+ wp_cursor_shape_device_v1_set_shape(scr_driver->wp_cursor_shape_device, serial, shape);
+ } else {
+#endif
+ Fl_Wayland_Screen_Driver::do_set_cursor(seat, cursor);
+#if HAVE_CURSOR_SHAPE
+ }
+#endif
seat->serial = serial;
seat->pointer_enter_serial = serial;
set_event_xy(win);
@@ -472,6 +516,15 @@ static struct wl_surface_listener cursor_surface_listener = {
static void init_cursors(struct Fl_Wayland_Screen_Driver::seat *seat) {
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ if (!seat->cursor_surface) {
+ seat->cursor_surface = wl_compositor_create_surface(scr_driver->wl_compositor);
+ wl_surface_add_listener(seat->cursor_surface, &cursor_surface_listener, seat);
+ }
+#if HAVE_CURSOR_SHAPE
+ if (scr_driver->wp_cursor_shape_manager) return;
+#endif
+
char *name;
int size;
struct wl_cursor_theme *theme;
@@ -481,10 +534,8 @@ static void init_cursors(struct Fl_Wayland_Screen_Driver::seat *seat) {
size = 24;
}
size *= seat->pointer_scale;
- Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
theme = wl_cursor_theme_load(name, size, scr_driver->wl_shm);
free(name);
- //struct wl_cursor_theme *old_theme = seat->cursor_theme;
if (theme != NULL) {
if (seat->cursor_theme) {
// caution to destroy theme because Fl_Wayland_Window_Driver::set_cursor(Fl_Cursor) caches used cursors
@@ -497,10 +548,6 @@ static void init_cursors(struct Fl_Wayland_Screen_Driver::seat *seat) {
seat->default_cursor = scr_driver->xc_cursor[Fl_Wayland_Screen_Driver::arrow] =
wl_cursor_theme_get_cursor(seat->cursor_theme, "left_ptr");
}
- if (!seat->cursor_surface) {
- seat->cursor_surface = wl_compositor_create_surface(scr_driver->wl_compositor);
- wl_surface_add_listener(seat->cursor_surface, &cursor_surface_listener, seat);
- }
}
@@ -1043,12 +1090,19 @@ void Fl_Wayland_Screen_Driver::disable_im() {
static void seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities)
{
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
struct Fl_Wayland_Screen_Driver::seat *seat =
(struct Fl_Wayland_Screen_Driver::seat*)data;
if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !seat->wl_pointer) {
seat->wl_pointer = wl_seat_get_pointer(wl_seat);
wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, seat);
seat->pointer_scale = 1;
+#if HAVE_CURSOR_SHAPE
+ if (scr_driver->wp_cursor_shape_manager) {
+ scr_driver->wp_cursor_shape_device =
+ wp_cursor_shape_manager_v1_get_pointer(scr_driver->wp_cursor_shape_manager, seat->wl_pointer);
+ }
+#endif // HAVE_CURSOR_SHAPE
init_cursors(seat);
} else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && seat->wl_pointer) {
wl_pointer_release(seat->wl_pointer);
@@ -1066,7 +1120,6 @@ static void seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capa
wl_keyboard_release(seat->wl_keyboard);
seat->wl_keyboard = NULL;
}
- Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
scr_driver->enable_im();
}
@@ -1334,6 +1387,11 @@ static void registry_handle_global(void *user_data, struct wl_registry *wl_regis
scr_driver->xdg_wm_dialog = (struct xdg_wm_dialog_v1 *)
wl_registry_bind(wl_registry, id, &xdg_wm_dialog_v1_interface, 1);
#endif // HAVE_XDG_DIALOG
+#if HAVE_CURSOR_SHAPE
+ } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {
+ scr_driver->wp_cursor_shape_manager = (struct wp_cursor_shape_manager_v1 *)
+ wl_registry_bind(wl_registry, id, &wp_cursor_shape_manager_v1_interface, 1);
+#endif // HAVE_CURSOR_SHAPE
}
}
@@ -1412,6 +1470,10 @@ Fl_Wayland_Screen_Driver::Fl_Wayland_Screen_Driver() : Fl_Unix_Screen_Driver() {
#if HAVE_XDG_DIALOG
xdg_wm_dialog = NULL;
#endif
+#if HAVE_CURSOR_SHAPE
+ wp_cursor_shape_manager = NULL;
+ wp_cursor_shape_device = NULL;
+#endif
}
@@ -1432,7 +1494,12 @@ static void sync_done(void *data, struct wl_callback *cb, uint32_t time) {
Fl_Wayland_Screen_Driver::compositor = (pair->found_wf_shell ?
Fl_Wayland_Screen_Driver::WAYFIRE : Fl_Wayland_Screen_Driver::MUTTER);
}
- if (scr_driver->seat) try_update_cursor(scr_driver->seat);
+ if (scr_driver->seat) {
+#if HAVE_CURSOR_SHAPE
+ if (!scr_driver->wp_cursor_shape_manager)
+#endif
+ try_update_cursor(scr_driver->seat);
+ }
if (Fl_Wayland_Screen_Driver::compositor != Fl_Wayland_Screen_Driver::OWL) scr_driver->init_workarea();
}
@@ -1566,6 +1633,22 @@ void Fl_Wayland_Screen_Driver::close_display() {
xdg_wm_base_destroy(xdg_wm_base); xdg_wm_base = NULL;
Fl_Wayland_Plugin *plugin = Fl_Wayland_Window_Driver::gl_plugin();
if (plugin) plugin->terminate();
+#if HAVE_XDG_DIALOG
+ if (xdg_wm_dialog) {
+ xdg_wm_dialog_v1_destroy(xdg_wm_dialog);
+ xdg_wm_dialog = NULL;
+ }
+#endif // HAVE_XDG_DIALOG
+#if HAVE_CURSOR_SHAPE
+ if (wp_cursor_shape_device ) {
+ wp_cursor_shape_device_v1_destroy(wp_cursor_shape_device);
+ wp_cursor_shape_device = NULL;
+ }
+ if (wp_cursor_shape_manager ) {
+ wp_cursor_shape_manager_v1_destroy(wp_cursor_shape_manager);
+ wp_cursor_shape_manager = NULL;
+ }
+#endif // HAVE_CURSOR_SHAPE
Fl::remove_fd(wl_display_get_fd(Fl_Wayland_Screen_Driver::wl_display));
wl_registry_destroy(wl_registry); wl_registry = NULL;
diff --git a/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx
index 7cd9810a9..9b709ee0a 100644
--- a/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx
+++ b/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx
@@ -1,7 +1,7 @@
//
// Implementation of the Wayland window driver.
//
-// Copyright 1998-2025 by Bill Spitzak and others.
+// Copyright 1998-2026 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@@ -1682,6 +1682,17 @@ void Fl_Wayland_Window_Driver::makeWindow()
int Fl_Wayland_Window_Driver::set_cursor(Fl_Cursor c) {
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
struct wld_window *xid = (struct wld_window *)Fl_Window_Driver::xid(pWindow);
+#if HAVE_CURSOR_SHAPE
+ if (scr_driver->wp_cursor_shape_device) {
+ if (xid->custom_cursor) {
+ delete_cursor(xid->custom_cursor);
+ xid->custom_cursor = NULL;
+ }
+ if (c == FL_CURSOR_NONE) return 0;
+ standard_cursor_ = c;
+ return 1;
+ }
+#endif // HAVE_CURSOR_SHAPE
if (!scr_driver->seat->cursor_theme) return 1;
// Cursor names are the files of directory /usr/share/icons/XXXX/cursors/
// where XXXX is the name of the current 'cursor theme'.