From 4a2c5a2c76c861ed68da800bc743870f87d04d8b Mon Sep 17 00:00:00 2001 From: Michael Webster Date: Fri, 16 Jan 2026 10:45:56 -0500 Subject: [PATCH 1/3] Implement gtk-layer-shell. ref: https://github.com/linuxmint/muffin/pull/784 todo: Workarea margins - in x11 mode nemo gets accurate, per-monitor workarea from Cinnamon over dbus, with _NET_WORKAREA values as a backup (which is only accurate for the primary monitor). From this, workarea padding is calculated and applied to the icon layouts. For wayland we can maybe utilize the layer-shell margin properties instead, but some refactoring is needed to provide the individual nemo-desktop windows with this (and keeping it up-to-date, should workarea change). --- config.h.meson.in | 2 ++ debian/control | 1 + debian/rules | 1 + meson.build | 8 ++++++ meson_options.txt | 2 ++ src/meson.build | 4 +++ src/nemo-blank-desktop-window.c | 44 +++++++++++++++++++++++++++++-- src/nemo-desktop-main.c | 2 ++ src/nemo-desktop-window.c | 46 ++++++++++++++++++++++++++++++--- 9 files changed, 105 insertions(+), 5 deletions(-) diff --git a/config.h.meson.in b/config.h.meson.in index 8b49bbd10..910413897 100644 --- a/config.h.meson.in +++ b/config.h.meson.in @@ -19,6 +19,8 @@ #mesondefine HAVE_SELINUX // Define to enable pango-1.44 fixes #mesondefine HAVE_PANGO_144 +// Define to use gtk-layer-shell +#mesondefine HAVE_GTK_LAYER_SHELL diff --git a/debian/control b/debian/control index b250e40d1..f6f4cf903 100644 --- a/debian/control +++ b/debian/control @@ -21,6 +21,7 @@ Build-Depends: libgsf-1-dev, libgtk-3-dev (>= 3.10), libgtk-3-doc, + libgtk-layer-shell-dev, libjson-glib-dev (>= 1.6), libpango1.0-dev, libx11-dev, diff --git a/debian/rules b/debian/rules index 6c68fb92b..3a73818b4 100755 --- a/debian/rules +++ b/debian/rules @@ -8,6 +8,7 @@ CONFIGURE_EXTRA_FLAGS = \ --buildtype=debugoptimized \ -D deprecated_warnings=false \ -D gtk_doc=true \ + -D gtk_layer_shell=true \ -D selinux=false export DEB_LDFLAGS_MAINT_APPEND = -Wl,-z,defs -Wl,-O1 -Wl,--as-needed diff --git a/meson.build b/meson.build index 53ad80c0c..80242f91e 100644 --- a/meson.build +++ b/meson.build @@ -133,6 +133,13 @@ if libselinux_enabled endif conf.set('HAVE_SELINUX', libselinux_enabled) +gtk_layer_shell_enabled = get_option('gtk_layer_shell') +gtk_layer_shell = dependency('', required: false) +if gtk_layer_shell_enabled + gtk_layer_shell = dependency('gtk-layer-shell-0', version: '>=0.8', required: true) + conf.set('HAVE_GTK_LAYER_SHELL', gtk_layer_shell.found()) +endif + # make sure pango development files are installed pango = dependency('pango', version: '>=1.40.0') # check for newer pango for necessary workarounds @@ -201,6 +208,7 @@ message('\n'.join(['', ' exempi support: @0@'.format(exempi_enabled), ' Tracker support: @0@'.format(tracker_enabled), ' Wayland support: @0@'.format(cc.has_header('gdk/gdkwayland.h', dependencies: gtk)), +' gtk-layer-shell: @0@'.format(gtk_layer_shell_enabled), '', ' nemo-extension documentation: @0@'.format(gtkdoc_enabled), ' nemo-extension introspection: @0@'.format(true), diff --git a/meson_options.txt b/meson_options.txt index 68814d64d..c3122fc2a 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -12,3 +12,5 @@ option('empty_view', type : 'boolean', value : false, description: 'Enable empty view') option('tracker',type : 'combo', choices : ['true', 'false', 'auto'], value : 'false', description: 'Tracker support') +option('gtk_layer_shell', type : 'boolean', value : false, + description: 'Use gtk-layer-shell to draw desktop on wayland') diff --git a/src/meson.build b/src/meson.build index 07a56957d..464f2fc7f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -113,6 +113,10 @@ if libexif_enabled nemo_deps += libexif endif +if gtk_layer_shell_enabled + nemo_deps += gtk_layer_shell +endif + nemo = executable('nemo', nemoCommon_sources + nemoWindow_sources, include_directories: [ rootInclude ], diff --git a/src/nemo-blank-desktop-window.c b/src/nemo-blank-desktop-window.c index f6d6e545d..cbcf420cf 100644 --- a/src/nemo-blank-desktop-window.c +++ b/src/nemo-blank-desktop-window.c @@ -30,6 +30,10 @@ #include #include +#ifdef HAVE_GTK_LAYER_SHELL +#include +#endif + #include #include #include @@ -402,7 +406,15 @@ nemo_blank_desktop_window_init (NemoBlankDesktopWindow *window) window->details->popup_menu = NULL; window->details->actions_changed_id = 0; - gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DESKTOP); + +#ifdef HAVE_GTK_LAYER_SHELL + if (gtk_layer_is_supported ()) { + gtk_layer_init_for_window (GTK_WINDOW (window)); + } else +#endif + { + gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DESKTOP); + } /* Make it easier for themes authors to style the desktop window separately */ gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (window)), "nemo-desktop-window"); @@ -436,7 +448,13 @@ map (GtkWidget *widget) { /* Chain up to realize our children */ GTK_WIDGET_CLASS (nemo_blank_desktop_window_parent_class)->map (widget); - gdk_window_lower (gtk_widget_get_window (widget)); + +#ifdef HAVE_GTK_LAYER_SHELL + if (!gtk_layer_is_layer_window (GTK_WINDOW (widget))) +#endif + { + gdk_window_lower (gtk_widget_get_window (widget)); + } GdkWindow *window; GdkRGBA transparent = { 0, 0, 0, 0 }; @@ -499,6 +517,28 @@ nemo_blank_desktop_window_update_geometry (NemoBlankDesktopWindow *window) { GdkRectangle rect; +#ifdef HAVE_GTK_LAYER_SHELL + if (gtk_layer_is_layer_window (GTK_WINDOW (window))) { + GdkDisplay *display = gdk_display_get_default (); + GdkMonitor *monitor = gdk_display_get_monitor (display, window->details->monitor); + + gtk_layer_set_layer (GTK_WINDOW (window), GTK_LAYER_SHELL_LAYER_BOTTOM); + gtk_layer_set_namespace (GTK_WINDOW (window), "nemo-desktop"); + gtk_layer_set_keyboard_mode (GTK_WINDOW (window), GTK_LAYER_SHELL_KEYBOARD_MODE_NONE); + gtk_layer_set_exclusive_zone (GTK_WINDOW (window), -1); + gtk_layer_set_anchor (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_TOP, TRUE); + gtk_layer_set_anchor (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_BOTTOM, TRUE); + gtk_layer_set_anchor (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_LEFT, TRUE); + gtk_layer_set_anchor (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_RIGHT, TRUE); + + if (monitor) { + gtk_layer_set_monitor (GTK_WINDOW (window), monitor); + } + + return; + } +#endif + nemo_desktop_manager_get_window_rect_for_monitor (nemo_desktop_manager_get (), window->details->monitor, &rect); diff --git a/src/nemo-desktop-main.c b/src/nemo-desktop-main.c index 5bbcad36a..d5fe2721e 100644 --- a/src/nemo-desktop-main.c +++ b/src/nemo-desktop-main.c @@ -91,7 +91,9 @@ main (int argc, char *argv[]) g_set_prgname ("nemo-desktop"); +#ifndef HAVE_GTK_LAYER_SHELL gdk_set_allowed_backends ("x11"); +#endif #ifdef HAVE_EXEMPI xmp_init(); diff --git a/src/nemo-desktop-window.c b/src/nemo-desktop-window.c index 22f5040a3..b7ecec8e9 100644 --- a/src/nemo-desktop-window.c +++ b/src/nemo-desktop-window.c @@ -36,6 +36,10 @@ #include #include +#ifdef HAVE_GTK_LAYER_SHELL +#include +#endif + #include #include #include @@ -177,12 +181,20 @@ nemo_desktop_window_init (NemoDesktopWindow *window) window->details = G_TYPE_INSTANCE_GET_PRIVATE (window, NEMO_TYPE_DESKTOP_WINDOW, NemoDesktopWindowDetails); - g_object_set_data (G_OBJECT (window), "is_desktop_window", + g_object_set_data (G_OBJECT (window), "is_desktop_window", GINT_TO_POINTER (1)); /* Make it easier for themes authors to style the desktop window separately */ gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (window)), "nemo-desktop-window"); - gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DESKTOP); + +#ifdef HAVE_GTK_LAYER_SHELL + if (gtk_layer_is_supported ()) { + gtk_layer_init_for_window (GTK_WINDOW (window)); + } else +#endif + { + gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DESKTOP); + } } NemoDesktopWindow * @@ -230,7 +242,13 @@ map (GtkWidget *widget) { /* Chain up to realize our children */ GTK_WIDGET_CLASS (nemo_desktop_window_parent_class)->map (widget); - gdk_window_lower (gtk_widget_get_window (widget)); + +#ifdef HAVE_GTK_LAYER_SHELL + if (!gtk_layer_is_layer_window (GTK_WINDOW (widget))) +#endif + { + gdk_window_lower (gtk_widget_get_window (widget)); + } GdkWindow *window; GdkRGBA transparent = { 0, 0, 0, 0 }; @@ -335,6 +353,28 @@ nemo_desktop_window_update_geometry (NemoDesktopWindow *window) { GdkRectangle rect; +#ifdef HAVE_GTK_LAYER_SHELL + if (gtk_layer_is_layer_window (GTK_WINDOW (window))) { + GdkDisplay *display = gdk_display_get_default (); + GdkMonitor *monitor = gdk_display_get_monitor (display, window->details->monitor); + + gtk_layer_set_layer (GTK_WINDOW (window), GTK_LAYER_SHELL_LAYER_BOTTOM); + gtk_layer_set_namespace (GTK_WINDOW (window), "nemo-desktop"); + gtk_layer_set_keyboard_mode (GTK_WINDOW (window), GTK_LAYER_SHELL_KEYBOARD_MODE_ON_DEMAND); + gtk_layer_set_exclusive_zone (GTK_WINDOW (window), -1); + gtk_layer_set_anchor (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_TOP, TRUE); + gtk_layer_set_anchor (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_BOTTOM, TRUE); + gtk_layer_set_anchor (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_LEFT, TRUE); + gtk_layer_set_anchor (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_RIGHT, TRUE); + + if (monitor) { + gtk_layer_set_monitor (GTK_WINDOW (window), monitor); + } + + return; + } +#endif + nemo_desktop_manager_get_window_rect_for_monitor (nemo_desktop_manager_get (), window->details->monitor, &rect); From 24bff6259c1a49b53830377f64c6240b8e9ae296 Mon Sep 17 00:00:00 2001 From: Michael Webster Date: Sun, 18 Jan 2026 19:16:10 -0500 Subject: [PATCH 2/3] gtk-layer-shell: Use zone 0 to get reliable workarea sizing. In a proper wlr-layer-shell implementation, exclusive zone 0 ensures the nemo-desktop window and icon layout are kept within the desktop workarea (avoiding panels or other layer-shell clients with zone > 0). Use this capability if available. --- src/nemo-blank-desktop-window.c | 5 ++++- src/nemo-desktop-manager.c | 27 +++++++++++++++++++++++---- src/nemo-desktop-window.c | 5 ++++- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/nemo-blank-desktop-window.c b/src/nemo-blank-desktop-window.c index cbcf420cf..2d9aaf869 100644 --- a/src/nemo-blank-desktop-window.c +++ b/src/nemo-blank-desktop-window.c @@ -522,10 +522,13 @@ nemo_blank_desktop_window_update_geometry (NemoBlankDesktopWindow *window) GdkDisplay *display = gdk_display_get_default (); GdkMonitor *monitor = gdk_display_get_monitor (display, window->details->monitor); + DEBUG ("NemoBlankDesktopWindow: using layer-shell for monitor %d", + window->details->monitor); + gtk_layer_set_layer (GTK_WINDOW (window), GTK_LAYER_SHELL_LAYER_BOTTOM); gtk_layer_set_namespace (GTK_WINDOW (window), "nemo-desktop"); gtk_layer_set_keyboard_mode (GTK_WINDOW (window), GTK_LAYER_SHELL_KEYBOARD_MODE_NONE); - gtk_layer_set_exclusive_zone (GTK_WINDOW (window), -1); + gtk_layer_set_exclusive_zone (GTK_WINDOW (window), 0); gtk_layer_set_anchor (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_TOP, TRUE); gtk_layer_set_anchor (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_BOTTOM, TRUE); gtk_layer_set_anchor (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_LEFT, TRUE); diff --git a/src/nemo-desktop-manager.c b/src/nemo-desktop-manager.c index 86928e5b9..b0389cdc5 100644 --- a/src/nemo-desktop-manager.c +++ b/src/nemo-desktop-manager.c @@ -15,6 +15,10 @@ #include #include +#ifdef HAVE_GTK_LAYER_SHELL +#include +#endif + #include "libnemo-private/nemo-action-manager.h" #include #include @@ -629,12 +633,16 @@ fallback_startup_idle_cb (NemoDesktopManager *manager) static gboolean is_cinnamon_desktop (void) { + const gchar *session_desktop = g_getenv ("XDG_SESSION_DESKTOP"); + const gchar *desktop_session = g_getenv ("DESKTOP_SESSION"); - if (g_strcmp0 (g_getenv ("XDG_SESSION_DESKTOP"), "cinnamon") == 0) { + if (session_desktop != NULL && + g_strstr_len (session_desktop, -1, "cinnamon") != NULL) { return TRUE; } - if (g_strstr_len (g_getenv ("DESKTOP_SESSION"), -1, "cinnamon") != NULL) { + if (desktop_session != NULL && + g_strstr_len (desktop_session, -1, "cinnamon") != NULL) { return TRUE; } @@ -882,13 +890,24 @@ nemo_desktop_manager_get_margins (NemoDesktopManager *manager, { FETCH_PRIV (manager); GdkRectangle work_rect, geometry; + gboolean use_layer_shell = FALSE; + +#ifdef HAVE_GTK_LAYER_SHELL + use_layer_shell = gtk_layer_is_supported (); +#endif - /* We don't use margins if we have reliable work area - * info (e.g. having an active Cinnamon session) */ + DEBUG ("NemoDesktopManager get_margins: monitor=%d proxy_owned=%d other_desktop=%d use_layer_shell=%d", + monitor, priv->proxy_owned, priv->other_desktop, use_layer_shell); + + /* When Cinnamon is running, we don't use margins because the window is + * sized to the work area (X11 mode) or the compositor sizes it to the + * workarea (layer-shell mode with exclusive_zone=0). */ if (priv->proxy_owned && !priv->other_desktop) { *left = *right = *top = *bottom = 0; + DEBUG ("NemoDesktopManager get_margins: returning 0 margins (Cinnamon running)"); + return; } diff --git a/src/nemo-desktop-window.c b/src/nemo-desktop-window.c index b7ecec8e9..3fa13f882 100644 --- a/src/nemo-desktop-window.c +++ b/src/nemo-desktop-window.c @@ -358,10 +358,13 @@ nemo_desktop_window_update_geometry (NemoDesktopWindow *window) GdkDisplay *display = gdk_display_get_default (); GdkMonitor *monitor = gdk_display_get_monitor (display, window->details->monitor); + DEBUG ("NemoDesktopWindow: using layer-shell for monitor %d", + window->details->monitor); + gtk_layer_set_layer (GTK_WINDOW (window), GTK_LAYER_SHELL_LAYER_BOTTOM); gtk_layer_set_namespace (GTK_WINDOW (window), "nemo-desktop"); gtk_layer_set_keyboard_mode (GTK_WINDOW (window), GTK_LAYER_SHELL_KEYBOARD_MODE_ON_DEMAND); - gtk_layer_set_exclusive_zone (GTK_WINDOW (window), -1); + gtk_layer_set_exclusive_zone (GTK_WINDOW (window), 0); gtk_layer_set_anchor (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_TOP, TRUE); gtk_layer_set_anchor (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_BOTTOM, TRUE); gtk_layer_set_anchor (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_LEFT, TRUE); From 8dc27b36f2719250f2e4f3c276a8ce6b1f9b0396 Mon Sep 17 00:00:00 2001 From: Michael Webster Date: Tue, 20 Jan 2026 23:39:30 -0500 Subject: [PATCH 3/3] nemo-desktop: Force the x11 backend if gtk-layer-shell isn't supported. If nemo-desktop is run as a Wayland client without layer-shell support, the desktop window will be treated a normal window, above other windows, and with a window-list/taskbar entry. We can't call gtk_layer_is_supported before gtk_main(), so we need to manually connect to the wayland display and check ourselves. --- debian/control | 1 + meson.build | 2 +- src/meson.build | 1 + src/nemo-desktop-main.c | 54 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/debian/control b/debian/control index f6f4cf903..2003578b9 100644 --- a/debian/control +++ b/debian/control @@ -28,6 +28,7 @@ Build-Depends: libxapp-dev (>= 2.0.0), libxext-dev, libxrender-dev, + libwayland-dev, meson, python3, python3-gi, diff --git a/meson.build b/meson.build index 80242f91e..bfeedbcca 100644 --- a/meson.build +++ b/meson.build @@ -134,9 +134,9 @@ endif conf.set('HAVE_SELINUX', libselinux_enabled) gtk_layer_shell_enabled = get_option('gtk_layer_shell') -gtk_layer_shell = dependency('', required: false) if gtk_layer_shell_enabled gtk_layer_shell = dependency('gtk-layer-shell-0', version: '>=0.8', required: true) + wayland_client = dependency('wayland-client', required: true) conf.set('HAVE_GTK_LAYER_SHELL', gtk_layer_shell.found()) endif diff --git a/src/meson.build b/src/meson.build index 464f2fc7f..da1b52b9e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -115,6 +115,7 @@ endif if gtk_layer_shell_enabled nemo_deps += gtk_layer_shell + nemo_deps += wayland_client endif nemo = executable('nemo', diff --git a/src/nemo-desktop-main.c b/src/nemo-desktop-main.c index d5fe2721e..c957f57f5 100644 --- a/src/nemo-desktop-main.c +++ b/src/nemo-desktop-main.c @@ -55,6 +55,47 @@ #ifdef HAVE_GTK_LAYER_SHELL #include +#include + +static gboolean layer_shell_available = FALSE; + +static void +registry_handle_global (void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) +{ + if (g_strcmp0 (interface, "zwlr_layer_shell_v1") == 0) + layer_shell_available = TRUE; +} + +static void +registry_handle_global_remove (void *data, struct wl_registry *registry, uint32_t name) +{ +} + +static const struct wl_registry_listener registry_listener = { + registry_handle_global, + registry_handle_global_remove, +}; + +static gboolean +check_layer_shell_support (void) +{ + struct wl_display *display; + struct wl_registry *registry; + + display = wl_display_connect (NULL); + if (!display) + return FALSE; + + registry = wl_display_get_registry (display); + wl_registry_add_listener (registry, ®istry_listener, NULL); + wl_display_roundtrip (display); + + wl_registry_destroy (registry); + wl_display_disconnect (display); + + return layer_shell_available; +} #endif int @@ -91,7 +132,18 @@ main (int argc, char *argv[]) g_set_prgname ("nemo-desktop"); -#ifndef HAVE_GTK_LAYER_SHELL +#ifdef HAVE_GTK_LAYER_SHELL + if (check_layer_shell_support ()) + { + g_message ("nemo-desktop: using Wayland backend, gtk-layer-shell supported"); + } + else + { + g_message ("nemo-desktop: Not a Wayland session, or wlr-layer-shell protocol not supported, using X11 backend"); + gdk_set_allowed_backends ("x11"); + } +#else + g_message ("nemo-desktop: using X11"); gdk_set_allowed_backends ("x11"); #endif