This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch wl/browser-all
in repository enlightenment.

View the commit online.

commit 6b550951bda05efe502a6d3fbbaebfca6a1856eb
Author: Cedric BAIL <[email protected]>
AuthorDate: Sun Aug 16 14:14:11 2026 -0600

    wl_desktop_shell - implement zxdg_decoration_manager_v1
    
    Who draws the titlebar. With no way to ask and no way to be told, E has
    decorated nothing on Wayland: _e_xdg_surface_cb_toplevel_get marks every
    external toplevel borderless on the assumption that the client draws its
    own. That is right for GTK and Chromium and wrong for everything that
    expected the compositor to do the job, and on a border config where E
    would have drawn one anyway the result is two titlebars.
    
    It goes in xdg.c rather than in e_comp_wl_extensions.c with the other
    globals because it is an extension of xdg_shell, not of the core: its one
    request takes an xdg_toplevel, so it can only be implemented where that
    interface is linked. Putting it in the binary fails to link.
    
    The negotiated mode is written into ec->borderless, which is E's own
    switch for the same question, so the answer joins the border logic
    already there. Unasked, E says server side: it is a shell with borders
    and a theme to draw them with, and a client that would rather draw its
    own only has to say so - which every real toolkit does. Nothing can be
    asking today, the global did not exist, so this changes no behaviour that
    anyone has seen.
    
    A decoration keeps a handle on the manager that made it, because both
    protocol errors are posted there: already_constructed is the manager's
    own failed request, and orphaned is noticed out of band with no request
    to blame. That handle needs watching - destroying a manager explicitly
    does not destroy the decorations it made, so it can outlive its own
    resource, and posting the orphaned error through a stale pointer would
    be a use after free.
    
    wlcs XdgDecorationV1Test: 6 skipped -> 6 passed.
---
 src/modules/wl_desktop_shell/meson.build |   1 +
 src/modules/wl_desktop_shell/xdg.c       | 242 ++++++++++++++++++++++++++++++-
 src/tests/wayland/globals.expected       |   1 +
 3 files changed, 243 insertions(+), 1 deletion(-)

diff --git a/src/modules/wl_desktop_shell/meson.build b/src/modules/wl_desktop_shell/meson.build
index 05a05405a..83803afaf 100644
--- a/src/modules/wl_desktop_shell/meson.build
+++ b/src/modules/wl_desktop_shell/meson.build
@@ -14,6 +14,7 @@ else
     '@0@/unstable/xdg-shell/xdg-shell-unstable-v6.xml'.format(dir_wayland_protocols),
     '@0@/stable/xdg-shell/xdg-shell.xml'.format(dir_wayland_protocols),
     '@0@/unstable/input-method/input-method-unstable-v1.xml'.format(dir_wayland_protocols),
+    '@0@/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml'.format(dir_wayland_protocols),
   ]
     src += gen_scanner_server.process(p)
     src += gen_scanner_impl.process(p)
diff --git a/src/modules/wl_desktop_shell/xdg.c b/src/modules/wl_desktop_shell/xdg.c
index f8b0c37aa..2da04365d 100644
--- a/src/modules/wl_desktop_shell/xdg.c
+++ b/src/modules/wl_desktop_shell/xdg.c
@@ -3,6 +3,7 @@
 #include "e_mod_main.h"
 
 #include "xdg-shell-server-protocol.h"
+#include "xdg-decoration-unstable-v1-server-protocol.h"
 
 /* Every child object is created at the version its parent was bound at,
  * so this is the only number to move when a version is finished. */
@@ -1998,6 +1999,237 @@ _xdg_popup_reactive_parent_moved(void *d EINA_UNUSED, E_Client *ec)
      }
 }
 
