aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac16
-rw-r--r--libdbusmenu-glib/dbus-menu.xml14
-rw-r--r--libdbusmenu-glib/server-marshal.list1
-rw-r--r--libdbusmenu-glib/server.c81
-rw-r--r--libdbusmenu-glib/server.h5
-rw-r--r--libdbusmenu-gtk/client.c4
-rw-r--r--tests/Makefile.am48
-rw-r--r--tests/test-gtk-submenu-client.c143
-rw-r--r--tests/test-gtk-submenu-server.c98
-rw-r--r--tools/Makefile.am7
-rw-r--r--tools/dbusmenu-dumper.c225
11 files changed, 608 insertions, 34 deletions
diff --git a/configure.ac b/configure.ac
index 6c2400c..d8211b1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,11 +1,11 @@
-AC_INIT(libdbusmenu, 0.3.6, ted@canonical.com)
+AC_INIT(libdbusmenu, 0.3.8, ted@canonical.com)
AC_COPYRIGHT([Copyright 2009,2010 Canonical])
AC_PREREQ(2.62)
AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE(libdbusmenu, 0.3.6, [-Wno-portability])
+AM_INIT_AUTOMAKE(libdbusmenu, 0.3.8, [-Wno-portability])
AM_MAINTAINER_MODE
@@ -58,6 +58,18 @@ AC_SUBST(DBUSMENUGTK_CFLAGS)
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
###########################
diff --git a/libdbusmenu-glib/dbus-menu.xml b/libdbusmenu-glib/dbus-menu.xml
index 53b67de..9e8013c 100644
--- a/libdbusmenu-glib/dbus-menu.xml
+++ b/libdbusmenu-glib/dbus-menu.xml
@@ -344,6 +344,20 @@ License version 3 and version 2.1 along with this program. If not, see
</dox:d>
</arg>
</signal>
+ <signal name="ItemActivationRequested">
+ <dox:d>
+ The server is requesting that all clients displaying this
+ menu open it to the user. This would be for things like
+ hotkeys that when the user presses them the menu should
+ open and display itself to the user.
+ </dox:d>
+ <arg type="i" name="id" direction="out" >
+ <dox:d>ID of the menu that should be activated</dox:d>
+ </arg>
+ <arg type="u" name="timestamp" direction="out" >
+ <dox:d>The time that the event occured</dox:d>
+ </arg>
+ </signal>
<!-- End of interesting stuff -->
diff --git a/libdbusmenu-glib/server-marshal.list b/libdbusmenu-glib/server-marshal.list
index 1689a05..0d68c4e 100644
--- a/libdbusmenu-glib/server-marshal.list
+++ b/libdbusmenu-glib/server-marshal.list
@@ -1,2 +1,3 @@
VOID: INT, STRING, POINTER
VOID: UINT, INT
+VOID: INT, UINT
diff --git a/libdbusmenu-glib/server.c b/libdbusmenu-glib/server.c
index 08a6631..d1b4888 100644
--- a/libdbusmenu-glib/server.c
+++ b/libdbusmenu-glib/server.c
@@ -48,6 +48,8 @@ static void _gvalue_array_append_hashtable(GValueArray *array, GHashTable * dict
#include "dbusmenu-server.h"
+static void layout_update_signal (DbusmenuServer * server);
+
#define DBUSMENU_VERSION_NUMBER 2
/* Privates, I'll show you mine... */
@@ -58,6 +60,7 @@ struct _DbusmenuServerPrivate
DbusmenuMenuitem * root;
gchar * dbusobject;
gint layout_revision;
+ guint layout_idle;
};
#define DBUSMENU_SERVER_GET_PRIVATE(o) \
@@ -68,6 +71,7 @@ enum {
ID_PROP_UPDATE,
ID_UPDATE,
LAYOUT_UPDATED,
+ ITEM_ACTIVATION,
LAST_SIGNAL
};
@@ -168,6 +172,22 @@ dbusmenu_server_class_init (DbusmenuServerClass *class)
NULL, NULL,
_dbusmenu_server_marshal_VOID__UINT_INT,
G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_INT);
+ /**
+ DbusmenuServer::item-activation-requested:
+ @arg0: The #DbusmenuServer emitting the signal.
+ @arg1: The ID of the parent for this update.
+ @arg2: The timestamp of when the event happened
+
+ This is signaled when a menuitem under this server
+ sends it's activate signal.
+ */
+ signals[ITEM_ACTIVATION] = g_signal_new(DBUSMENU_SERVER_SIGNAL_ITEM_ACTIVATION,
+ G_TYPE_FROM_CLASS(class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(DbusmenuServerClass, item_activation),
+ NULL, NULL,
+ _dbusmenu_server_marshal_VOID__INT_UINT,
+ G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_UINT);
g_object_class_install_property (object_class, PROP_DBUS_OBJECT,
@@ -199,6 +219,7 @@ dbusmenu_server_init (DbusmenuServer *self)
priv->root = NULL;
priv->dbusobject = NULL;
priv->layout_revision = 1;
+ priv->layout_idle = 0;
return;
}
@@ -208,6 +229,10 @@ dbusmenu_server_dispose (GObject *object)
{
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(object);
+ if (priv->layout_idle != 0) {
+ g_source_remove(priv->layout_idle);
+ }
+
if (priv->root != NULL) {
dbusmenu_menuitem_foreach(priv->root, menuitem_signals_remove, object);
g_object_unref(priv->root);
@@ -260,8 +285,7 @@ set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
} else {
g_debug("Setting root node to NULL");
}
- priv->layout_revision++;
- g_signal_emit(obj, signals[LAYOUT_UPDATED], 0, priv->layout_revision, 0, TRUE);
+ layout_update_signal(DBUSMENU_SERVER(obj));
break;
default:
g_return_if_reached();
@@ -305,6 +329,35 @@ get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
return;
}
+/* Handle actually signalling in the idle loop. This way we collect all
+ the updates. */
+static gboolean
+layout_update_idle (gpointer user_data)
+{
+ DbusmenuServer * server = DBUSMENU_SERVER(user_data);
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
+ g_signal_emit(G_OBJECT(server), signals[LAYOUT_UPDATED], 0, priv->layout_revision, 0, TRUE);
+
+ priv->layout_idle = 0;
+
+ return FALSE;
+}
+
+/* Signals that the layout has been updated */
+static void
+layout_update_signal (DbusmenuServer * server)
+{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+ priv->layout_revision++;
+
+ if (priv->layout_idle == 0) {
+ priv->layout_idle = g_idle_add(layout_update_idle, server);
+ }
+
+ return;
+}
+
static void
menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, GValue * value, DbusmenuServer * server)
{
@@ -335,10 +388,7 @@ menuitem_child_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint
menuitem_signals_create(child, server);
g_list_foreach(dbusmenu_menuitem_get_children(child), added_check_children, server);
- /* TODO: We probably need to group the layout update signals to make the number more reasonble. */
- DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
- priv->layout_revision++;
- g_signal_emit(G_OBJECT(server), signals[LAYOUT_UPDATED], 0, priv->layout_revision, 0, TRUE);
+ layout_update_signal(server);
return;
}
@@ -346,19 +396,23 @@ static void
menuitem_child_removed (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, DbusmenuServer * server)
{
menuitem_signals_remove(child, server);
- /* TODO: We probably need to group the layout update signals to make the number more reasonble. */
- DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
- priv->layout_revision++;
- g_signal_emit(G_OBJECT(server), signals[LAYOUT_UPDATED], 0, priv->layout_revision, 0, TRUE);
+ layout_update_signal(server);
return;
}
static void
menuitem_child_moved (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint newpos, guint oldpos, DbusmenuServer * server)
{
- DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
- priv->layout_revision++;
- g_signal_emit(G_OBJECT(server), signals[LAYOUT_UPDATED], 0, priv->layout_revision, 0, TRUE);
+ layout_update_signal(server);
+ return;
+}
+
+/* Called when a menu item emits its activated signal so it
+ gets passed across the bus. */
+static void
+menuitem_activated (DbusmenuMenuitem * mi, guint timestamp, DbusmenuServer * server)
+{
+ g_signal_emit(G_OBJECT(server), signals[ITEM_ACTIVATION], 0, dbusmenu_menuitem_get_id(mi), timestamp, TRUE);
return;
}
@@ -371,6 +425,7 @@ menuitem_signals_create (DbusmenuMenuitem * mi, gpointer data)
g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED, G_CALLBACK(menuitem_child_removed), data);
g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED, G_CALLBACK(menuitem_child_moved), data);
g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(menuitem_property_changed), data);
+ g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(menuitem_activated), data);
return;
}
diff --git a/libdbusmenu-glib/server.h b/libdbusmenu-glib/server.h
index f4e3527..a9bf213 100644
--- a/libdbusmenu-glib/server.h
+++ b/libdbusmenu-glib/server.h
@@ -46,6 +46,7 @@ G_BEGIN_DECLS
#define DBUSMENU_SERVER_SIGNAL_ID_PROP_UPDATE "item-property-updated"
#define DBUSMENU_SERVER_SIGNAL_ID_UPDATE "item-updated"
#define DBUSMENU_SERVER_SIGNAL_LAYOUT_UPDATED "layout-updated"
+#define DBUSMENU_SERVER_SIGNAL_ITEM_ACTIVATION "item-activation-requested"
#define DBUSMENU_SERVER_SIGNAL_LAYOUT_UPDATE DBUSMENU_SERVER_SIGNAL_LAYOUT_UPDATED
#define DBUSMENU_SERVER_PROP_DBUS_OBJECT "dbus-object"
@@ -58,10 +59,10 @@ G_BEGIN_DECLS
@id_prop_update: Slot for #DbusmenuServer::id-prop-update.
@id_update: Slot for #DbusmenuServer::id-update.
@layout_updated: Slot for #DbusmenuServer::layout-update.
+ @item_activation_requested: Slot for #DbusmenuServer::item-activation-requested.
@dbusmenu_server_reserved1: Reserved for future use.
@dbusmenu_server_reserved2: Reserved for future use.
@dbusmenu_server_reserved3: Reserved for future use.
- @dbusmenu_server_reserved4: Reserved for future use.
The class implementing the virtual functions for #DbusmenuServer.
*/
@@ -73,12 +74,12 @@ struct _DbusmenuServerClass {
void (*id_prop_update)(gint id, gchar * property, gchar * value);
void (*id_update)(gint id);
void (*layout_updated)(gint revision);
+ void (*item_activation)(gint id, guint timestamp);
/* Reserved */
void (*dbusmenu_server_reserved1)(void);
void (*dbusmenu_server_reserved2)(void);
void (*dbusmenu_server_reserved3)(void);
- void (*dbusmenu_server_reserved4)(void);
};
/**
diff --git a/libdbusmenu-gtk/client.c b/libdbusmenu-gtk/client.c
index b406697..b5b509f 100644
--- a/libdbusmenu-gtk/client.c
+++ b/libdbusmenu-gtk/client.c
@@ -491,7 +491,7 @@ dbusmenu_gtkclient_newitem_base (DbusmenuGtkClient * client, DbusmenuMenuitem *
/* Oh, we're a child, let's deal with that */
if (parent != NULL) {
- new_child(parent, item, dbusmenu_menuitem_get_position_realized(item, parent), DBUSMENU_GTKCLIENT(client));
+ new_child(parent, item, dbusmenu_menuitem_get_position(item, parent), DBUSMENU_GTKCLIENT(client));
}
return;
@@ -519,7 +519,7 @@ new_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position, Dbus
}
GtkMenuItem * childmi = dbusmenu_gtkclient_menuitem_get(gtkclient, child);
- gtk_menu_shell_insert(GTK_MENU_SHELL(menu), GTK_WIDGET(childmi), dbusmenu_menuitem_get_position_realized(child, mi));
+ gtk_menu_shell_insert(GTK_MENU_SHELL(menu), GTK_WIDGET(childmi), position);
gtk_widget_show(GTK_WIDGET(menu));
return;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 63857a2..839305f 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -10,11 +10,11 @@ TESTS = \
test-glib-proxy \
test-glib-simple-items \
test-glib-submenu \
- test-json \
test-gtk-objects-test \
test-gtk-label \
test-gtk-shortcut \
- test-gtk-reorder
+ test-gtk-reorder \
+ test-gtk-submenu
check_PROGRAMS = \
glib-server-nomenu \
@@ -36,7 +36,9 @@ check_PROGRAMS = \
test-glib-simple-items \
test-gtk-reorder-server \
test-json-client \
- test-json-server
+ test-json-server \
+ test-gtk-submenu-server \
+ test-gtk-submenu-client
XVFB_RUN=". $(srcdir)/run-xvfb.sh"
@@ -454,6 +456,46 @@ test_gtk_reorder_server_LDADD = \
$(DBUSMENUTESTS_LIBS)
#########################
+# Test GTK Submenu
+#########################
+
+test-gtk-submenu: test-gtk-submenu-client test-gtk-submenu-server Makefile.am
+ @echo "#!/bin/bash" > $@
+ @echo $(XVFB_RUN) >> $@
+ @echo $(DBUS_RUNNER) --task ./test-gtk-submenu-client --task-name Client --task ./test-gtk-submenu-server --task-name Server --ignore-return >> $@
+ @chmod +x $@
+
+test_gtk_submenu_server_SOURCES = \
+ test-gtk-submenu-server.c
+
+test_gtk_submenu_server_CFLAGS = \
+ -I $(srcdir)/.. \
+ $(DBUSMENUGTK_CFLAGS) \
+ $(DBUSMENUTESTS_CFLAGS) \
+ $(DBUSMENUGLIB_CFLAGS) -Wall -Werror
+
+test_gtk_submenu_server_LDADD = \
+ ../libdbusmenu-glib/libdbusmenu-glib.la \
+ ../libdbusmenu-gtk/libdbusmenu-gtk.la \
+ $(DBUSMENUGTK_LIBS) \
+ $(DBUSMENUTESTS_LIBS)
+
+test_gtk_submenu_client_SOURCES = \
+ test-gtk-submenu-client.c
+
+test_gtk_submenu_client_CFLAGS = \
+ -I $(srcdir)/.. \
+ $(DBUSMENUGTK_CFLAGS) \
+ $(DBUSMENUTESTS_CFLAGS) \
+ $(DBUSMENUGLIB_CFLAGS) -Wall -Werror
+
+test_gtk_submenu_client_LDADD = \
+ ../libdbusmenu-glib/libdbusmenu-glib.la \
+ ../libdbusmenu-gtk/libdbusmenu-gtk.la \
+ $(DBUSMENUGTK_LIBS) \
+ $(DBUSMENUTESTS_LIBS)
+
+#########################
# Test Mago
#########################
diff --git a/tests/test-gtk-submenu-client.c b/tests/test-gtk-submenu-client.c
new file mode 100644
index 0000000..2e1ef7a
--- /dev/null
+++ b/tests/test-gtk-submenu-client.c
@@ -0,0 +1,143 @@
+/*
+A test for libdbusmenu to ensure its quality.
+
+Copyright 2009 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License version 3, as published
+by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <gtk/gtk.h>
+#include <libdbusmenu-gtk/menu.h>
+
+static GMainLoop * mainloop = NULL;
+static gboolean passed = TRUE;
+
+static gboolean check_menu_content(GtkMenu * menu, char ** content)
+{
+ GList * child = gtk_container_get_children(GTK_CONTAINER(menu));
+ char ** expected = content;
+ for (; child != NULL; child = g_list_next(child), ++expected) {
+ if (*expected == NULL) {
+ g_warning("Too many gtk items");
+ return FALSE;
+ }
+ const char * label = gtk_menu_item_get_label(GTK_MENU_ITEM(child->data));
+ if (g_strcmp0(label, *expected) != 0) {
+ g_warning("Expected '%s', got '%s'", *expected, label);
+ return FALSE;
+ }
+ }
+ if (*expected != NULL) {
+ g_warning("Not enough gtk items");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+abort_test(const char * message)
+{
+ if (message) {
+ g_warning("%s", message);
+ }
+ passed = FALSE;
+ g_main_loop_quit(mainloop);
+}
+
+static gboolean
+timer_func (gpointer data)
+{
+ static char * root_content[] = { "Folder 1", "Folder 2", NULL };
+ static char * folder1_content[] = { "1.1", "1.2", "1.3", NULL };
+ static char * folder2_content[] = { "2.1", "2.2", "2.3", NULL };
+
+ GtkMenuItem * root_item = GTK_MENU_ITEM(data);
+ GtkMenu * menu = GTK_MENU(gtk_menu_item_get_submenu(root_item));
+
+ /* Root */
+ if (!check_menu_content(menu, root_content)) {
+ abort_test("Checking root content failed");
+ return FALSE;
+ }
+
+ /* Folder 1 */
+ GList * child = gtk_container_get_children(GTK_CONTAINER(menu));
+ GtkMenuItem * item = GTK_MENU_ITEM(child->data);
+ GtkMenu * folder_menu = GTK_MENU(gtk_menu_item_get_submenu(item));
+ if (!folder_menu) {
+ abort_test("Folder 1 has no menu");
+ return FALSE;
+ }
+
+ if (!check_menu_content(folder_menu, folder1_content)) {
+ abort_test("Checking folder1 content failed");
+ return FALSE;
+ }
+
+ /* Folder 2 */
+ child = g_list_next(child);
+ item = GTK_MENU_ITEM(child->data);
+ folder_menu = GTK_MENU(gtk_menu_item_get_submenu(item));
+ if (!folder_menu) {
+ abort_test("Folder 2 has no menu");
+ return FALSE;
+ }
+
+ if (!check_menu_content(folder_menu, folder2_content)) {
+ abort_test("Checking folder2 content failed");
+ return FALSE;
+ }
+
+ passed = TRUE;
+ g_main_loop_quit(mainloop);
+ return FALSE;
+}
+
+int
+main (int argc, char ** argv)
+{
+ gtk_init(&argc, &argv);
+
+ g_debug("Client Initialized. Waiting.");
+ /* Make sure the server starts up and all that */
+ g_usleep(500000);
+
+ g_debug("Building Window");
+ GtkWidget * window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ GtkWidget * menubar = gtk_menu_bar_new();
+ GtkWidget * menuitem = gtk_menu_item_new_with_label("Test");
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), GTK_WIDGET(dbusmenu_gtkmenu_new ("glib.label.test", "/org/test")));
+ gtk_widget_show(menuitem);
+ gtk_menu_bar_append(menubar, menuitem);
+ gtk_widget_show(menubar);
+ gtk_container_add(GTK_CONTAINER(window), menubar);
+ gtk_window_set_title(GTK_WINDOW(window), "libdbusmenu-gtk test");
+ gtk_widget_show(window);
+
+ g_timeout_add_seconds(1, timer_func, menuitem);
+
+ g_debug("Entering Mainloop");
+ mainloop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(mainloop);
+
+ if (passed) {
+ g_debug("Quiting");
+ return 0;
+ } else {
+ g_debug("Quiting as we're a failure");
+ return 1;
+ }
+}
diff --git a/tests/test-gtk-submenu-server.c b/tests/test-gtk-submenu-server.c
new file mode 100644
index 0000000..ba3993e
--- /dev/null
+++ b/tests/test-gtk-submenu-server.c
@@ -0,0 +1,98 @@
+/*
+A test for libdbusmenu to ensure its quality.
+
+Copyright 2009 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License version 3, as published
+by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <dbus/dbus-glib-bindings.h>
+
+#include <libdbusmenu-glib/menuitem.h>
+#include <libdbusmenu-glib/server.h>
+
+static GMainLoop *mainloop = NULL;
+
+static gboolean
+timer_func (gpointer data)
+{
+ g_main_loop_quit (mainloop);
+
+ return FALSE;
+}
+
+DbusmenuMenuitem *
+add_item(DbusmenuMenuitem * parent, const char * label)
+{
+ DbusmenuMenuitem * item = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set(item, "label", label);
+ dbusmenu_menuitem_child_append(parent, item);
+ return item;
+}
+
+int
+main (int argc, char ** argv)
+{
+ GError * error = NULL;
+
+ g_type_init();
+
+ DBusGConnection * connection = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
+ g_debug("DBus ID: %s", dbus_connection_get_server_id(dbus_g_connection_get_connection(dbus_g_bus_get(DBUS_BUS_SESSION, NULL))));
+
+ DBusGProxy * bus_proxy = dbus_g_proxy_new_for_name(connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
+ guint nameret = 0;
+
+ if (!org_freedesktop_DBus_request_name(bus_proxy, "glib.label.test", 0, &nameret, &error)) {
+ g_error("Unable to call to request name");
+ return 1;
+ }
+
+ if (nameret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+ g_error("Unable to get name");
+ return 1;
+ }
+
+ DbusmenuServer * server = dbusmenu_server_new("/org/test");
+ DbusmenuMenuitem * root = dbusmenu_menuitem_new();
+ dbusmenu_server_set_root(server, root);
+
+ DbusmenuMenuitem * item;
+ item = add_item(root, "Folder 1");
+ add_item(item, "1.1");
+ add_item(item, "1.2");
+ add_item(item, "1.3");
+
+ item = add_item(root, "Folder 2");
+ add_item(item, "2.1");
+ add_item(item, "2.2");
+ add_item(item, "2.3");
+
+ g_timeout_add_seconds(3, timer_func, NULL);
+
+ mainloop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(mainloop);
+
+ g_debug("Quiting");
+
+ return 0;
+}
+
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);