diff options
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | libowl/owlwindowmenu.c | 53 |
2 files changed, 50 insertions, 8 deletions
@@ -1,5 +1,10 @@ 2007-04-20 Ross Burton <ross@openedhand.com> + * libowl/owlwindowmenu.c: + Comments, sanity checks. + +2007-04-20 Ross Burton <ross@openedhand.com> + * libowl/Makefile.am: * libowl/owlwindowmenu.[ch]: Add function so that if you send the CUSTOM message (by clicking diff --git a/libowl/owlwindowmenu.c b/libowl/owlwindowmenu.c index 7be5f0f..4f6b2d7 100644 --- a/libowl/owlwindowmenu.c +++ b/libowl/owlwindowmenu.c @@ -28,13 +28,17 @@ static Atom atom_custom, atom_protocol; +/* + * Ensures that the atoms we need are defined. + */ static void ensure_atoms (GdkDrawable *drawable) { static gboolean done = FALSE; + if (G_UNLIKELY (!done)) { GdkDisplay *display = gdk_drawable_get_display (drawable); - + atom_custom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_CONTEXT_CUSTOM"); atom_protocol = gdk_x11_get_xatom_by_name_for_display @@ -42,10 +46,14 @@ ensure_atoms (GdkDrawable *drawable) g_assert (atom_custom); g_assert (atom_protocol); + done = TRUE; } } +/* + * Menu positioning function, called by gtk_menu_popup. + */ static void position_func (GtkMenu *menu, gint *x, @@ -55,37 +63,59 @@ position_func (GtkMenu *menu, { GtkWidget *window = user_data; - /* Set the position of the menu to the origin of the window. This means that - the menu is in the top left of the application window. */ + /* + * Set the position of the menu to the origin of the window. This means that + * the menu is in the top left of the application window. It might be + * interesting to see if the theme could specify an offset, so that the menu + * aligns itself with the decorations. + */ gdk_window_get_origin (window->window, x, y); } +/* + * Gdk event filter. This should be as fast as possible for non-client messages + * as it gets called *frequently*. + */ static GdkFilterReturn filter_func (GdkXEvent *xevent, GdkEvent *event, gpointer data) { - if (((XEvent*)xevent)->type == ClientMessage) { + if (G_UNLIKELY (((XEvent*)xevent)->type == ClientMessage)) { XClientMessageEvent *xev = xevent; + if (xev->message_type == atom_protocol) { Atom protocol = xev->data.l[0]; + if (protocol == atom_custom) { GtkMenu *menu; - + + /* Sanity check the user data is the window */ g_return_val_if_fail (GTK_IS_WINDOW (data), GDK_FILTER_CONTINUE); menu = g_object_get_data (G_OBJECT (data), MENU_PROP); - /* I wonder if this offset from the window should be specified by the theme */ - gtk_menu_popup (menu, NULL, NULL, - position_func, data, + if (menu) { + gtk_menu_popup (menu, NULL, NULL, + position_func, data, 0, gtk_get_current_event_time()); + } return GDK_FILTER_REMOVE; } } } + return GDK_FILTER_CONTINUE; } +/** + * owl_set_window_menu: + * @window: a top-level #GtkWindow + * @menu: a #GtkMenu + * + * Make @menu pop up when the CUSTOM protocol is sent to @window. This means + * that when the user clicks on the title bar with a suitable window manager, + * the menu appears. + */ void owl_set_window_menu (GtkWindow *window, GtkMenu *menu) { @@ -96,15 +126,18 @@ owl_set_window_menu (GtkWindow *window, GtkMenu *menu) int count = 0; g_return_if_fail (GTK_IS_WINDOW (window)); + /* TODO: allow NULL menu to unset? */ g_return_if_fail (GTK_IS_MENU (menu)); w = GTK_WIDGET (window)->window; + /* Make sure the atoms we need are defined */ ensure_atoms (w); /* We only need to setup if the property isn't already set */ done_setup = g_object_get_data (G_OBJECT (window), MENU_PROP) != NULL; + /* Set the menu. If there is a menu already defined, it will be unreffed */ g_object_set_data_full (G_OBJECT (window), MENU_PROP, g_object_ref (menu), g_object_unref); /* It's possible that we are replacing the menu with another menu, so the @@ -112,21 +145,25 @@ owl_set_window_menu (GtkWindow *window, GtkMenu *menu) if (done_setup) return; + /* Get the protocols supported by this window */ status = XGetWMProtocols(GDK_WINDOW_XDISPLAY (w), GDK_WINDOW_XID (w), &old_atoms, &count); /* TODO: check error */ /* TODO: check if old_atoms already contains custom */ + /* Add _NEW_WM_CONTEXT_CUSTOM to the list */ atoms = g_new0 (Atom, count+1); memcpy (atoms, old_atoms, count * sizeof (Atom)); atoms[count] = atom_custom; + /* And set the list */ XSetWMProtocols (GDK_WINDOW_XDISPLAY (w), GDK_WINDOW_XID (w), atoms, count+1); /* TODO: check error */ XFree (old_atoms); g_free (atoms); + /* Add a filter so that we can catch the CUSTOM event and display the menu. */ gdk_window_add_filter (w, filter_func, window); } |