← Back to team overview

ayatana-commits team mailing list archive

[Merge] lp:~ted/indicator-appmenu/working-close-menu-item into lp:indicator-appmenu

 

Ted Gould has proposed merging lp:~ted/indicator-appmenu/working-close-menu-item into lp:indicator-appmenu with lp:~ted/indicator-appmenu/desktop-menu-handling as a prerequisite.

Requested reviews:
  Indicator Applet Developers (indicator-applet-developers)


Have the close menu work for windows.
-- 
https://code.launchpad.net/~ted/indicator-appmenu/working-close-menu-item/+merge/34554
Your team ayatana-commits is subscribed to branch lp:indicator-appmenu.
=== modified file '.bzrignore'
--- .bzrignore	2010-06-29 15:04:15 +0000
+++ .bzrignore	2010-09-03 16:54:43 +0000
@@ -17,6 +17,7 @@
 src/application-menu-debug-client.h
 src/application-menu-debug-server.h
 tools/current-menu-dump
+src/libappmenu_la-gdk-get-func.lo
 tools/mock-json-app
 tools/.deps
 tools/.libs

=== modified file 'src/Makefile.am'
--- src/Makefile.am	2010-07-22 13:06:09 +0000
+++ src/Makefile.am	2010-09-03 16:54:43 +0000
@@ -14,12 +14,15 @@
 libappmenu_la_SOURCES = \
 	application-menu-registrar-server.h \
 	dbus-shared.h \
+	gdk-get-func.h \
+	gdk-get-func.c \
+	MwmUtil.h \
 	indicator-appmenu.c \
 	indicator-appmenu-marshal.c \
 	window-menus.c \
 	window-menus.h
 libappmenu_la_CFLAGS = $(INDICATOR_CFLAGS) -Wall -Wl,-Bsymbolic-functions -Wl,-z,defs -Wl,--as-needed -Werror
-libappmenu_la_LIBADD = $(INDICATOR_LIBS)
+libappmenu_la_LIBADD = $(INDICATOR_LIBS) -lX11
 libappmenu_la_LDFLAGS = -module -avoid-version
 
 ######################################

