← Back to team overview

ayatana-commits team mailing list archive

[Merge] lp:~agateau/dbusmenu/click-to-dump into lp:dbusmenu

 

Aurélien Gâteau has proposed merging lp:~agateau/dbusmenu/click-to-dump into lp:dbusmenu.

Requested reviews:
  DBus Menu Team (dbusmenu-team)


This branch implements click-to-dump. If you run dbusmenu-dumper without arguments, the cursor turns into a cross-hair and you can click a window to dump its menu.
-- 
https://code.launchpad.net/~agateau/dbusmenu/click-to-dump/+merge/30401
Your team ayatana-commits is subscribed to branch lp:dbusmenu.
=== modified file 'configure.ac'
--- configure.ac	2010-07-15 14:35:16 +0000
+++ configure.ac	2010-07-20 13:33:48 +0000
@@ -58,6 +58,18 @@
 AC_SUBST(DBUSMENUGTK_LIBS)
 
 ###########################
+# Dependencies - dumper
+###########################
+
+X11_REQUIRED_VERSION=1.3
+
+PKG_CHECK_MODULES(DBUSMENUDUMPER, gtk+-2.0 >= $GTK_REQUIRED_VERSION
+                                  x11 >= $X11_REQUIRED_VERSION)
+
+AC_SUBST(DBUSMENUDUMPER_CFLAGS)
+AC_SUBST(DBUSMENUDUMPER_LIBS)
+
+###########################
 # Dependencies - Testing
 ###########################
 

=== modified file 'tools/Makefile.am'
--- tools/Makefile.am	2010-02-05 18:35:46 +0000
+++ tools/Makefile.am	2010-07-20 13:33:48 +0000
@@ -10,11 +10,11 @@
 
 dbusmenu_dumper_CFLAGS = \
 	-I $(srcdir)/.. \
-	$(DBUSMENUGLIB_CFLAGS) -Wall -Werror
+	$(DBUSMENUGLIB_CFLAGS) $(DBUSMENUDUMPER_CFLAGS) -Wall -Werror
 
 dbusmenu_dumper_LDADD = \
 	../libdbusmenu-glib/libdbusmenu-glib.la \
-	$(DBUSMENUGLIB_LIBS)
+	$(DBUSMENUGLIB_LIBS) $(DBUSMENUDUMPER_LIBS)
 
 doc_DATA = README.dbusmenu-bench
 

=== modified file 'tools/dbusmenu-dumper.c'
--- tools/dbusmenu-dumper.c	2010-06-28 23:04:06 +0000
+++ tools/dbusmenu-dumper.c	2010-07-20 13:33:48 +0000
@@ -21,12 +21,22 @@
 */
 
 #include <glib.h>
+#include <dbus/dbus-glib.h>
+
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
 
 #include <libdbusmenu-glib/client.h>
 #include <libdbusmenu-glib/menuitem.h>
 
+<<<<<<< TREE
 #include <dbus/dbus-gtype-specialized.h>
 
+=======
+#include <X11/Xlib.h>
+
+>>>>>>> MERGE-SOURCE
 static GMainLoop * mainloop = NULL;
 
 static gchar * value2string (const GValue * value, int depth);
@@ -191,11 +201,191 @@
 	return;
 }
 
