summaryrefslogtreecommitdiff
path: root/plugins/background
diff options
context:
space:
mode:
authorSunderland93 <[email protected]>2026-08-02 18:08:40 +0400
committerLuke from DC <[email protected]>2026-08-19 19:15:44 +0000
commit09e3e297fa5534d5676e7a445a4494f2a1e817c1 (patch)
tree9ce1b8074517a3443c88e9d0ae52414fa47041be /plugins/background
parent6bc2c99d15d9b2cd8e38ee834e10632ad60af101 (diff)
downloadmate-settings-daemon-09e3e297fa5534d5676e7a445a4494f2a1e817c1.tar.bz2
mate-settings-daemon-09e3e297fa5534d5676e7a445a4494f2a1e817c1.tar.xz
background: port to wayland via gtk-layer-shell
Diffstat (limited to 'plugins/background')
-rw-r--r--plugins/background/Makefile.am21
-rw-r--r--plugins/background/background.mate-settings-plugin.desktop.in1
-rw-r--r--plugins/background/msd-background-manager-wayland.c588
-rw-r--r--plugins/background/msd-background-manager-wayland.h38
-rw-r--r--plugins/background/msd-background-manager.c32
5 files changed, 679 insertions, 1 deletions
diff --git a/plugins/background/Makefile.am b/plugins/background/Makefile.am
index b2ab685..ce8109f 100644
--- a/plugins/background/Makefile.am
+++ b/plugins/background/Makefile.am
@@ -1,5 +1,14 @@
NULL =
+if BUILD_WAYLAND
+WAYLAND_BACKGROUND_SOURCES = \
+ msd-background-manager-wayland.c \
+ msd-background-manager-wayland.h \
+ $(NULL)
+else
+WAYLAND_BACKGROUND_SOURCES =
+endif
+
noinst_PROGRAMS = \
test-background \
$(NULL)
@@ -8,6 +17,7 @@ test_background_SOURCES = \
test-background.c \
msd-background-manager.h \
msd-background-manager.c \
+ $(WAYLAND_BACKGROUND_SOURCES) \
$(NULL)
test_background_CPPFLAGS = \
@@ -18,6 +28,8 @@ test_background_CPPFLAGS = \
test_background_CFLAGS = \
$(SETTINGS_PLUGIN_CFLAGS) \
$(MATE_DESKTOP_CFLAGS) \
+ $(GTK_LAYER_SHELL_CFLAGS) \
+ $(WAYLAND_CLIENT_CFLAGS) \
$(AM_CFLAGS) \
$(WARN_CFLAGS)
@@ -26,6 +38,8 @@ test_background_LDADD = \
$(SETTINGS_PLUGIN_LIBS) \
$(MATE_DESKTOP_LIBS) \
$(X11_LIBS) \
+ $(GTK_LAYER_SHELL_LIBS) \
+ $(WAYLAND_CLIENT_LIBS) \
$(NULL)
plugin_LTLIBRARIES = \
@@ -37,17 +51,22 @@ libbackground_la_SOURCES = \
msd-background-plugin.c \
msd-background-manager.h \
msd-background-manager.c \
+ $(WAYLAND_BACKGROUND_SOURCES) \
$(NULL)
libbackground_la_CPPFLAGS = \
-I$(top_srcdir)/mate-settings-daemon \
-I$(top_srcdir)/plugins/background/libbackground \
-DMATE_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \
+ $(GTK_LAYER_SHELL_CFLAGS) \
+ $(WAYLAND_CLIENT_CFLAGS) \
$(AM_CPPFLAGS)
libbackground_la_CFLAGS = \
$(SETTINGS_PLUGIN_CFLAGS) \
$(MATE_DESKTOP_CFLAGS) \
+ $(GTK_LAYER_SHELL_CFLAGS) \
+ $(WAYLAND_CLIENT_CFLAGS) \
$(AM_CFLAGS) \
$(WARN_CFLAGS)
@@ -59,6 +78,8 @@ libbackground_la_LIBADD = \
$(SETTINGS_PLUGIN_LIBS) \
$(MATE_DESKTOP_LIBS) \
$(X11_LIBS) \
+ $(GTK_LAYER_SHELL_LIBS) \
+ $(WAYLAND_CLIENT_LIBS) \
$(NULL)
plugin_in_files = \
diff --git a/plugins/background/background.mate-settings-plugin.desktop.in b/plugins/background/background.mate-settings-plugin.desktop.in
index ae5fc48..3c7a6a2 100644
--- a/plugins/background/background.mate-settings-plugin.desktop.in
+++ b/plugins/background/background.mate-settings-plugin.desktop.in
@@ -1,7 +1,6 @@
[MATE Settings Plugin]
Module=background
IAge=0
-X11Only=true
Name=Background
Description=Background plugin
Authors=
diff --git a/plugins/background/msd-background-manager-wayland.c b/plugins/background/msd-background-manager-wayland.c
new file mode 100644
index 0000000..260c49a
--- /dev/null
+++ b/plugins/background/msd-background-manager-wayland.c
@@ -0,0 +1,588 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2026 MATE Developers
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkwayland.h>
+#include <gtk/gtk.h>
+#include <wayland-client.h>
+
+#include <gtk-layer-shell.h>
+
+#define MATE_DESKTOP_USE_UNSTABLE_API
+#include <libmate-desktop/mate-bg.h>
+
+#include "msd-background-manager-wayland.h"
+
+struct _MsdBackgroundManagerWayland
+{
+ GdkDisplay *display;
+ GSettings *settings;
+ MateBG *bg;
+ GPtrArray *monitor_windows;
+ gboolean caja_desktop_active;
+ guint caja_check_timeout_id;
+};
+
+typedef struct
+{
+ GtkWidget *window;
+ GdkMonitor *monitor;
+ cairo_surface_t *surface;
+ cairo_surface_t *fading_surface;
+ cairo_surface_t *end_surface;
+ guint fade_timeout_id;
+ gdouble fade_start_time;
+ gdouble fade_total_duration;
+ gboolean fade_is_first_frame;
+ int width;
+ int height;
+} MonitorWindow;
+
+static void
+free_monitor_window_surface (MonitorWindow *mw)
+{
+ if (mw->surface != NULL) {
+ cairo_surface_destroy (mw->surface);
+ mw->surface = NULL;
+ }
+}
+
+static gdouble
+get_current_time (void)
+{
+ return g_get_monotonic_time () / 1000000.0;
+}
+
+/* Caja manages the desktop in-process, so a running "caja" process with
+ * the desktop enabled owns the layer-shell "desktop" surface. There is no
+ * Wayland protocol to enumerate layer surfaces, so detect it via the process.
+ */
+static gboolean
+caja_desktop_is_running (void)
+{
+ gchar *stdout = NULL;
+ gboolean running;
+
+ running = g_spawn_command_line_sync ("pgrep -x caja", &stdout,
+ NULL, NULL, NULL);
+ if (!running || stdout == NULL || *stdout == '\0')
+ running = FALSE;
+ else
+ running = TRUE;
+
+ g_free (stdout);
+
+ return running;
+}
+
+static gboolean
+animations_are_disabled (MonitorWindow *mw G_GNUC_UNUSED)
+{
+ GtkSettings *settings;
+ gboolean enabled;
+
+ settings = gtk_settings_get_default ();
+ if (settings == NULL)
+ return TRUE;
+
+ g_object_get (settings, "gtk-enable-animations", &enabled, NULL);
+
+ return !enabled;
+}
+
+static void
+cancel_fade (MonitorWindow *mw)
+{
+ if (mw->fade_timeout_id == 0)
+ return;
+
+ g_source_remove (mw->fade_timeout_id);
+ mw->fade_timeout_id = 0;
+}
+
+static void
+on_fade_finished (MonitorWindow *mw)
+{
+ cairo_t *cr;
+
+ mw->fade_timeout_id = 0;
+
+ if (mw->fading_surface == NULL || mw->end_surface == NULL) {
+ if (mw->end_surface != NULL) {
+ cairo_surface_destroy (mw->end_surface);
+ mw->end_surface = NULL;
+ }
+ return;
+ }
+
+ cr = cairo_create (mw->fading_surface);
+ cairo_set_source_surface (cr, mw->end_surface, 0, 0);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ cairo_surface_destroy (mw->end_surface);
+ mw->end_surface = NULL;
+
+ free_monitor_window_surface (mw);
+ mw->surface = mw->fading_surface;
+ mw->fading_surface = NULL;
+
+ gtk_widget_queue_draw (mw->window);
+}
+
+static gboolean
+on_fade_tick (MonitorWindow *mw)
+{
+ gdouble now, percent_done;
+ cairo_t *cr;
+ cairo_status_t status;
+
+ if (mw->fading_surface == NULL || mw->end_surface == NULL)
+ return FALSE;
+
+ now = get_current_time ();
+ percent_done = (now - mw->fade_start_time) / mw->fade_total_duration;
+ percent_done = CLAMP (percent_done, 0.0, 1.0);
+
+ /* If it's taking a long time to get to the first frame,
+ * then lengthen the duration, so the user will get to see
+ * the effect.
+ */
+ if (mw->fade_is_first_frame && percent_done > .33) {
+ mw->fade_is_first_frame = FALSE;
+ mw->fade_total_duration *= 1.5;
+ return TRUE;
+ }
+
+ if (animations_are_disabled (mw))
+ return FALSE;
+
+ /* Accumulate the end surface in place, so the fade is
+ * exponential rather than linear (looks good, matches X11).
+ */
+ cr = cairo_create (mw->fading_surface);
+ cairo_set_source_surface (cr, mw->end_surface, 0, 0);
+ cairo_paint_with_alpha (cr, percent_done);
+ status = cairo_status (cr);
+ cairo_destroy (cr);
+
+ if (status == CAIRO_STATUS_SUCCESS)
+ gtk_widget_queue_draw (mw->window);
+
+ return percent_done <= .99;
+}
+
+static void
+start_or_continue_fade (MsdBackgroundManagerWayland *manager,
+ MonitorWindow *mw,
+ cairo_surface_t *new_surface)
+{
+ gboolean fade_enabled;
+ gboolean animations_enabled;
+ cairo_t *cr;
+ GSource *source;
+
+ fade_enabled = g_settings_get_boolean (manager->settings,
+ MATE_BG_KEY_BACKGROUND_FADE);
+ animations_enabled = !animations_are_disabled (mw);
+
+ if (!fade_enabled || !animations_enabled || mw->surface == NULL) {
+ /* No fade: cancel any in-flight one and swap instantly.
+ * This also covers the first render after startup.
+ */
+ if (mw->fade_timeout_id != 0) {
+ if (mw->fading_surface != NULL) {
+ cairo_surface_destroy (mw->fading_surface);
+ mw->fading_surface = NULL;
+ }
+ if (mw->end_surface != NULL) {
+ cairo_surface_destroy (mw->end_surface);
+ mw->end_surface = NULL;
+ }
+ g_source_remove (mw->fade_timeout_id);
+ mw->fade_timeout_id = 0;
+ }
+ free_monitor_window_surface (mw);
+ mw->surface = new_surface;
+ gtk_widget_queue_draw (mw->window);
+ return;
+ }
+
+ /* If a fade is already running, restart from the current blended
+ * state (its completion commits the blend into mw->surface).
+ */
+ cancel_fade (mw);
+
+ mw->fading_surface = cairo_surface_create_similar (mw->surface,
+ cairo_surface_get_content (mw->surface),
+ mw->width, mw->height);
+ cr = cairo_create (mw->fading_surface);
+ cairo_set_source_surface (cr, mw->surface, 0, 0);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ mw->end_surface = new_surface;
+ mw->fade_start_time = get_current_time ();
+ mw->fade_total_duration = 0.75;
+ mw->fade_is_first_frame = TRUE;
+
+ source = g_timeout_source_new (1000 / 60.0);
+ g_source_set_callback (source, (GSourceFunc) on_fade_tick, mw,
+ (GDestroyNotify) on_fade_finished);
+ mw->fade_timeout_id = g_source_attach (source, NULL);
+ g_source_unref (source);
+
+ gtk_widget_queue_draw (mw->window);
+}
+
+static void
+render_surface_for_monitor (MsdBackgroundManagerWayland *manager,
+ MonitorWindow *mw)
+{
+ GdkRectangle rect;
+ GdkPixbuf *pixbuf;
+ cairo_surface_t *new_surface;
+ cairo_t *cr;
+
+ gdk_monitor_get_geometry (mw->monitor, &rect);
+
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
+ rect.width, rect.height);
+ if (pixbuf == NULL) {
+ g_warning ("msd-background-wayland: unable to create %dx%d pixbuf",
+ rect.width, rect.height);
+ return;
+ }
+
+ mate_bg_draw (manager->bg, pixbuf, NULL, FALSE);
+
+ new_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ rect.width, rect.height);
+ cr = cairo_create (new_surface);
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ g_object_unref (pixbuf);
+
+ start_or_continue_fade (manager, mw, new_surface);
+}
+
+static gboolean
+on_window_draw (GtkWidget *widget,
+ cairo_t *cr,
+ MonitorWindow *mw)
+{
+ GtkAllocation alloc;
+ cairo_surface_t *src;
+ gdouble sx, sy;
+
+ if (mw->surface == NULL && mw->fading_surface == NULL)
+ return FALSE;
+
+ src = (mw->fading_surface != NULL) ? mw->fading_surface : mw->surface;
+
+ gtk_widget_get_allocation (widget, &alloc);
+ if (alloc.width <= 0 || alloc.height <= 0)
+ return FALSE;
+
+ sx = alloc.width / (gdouble) cairo_image_surface_get_width (src);
+ sy = alloc.height / (gdouble) cairo_image_surface_get_height (src);
+ cairo_scale (cr, sx, sy);
+ cairo_set_source_surface (cr, src, 0, 0);
+ cairo_paint (cr);
+
+ return FALSE;
+}
+
+static void
+redraw_all (MsdBackgroundManagerWayland *manager)
+{
+ guint i;
+
+ for (i = 0; i < manager->monitor_windows->len; i++) {
+ MonitorWindow *mw = g_ptr_array_index (manager->monitor_windows, i);
+ render_surface_for_monitor (manager, mw);
+ gtk_widget_queue_draw (mw->window);
+ }
+}
+
+static void
+on_window_destroy (GtkWidget *widget G_GNUC_UNUSED,
+ MonitorWindow *mw)
+{
+ if (mw->fade_timeout_id != 0) {
+ /* Make on_fade_finished a no-op, then drop the source */
+ if (mw->fading_surface != NULL) {
+ cairo_surface_destroy (mw->fading_surface);
+ mw->fading_surface = NULL;
+ }
+ if (mw->end_surface != NULL) {
+ cairo_surface_destroy (mw->end_surface);
+ mw->end_surface = NULL;
+ }
+ g_source_remove (mw->fade_timeout_id);
+ mw->fade_timeout_id = 0;
+ }
+ free_monitor_window_surface (mw);
+ g_free (mw);
+}
+
+static void
+set_input_region_empty (GtkWidget *window)
+{
+ struct wl_surface *wl_surface;
+ GdkWindow *gdk_window;
+
+ gdk_window = gtk_widget_get_window (window);
+ if (gdk_window == NULL)
+ return;
+
+ wl_surface = gdk_wayland_window_get_wl_surface (gdk_window);
+ if (wl_surface != NULL)
+ wl_surface_set_input_region (wl_surface, NULL);
+}
+
+static MonitorWindow *
+create_monitor_window (MsdBackgroundManagerWayland *manager G_GNUC_UNUSED,
+ GdkMonitor *monitor)
+{
+ MonitorWindow *mw;
+ GtkWidget *window;
+ GdkRectangle rect;
+ guint i;
+
+ gdk_monitor_get_geometry (monitor, &rect);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_app_paintable (window, TRUE);
+ gtk_widget_set_size_request (window, rect.width, rect.height);
+ gtk_window_set_default_size (GTK_WINDOW (window), rect.width, rect.height);
+ gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
+
+ g_debug ("msd-background-wayland: monitor %dx%d at %d,%d", rect.width, rect.height,
+ rect.x, rect.y);
+
+ gtk_layer_init_for_window (GTK_WINDOW (window));
+ gtk_layer_set_layer (GTK_WINDOW (window), GTK_LAYER_SHELL_LAYER_BACKGROUND);
+ gtk_layer_set_namespace (GTK_WINDOW (window), "mate-background");
+ gtk_layer_set_monitor (GTK_WINDOW (window), monitor);
+ for (i = 0; i < GTK_LAYER_SHELL_EDGE_ENTRY_NUMBER; i++)
+ gtk_layer_set_anchor (GTK_WINDOW (window), i, TRUE);
+ gtk_layer_set_keyboard_mode (GTK_WINDOW (window), GTK_LAYER_SHELL_KEYBOARD_MODE_NONE);
+
+ mw = g_new0 (MonitorWindow, 1);
+ mw->window = window;
+ mw->monitor = monitor;
+ mw->width = rect.width;
+ mw->height = rect.height;
+
+ g_signal_connect (window, "draw", G_CALLBACK (on_window_draw), mw);
+ g_signal_connect (window, "destroy", G_CALLBACK (on_window_destroy), mw);
+
+ gtk_widget_show (window);
+
+ set_input_region_empty (window);
+
+ return mw;
+}
+
+static void
+destroy_all_monitor_windows (MsdBackgroundManagerWayland *manager)
+{
+ guint i;
+
+ for (i = 0; i < manager->monitor_windows->len; i++) {
+ MonitorWindow *mw = g_ptr_array_index (manager->monitor_windows, i);
+ gtk_widget_destroy (mw->window);
+ }
+ g_ptr_array_set_size (manager->monitor_windows, 0);
+}
+
+static void
+rebuild_monitor_windows (MsdBackgroundManagerWayland *manager)
+{
+ GdkDisplay *display = manager->display;
+ int n_monitors;
+ int i;
+
+ destroy_all_monitor_windows (manager);
+
+ /* Caja's desktop window (layer "desktop", BACKGROUND) owns the
+ * wallpaper and icons. When it is running we must not map our own
+ * layer windows, or we would cover it.
+ */
+ if (caja_desktop_is_running ()) {
+ manager->caja_desktop_active = TRUE;
+ g_debug ("msd-background-wayland: caja desktop detected, background disabled");
+ return;
+ }
+ manager->caja_desktop_active = FALSE;
+
+ n_monitors = gdk_display_get_n_monitors (display);
+ for (i = 0; i < n_monitors; i++) {
+ GdkMonitor *monitor;
+ MonitorWindow *mw;
+
+ monitor = gdk_display_get_monitor (display, i);
+ mw = create_monitor_window (manager, monitor);
+ g_ptr_array_add (manager->monitor_windows, mw);
+ }
+}
+
+static gboolean
+caja_check_timeout_cb (MsdBackgroundManagerWayland *manager)
+{
+ gboolean active;
+
+ active = caja_desktop_is_running ();
+ if (active == manager->caja_desktop_active)
+ return G_SOURCE_CONTINUE;
+
+ manager->caja_desktop_active = active;
+
+ if (active) {
+ g_debug ("msd-background-wayland: caja desktop detected, hiding background");
+ destroy_all_monitor_windows (manager);
+ } else {
+ g_debug ("msd-background-wayland: caja desktop gone, showing background");
+ rebuild_monitor_windows (manager);
+ redraw_all (manager);
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+on_monitors_changed (GdkScreen *screen G_GNUC_UNUSED,
+ MsdBackgroundManagerWayland *manager)
+{
+ rebuild_monitor_windows (manager);
+ redraw_all (manager);
+}
+
+static void
+on_bg_changed (MateBG *bg G_GNUC_UNUSED,
+ MsdBackgroundManagerWayland *manager)
+{
+ if (manager->caja_desktop_active)
+ return;
+
+ g_debug ("msd-background-wayland: background changed, redrawing");
+ redraw_all (manager);
+}
+
+static gboolean
+settings_change_event_idle_cb (MsdBackgroundManagerWayland *manager)
+{
+ g_debug ("msd-background-wayland: applying settings change");
+ mate_bg_load_from_preferences (manager->bg);
+ return FALSE;
+}
+
+static gboolean
+settings_change_event_cb (GSettings *settings G_GNUC_UNUSED,
+ gpointer keys G_GNUC_UNUSED,
+ gint n_keys G_GNUC_UNUSED,
+ MsdBackgroundManagerWayland *manager)
+{
+ g_idle_add ((GSourceFunc) settings_change_event_idle_cb, manager);
+ return FALSE;
+}
+
+MsdBackgroundManagerWayland *
+msd_background_manager_wayland_new (GError **error)
+{
+ MsdBackgroundManagerWayland *manager;
+ GdkDisplay *display;
+
+ display = gdk_display_get_default ();
+ if (display == NULL || !GDK_IS_WAYLAND_DISPLAY (display)) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "A Wayland display is required for the Wayland background backend");
+ return NULL;
+ }
+
+ manager = g_new0 (MsdBackgroundManagerWayland, 1);
+ manager->display = display;
+ manager->monitor_windows = g_ptr_array_new ();
+
+ manager->settings = g_settings_new (MATE_BG_SCHEMA);
+ manager->bg = mate_bg_new ();
+
+ g_signal_connect (manager->bg, "changed",
+ G_CALLBACK (on_bg_changed), manager);
+ g_signal_connect (manager->bg, "transitioned",
+ G_CALLBACK (on_bg_changed), manager);
+ g_signal_connect (manager->settings, "change-event",
+ G_CALLBACK (settings_change_event_cb), manager);
+ g_signal_connect (gdk_screen_get_default (), "monitors-changed",
+ G_CALLBACK (on_monitors_changed), manager);
+
+ g_debug ("msd-background-wayland: initializing on %d monitors",
+ gdk_display_get_n_monitors (display));
+
+ mate_bg_load_from_gsettings (manager->bg, manager->settings);
+
+ rebuild_monitor_windows (manager);
+ redraw_all (manager);
+
+ /* Re-evaluate when caja starts or stops */
+ manager->caja_check_timeout_id =
+ g_timeout_add_seconds (2, (GSourceFunc) caja_check_timeout_cb, manager);
+
+ return manager;
+}
+
+void
+msd_background_manager_wayland_destroy (MsdBackgroundManagerWayland *manager)
+{
+ if (manager == NULL)
+ return;
+
+ g_signal_handlers_disconnect_by_func (gdk_screen_get_default (),
+ G_CALLBACK (on_monitors_changed), manager);
+
+ if (manager->caja_check_timeout_id != 0) {
+ g_source_remove (manager->caja_check_timeout_id);
+ manager->caja_check_timeout_id = 0;
+ }
+
+ destroy_all_monitor_windows (manager);
+ g_ptr_array_free (manager->monitor_windows, TRUE);
+
+ if (manager->settings != NULL) {
+ g_signal_handlers_disconnect_by_func (manager->settings,
+ G_CALLBACK (settings_change_event_cb), manager);
+ g_object_unref (manager->settings);
+ }
+
+ if (manager->bg != NULL) {
+ g_signal_handlers_disconnect_by_func (manager->bg,
+ G_CALLBACK (on_bg_changed), manager);
+ g_object_unref (manager->bg);
+ }
+
+ g_free (manager);
+}
diff --git a/plugins/background/msd-background-manager-wayland.h b/plugins/background/msd-background-manager-wayland.h
new file mode 100644
index 0000000..8125f58
--- /dev/null
+++ b/plugins/background/msd-background-manager-wayland.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2026 MATE Developers
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MSD_BACKGROUND_MANAGER_WAYLAND_H
+#define MSD_BACKGROUND_MANAGER_WAYLAND_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _MsdBackgroundManagerWayland MsdBackgroundManagerWayland;
+
+MsdBackgroundManagerWayland *msd_background_manager_wayland_new (GError **error);
+
+void msd_background_manager_wayland_destroy (MsdBackgroundManagerWayland *manager);
+
+G_END_DECLS
+
+#endif /* MSD_BACKGROUND_MANAGER_WAYLAND_H */
diff --git a/plugins/background/msd-background-manager.c b/plugins/background/msd-background-manager.c
index 5b8acea..514f74c 100644
--- a/plugins/background/msd-background-manager.c
+++ b/plugins/background/msd-background-manager.c
@@ -40,6 +40,9 @@
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif /* GDK_WINDOWING_X11 */
+#ifdef HAVE_WAYLAND
+#include <gdk/gdkwayland.h>
+#endif /* HAVE_WAYLAND */
#include <gio/gio.h>
#define MATE_DESKTOP_USE_UNSTABLE_API
@@ -49,6 +52,10 @@
#include "mate-settings-profile.h"
#include "msd-background-manager.h"
+#ifdef HAVE_WAYLAND
+#include "msd-background-manager-wayland.h"
+#endif /* HAVE_WAYLAND */
+
struct _MsdBackgroundManager {
GObject parent;
@@ -57,6 +64,10 @@ struct _MsdBackgroundManager {
cairo_surface_t *surface;
gboolean draw_in_progress;
+
+#ifdef HAVE_WAYLAND
+ MsdBackgroundManagerWayland *wayland;
+#endif /* HAVE_WAYLAND */
};
G_DEFINE_TYPE (MsdBackgroundManager, msd_background_manager, G_TYPE_OBJECT)
@@ -243,6 +254,19 @@ msd_background_manager_start (MsdBackgroundManager *manager,
g_debug ("Starting background manager");
mate_settings_profile_start (NULL);
+#ifdef HAVE_WAYLAND
+ if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ())) {
+ manager->wayland = msd_background_manager_wayland_new (error);
+ if (manager->wayland == NULL) {
+ mate_settings_profile_end (NULL);
+ return FALSE;
+ }
+
+ mate_settings_profile_end (NULL);
+ return TRUE;
+ }
+#endif /* HAVE_WAYLAND */
+
#ifdef GDK_WINDOWING_X11
if (!GDK_IS_X11_DISPLAY (gdk_display_get_default ())) {
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
@@ -281,6 +305,14 @@ msd_background_manager_stop (MsdBackgroundManager *manager)
{
g_debug ("Stopping background manager");
+#ifdef HAVE_WAYLAND
+ if (manager->wayland != NULL) {
+ msd_background_manager_wayland_destroy (manager->wayland);
+ manager->wayland = NULL;
+ return;
+ }
+#endif /* HAVE_WAYLAND */
+
#ifdef GDK_WINDOWING_X11
if (!GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
return;