=== added file 'src/MwmUtil.h'
--- src/MwmUtil.h	1970-01-01 00:00:00 +0000
+++ src/MwmUtil.h	2010-09-03 16:54:43 +0000
@@ -0,0 +1,136 @@
+/**
+ *
+ * $Id$
+ *
+ * Copyright (C) 1995 Free Software Foundation, Inc.
+ *
+ * This file is part of the GNU LessTif Library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ *
+ * * Feb 21 1999 - George Lebl (jirka@xxxxxx)
+ *                 Owen Taylor (otaylor@xxxxxxxxxx)
+ *
+ *   Modified so that the MotifWmHints structure defined here
+ *   is suitable for client side use on 64-bit architectures.
+ *   X expects fields with a format of 32 to be longs, even
+ *   when sizeof(long) == 8.
+ **/
+
+#ifndef MWMUTIL_H_INCLUDED
+#define MWMUTIL_H_INCLUDED
+
+#include <X11/Xmd.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+    unsigned long flags;
+    unsigned long functions;
+    unsigned long decorations;
+    long input_mode;
+    unsigned long status;
+} MotifWmHints, MwmHints;
+
+#define MWM_HINTS_FUNCTIONS     (1L << 0)
+#define MWM_HINTS_DECORATIONS   (1L << 1)
+#define MWM_HINTS_INPUT_MODE    (1L << 2)
+#define MWM_HINTS_STATUS        (1L << 3)
+
+#define MWM_FUNC_ALL            (1L << 0)
+#define MWM_FUNC_RESIZE         (1L << 1)
+#define MWM_FUNC_MOVE           (1L << 2)
+#define MWM_FUNC_MINIMIZE       (1L << 3)
+#define MWM_FUNC_MAXIMIZE       (1L << 4)
+#define MWM_FUNC_CLOSE          (1L << 5)
+
+#define MWM_DECOR_ALL           (1L << 0)
+#define MWM_DECOR_BORDER        (1L << 1)
+#define MWM_DECOR_RESIZEH       (1L << 2)
+#define MWM_DECOR_TITLE         (1L << 3)
+#define MWM_DECOR_MENU          (1L << 4)
+#define MWM_DECOR_MINIMIZE      (1L << 5)
+#define MWM_DECOR_MAXIMIZE      (1L << 6)
+
+#define MWM_INPUT_MODELESS 0
+#define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1
+#define MWM_INPUT_SYSTEM_MODAL 2
+#define MWM_INPUT_FULL_APPLICATION_MODAL 3
+#define MWM_INPUT_APPLICATION_MODAL MWM_INPUT_PRIMARY_APPLICATION_MODAL
+
+#define MWM_TEAROFF_WINDOW	(1L<<0)
+
+/*
+ * atoms
+ */
+#define _XA_MOTIF_BINDINGS		"_MOTIF_BINDINGS"
+#define _XA_MOTIF_WM_HINTS		"_MOTIF_WM_HINTS"
+#define _XA_MOTIF_WM_MESSAGES		"_MOTIF_WM_MESSAGES"
+#define _XA_MOTIF_WM_OFFSET		"_MOTIF_WM_OFFSET"
+#define _XA_MOTIF_WM_MENU		"_MOTIF_WM_MENU"
+#define _XA_MOTIF_WM_INFO		"_MOTIF_WM_INFO"
+#define _XA_MWM_HINTS			_XA_MOTIF_WM_HINTS
+#define _XA_MWM_MESSAGES		_XA_MOTIF_WM_MESSAGES
+#define _XA_MWM_MENU			_XA_MOTIF_WM_MENU
+#define _XA_MWM_INFO			_XA_MOTIF_WM_INFO
+
+
+/*
+ * _MWM_INFO property
+ */
+typedef struct {
+    long flags;
+    Window wm_window;
+} MotifWmInfo;
+
+typedef MotifWmInfo MwmInfo;
+
+#define MWM_INFO_STARTUP_STANDARD	(1L<<0)
+#define MWM_INFO_STARTUP_CUSTOM		(1L<<1)
+
+/*
+ * _MWM_HINTS property
+ */
+typedef struct {
+    unsigned long flags;
+    unsigned long functions;
+    unsigned long decorations;
+    long inputMode;
+    unsigned long status;
+} PropMotifWmHints;
+
+typedef PropMotifWmHints PropMwmHints;
+
+#define PROP_MOTIF_WM_HINTS_ELEMENTS 5
+#define PROP_MWM_HINTS_ELEMENTS PROP_MOTIF_WM_HINTS_ELEMENTS
+
+/*
+ * _MWM_INFO property, slight return
+ */
+typedef struct {
+    unsigned long flags;
+    unsigned long wmWindow;
+} PropMotifWmInfo;
+
+typedef PropMotifWmInfo PropMwmInfo;
+
+#define PROP_MOTIF_WM_INFO_ELEMENTS 2
+#define PROP_MWM_INFO_ELEMENTS PROP_MOTIF_WM_INFO_ELEMENTS
+
+G_END_DECLS
+
+#endif /* MWMUTIL_H_INCLUDED */