+/* Window clicking ***************************************************/
+static GdkFilterReturn
+click_filter (GdkXEvent *gdk_xevent,
+              GdkEvent  *event,
+              gpointer   data);
+
+static Window
+find_real_window (Window w, int depth)
+{
+	if (depth > 5) {
+		return None;
+	}
+	/*static*/ Atom wm_state = XInternAtom(gdk_display, "WM_STATE", False);
+	Atom type;
+	int format;
+	unsigned long nitems, after;
+	unsigned char* prop;
+	if (XGetWindowProperty(gdk_display, w, wm_state, 0, 0, False, AnyPropertyType,
+				&type, &format, &nitems, &after, &prop) == Success) {
+		if (prop != NULL) {
+			XFree(prop);
+		}
+		if (type != None) {
+			return w;
+		}
+	}
+	Window root, parent;
+	Window* children;
+	unsigned int nchildren;
+	Window ret = None;
+	if (XQueryTree(gdk_display, w, &root, &parent, &children, &nchildren) != 0) {
+		unsigned int i;
+		for(i = 0; i < nchildren && ret == None; ++i) {
+			ret = find_real_window(children[ i ], depth + 1);
+		}
+		if (children != NULL) {
+			XFree(children);
+		}
+	}
+	return ret;
+}
+
+static Window
+get_window_under_cursor (void)
+{
+	Window root;
+	Window child;
+	uint mask;
+	int rootX, rootY, winX, winY;
+	XQueryPointer(gdk_display, gdk_x11_get_default_root_xwindow(), &root, &child, &rootX, &rootY, &winX, &winY, &mask);
+	if (child == None) {
+		return None;
+	}
+	return find_real_window(child, 0);
+}
+
+static void
+uninstall_click_filter (void)
+{
+	GdkWindow *root;
+
+	root = gdk_get_default_root_window ();
+	gdk_window_remove_filter (root, (GdkFilterFunc) click_filter, NULL);
+
+	gdk_pointer_ungrab (GDK_CURRENT_TIME);
+	gdk_keyboard_ungrab (GDK_CURRENT_TIME);
+
+	gtk_main_quit ();
+}
+
+static GdkFilterReturn
+click_filter (GdkXEvent *gdk_xevent,
+              GdkEvent  *event,
+              gpointer   data)
+
+{
+	XEvent *xevent = (XEvent *) gdk_xevent;
+	gboolean *success = (gboolean *)data;
+
+	switch (xevent->type) {
+	case ButtonPress:
+		uninstall_click_filter();
+		*success = TRUE;
+		return GDK_FILTER_REMOVE;
+	case KeyPress:
+		if (xevent->xkey.keycode == XKeysymToKeycode(gdk_display, XK_Escape)) {
+			uninstall_click_filter();
+			*success = FALSE;
+			return GDK_FILTER_REMOVE;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return GDK_FILTER_CONTINUE;
+}
+
+static gboolean
+install_click_filter (gpointer data)
+{
+	GdkGrabStatus  status;
+	GdkCursor     *cross;
+	GdkWindow     *root;
+
+	root = gdk_get_default_root_window();
+
+	gdk_window_add_filter(root, (GdkFilterFunc) click_filter, data);
+
+	cross = gdk_cursor_new(GDK_CROSS);
+	status = gdk_pointer_grab(root, FALSE, GDK_BUTTON_PRESS_MASK,
+	                          NULL, cross, GDK_CURRENT_TIME);
+	gdk_cursor_unref(cross);
+
+	if (status != GDK_GRAB_SUCCESS) {
+		g_warning("Pointer grab failed.\n");
+		uninstall_click_filter();
+		return FALSE;
+	}
+
+	status = gdk_keyboard_grab(root, FALSE, GDK_CURRENT_TIME);
+	if (status != GDK_GRAB_SUCCESS) {
+		g_warning("Keyboard grab failed.\n");
+		uninstall_click_filter();
+		return FALSE;
+	}
+
+	gdk_flush();
+	return FALSE;
+}
+
+static gboolean
+wait_for_click (void)
+{
+	gboolean success;
+	g_idle_add (install_click_filter, (gpointer)(&success));
+	gtk_main ();
+	return success;
+}
 
 static gchar * dbusname = NULL;
 static gchar * dbusobject = NULL;
 
 static gboolean
+init_dbus_vars_from_window(Window window)
+{
+	DBusGConnection *connection;
+	GError *error;
+	DBusGProxy *proxy;
+
+	error = NULL;
+	connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+	if (connection == NULL) {
+		g_printerr("Failed to open connection to bus: %s\n", error->message);
+		g_error_free(error);
+		return FALSE;
+	}
+
+	proxy = dbus_g_proxy_new_for_name (connection,
+			"org.ayatana.AppMenu.Registrar",
+			"/org/ayatana/AppMenu/Registrar",
+			"org.ayatana.AppMenu.Registrar");
+
+	error = NULL;
+	if (!dbus_g_proxy_call (proxy, "GetMenuForWindow", &error,
+		G_TYPE_UINT, window, G_TYPE_INVALID, 
+		G_TYPE_STRING, &dbusname, DBUS_TYPE_G_OBJECT_PATH, &dbusobject, G_TYPE_INVALID))
+	{
+		g_printerr("ERROR: %s\n", error->message);
+		g_error_free(error);
+        g_object_unref(proxy);
+		return FALSE;
+	}
+
+	if (!g_strcmp0(dbusobject, "/")) {
+		return FALSE;
+	}
+
+	g_object_unref (proxy);
+
+	return TRUE;
+}
+
+/* Option parser *****************************************************/
+static gboolean
 option_dbusname (const gchar * arg, const gchar * value, gpointer data, GError ** error)
 {
 	if (dbusname != NULL) {
@@ -228,7 +418,8 @@
 
 static GOptionEntry general_options[] = {
 	{"dbus-name",     'd',  0,                        G_OPTION_ARG_CALLBACK,  option_dbusname, "The name of the program to connect to (i.e. org.test.bob", "dbusname"},
-	{"dbus-object",   'o',  0,                        G_OPTION_ARG_CALLBACK,  option_dbusobject, "The path to the Dbus object (i.e /org/test/bob/alvin)", "dbusobject"}
+	{"dbus-object",   'o',  0,                        G_OPTION_ARG_CALLBACK,  option_dbusobject, "The path to the Dbus object (i.e /org/test/bob/alvin)", "dbusobject"},
+	{NULL}
 };
 
 int
@@ -248,16 +439,34 @@
 		return 1;
 	}
 
-	if (dbusname == NULL) {
-		g_printerr("ERROR: dbus-name not specified\n");
-		usage();
-		return 1;
-	}
+	if (dbusname == NULL && dbusobject == NULL) {
+		gtk_init(&argc, &argv);
+		if (!wait_for_click()) {
+			return 1;
+		}
+		Window window = get_window_under_cursor();
+		if (window == None) {
+			g_printerr("ERROR: could not get the id for the pointed window\n");
+			return 1;
+		}
+		g_debug("window: %u", (unsigned int)window);
+		if (!init_dbus_vars_from_window(window)) {
+			g_printerr("ERROR: could not find a menu for the pointed window\n");
+			return 1;
+		}
+		g_debug("dbusname: %s, dbusobject: %s", dbusname, dbusobject);
+	} else {
+		if (dbusname == NULL) {
+			g_printerr("ERROR: dbus-name not specified\n");
+			usage();
+			return 1;
+		}
 
-	if (dbusobject == NULL) {
-		g_printerr("ERROR: dbus-object not specified\n");
-		usage();
-		return 1;
+		if (dbusobject == NULL) {
+			g_printerr("ERROR: dbus-object not specified\n");
+			usage();
+			return 1;
+		}
 	}
 
 	DbusmenuClient * client = dbusmenu_client_new (dbusname, dbusobject);


Follow ups