[gtk+] wayland: Fix GtkMenuButton popups in a terrible, hacky way



commit 75ecdf50a3d1de29a982df6cd614c2f652504678
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Thu May 15 17:20:00 2014 -0400

    wayland: Fix GtkMenuButton popups in a terrible, hacky way
    
    Since you can't take grabs on unmapped windows, GtkMenu takes a grab on
    the menu in a convoluted way: it first grabs another window, shows the
    menu window, and then transfers the grab over to the GtkMenu widget.
    
    For normal menubars, this is perfectly fine, as the first window it grabs
    is our toplevel, and that gets picked up in our transient path.  For
    GtkMenuButton or other spurious uses of gtk_menu_popup, it creates a new
    temporary input-only window which it takes the grab on, known as the "grab
    transfer window". Since this window isn't a transient-for of our new menu
    widget window, the grab isn't noticed when we go to show it, and thus the
    menu ends up as a new toplevel.
    
    Add a special hack to GtkMenu and the Wayland backend which lets us notice
    this "grab transfer window", and include it in our grab finding path.
    
    It's sort of terrible to have to hack up the widgets instead of just the
    backend, but the alternative would be an entirely new window type which is
    managed correctly by GDK. I don't want to write that.

 gdk/wayland/gdkwindow-wayland.c |   15 +++++++++++++++
 gtk/gtkmenu.c                   |   23 +++++++++++++++++++++++
 2 files changed, 38 insertions(+), 0 deletions(-)
---
diff --git a/gdk/wayland/gdkwindow-wayland.c b/gdk/wayland/gdkwindow-wayland.c
index 9d04aec..cf1f02d 100644
--- a/gdk/wayland/gdkwindow-wayland.c
+++ b/gdk/wayland/gdkwindow-wayland.c
@@ -993,6 +993,7 @@ gdk_wayland_window_create_xdg_popup (GdkWindow            *window,
 static struct wl_seat *
 find_grab_input_seat (GdkWindow *window, GdkWindow *transient_for)
 {
+  GdkWindow *attached_grab_window;
   GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
   GdkWindowImplWayland *tmp_impl;
 
@@ -1003,6 +1004,20 @@ find_grab_input_seat (GdkWindow *window, GdkWindow *transient_for)
   if (impl->grab_input_seat)
     return impl->grab_input_seat;
 
+  /* HACK: GtkMenu grabs a special window known as the "grab transfer window"
+   * and then transfers the grab over to the correct window later. Look for
+   * this window when taking the grab to know it's correct.
+   *
+   * See: associate_menu_grab_transfer_window in gtkmenu.c
+   */
+  attached_grab_window = g_object_get_data (G_OBJECT (window), "gdk-attached-grab-window");
+  if (attached_grab_window)
+    {
+      tmp_impl = GDK_WINDOW_IMPL_WAYLAND (attached_grab_window->impl);
+      if (tmp_impl->grab_input_seat)
+        return tmp_impl->grab_input_seat;
+    }
+
   while (transient_for)
     {
       tmp_impl = GDK_WINDOW_IMPL_WAYLAND (transient_for->impl);
diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c
index f29ac12..59467e5 100644
--- a/gtk/gtkmenu.c
+++ b/gtk/gtkmenu.c
@@ -1467,6 +1467,22 @@ popup_grab_on_window (GdkWindow *window,
   return TRUE;
 }
 
+static void
+associate_menu_grab_transfer_window (GtkMenu *menu)
+{
+  GtkMenuPrivate *priv = menu->priv;
+  GdkWindow *toplevel_window;
+  GdkWindow *transfer_window;
+
+  toplevel_window = gtk_widget_get_window (priv->toplevel);
+  transfer_window = g_object_get_data (G_OBJECT (menu), "gtk-menu-transfer-window");
+
+  if (toplevel_window == NULL || transfer_window == NULL)
+    return;
+
+  g_object_set_data (G_OBJECT (toplevel_window), I_("gdk-attached-grab-window"), transfer_window);
+}
+
 /**
  * gtk_menu_popup_for_device:
  * @menu: a #GtkMenu
@@ -1704,6 +1720,8 @@ gtk_menu_popup_for_device (GtkMenu             *menu,
    */
   gtk_menu_position (menu, TRUE);
 
+  associate_menu_grab_transfer_window (menu);
+
   gtk_menu_scroll_to (menu, priv->scroll_offset);
 
   /* if no item is selected, select the first one */
@@ -2689,9 +2707,14 @@ menu_grab_transfer_window_destroy (GtkMenu *menu)
   GdkWindow *window = g_object_get_data (G_OBJECT (menu), "gtk-menu-transfer-window");
   if (window)
     {
+      GdkWindow *widget_window;
+
       gtk_widget_unregister_window (GTK_WIDGET (menu), window);
       gdk_window_destroy (window);
       g_object_set_data (G_OBJECT (menu), I_("gtk-menu-transfer-window"), NULL);
+
+      widget_window = gtk_widget_get_window (GTK_WIDGET (menu));
+      g_object_set_data (G_OBJECT (widget_window), I_("gdk-attached-grab-window"), window);
     }
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]