diff --git a/debian/libmuffin0.symbols b/debian/libmuffin0.symbols
index 999b34fad..35952ccd3 100644
--- a/debian/libmuffin0.symbols
+++ b/debian/libmuffin0.symbols
@@ -2637,6 +2637,7 @@ libmuffin.so.0 libmuffin0 #MINVER#
meta_window_get_sandboxed_app_id@Base 5.3.0
meta_window_get_stable_sequence@Base 5.3.0
meta_window_get_startup_id@Base 5.3.0
+ meta_window_get_tag@Base 6.4.1
meta_window_get_tile_match@Base 5.3.0
meta_window_get_title@Base 5.3.0
meta_window_get_transient_for@Base 5.3.0
diff --git a/src/core/window-private.h b/src/core/window-private.h
index 6a41f3767..72274a6cf 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -203,6 +203,8 @@ struct _MetaWindow
char *sm_client_id;
char *wm_client_machine;
+ char *tag;
+
char *startup_id;
char *muffin_hints;
char *sandboxed_app_id;
@@ -912,4 +914,7 @@ gboolean meta_window_is_focus_async (MetaWindow *window);
gboolean meta_window_calculate_bounds (MetaWindow *window,
int *bounds_width,
int *bounds_height);
+
+void meta_window_set_tag (MetaWindow *window,
+ const char *tag);
#endif
diff --git a/src/core/window.c b/src/core/window.c
index f3b18a792..4dd8193b9 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -219,6 +219,7 @@ enum
PROP_PROGRESS_PULSE,
PROP_TILE_MODE,
PROP_OPACITY,
+ PROP_TAG,
PROP_LAST,
};
@@ -352,6 +353,7 @@ meta_window_finalize (GObject *object)
g_free (window->gtk_app_menu_object_path);
g_free (window->gtk_menubar_object_path);
g_free (window->placement.rule);
+ g_free (window->tag);
G_OBJECT_CLASS (meta_window_parent_class)->finalize (object);
}
@@ -453,6 +455,9 @@ meta_window_get_property(GObject *object,
case PROP_OPACITY:
g_value_set_uint (value, win->opacity);
break;
+ case PROP_TAG:
+ g_value_set_string (value, win->tag);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -674,6 +679,12 @@ meta_window_class_init (MetaWindowClass *klass)
0xFF,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_TAG] =
+ g_param_spec_string ("tag", NULL, NULL,
+ NULL,
+ G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY |
+ G_PARAM_STATIC_STRINGS);
+
g_object_class_install_properties (object_class, PROP_LAST, obj_props);
window_signals[WORKSPACE_CHANGED] =
@@ -9526,3 +9537,29 @@ meta_window_calculate_bounds (MetaWindow *window,
return FALSE;
}
}
+
+void
+meta_window_set_tag (MetaWindow *window,
+ const char *tag)
+{
+ if (g_set_str (&window->tag, tag))
+ g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_TAG]);
+}
+
+/**
+ * meta_window_get_tag:
+ * @window: A #MetaWindow
+ *
+ * Get a tag associated to the window.
+ * Under wayland the tag can be set using the toplevel tag protocol,
+ * and under x11 it falls back to using `NET_WM_WINDOW_TAG` atom.
+ *
+ * Returns: (nullable): An associated toplevel tag
+ */
+const char *
+meta_window_get_tag (MetaWindow *window)
+{
+ g_return_val_if_fail (META_IS_WINDOW (window), NULL);
+
+ return window->tag;
+}
diff --git a/src/meson.build b/src/meson.build
index 8a11856e5..056306e81 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -587,6 +587,8 @@ if have_wayland
'wayland/meta-wayland-xdg-shell.h',
'wayland/meta-wayland-xdg-dialog.c',
'wayland/meta-wayland-xdg-dialog.h',
+ 'wayland/meta-wayland-xdg-toplevel-tag.c',
+ 'wayland/meta-wayland-xdg-toplevel-tag.h',
'wayland/meta-window-wayland.c',
'wayland/meta-window-wayland.h',
'wayland/meta-window-xwayland.c',
@@ -811,6 +813,7 @@ if have_wayland
['xdg-foreign', 'unstable', 'v2', ],
['xdg-output', 'unstable', 'v1', ],
['xdg-shell', 'stable', ],
+ ['xdg-toplevel-tag-v1', 'private', ],
['xwayland-keyboard-grab', 'unstable', 'v1', ],
]
if have_wayland_eglstream
diff --git a/src/meta/window.h b/src/meta/window.h
index 252129f6f..7ed58f7da 100644
--- a/src/meta/window.h
+++ b/src/meta/window.h
@@ -469,4 +469,7 @@ void meta_window_set_opacity (MetaWindow *window,
META_EXPORT
guint8 meta_window_get_opacity (MetaWindow *window);
+META_EXPORT
+const char * meta_window_get_tag (MetaWindow *window);
+
#endif
diff --git a/src/wayland/meta-wayland-versions.h b/src/wayland/meta-wayland-versions.h
index c9e9e911d..1a9fb4496 100644
--- a/src/wayland/meta-wayland-versions.h
+++ b/src/wayland/meta-wayland-versions.h
@@ -56,5 +56,6 @@
#define META_WP_VIEWPORTER_VERSION 1
#define META_XDG_DIALOG_VERSION 1
#define META_ZWP_PRIMARY_SELECTION_V1_VERSION 1
+#define META_XDG_TOPLEVEL_TAG_V1_VERSION 1
#endif
diff --git a/src/wayland/meta-wayland-xdg-toplevel-tag.c b/src/wayland/meta-wayland-xdg-toplevel-tag.c
new file mode 100644
index 000000000..628289fb4
--- /dev/null
+++ b/src/wayland/meta-wayland-xdg-toplevel-tag.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 Red Hat
+ *
+ * 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, see .
+ *
+ * Written by:
+ * Bilal Elmoussaoui
+ */
+
+#include "config.h"
+
+#include "wayland/meta-wayland-xdg-toplevel-tag.h"
+
+#include
+
+#include "wayland/meta-wayland-private.h"
+#include "wayland/meta-wayland-versions.h"
+#include "wayland/meta-wayland-xdg-shell.h"
+
+#include "xdg-toplevel-tag-v1-server-protocol.h"
+
+static void
+xdg_toplevel_tag_manager_destroy (struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy (resource);
+}
+
+static void
+xdg_toplevel_tag_manager_set_toplevel_tag (struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *toplevel_resource,
+ const char *tag)
+{
+ MetaWaylandXdgToplevel *xdg_toplevel;
+ MetaWaylandSurfaceRole *surface_role;
+ MetaWaylandSurface *surface;
+ MetaWindow *window;
+
+ if (!toplevel_resource)
+ return;
+
+ xdg_toplevel = wl_resource_get_user_data (toplevel_resource);
+ surface_role = META_WAYLAND_SURFACE_ROLE (xdg_toplevel);
+ surface = meta_wayland_surface_role_get_surface (surface_role);
+ window = meta_wayland_surface_get_window (surface);
+
+ meta_window_set_tag (window, tag);
+}
+
+static void
+xdg_toplevel_tag_manager_set_toplevel_tag_description (struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *toplevel,
+ const char *tag_description)
+{
+ /* We don't make use of the toplevel tag description */
+}
+
+static const struct xdg_toplevel_tag_manager_v1_interface meta_xdg_toplevel_tag_interface = {
+ xdg_toplevel_tag_manager_destroy,
+ xdg_toplevel_tag_manager_set_toplevel_tag,
+ xdg_toplevel_tag_manager_set_toplevel_tag_description,
+};
+
+
+static void
+bind_xdg_toplevel_tag (struct wl_client *client,
+ void *data,
+ uint32_t version,
+ uint32_t id)
+{
+ struct wl_resource *resource;
+
+ resource = wl_resource_create (client,
+ &xdg_toplevel_tag_manager_v1_interface,
+ META_XDG_TOPLEVEL_TAG_V1_VERSION,
+ id);
+
+ wl_resource_set_implementation (resource,
+ &meta_xdg_toplevel_tag_interface,
+ NULL, NULL);
+}
+
+void
+meta_wayland_xdg_toplevel_tag_init (MetaWaylandCompositor *compositor)
+{
+ if (wl_global_create (compositor->wayland_display,
+ &xdg_toplevel_tag_manager_v1_interface,
+ META_XDG_TOPLEVEL_TAG_V1_VERSION,
+ NULL,
+ bind_xdg_toplevel_tag) == NULL)
+ g_error ("Failed to register a global xdg-toplevel-tag object");
+}
diff --git a/src/wayland/meta-wayland-xdg-toplevel-tag.h b/src/wayland/meta-wayland-xdg-toplevel-tag.h
new file mode 100644
index 000000000..58f32fd28
--- /dev/null
+++ b/src/wayland/meta-wayland-xdg-toplevel-tag.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 Red Hat
+ *
+ * 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, see .
+ *
+ * Written by:
+ * Bilal Elmoussaoui
+ */
+
+#pragma once
+
+#include
+
+#include "wayland/meta-wayland-types.h"
+
+void meta_wayland_xdg_toplevel_tag_init (MetaWaylandCompositor *compositor);
diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
index 524fd02f3..fd9659b86 100644
--- a/src/wayland/meta-wayland.c
+++ b/src/wayland/meta-wayland.c
@@ -45,6 +45,7 @@
#include "wayland/meta-wayland-tablet-manager.h"
#include "wayland/meta-wayland-xdg-dialog.h"
#include "wayland/meta-wayland-xdg-foreign.h"
+#include "wayland/meta-wayland-xdg-toplevel-tag.h"
#include "wayland/meta-xwayland-grab-keyboard.h"
#include "wayland/meta-xwayland-private.h"
#include "wayland/meta-xwayland.h"
@@ -440,6 +441,7 @@ meta_wayland_compositor_setup (MetaWaylandCompositor *wayland_compositor)
meta_wayland_surface_inhibit_shortcuts_dialog_init ();
meta_wayland_text_input_init (compositor);
meta_wayland_init_xdg_wm_dialog (compositor);
+ meta_wayland_xdg_toplevel_tag_init (compositor);
/* Xwayland specific protocol, needs to be filtered out for all other clients */
if (meta_xwayland_grab_keyboard_init (compositor))
diff --git a/src/wayland/protocol/xdg-toplevel-tag-v1.xml b/src/wayland/protocol/xdg-toplevel-tag-v1.xml
new file mode 100644
index 000000000..5e20bdf45
--- /dev/null
+++ b/src/wayland/protocol/xdg-toplevel-tag-v1.xml
@@ -0,0 +1,85 @@
+
+
+
+ Copyright © 2024 Xaver Hugl
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+
+
+
+ In order to make some window properties like position, size,
+ "always on top" or user defined rules for window behavior persistent, the
+ compositor needs some way to identify windows even after the application
+ has been restarted.
+ This protocol allows clients to make this possible by setting a tag for
+ toplevels.
+
+ Warning! The protocol described in this file is currently in the testing
+ phase. Backward compatible changes may be added together with the
+ corresponding interface version bump. Backward incompatible changes can
+ only be done by creating a new major version of the extension.
+
+
+
+
+ Destroy this toplevel tag manager object. This request has no other
+ effects.
+
+
+
+
+
+ Set a tag for a toplevel. The tag may be shown to the user in UI, so
+ it's preferable for it to be human readable, but it must be suitable
+ for configuration files and should not be translated.
+ Suitable tags would for example be "main window", "settings",
+ "e-mail composer" or similar.
+
+ The tag does not need to be unique across applications, and the client
+ may set the same tag for multiple windows, for example if the user has
+ opened the same UI twice. How the potentially resulting conflicts are
+ handled is compositor policy.
+
+ The client should set the tag as part of the initial commit on the
+ associated toplevel, but it may set it at any time afterwards as well,
+ for example if the purpose of the toplevel changes.
+
+
+
+
+
+
+
+ Set a description for a toplevel. This description may be shown to the
+ user in UI or read by a screen reader for accessibility purposes, and
+ should be translated.
+ It is recommended to make the description the translation of the tag.
+
+ The client should set the description as part of the initial commit on
+ the associated toplevel, but it may set it at any time afterwards as
+ well, for example if the purpose of the toplevel changes.
+
+
+
+
+
+
+
diff --git a/src/x11/atomnames.h b/src/x11/atomnames.h
index 8b8f5e506..3f3bc7964 100644
--- a/src/x11/atomnames.h
+++ b/src/x11/atomnames.h
@@ -180,6 +180,7 @@ item(_NET_WM_OPAQUE_REGION)
item(_NET_WM_FRAME_DRAWN)
item(_NET_WM_FRAME_TIMINGS)
item(_NET_WM_WINDOW_OPACITY)
+item(_NET_WM_WINDOW_TAG)
item(_NET_RESTACK_WINDOW)
item(_NET_WM_XAPP_ICON_NAME)
item(_NET_WM_XAPP_PROGRESS)
diff --git a/src/x11/window-props.c b/src/x11/window-props.c
index abc714973..6d06be225 100644
--- a/src/x11/window-props.c
+++ b/src/x11/window-props.c
@@ -418,6 +418,14 @@ reload_struts (MetaWindow *window,
meta_window_update_struts (window);
}
+static void
+reload_toplevel_tag (MetaWindow *window,
+ MetaPropValue *value,
+ gboolean initial)
+{
+ meta_window_set_tag (window, value->v.str);
+}
+
static void
reload_wm_window_role (MetaWindow *window,
MetaPropValue *value,
@@ -2055,6 +2063,7 @@ meta_x11_display_init_window_prop_hooks (MetaX11Display *x11_display)
{ x11_display->atom__NET_WM_STRUT_PARTIAL, META_PROP_VALUE_INVALID, reload_struts, NONE },
{ x11_display->atom__NET_WM_BYPASS_COMPOSITOR, META_PROP_VALUE_CARDINAL, reload_bypass_compositor, LOAD_INIT | INCLUDE_OR },
{ x11_display->atom__NET_WM_WINDOW_OPACITY, META_PROP_VALUE_CARDINAL, reload_window_opacity, LOAD_INIT | INCLUDE_OR },
+ { x11_display->atom__NET_WM_WINDOW_TAG, META_PROP_VALUE_STRING, reload_toplevel_tag, LOAD_INIT },
{ x11_display->atom__NET_WM_XAPP_ICON_NAME, META_PROP_VALUE_UTF8, reload_theme_icon_name, LOAD_INIT | INCLUDE_OR },
{ x11_display->atom__NET_WM_XAPP_PROGRESS, META_PROP_VALUE_CARDINAL, reload_progress, LOAD_INIT | INCLUDE_OR },
{ x11_display->atom__NET_WM_XAPP_PROGRESS_PULSE, META_PROP_VALUE_CARDINAL, reload_progress_pulse, LOAD_INIT | INCLUDE_OR },