=== added file 'src/gdk-get-func.c'
--- src/gdk-get-func.c	1970-01-01 00:00:00 +0000
+++ src/gdk-get-func.c	2010-09-03 16:54:43 +0000
@@ -0,0 +1,145 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-2007 Peter Mattis, Spencer Kimball,
+ * Josh MacDonald, Ryan Lortie
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
+ * file for a list of people on the GTK+ Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
+ */
+
+#include "config.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+/*
+#ifdef HAVE_XKB
+#include <X11/XKBlib.h>
+#endif
+
+#include <netinet/in.h>
+#include <unistd.h>
+*/
+#include <gdk/gdk.h>
+#include <gdk/gdkprivate.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdkwindow.h>
+
+#include "MwmUtil.h"
+/*
+#include "gdkwindowimpl.h"
+#include "gdkasync.h"
+#include "gdkinputprivate.h"
+#include "gdkdisplay-x11.h"
+#include "gdkprivate-x11.h"
+#include "gdkregion.h"
+#include "gdkinternals.h"
+#include "gdkwindow-x11.h"
+#include "gdkalias.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+
+#include <X11/extensions/shape.h>
+
+#ifdef HAVE_XCOMPOSITE
+#include <X11/extensions/Xcomposite.h>
+#endif
+
+#ifdef HAVE_XFIXES
+#include <X11/extensions/Xfixes.h>
+#endif
+
+#ifdef HAVE_XDAMAGE
+#include <X11/extensions/Xdamage.h>
+#endif
+*/
+
+#define WINDOW_IS_TOPLEVEL_OR_FOREIGN(window) \
+  (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD &&   \
+     GDK_WINDOW_TYPE (window) != GDK_WINDOW_OFFSCREEN)
+
+static MotifWmHints *
+gdk_window_get_mwm_hints (GdkWindow *window)
+{
+  GdkDisplay *display;
+  Atom hints_atom = None;
+  guchar *data;
+  Atom type;
+  gint format;
+  gulong nitems;
+  gulong bytes_after;
+  
+  if (GDK_WINDOW_DESTROYED (window))
+    return NULL;
+
+  display = gdk_drawable_get_display (window);
+  
+  hints_atom = gdk_x11_get_xatom_by_name_for_display (display, _XA_MOTIF_WM_HINTS);
+
+  XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window),
+		      hints_atom, 0, sizeof (MotifWmHints)/sizeof (long),
+		      False, AnyPropertyType, &type, &format, &nitems,
+		      &bytes_after, &data);
+
+  if (type == None)
+    return NULL;
+  
+  return (MotifWmHints *)data;
+}
+
+/**
+ * gdk_window_get_functions:
+ * @window: The toplevel #GdkWindow to get the functions from
+ * @functions: The window functions will be written here
+ *
+ * Returns the functions set on the GdkWindow with #gdk_window_set_functions
+ * Returns: TRUE if the window has functions set, FALSE otherwise.
+ **/
+gboolean
+egg_window_get_functions(GdkWindow       *window,
+			   GdkWMFunction *functions)
+{
+  MotifWmHints *hints;
+  gboolean result = FALSE;
+
+  if (GDK_WINDOW_DESTROYED (window) ||
+      !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+    return FALSE;
+  
+  hints = gdk_window_get_mwm_hints (window);
+  
+  if (hints)
+    {
+      if (hints->flags & MWM_HINTS_FUNCTIONS)
+	{
+	  if (functions)
+	    *functions = hints->functions;
+	  result = TRUE;
+	}
+      
+      XFree (hints);
+    }
+
+  return result;
+}

=== added file 'src/gdk-get-func.h'
--- src/gdk-get-func.h	1970-01-01 00:00:00 +0000
+++ src/gdk-get-func.h	2010-09-03 16:54:43 +0000
@@ -0,0 +1,3 @@
+
+gboolean egg_window_get_functions(GdkWindow       *window, GdkWMFunction *functions);
+

=== modified file 'src/indicator-appmenu.c'
--- src/indicator-appmenu.c	2010-09-03 16:54:43 +0000
+++ src/indicator-appmenu.c	2010-09-03 16:54:43 +0000
@@ -23,6 +23,9 @@
 #include "config.h"
 #endif
 
+#include <X11/Xlib.h>
+#include <gdk/gdkx.h>
+
 #include <dbus/dbus-glib.h>
 #include <dbus/dbus-glib-bindings.h>
 #include <dbus/dbus-glib-lowlevel.h>
@@ -39,6 +42,7 @@
 #include "indicator-appmenu-marshal.h"
 #include "window-menus.h"
 #include "dbus-shared.h"
+#include "gdk-get-func.h"
 
 /**********************
   Indicator Object
@@ -79,6 +83,8 @@
 	gulong sig_entry_added;
 	gulong sig_entry_removed;
 
+	GtkMenuItem * close_item;
+
 	GArray * window_menus;
 
 	GHashTable * desktop_windows;
@@ -244,6 +250,7 @@
 	self->apps = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_object_unref);
 	self->matcher = NULL;
 	self->active_window = NULL;
+	self->close_item = NULL;
 
 	/* Setup the entries for the fallbacks */
 	self->window_menus = g_array_sized_new(FALSE, FALSE, sizeof(IndicatorObjectEntry), 2);
@@ -381,6 +388,46 @@
 	return;
 }
 