+/* zxdg_decoration_manager_v1.
+ *
+ * Who draws the titlebar. With no way to ask and no way to be told, E has
+ * decorated nothing on Wayland - _e_xdg_surface_cb_toplevel_get above marks
+ * every external toplevel borderless on the assumption that the client draws
+ * its own. That is right for GTK and Chromium and wrong for everything that
+ * expected the compositor to do the job, and on a border config where E would
+ * have drawn one anyway the result is two titlebars.
+ *
+ * It lives here rather than in e_comp_wl_extensions.c with the other globals
+ * because it is an extension *of xdg_shell*: its one request takes an
+ * xdg_toplevel, so it can only be implemented where that interface is linked.
+ *
+ * The negotiated mode is written into ec->borderless, E's own switch for the
+ * same question, so the answer joins the border logic already here instead of
+ * sitting beside it. Unasked, E says server side: it is a shell with borders
+ * and a theme to draw them with, and a client that would rather draw its own
+ * only has to say so.
+ *
+ * Both protocol errors go to the manager rather than to the decoration. For
+ * already_constructed that is the object whose request failed; orphaned is
+ * noticed out of band, with no request of its own to blame, so it follows
+ * suit - which is also where wlcs looks for both. */
+
+typedef struct _E_Xdg_Decoration
+{
+   struct wl_resource *resource; //zxdg_toplevel_decoration_v1
+   /* The manager the decoration was asked for. Destroying a manager does not
+    * destroy the decorations it made, so this outlives its own resource often
+    * enough to need watching. */
+   struct wl_resource *manager;
+   struct wl_listener manager_destroy;
+   struct wl_resource *toplevel; //xdg_toplevel, NULL once that has gone
+   struct wl_listener toplevel_destroy;
+} E_Xdg_Decoration;
+
+static Eina_Hash *xdg_decorations; //xdg_toplevel resource -> E_Xdg_Decoration
+
+static void
+_e_xdg_toplevel_decoration_configure(E_Xdg_Decoration *deco, uint32_t mode)
+{
+   E_Client *ec;
+
+   zxdg_toplevel_decoration_v1_send_configure(deco->resource, mode);
+
+   if (!deco->toplevel) return;
+   ec = wl_resource_get_user_data(deco->toplevel);
+   if ((!ec) || e_object_is_del(E_OBJECT(ec))) return;
+
+   /* lock_border stays as toplevel_get set it: that stops the user toggling
+    * the border by hand, not the client asking for a mode. */
+   ec->borderless = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);
+   ec->border.changed = 1;
+   EC_CHANGED(ec);
+
+   /* The decoration configure is only half of it - the client acks against
+    * xdg_surface, and until it has that serial it may not attach at all. A
+    * zero size leaves the size itself with the client. */
+   if (ec->comp_data->shell.surface)
+     _e_xdg_shell_surface_configure_send(ec->comp_data->shell.surface, 0, 0, 0);
+}
+
+static void
+_e_xdg_toplevel_decoration_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+   wl_resource_destroy(resource);
+}
+
+static void
+_e_xdg_toplevel_decoration_cb_mode_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, uint32_t mode)
+{
+   E_Xdg_Decoration *deco;
+
+   if (!(deco = wl_resource_get_user_data(resource))) return;
+   if ((mode != ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE) &&
+       (mode != ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE))
+     mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
+
+   /* Whatever the client asks for it gets. E has a frame to offer but no
+    * reason to force it on a client that says it draws its own - that way
+    * lies the double titlebar this protocol exists to prevent. */
+   _e_xdg_toplevel_decoration_configure(deco, mode);
+}
+
+static void
+_e_xdg_toplevel_decoration_cb_mode_unset(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+   E_Xdg_Decoration *deco;
+
+   if (!(deco = wl_resource_get_user_data(resource))) return;
+   _e_xdg_toplevel_decoration_configure(deco,
+     ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
+}
+
+static const struct zxdg_toplevel_decoration_v1_interface _e_xdg_toplevel_decoration_interface =
+{
+   _e_xdg_toplevel_decoration_cb_destroy,
+   _e_xdg_toplevel_decoration_cb_mode_set,
+   _e_xdg_toplevel_decoration_cb_mode_unset,
+};
+
+static void
+_e_xdg_toplevel_decoration_cb_toplevel_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
+{
+   E_Xdg_Decoration *deco;
+
+   deco = container_of(listener, E_Xdg_Decoration, toplevel_destroy);
+
+   eina_hash_del_by_key(xdg_decorations, &deco->toplevel);
+   deco->toplevel = NULL;
+
+   wl_resource_post_error(deco->manager ?: deco->resource,
+                          ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ORPHANED,
+                          "xdg_toplevel destroyed before its decoration");
+}
+
+static void
+_e_xdg_toplevel_decoration_cb_manager_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
+{
+   E_Xdg_Decoration *deco;
+
+   deco = container_of(listener, E_Xdg_Decoration, manager_destroy);
+   deco->manager = NULL;
+}
+
+/* called by wl_resource_destroy */
+static void
+_e_xdg_toplevel_decoration_cb_resource_destroy(struct wl_resource *resource)
+{
+   E_Xdg_Decoration *deco;
+
+   if (!(deco = wl_resource_get_user_data(resource))) return;
+
+   if (deco->toplevel)
+     {
+        wl_list_remove(&deco->toplevel_destroy.link);
+        eina_hash_del_by_key(xdg_decorations, &deco->toplevel);
+     }
+   if (deco->manager) wl_list_remove(&deco->manager_destroy.link);
+
+   free(deco);
+}
+
+static void
+_e_xdg_decoration_manager_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+   wl_resource_destroy(resource);
+}
+
+static void
+_e_xdg_decoration_manager_cb_toplevel_decoration_get(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *toplevel_resource)
+{
+   E_Xdg_Decoration *deco;
+   E_Client *ec;
+
+   if (eina_hash_find(xdg_decorations, &toplevel_resource))
+     {
+        wl_resource_post_error(resource,
+                               ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ALREADY_CONSTRUCTED,
+                               "xdg_toplevel already has a decoration");
+        return;
+     }
+
+   ec = wl_resource_get_user_data(toplevel_resource);
+   if ((ec) && (!e_object_is_del(E_OBJECT(ec))) && e_pixmap_usable_get(ec->pixmap))
+     {
+        wl_resource_post_error(resource,
+                               ZXDG_TOPLEVEL_DECORATION_V1_ERROR_UNCONFIGURED_BUFFER,
+                               "xdg_toplevel already has a buffer");
+        return;
+     }
+
+   deco = E_NEW(E_Xdg_Decoration, 1);
+   if (!deco)
+     {
+        wl_resource_post_no_memory(resource);
+        return;
+     }
+
+   deco->resource =
+     wl_resource_create(client, &zxdg_toplevel_decoration_v1_interface,
+                        wl_resource_get_version(resource), id);
+   if (!deco->resource)
+     {
+        free(deco);
+        wl_resource_post_no_memory(resource);
+        return;
+     }
+
+   deco->manager = resource;
+   deco->manager_destroy.notify = _e_xdg_toplevel_decoration_cb_manager_destroy;
+   wl_resource_add_destroy_listener(resource, &deco->manager_destroy);
+
+   deco->toplevel = toplevel_resource;
+   deco->toplevel_destroy.notify = _e_xdg_toplevel_decoration_cb_toplevel_destroy;
+   wl_resource_add_destroy_listener(toplevel_resource, &deco->toplevel_destroy);
+   eina_hash_add(xdg_decorations, &toplevel_resource, deco);
+
+   wl_resource_set_implementation(deco->resource,
+                                  &_e_xdg_toplevel_decoration_interface, deco,
+                                  _e_xdg_toplevel_decoration_cb_resource_destroy);
+
+   /* The client may not attach until it has been told a mode, so say so now
+    * rather than waiting to be asked. */
+   _e_xdg_toplevel_decoration_configure(deco,
+     ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
+}
+
+static const struct zxdg_decoration_manager_v1_interface _e_xdg_decoration_manager_interface =
+{
+   _e_xdg_decoration_manager_cb_destroy,
+   _e_xdg_decoration_manager_cb_toplevel_decoration_get,
+};
+
+static void
+_e_xdg_decoration_manager_cb_bind(struct wl_client *client, void *data EINA_UNUSED, uint32_t version, uint32_t id)
+{
+   struct wl_resource *res;
+
+   res = wl_resource_create(client, &zxdg_decoration_manager_v1_interface,
+                            version, id);
+   if (!res)
+     {
+        wl_client_post_no_memory(client);
+        return;
+     }
+
+   wl_resource_set_implementation(res, &_e_xdg_decoration_manager_interface,
+                                  NULL, NULL);
+}
+
 EINTERN Eina_Bool
 e_xdg_shell_init(void)
 {
@@ -2009,7 +2241,15 @@ e_xdg_shell_init(void)
         ERR("Could not create xdg_shell global");
         return EINA_FALSE;
      }
-   hooks = eina_list_append(hooks, e_client_hook_add(E_CLIENT_HOOK_DEL, _xdg_client_hook_del, NULL));
+   if (!wl_global_create(e_comp_wl->wl.disp,
+                         &zxdg_decoration_manager_v1_interface, 1,
+                         NULL, _e_xdg_decoration_manager_cb_bind))
+     {
+        ERR("Could not create zxdg_decoration_manager_v1 global");
+        return EINA_FALSE;
+     }
+   xdg_decorations = eina_hash_pointer_new(NULL);
+   hooks =eina_list_append(hooks, e_client_hook_add(E_CLIENT_HOOK_DEL, _xdg_client_hook_del, NULL));
    hooks = eina_list_append(hooks, e_client_hook_add(E_CLIENT_HOOK_MOVE_UPDATE, _xdg_popup_reactive_parent_moved, NULL));
    hooks = eina_list_append(hooks, e_client_hook_add(E_CLIENT_HOOK_MOVE_END, _xdg_popup_reactive_parent_moved, NULL));
    return EINA_TRUE;
diff --git a/src/tests/wayland/globals.expected b/src/tests/wayland/globals.expected
index 6197f36ab..a5c87928c 100644
--- a/src/tests/wayland/globals.expected
+++ b/src/tests/wayland/globals.expected
@@ -13,6 +13,7 @@ xdg_wm_base	6
 zwp_e_session_recovery	1
 zwp_pointer_constraints_v1	1
 zwp_relative_pointer_manager_v1	1
+zxdg_decoration_manager_v1	1
 zxdg_exporter_v1	1
 zxdg_importer_v1	1
 zxdg_output_manager_v1	3

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to