aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorTed Gould <ted@gould.cx>2010-07-20 16:36:57 -0500
committerTed Gould <ted@gould.cx>2010-07-20 16:36:57 -0500
commitaccf3124c2a994078cf5b2520edcd5f841922c6c (patch)
tree8fd6ea13e4e197326d1f9ac4055e367a7f670bf1 /tools
parent98b3815a25ffcf89f7d5b062bda254f5fcaa1ab2 (diff)
parent70961ba202ced579d434e19f185f442684814722 (diff)
downloadlibdbusmenu-accf3124c2a994078cf5b2520edcd5f841922c6c.tar.gz
libdbusmenu-accf3124c2a994078cf5b2520edcd5f841922c6c.tar.bz2
libdbusmenu-accf3124c2a994078cf5b2520edcd5f841922c6c.zip
Making it so that dbus-dumper has a click to dump feature.
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile.am7
-rw-r--r--tools/dbusmenu-dumper.c225
2 files changed, 220 insertions, 12 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 77d6eef..48993f1 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -10,11 +10,14 @@ dbusmenu_dumper_SOURCES = \
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
diff --git a/tools/dbusmenu-dumper.c b/tools/dbusmenu-dumper.c
index 6ce9655..40ab1e1 100644
--- a/tools/dbusmenu-dumper.c
+++ b/tools/dbusmenu-dumper.c
@@ -21,11 +21,17 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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>
#include <dbus/dbus-gtype-specialized.h>
+#include <X11/Xlib.h>
static GMainLoop * mainloop = NULL;
@@ -191,11 +197,191 @@ new_root_cb (DbusmenuClient * client, DbusmenuMenuitem * newroot)
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 +414,8 @@ usage (void)
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 +435,34 @@ main (int argc, char ** argv)
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);