diff --git a/CMake/options.cmake b/CMake/options.cmake index c1aa0dcea..b27f9adf5 100644 --- a/CMake/options.cmake +++ b/CMake/options.cmake @@ -297,7 +297,7 @@ if(UNIX) endif() unset(FLTK_GRAPHICS_CAIRO CACHE) set(FLTK_GRAPHICS_CAIRO TRUE CACHE BOOL "all drawing to X11 windows uses Cairo") - option(FLTK_USE_SYSTEM_LIBDECOR "use libdecor from the system" OFF) + option(FLTK_USE_SYSTEM_LIBDECOR "use libdecor from the system" ON) set(USE_SYSTEM_LIBDECOR 1) unset(FLTK_USE_XRENDER CACHE) unset(FLTK_USE_XINERAMA CACHE) @@ -333,9 +333,9 @@ if(UNIX) unset(FLTK_USE_PANGO CACHE) set(FLTK_USE_PANGO TRUE CACHE BOOL "use lib Pango") if(FLTK_USE_SYSTEM_LIBDECOR) - pkg_check_modules(SYSTEM_LIBDECOR IMPORTED_TARGET libdecor-0>0.2.2 QUIET) + pkg_check_modules(SYSTEM_LIBDECOR libdecor-0>=0.2.0 QUIET) if(NOT SYSTEM_LIBDECOR_FOUND) - message(STATUS "Warning: system libdecor doesn't satisfy version > 0.2.2,") + message(STATUS "Warning: system libdecor doesn't satisfy version ≥ 0.2.0,") message(STATUS " using bundled libdecor library instead.") set(USE_SYSTEM_LIBDECOR 0) else() diff --git a/README.CMake.txt b/README.CMake.txt index a915b3b23..03325a9e4 100644 --- a/README.CMake.txt +++ b/README.CMake.txt @@ -282,12 +282,10 @@ FLTK_USE_PTHREADS - default ON except on Windows. This option is ignored (switched OFF internally) on Windows except when using Cygwin. -FLTK_USE_SYSTEM_LIBDECOR - default OFF (Wayland only) +FLTK_USE_SYSTEM_LIBDECOR - default ON (Wayland only) This option makes FLTK use package libdecor-0-dev to draw window titlebars - under Wayland. When OFF or when this package has a version ≤ 0.2.2, FLTK + under Wayland. When OFF or when this package has a version < 0.2.0, FLTK uses its bundled copy of libdecor to draw window titlebars. - As of early 2024, no version > 0.2.2 of package libdecor-0-dev is available - yet. FLTK_USE_SYSTEM_LIBJPEG - default ON (macOS and Windows: OFF) FLTK_USE_SYSTEM_LIBPNG - default ON (macOS and Windows: OFF) diff --git a/README.Wayland.txt b/README.Wayland.txt index dc4c24c36..b88184468 100644 --- a/README.Wayland.txt +++ b/README.Wayland.txt @@ -126,8 +126,9 @@ cross-compiling for systems that lack X11 headers and libraries. The FLTK Wayland platform uses a library called libdecor which handles window decorations (i.e., titlebars, shade). On very recent Linux distributions (e.g., Debian trixie) libdecor is available as Linux packages (libdecor-0-dev and libdecor-0-plugin-1-gtk). -FLTK requires a version > 0.2.2 of these packages that's not yet available. -Therefore, FLTK uses a copy of libdecor bundled in the FLTK source code. +FLTK requires version 0.2.0 or more recent of these packages. +When libdecor is not available or not recent enough, FLTK uses a copy of libdecor +bundled in the FLTK source code. FLTK equipped with libdecor supports both the client-side decoration mode (CSD) and the server-side decoration mode (SSD) as determined by the active Wayland compositor. Mutter (gnome's Wayland compositor) and Weston use CSD mode, KWin and Sway use SSD mode. diff --git a/configure.ac b/configure.ac index 9429b0458..ad2dd5996 100644 --- a/configure.ac +++ b/configure.ac @@ -1092,7 +1092,7 @@ AS_CASE([$host_os_gui], [cygwin* | mingw*], [ BUILD="WAYLANDX11" graphics="Wayland or X11 with cairo" ]) - AS_IF([$PKGCONFIG --exists 'libdecor-0 > 0.2.2'], + AS_IF([$PKGCONFIG --exists 'libdecor-0 >= 0.2.0'], [ plugin_dir="$($PKGCONFIG --variable=libdir libdecor-0)/libdecor/plugins-1" CFLAGS="$CFLAGS -DUSE_SYSTEM_LIBDECOR" diff --git a/documentation/src/wayland.dox b/documentation/src/wayland.dox index 8af4555de..3990556b8 100644 --- a/documentation/src/wayland.dox +++ b/documentation/src/wayland.dox @@ -1108,16 +1108,13 @@ Desktop. As of late 2023, libdecor contains two titlebar-drawing plugins: - \c libdecor-gtk intended for the Gnome desktop; - \c libdecor-cairo for other situations. -Because \c libdecor is not yet in major Linux packages, or only at version 0.1.x, -FLTK bundles the most recent source code of \c libdecor and its plugins. This code +On recent Linux distributions, FLTK uses the system \c libdecor shared library +available via packages \c libdecor-0-dev and \c libdecor-0-plugin-1-gtk. +On earlier Linux versions, or if CMake option \c FLTK_USE_SYSTEM_LIBDECOR is set +to OFF, FLTK bundles the most recent source code of \c libdecor and its plugins. This code is included in libfltk. FLTK uses \c libdecor-gtk when software package \c libgtk-3-dev is present in the build system, and \c libdecor-cairo otherwise. -As of early 2024, libdecor version 0.2.2 is available in very recent Linux distributions. -This version is not binary compatible with the libdecor version bundled by FLTK. -For this reason, CMake option \c FLTK_USE_SYSTEM_LIBDECOR is OFF by default, and -FLTK uses the bundled \c libdecor copy to draw titlebars. - \c Libdecor uses the Wayland protocol XDG decoration to request being decorated by a supporting compositor. diff --git a/libdecor/build/fl_libdecor-plugins.c b/libdecor/build/fl_libdecor-plugins.c index 1e73d736a..60f6bf56e 100644 --- a/libdecor/build/fl_libdecor-plugins.c +++ b/libdecor/build/fl_libdecor-plugins.c @@ -317,13 +317,34 @@ struct libdecor { // copied from libdecor.c, for libdecor versions > 0.2.2 struct wl_list frames; }; +struct libdecor_022 { // for libdecor versions ≤ 0.2.2 + int ref_count; + const struct libdecor_interface *iface; + struct libdecor_plugin *plugin; + bool plugin_ready; + struct wl_display *wl_display; + struct wl_registry *wl_registry; + struct xdg_wm_base *xdg_wm_base; + struct zxdg_decoration_manager_v1 *decoration_manager; + struct wl_callback *init_callback; + bool init_done; + bool has_error; + struct wl_list frames; +}; + /* Returns whether surface is a GTK-titlebar created by libdecor-gtk */ -bool fl_is_surface_gtk_titlebar(struct wl_surface *surface, struct libdecor *context) { +bool fl_is_surface_gtk_titlebar(struct wl_surface *surface, struct libdecor *context, + struct wl_display *wl_display) { if (!context || get_plugin_kind(NULL) != GTK3) return false; // loop over all decorations created by libdecor-gtk struct libdecor_frame *frame; - wl_list_for_each(frame, &context->frames, link) { + struct wl_list *frames; + if (context->wl_display == wl_display) frames = &context->frames; + else if (((struct libdecor_022*)context)->wl_display == wl_display) + frames = &(((struct libdecor_022*)context)->frames); + else return false; + wl_list_for_each(frame, frames, link) { struct libdecor_frame_gtk *frame_gtk = (struct libdecor_frame_gtk*)frame; if (frame_gtk->headerbar.wl_surface == surface) return true; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b01ee09ab..81a7b6605 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -756,7 +756,7 @@ if(UNIX AND FLTK_BACKEND_WAYLAND) list(APPEND OPTIONAL_LIBS PkgConfig::WLD_EGL PkgConfig::PKG_EGL) endif(FLTK_USE_GL) if(USE_SYSTEM_LIBDECOR) - list(APPEND OPTIONAL_LIBS PkgConfig::SYSTEM_LIBDECOR) + list(APPEND OPTIONAL_LIBS ${SYSTEM_LIBDECOR_LDFLAGS}) elseif(GTK_FOUND AND FLTK_USE_LIBDECOR_GTK) list(APPEND OPTIONAL_LIBS PkgConfig::GTK) endif(USE_SYSTEM_LIBDECOR) diff --git a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx index efbb176ab..60011393d 100644 --- a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx +++ b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx @@ -47,7 +47,7 @@ #include // for strerror() extern "C" { bool libdecor_get_cursor_settings(char **theme, int *size); - bool fl_is_surface_gtk_titlebar(struct wl_surface *, struct libdecor *); + bool fl_is_surface_gtk_titlebar(struct wl_surface *, struct libdecor *, struct wl_display *); } // set this to 1 for keyboard debug output, 0 for no debug output @@ -206,7 +206,8 @@ static void pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t se Fl_Window *win = event_coords_from_surface(surface, surface_x, surface_y); if (!win && gtk_shell) { // check that surface is the headerbar of a GTK-decorated window Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - if (fl_is_surface_gtk_titlebar(surface, scr_driver->libdecor_context)) { + if (fl_is_surface_gtk_titlebar(surface, scr_driver->libdecor_context, + Fl_Wayland_Screen_Driver::wl_display)) { gtk_shell_surface = surface; } } diff --git a/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx index e8a0a1af9..ff1682e31 100644 --- a/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx +++ b/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx @@ -769,6 +769,69 @@ static struct Fl_Wayland_Screen_Driver::output *screen_num_to_output(int num_scr } +#define LIBDECOR_MR131 1 // this means libdecor does not include MR!131 yet + +#ifdef LIBDECOR_MR131 +/* === Beginning of hack that would become un-needed if libdecor accepted MR!131 === */ + +// true while the GUI is interactively resizing a decorated window +static bool in_decorated_window_resizing = false; + + +// libdecor's configure cb function for xdg_toplevel objects +static void (*decor_xdg_toplevel_configure)(void*, struct xdg_toplevel *, int32_t, + int32_t, struct wl_array *); + + +static void fltk_xdg_toplevel_configure(void *user_data, struct xdg_toplevel *xdg_toplevel, + int32_t width, int32_t height, + struct wl_array *states) { + uint32_t *p; + in_decorated_window_resizing = false; + // Replace wl_array_for_each(p, states) rejected by C++ + for (p = (uint32_t *)(states)->data; + (const char *) p < ((const char *) (states)->data + (states)->size); + (p)++) { + if (*p == XDG_TOPLEVEL_STATE_RESIZING) { + in_decorated_window_resizing = true; + break; + } + } + decor_xdg_toplevel_configure(user_data, xdg_toplevel, width, height, states); +} + + +struct wl_object { // copied from wayland-private.h + const struct wl_interface *interface; + const void *implementation; + uint32_t id; +}; + + +// replace libdecor's toplevel configure cb by FLTK's +static void use_FLTK_toplevel_configure_cb(struct libdecor_frame *frame) { + struct wl_object *object = (struct wl_object *)libdecor_frame_get_xdg_toplevel(frame); + static struct xdg_toplevel_listener *fltk_listener = NULL; + if (!fltk_listener) { + struct xdg_toplevel_listener *decor_listener = (struct xdg_toplevel_listener*) + object->implementation; + fltk_listener = (struct xdg_toplevel_listener*) + malloc(sizeof(struct xdg_toplevel_listener)); + // initialize FLTK's listener with libdecor's values + *fltk_listener = *decor_listener; + // memorize libdecor's toplevel configure cb + decor_xdg_toplevel_configure = decor_listener->configure; + // replace libdecor's toplevel configure cb by FLTK's + fltk_listener->configure = fltk_xdg_toplevel_configure; + } + // replace the toplevel listener by a copy whose configure member is FLTK's + object->implementation = fltk_listener; +} + +/* === End of hack that would become un-needed if libdecor accepted MR!131 === */ +#endif // LIBDECOR_MR131 + + static void handle_configure(struct libdecor_frame *frame, struct libdecor_configuration *configuration, void *user_data) { @@ -786,6 +849,9 @@ static void handle_configure(struct libdecor_frame *frame, if (!window->xdg_surface) window->xdg_surface = libdecor_frame_get_xdg_surface(frame); +#ifdef LIBDECOR_MR131 + if (is_1st_run) use_FLTK_toplevel_configure_cb(frame); +#endif struct wl_output *wl_output = NULL; if (window->fl_win->fullscreen_active()) { if (!(window->state & LIBDECOR_WINDOW_STATE_FULLSCREEN)) { @@ -839,7 +905,10 @@ static void handle_configure(struct libdecor_frame *frame, //fprintf(stderr,"handle_configure: using floating %dx%d\n",width,height); } - bool condition = (window->state & LIBDECOR_WINDOW_STATE_RESIZING); +#ifndef LIBDECOR_MR131 + bool in_decorated_window_resizing = (window->state & LIBDECOR_WINDOW_STATE_RESIZING); +#endif + bool condition = in_decorated_window_resizing; if (condition) { // see issue #878 condition = (window->covered ? (window->buffer && window->buffer->in_use) : (window->frame_cb != NULL)); }