+/* Close the current application using magic */
+static void
+close_current (GtkMenuItem * mi, gpointer user_data)
+{
+	IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
+
+	if (iapp->active_window == NULL) {
+		g_warning("Can't close a window we don't have.  NULL not cool.");
+		return;
+	}
+
+	guint xid = bamf_window_get_xid(iapp->active_window);
+	guint timestamp = gdk_event_get_time(NULL);
+
+	XEvent xev;
+
+	xev.xclient.type = ClientMessage;
+	xev.xclient.serial = 0;
+	xev.xclient.send_event = True;
+	xev.xclient.display = gdk_x11_get_default_xdisplay ();
+	xev.xclient.window = xid;
+	xev.xclient.message_type = gdk_x11_atom_to_xatom (gdk_atom_intern ("_NET_CLOSE_WINDOW", TRUE));
+	xev.xclient.format = 32;
+	xev.xclient.data.l[0] = timestamp;
+	xev.xclient.data.l[1] = 2; /* Client type pager, so it listens to us */
+	xev.xclient.data.l[2] = 0;
+	xev.xclient.data.l[3] = 0;
+	xev.xclient.data.l[4] = 0;
+
+	gdk_error_trap_push ();
+	XSendEvent (gdk_x11_get_default_xdisplay (),
+	            gdk_x11_get_default_root_xwindow (),
+	            False,
+	            SubstructureRedirectMask | SubstructureNotifyMask,
+	            &xev);
+	gdk_error_trap_pop ();
+
+	return;
+}
+
 /* Create the default window menus */
 static void
 build_window_menus (IndicatorAppmenu * iapp)
@@ -404,8 +451,10 @@
 
 	mi = GTK_MENU_ITEM(gtk_image_menu_item_new_from_stock(GTK_STOCK_CLOSE, agroup));
 	gtk_widget_set_sensitive(GTK_WIDGET(mi), FALSE);
+	g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(close_current), iapp);
 	gtk_widget_show(GTK_WIDGET(mi));
 	gtk_menu_append(entries[0].menu, GTK_WIDGET(mi));
+	iapp->close_item = mi;
 
 	gtk_widget_show(GTK_WIDGET(entries[0].menu));
 
@@ -620,6 +669,50 @@
 	return count;
 }
 
+/* A helper for switch_default_app that takes care of the
+   switching of the active window variable */
+static void
+switch_active_window (IndicatorAppmenu * iapp, BamfWindow * active_window)
+{
+	if (iapp->active_window == active_window) {
+		return;
+	}
+
+	iapp->active_window = active_window;
+
+	if (iapp->close_item == NULL) {
+		g_warning("No close item!?!?!");
+		return;
+	}
+
+	gtk_widget_set_sensitive(GTK_WIDGET(iapp->close_item), FALSE);
+
+	guint xid = bamf_window_get_xid(iapp->active_window);
+	if (xid == 0) {
+		return;
+	}
+
+	GdkWindow * window = gdk_window_foreign_new(xid);
+	if (window == NULL) {
+		g_warning("Unable to get foreign window for: %d", xid);
+		return;
+	}
+
+	GdkWMFunction functions;
+	if (!egg_window_get_functions(window, &functions)) {
+		g_debug("Unable to get MWM functions for: %d", xid);
+		functions = GDK_FUNC_ALL;
+	}
+
+	if (functions & GDK_FUNC_ALL || functions & GDK_FUNC_CLOSE) {
+		gtk_widget_set_sensitive(GTK_WIDGET(iapp->close_item), TRUE);
+	}
+
+	g_object_unref(window);
+
+	return;
+}
+
 /* Switch applications, remove all the entires for the previous
    one and add them for the new application */
 static void
@@ -632,7 +725,7 @@
 
 		/* Keep active window up-to-date, though we're probably not
 		   using it much. */
-		iapp->active_window = active_window;
+		switch_active_window(iapp, active_window);
 		return;
 	}
 	if (iapp->default_app == NULL && iapp->active_window == active_window && newdef == NULL) {
@@ -673,7 +766,7 @@
 	iapp->default_app = NULL;
 
 	/* Update the active window pointer -- may be NULL */
-	iapp->active_window = active_window;
+	switch_active_window(iapp, active_window);
 
 	/* If we're putting up a new window, let's do that now. */
 	if (newdef != NULL) {


Follow ups