From 0ea1a526f93411f8a2aef60dcb5a429a7694ca99 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Thu, 13 Mar 2014 17:28:01 -0400 Subject: [PATCH] gtkwindow: Use window-manager-side window menus This avoids a bunch of policy problems with deciding how to lay out the window menu under different WMs. For now, we use the special event _GTK_SHOW_WINDOW_MENU, but we hope to have this standardized in wm-spec quite soon, as KDE wants it as well. --- gdk/gdkwindow.c | 33 +++++++ gdk/gdkwindow.h | 3 + gdk/gdkwindowimpl.h | 2 + gdk/x11/gdkwindow-x11.c | 42 +++++++++ gtk/gtkwindow.c | 200 +++------------------------------------- 5 files changed, 91 insertions(+), 189 deletions(-) diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index e50601ab32..7f72dca180 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -10921,3 +10921,36 @@ gdk_window_set_shadow_width (GdkWindow *window, if (impl_class->set_shadow_width) impl_class->set_shadow_width (window, left, right, top, bottom); } + +/** + * gdk_window_show_window_menu: + * @window: a #GdkWindow + * @event: a #GdkEvent to show the menu for + * + * Asks the window menu to show the window menu. The window menu is + * the menu shown when right-clicking the titlebar on traditional + * windows managed by the window manager. This is useful for windows + * using client-side decorations, activating it with a right-click + * on the window decorations. + * + * Returns: %TRUE if the window menu was shown by the window + * manager and %FALSE otherwise. + * + * Since: 3.14 + */ +gboolean +gdk_window_show_window_menu (GdkWindow *window, + GdkEvent *event) +{ + GdkWindowImplClass *impl_class; + + g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE); + g_return_val_if_fail (!GDK_WINDOW_DESTROYED (window), FALSE); + + impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl); + + if (impl_class->show_window_menu) + return impl_class->show_window_menu (window, event); + else + return FALSE; +} diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h index d6c72052a3..09a4af3d2b 100644 --- a/gdk/gdkwindow.h +++ b/gdk/gdkwindow.h @@ -1100,6 +1100,9 @@ void gdk_window_set_shadow_width (GdkWindow *window, gint right, gint top, gint bottom); +GDK_AVAILABLE_IN_3_14 +gboolean gdk_window_show_window_menu (GdkWindow *window, + GdkEvent *event); G_END_DECLS diff --git a/gdk/gdkwindowimpl.h b/gdk/gdkwindowimpl.h index bd1714d7f7..b1e9290bc1 100644 --- a/gdk/gdkwindowimpl.h +++ b/gdk/gdkwindowimpl.h @@ -301,6 +301,8 @@ struct _GdkWindowImplClass gint right, gint top, gint bottom); + gboolean (* show_window_menu) (GdkWindow *window, + GdkEvent *event); }; /* Interface Functions */ diff --git a/gdk/x11/gdkwindow-x11.c b/gdk/x11/gdkwindow-x11.c index e5e0b23252..d3120a65dd 100644 --- a/gdk/x11/gdkwindow-x11.c +++ b/gdk/x11/gdkwindow-x11.c @@ -5702,6 +5702,47 @@ gdk_x11_window_set_opaque_region (GdkWindow *window, g_free (data); } +static gboolean +gdk_x11_window_show_window_menu (GdkWindow *window, + GdkEvent *event) +{ + GdkDisplay *display = GDK_WINDOW_DISPLAY (window); + GdkDevice *device; + int device_id; + XClientMessageEvent xclient = { 0 }; + + switch (event->type) + { + case GDK_BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + break; + default: + return FALSE; + } + + if (!gdk_x11_screen_supports_net_wm_hint (GDK_WINDOW_SCREEN (window), + gdk_atom_intern_static_string ("_GTK_SHOW_WINDOW_MENU"))) + return FALSE; + + device = gdk_event_get_device (event); + + g_object_get (G_OBJECT (device), + "device-id", &device_id, + NULL); + + xclient.type = ClientMessage; + xclient.window = GDK_WINDOW_XID (window); + xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_GTK_SHOW_WINDOW_MENU"); + xclient.data.l[0] = device_id; + xclient.format = 32; + + XSendEvent (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XROOTWIN (window), False, + SubstructureRedirectMask | SubstructureNotifyMask, + (XEvent *)&xclient); + + return TRUE; +} + static void gdk_window_impl_x11_class_init (GdkWindowImplX11Class *klass) { @@ -5791,4 +5832,5 @@ gdk_window_impl_x11_class_init (GdkWindowImplX11Class *klass) impl_class->get_scale_factor = gdk_x11_window_get_scale_factor; impl_class->set_opaque_region = gdk_x11_window_set_opaque_region; impl_class->set_shadow_width = gdk_x11_window_set_shadow_width; + impl_class->show_window_menu = gdk_x11_window_show_window_menu; } diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 20f7251e29..537cdf1fe6 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -8632,48 +8632,6 @@ popup_position_func (GtkMenu *menu, { } -static void -ontop_window_clicked (GtkMenuItem *menuitem, - gpointer user_data) -{ - GtkWindow *window = (GtkWindow *)user_data; - - gtk_window_set_keep_above (window, !window->priv->above_initially); -} - -#ifdef GDK_WINDOWING_X11 -static void -stick_window_clicked (GtkMenuItem *menuitem, - gpointer user_data) -{ - GtkWindow *window = (GtkWindow *)user_data; - - gtk_window_stick (window); -} - -static void -unstick_window_clicked (GtkMenuItem *menuitem, - gpointer user_data) -{ - GtkWindow *window = (GtkWindow *)user_data; - - gtk_window_unstick (window); -} - -static void -workspace_change_clicked (GtkMenuItem *menuitem, - gpointer user_data) -{ - GtkWindow *window = (GtkWindow *)user_data; - GdkWindow *gdk_window; - guint32 desktop; - - gdk_window = gtk_widget_get_window (GTK_WIDGET (window)); - desktop = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (menuitem), "workspace")); - gdk_x11_window_move_to_desktop (gdk_window, desktop); -} -#endif - static void close_window_clicked (GtkMenuItem *menuitem, gpointer user_data) @@ -8685,30 +8643,8 @@ close_window_clicked (GtkMenuItem *menuitem, } static void -move_window_clicked (GtkMenuItem *menuitem, - gpointer user_data) -{ - GtkWidget *widget = (GtkWidget *)user_data; - - gdk_window_begin_move_drag (gtk_widget_get_window (widget), - 0, 0, 0, - gtk_get_current_event_time ()); -} - -static void -resize_window_clicked (GtkMenuItem *menuitem, - gpointer user_data) -{ - GtkWidget *widget = (GtkWidget *)user_data; - - gdk_window_begin_resize_drag (gtk_widget_get_window (widget), - 0, 0, 0, 0, - gtk_get_current_event_time ()); -} - -static void -gtk_window_do_popup (GtkWindow *window, - GdkEventButton *event) +gtk_window_do_popup_fallback (GtkWindow *window, + GdkEventButton *event) { GtkWindowPrivate *priv = window->priv; GtkWidget *menuitem; @@ -8724,129 +8660,6 @@ gtk_window_do_popup (GtkWindow *window, GTK_WIDGET (window), popup_menu_detach); - menuitem = gtk_menu_item_new_with_label (_("Minimize")); - gtk_widget_show (menuitem); - if (priv->gdk_type_hint != GDK_WINDOW_TYPE_HINT_NORMAL) - gtk_widget_set_sensitive (menuitem, FALSE); - g_signal_connect_swapped (G_OBJECT (menuitem), "activate", - G_CALLBACK (gtk_window_iconify), window); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem); - - menuitem = gtk_menu_item_new_with_label (priv->maximized ? _("Unmaximize") : _("Maximize")); - gtk_widget_show (menuitem); - if (!priv->resizable || - priv->gdk_type_hint != GDK_WINDOW_TYPE_HINT_NORMAL) - gtk_widget_set_sensitive (menuitem, FALSE); - g_signal_connect_swapped (G_OBJECT (menuitem), "activate", - G_CALLBACK (_gtk_window_toggle_maximized), window); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem); - - menuitem = gtk_menu_item_new_with_label (_("Move")); - gtk_widget_show (menuitem); - g_signal_connect (G_OBJECT (menuitem), "activate", - G_CALLBACK (move_window_clicked), window); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem); - - menuitem = gtk_menu_item_new_with_label (_("Resize")); - gtk_widget_show (menuitem); - if (!priv->resizable || priv->maximized) - gtk_widget_set_sensitive (menuitem, FALSE); - g_signal_connect (G_OBJECT (menuitem), "activate", - G_CALLBACK (resize_window_clicked), window); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem); - - menuitem = gtk_separator_menu_item_new (); - gtk_widget_show (menuitem); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem); - - menuitem = gtk_check_menu_item_new_with_label (_("Always on Top")); - gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), priv->above_initially); - if (priv->maximized) - gtk_widget_set_sensitive (menuitem, FALSE); - gtk_widget_show (menuitem); - g_signal_connect (G_OBJECT (menuitem), "activate", - G_CALLBACK (ontop_window_clicked), window); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem); - -#ifdef GDK_WINDOWING_X11 - if (GDK_IS_X11_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window)))) - { - menuitem = gtk_check_menu_item_new_with_label (_("Always on Visible Workspace")); - gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (menuitem), TRUE); - gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), priv->stick_initially); - gtk_widget_show (menuitem); - g_signal_connect (G_OBJECT (menuitem), "activate", - G_CALLBACK (stick_window_clicked), window); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem); - - menuitem = gtk_check_menu_item_new_with_label (_("Only on This Workspace")); - gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (menuitem), TRUE); - gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), !priv->stick_initially); - gtk_widget_show (menuitem); - g_signal_connect (G_OBJECT (menuitem), "activate", - G_CALLBACK (unstick_window_clicked), window); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem); - - if (!priv->stick_initially) - { - guint32 n_desktops, desktop; - - n_desktops = gdk_x11_screen_get_number_of_desktops (gtk_widget_get_screen (GTK_WIDGET (window))); - desktop = gdk_x11_window_get_desktop (gtk_widget_get_window (GTK_WIDGET (window))); - - if (desktop > 0) - { - menuitem = gtk_menu_item_new_with_label (_("Move to Workspace Up")); - g_object_set_data (G_OBJECT (menuitem), "workspace", GUINT_TO_POINTER (desktop - 1)); - gtk_widget_show (menuitem); - g_signal_connect (G_OBJECT (menuitem), "activate", - G_CALLBACK (workspace_change_clicked), window); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem); - } - if (desktop + 1 < n_desktops) - { - menuitem = gtk_menu_item_new_with_label (_("Move to Workspace Down")); - g_object_set_data (G_OBJECT (menuitem), "workspace", GUINT_TO_POINTER (desktop + 1)); - gtk_widget_show (menuitem); - g_signal_connect (G_OBJECT (menuitem), "activate", - G_CALLBACK (workspace_change_clicked), window); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem); - } - if (n_desktops > 2) - { - GtkWidget *submenu; - gint d; - guint32 current; - - current = gdk_x11_screen_get_current_desktop (gtk_widget_get_screen (GTK_WIDGET (window))); - menuitem = gtk_menu_item_new_with_label (_("Move to Another Workspace")); - gtk_widget_show (menuitem); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem); - submenu = gtk_menu_new (); - gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu); - for (d = 0; d < n_desktops; d++) - { - gchar *label; - label = g_strdup_printf (_("Workspace %d"), d + 1); - menuitem = gtk_menu_item_new_with_label (label); - g_free (label); - g_object_set_data (G_OBJECT (menuitem), "workspace", GUINT_TO_POINTER (d)); - if (d == current) - gtk_widget_set_sensitive (menuitem, FALSE); - gtk_widget_show (menuitem); - g_signal_connect (G_OBJECT (menuitem), "activate", - G_CALLBACK (workspace_change_clicked), window); - gtk_menu_shell_append (GTK_MENU_SHELL (submenu), menuitem); - } - } - } - } -#endif - - menuitem = gtk_separator_menu_item_new (); - gtk_widget_show (menuitem); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem); - menuitem = gtk_menu_item_new_with_label (_("Close")); gtk_widget_show (menuitem); if (!priv->deletable) @@ -8867,6 +8680,15 @@ gtk_window_do_popup (GtkWindow *window, 0, gtk_get_current_event_time ()); } +static void +gtk_window_do_popup (GtkWindow *window, + GdkEventButton *event) +{ + if (!gdk_window_show_window_menu (gtk_widget_get_window (GTK_WIDGET (window)), + (GdkEvent *) event)) + gtk_window_do_popup_fallback (window, event); +} + /********************************* * Functions related to resizing * *********************************/