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 fc7dab220d430efd17a4bce180291c30efb50143
Author: Cedric BAIL <[email protected]>
AuthorDate: Mon Aug 10 09:50:03 2026 -0600

    wl_desktop_shell - a new toplevel ends any open menu
    
    xdg-shell expects a grabbing popup to be dismissed when a new toplevel
    turns up: the menu the user opened is not what the pointer and keyboard
    belong to any more, and a client that is never told will sit there
    holding a grab over an unrelated window.
    
    E had the machinery and half the rule. e_comp_wl_grab_client_add()
    already dismissed outstanding grabs when a *new grab* arrived from a
    different toplevel; nothing covered a toplevel merely being created.
    Factor that block out and call it from every shell's toplevel path.
    
    Every shell, not just xdg: wlcs asks the compositor for a surface using
    whichever shell it advertises and prefers wl_shell, so hooking xdg alone
    fixed nothing and measured as such.
    
    Dismiss when the role is taken rather than on map. A client is entitled
    to sit on its first configure for as long as it likes, and a menu left
    grabbing the keyboard in the meantime is exactly the stuck menu the grab
    protocol exists to avoid - the test also measures the created case, and
    map was too late for it.
    
    XdgPopupTest: 13 passed / 4 failed -> 15 / 2. The two left are
    popup_gives_up_pointer_focus_when_gone, which is the surface-unmap
    cluster rather than anything to do with grabs.
    
    Verified against the same wlcs filter on the branch point, 262 tests
    across popup, surface, toplevel, wl_shell, subsurface and pointer: four
    fixed, no regressions.
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01FtoiXoSKUmZb6Aix6U3GZS
---
 src/bin/e_comp_wl.c                     | 44 ++++++++++++++++++++++++---------
 src/bin/e_comp_wl.h                     |  1 +
 src/modules/wl_desktop_shell/wl_shell.c |  3 +++
 src/modules/wl_desktop_shell/xdg.c      |  7 ++++++
 src/modules/wl_desktop_shell/xdg5.c     |  3 +++
 src/modules/wl_desktop_shell/xdg6.c     |  3 +++
 6 files changed, 50 insertions(+), 11 deletions(-)

diff --git a/src/bin/e_comp_wl.c b/src/bin/e_comp_wl.c
index 98f42cf60..a0eaed62c 100644
--- a/src/bin/e_comp_wl.c
+++ b/src/bin/e_comp_wl.c
@@ -3785,22 +3785,44 @@ e_comp_wl_xwayland_client_queue(E_Client *ec)
    e_comp_wl->xwl_pending = eina_list_append(e_comp_wl->xwl_pending, ec);
 }
 
+/* Dismiss every outstanding grab, innermost first: grab_clients is prepended
+ * to, so the head is the most recent popup and a nested menu tears down from
+ * the leaf up, which is the order xdg-shell requires the popup_done events in.
+ */
+static void
+_e_comp_wl_grab_clients_dismiss_all(void)
+{
+   E_Client *gec;
+   E_Comp_Wl_Grab_End_Cb grabcb;
+
+   EINA_LIST_FREE(grab_clients, gec)
+     {
+        grabcb = eina_list_data_get(grab_cbs);
+        if (grabcb) grabcb(gec);
+        grab_cbs = eina_list_remove_list(grab_cbs, grab_cbs);
+     }
+}
+
+/* A new toplevel appearing supersedes any popup grab: whatever menu was open
+ * is no longer what the pointer and keyboard belong to, and xdg-shell requires
+ * the grabbing popup be told so it can tear the chain down rather than linger
+ * over an unrelated window. */
+E_API void
+e_comp_wl_grab_clients_dismiss(void)
+{
+   if (!grab_clients) return;
+   _e_comp_wl_grab_clients_dismiss_all();
+   e_bindings_disabled_set(0);
+}
+
 E_API void
 e_comp_wl_grab_client_add(E_Client *ec, E_Comp_Wl_Grab_End_Cb cb)
 {
-   E_Client *gec, *parent = e_client_util_top_parent_get(ec);
-   E_Comp_Wl_Grab_End_Cb grabcb;
+   E_Client *parent = e_client_util_top_parent_get(ec);
 
    if (grab_clients && (parent != e_client_util_top_parent_get(eina_list_data_get(grab_clients))))
-     {
-        /* dismiss grabs in order when grabbing from new toplevel */
-        EINA_LIST_FREE(grab_clients, gec)
-          {
-             grabcb = eina_list_data_get(grab_cbs);
-             if (grabcb) grabcb(gec);
-             grab_cbs = eina_list_remove_list(grab_cbs, grab_cbs);
-          }
-     }
+     /* dismiss grabs in order when grabbing from new toplevel */
+     _e_comp_wl_grab_clients_dismiss_all();
    grab_clients = eina_list_prepend(grab_clients, ec);
    grab_cbs = eina_list_prepend(grab_cbs, cb);
    ec->comp_data->grab = 1;
diff --git a/src/bin/e_comp_wl.h b/src/bin/e_comp_wl.h
index b01af21d3..3388d74ce 100644
--- a/src/bin/e_comp_wl.h
+++ b/src/bin/e_comp_wl.h
@@ -421,6 +421,7 @@ E_API extern int E_EVENT_WAYLAND_GLOBAL_ADD;
 
 E_API void e_comp_wl_grab_client_add(E_Client *ec, E_Comp_Wl_Grab_End_Cb cb);
 E_API void e_comp_wl_grab_client_del(E_Client *ec, Eina_Bool dismiss);
+E_API void e_comp_wl_grab_clients_dismiss(void);
 E_API Eina_Bool e_comp_wl_client_is_grabbed(const E_Client *ec);
 E_API Eina_Bool e_comp_wl_grab_client_mouse_move(const Ecore_Event_Mouse_Move *ev);
 E_API Eina_Bool e_comp_wl_grab_client_mouse_button(const Ecore_Event_Mouse_Button *ev);
diff --git a/src/modules/wl_desktop_shell/wl_shell.c b/src/modules/wl_desktop_shell/wl_shell.c
index 49a709d1e..c7832b872 100644
--- a/src/modules/wl_desktop_shell/wl_shell.c
+++ b/src/modules/wl_desktop_shell/wl_shell.c
@@ -121,6 +121,9 @@ _wl_shell_surface_cb_toplevel_set(struct wl_client *client EINA_UNUSED, struct w
      }
    if (e_object_is_del(E_OBJECT(ec))) return;
 
+   /* A new toplevel supersedes any open menu; see the same call in xdg.c. */
+   e_comp_wl_grab_clients_dismiss();
+
    /* set toplevel client properties */
    ec->icccm.accepts_focus = 1;
    if (!ec->internal)
diff --git a/src/modules/wl_desktop_shell/xdg.c b/src/modules/wl_desktop_shell/xdg.c
index 56d13a612..8b652b06f 100644
--- a/src/modules/wl_desktop_shell/xdg.c
+++ b/src/modules/wl_desktop_shell/xdg.c
@@ -1410,6 +1410,13 @@ _e_xdg_surface_cb_toplevel_get(struct wl_client *client EINA_UNUSED, struct wl_r
                                   &_e_xdg_toplevel_interface, ec,
                                   e_shell_surface_cb_destroy);
 
+   /* A new toplevel supersedes any open menu. Dismiss here, when the role is
+    * taken, rather than on map: the client is entitled to sit on the first
+    * configure for as long as it likes, and a menu left grabbing the keyboard
+    * in the meantime is exactly the stuck-menu the grab protocol exists to
+    * avoid. */
+   e_comp_wl_grab_clients_dismiss();
+
    e_object_ref(E_OBJECT(ec));
 
    cdata->shell.configure_send = _e_xdg_shell_surface_configure_send;
diff --git a/src/modules/wl_desktop_shell/xdg5.c b/src/modules/wl_desktop_shell/xdg5.c
index 2109e7fe4..69a9e723b 100644
--- a/src/modules/wl_desktop_shell/xdg5.c
+++ b/src/modules/wl_desktop_shell/xdg5.c
@@ -699,6 +699,9 @@ _e_xdg_shell_cb_surface_get(struct wl_client *client, struct wl_resource *resour
    cdata->shell.data = ""
    cdata->is_xdg_surface = EINA_TRUE;
 
+   /* A new toplevel supersedes any open menu; see the same call in xdg.c. */
+   e_comp_wl_grab_clients_dismiss();
+
    /* set toplevel client properties */
    ec->icccm.accepts_focus = 1;
    if (!ec->internal)
diff --git a/src/modules/wl_desktop_shell/xdg6.c b/src/modules/wl_desktop_shell/xdg6.c
index 0e7ed68e7..fa938a771 100644
--- a/src/modules/wl_desktop_shell/xdg6.c
+++ b/src/modules/wl_desktop_shell/xdg6.c
@@ -1256,6 +1256,9 @@ _e_xdg_surface_cb_toplevel_get(struct wl_client *client EINA_UNUSED, struct wl_r
                                   &_e_xdg_toplevel_interface, ec,
                                   e_shell_surface_cb_destroy);
 
+   /* A new toplevel supersedes any open menu; see the same call in xdg.c. */
+   e_comp_wl_grab_clients_dismiss();
+
    e_object_ref(E_OBJECT(ec));
 
    cdata->shell.configure_send = _e_xdg_shell_surface_configure_send;

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

Reply via email to