aboutsummaryrefslogtreecommitdiff
path: root/libdbusmenu-glib
diff options
context:
space:
mode:
Diffstat (limited to 'libdbusmenu-glib')
-rw-r--r--libdbusmenu-glib/Makefile.in9
-rw-r--r--libdbusmenu-glib/client.c411
-rw-r--r--libdbusmenu-glib/dbus-menu.xml14
-rw-r--r--libdbusmenu-glib/dbusmenu-server.h2
-rw-r--r--libdbusmenu-glib/menuitem-marshal.c2
-rw-r--r--libdbusmenu-glib/server-marshal.c37
-rw-r--r--libdbusmenu-glib/server-marshal.h8
-rw-r--r--libdbusmenu-glib/server-marshal.list1
-rw-r--r--libdbusmenu-glib/server.c132
-rw-r--r--libdbusmenu-glib/server.h5
10 files changed, 535 insertions, 86 deletions
diff --git a/libdbusmenu-glib/Makefile.in b/libdbusmenu-glib/Makefile.in
index 1bc00f8..0911e1d 100644
--- a/libdbusmenu-glib/Makefile.in
+++ b/libdbusmenu-glib/Makefile.in
@@ -43,12 +43,7 @@ subdir = libdbusmenu-glib
DIST_COMMON = $(libdbusmenu_glibinclude_HEADERS) $(srcdir)/Makefile.am \
$(srcdir)/Makefile.in $(srcdir)/dbusmenu-glib.pc.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/gnome-doc-utils.m4 \
- $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \
- $(top_srcdir)/m4/introspection.m4 $(top_srcdir)/m4/libtool.m4 \
- $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
- $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
- $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
@@ -231,8 +226,6 @@ PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
PKG_CONFIG = @PKG_CONFIG@
-PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
-PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
POFILES = @POFILES@
POSUB = @POSUB@
PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
diff --git a/libdbusmenu-glib/client.c b/libdbusmenu-glib/client.c
index 2e985d6..7c73b7b 100644
--- a/libdbusmenu-glib/client.c
+++ b/libdbusmenu-glib/client.c
@@ -78,6 +78,10 @@ struct _DbusmenuClientPrivate
DBusGProxy * dbusproxy;
GHashTable * type_handlers;
+
+ GArray * delayed_property_list;
+ GArray * delayed_property_listeners;
+ gint delayed_idle;
};
typedef struct _newItemPropData newItemPropData;
@@ -88,6 +92,14 @@ struct _newItemPropData
DbusmenuMenuitem * parent;
};
+typedef struct _properties_listener_t properties_listener_t;
+struct _properties_listener_t {
+ gint id;
+ org_ayatana_dbusmenu_get_properties_reply callback;
+ gpointer user_data;
+ gboolean replied;
+};
+
#define DBUSMENU_CLIENT_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_CLIENT, DbusmenuClientPrivate))
@@ -109,6 +121,8 @@ static gint parse_layout (DbusmenuClient * client, const gchar * layout);
static void update_layout_cb (DBusGProxy * proxy, guint rev, gchar * xml, GError * in_error, void * data);
static void update_layout (DbusmenuClient * client);
static void menuitem_get_properties_cb (DBusGProxy * proxy, GHashTable * properties, GError * error, gpointer data);
+static void get_properties_globber (DbusmenuClient * client, gint id, const gchar ** properties, org_ayatana_dbusmenu_get_properties_reply callback, gpointer user_data);
+static GQuark error_domain (void);
/* Build a type */
G_DEFINE_TYPE (DbusmenuClient, dbusmenu_client, G_TYPE_OBJECT);
@@ -211,6 +225,10 @@ dbusmenu_client_init (DbusmenuClient *self)
priv->type_handlers = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, NULL);
+ priv->delayed_idle = 0;
+ priv->delayed_property_list = g_array_new(TRUE, FALSE, sizeof(gchar *));
+ priv->delayed_property_listeners = g_array_new(FALSE, FALSE, sizeof(properties_listener_t));
+
return;
}
@@ -219,6 +237,44 @@ dbusmenu_client_dispose (GObject *object)
{
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(object);
+ if (priv->delayed_idle != 0) {
+ g_source_remove(priv->delayed_idle);
+ priv->delayed_idle = 0;
+ }
+
+ /* Only used for queueing up a new command, so we can
+ just drop this array. */
+ if (priv->delayed_property_list == NULL) {
+ gchar ** dataregion = (gchar **)g_array_free(priv->delayed_property_list, FALSE);
+ if (dataregion != NULL) {
+ g_strfreev(dataregion);
+ }
+ priv->delayed_property_list = NULL;
+ }
+
+ if (priv->delayed_property_listeners == NULL) {
+ gint i;
+ GError * localerror = NULL;
+
+ /* Making sure all the callbacks get called so that if they had
+ memory in their user_data that needs to be free'd that happens. */
+ for (i = 0; i < priv->delayed_property_listeners->len; i++) {
+ properties_listener_t * listener = &g_array_index(priv->delayed_property_listeners, properties_listener_t, i);
+ if (!listener->replied) {
+ if (localerror == NULL) {
+ g_set_error_literal(&localerror, error_domain(), 0, "DbusmenuClient Shutdown");
+ }
+ listener->callback(priv->menuproxy, NULL, localerror, listener->user_data);
+ }
+ }
+ if (localerror != NULL) {
+ g_error_free(localerror);
+ }
+
+ g_array_free(priv->delayed_property_listeners, TRUE);
+ priv->delayed_property_listeners = NULL;
+ }
+
if (priv->layoutcall != NULL) {
dbus_g_proxy_cancel_call(priv->menuproxy, priv->layoutcall);
priv->layoutcall = NULL;
@@ -310,6 +366,222 @@ get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
/* Internal funcs */
+static GQuark
+error_domain (void)
+{
+ static GQuark error = 0;
+ if (error == 0) {
+ error = g_quark_from_static_string(G_LOG_DOMAIN "-CLIENT");
+ }
+ return error;
+}
+
+/* Quick little function to search through the listeners and find
+ one that matches an ID */
+static properties_listener_t *
+find_listener (GArray * listeners, guint index, gint id)
+{
+ if (index >= listeners->len) {
+ return NULL;
+ }
+
+ properties_listener_t * retval = &g_array_index(listeners, properties_listener_t, index);
+ if (retval->id == id) {
+ return retval;
+ }
+
+ return find_listener(listeners, index + 1, id);
+}
+
+/* Call back from getting the group properties, now we need
+ to unwind and call the various functions. */
+static void
+get_properties_callback (DBusGProxy *proxy, GPtrArray *OUT_properties, GError *error, gpointer userdata)
+{
+ GArray * listeners = (GArray *)userdata;
+ int i;
+
+ #ifdef MASSIVEDEBUGGING
+ g_debug("Get properties callback: %d", OUT_properties->len);
+ #endif
+
+ if (error != NULL) {
+ /* If we get an error, all our callbacks need to hear about it. */
+ g_warning("Group Properties error: %s", error->message);
+ for (i = 0; i < listeners->len; i++) {
+ properties_listener_t * listener = &g_array_index(listeners, properties_listener_t, i);
+ listener->callback(proxy, NULL, error, listener->user_data);
+ }
+ g_array_free(listeners, TRUE);
+ return;
+ }
+
+ /* Callback all the folks we can find */
+ for (i = 0; i < OUT_properties->len; i++) {
+ GValueArray * varray = (GValueArray *)g_ptr_array_index(OUT_properties, i);
+
+ if (varray->n_values != 2) {
+ g_warning("Value Array is %d entries long but we expected 2.", varray->n_values);
+ continue;
+ }
+
+ GValue * vid = g_value_array_get_nth(varray, 0);
+ GValue * vproperties = g_value_array_get_nth(varray, 1);
+
+ if (G_VALUE_TYPE(vid) != G_TYPE_INT) {
+ g_warning("ID Entry not holding an int: %s", G_VALUE_TYPE_NAME(vid));
+ }
+ if (G_VALUE_TYPE(vproperties) != dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)) {
+ g_warning("Properties Entry not holding an a{sv}: %s", G_VALUE_TYPE_NAME(vproperties));
+ }
+
+ gint id = g_value_get_int(vid);
+ GHashTable * properties = g_value_get_boxed(vproperties);
+
+ properties_listener_t * listener = find_listener(listeners, 0, id);
+ if (listener == NULL) {
+ g_warning("Unable to find listener for ID %d", id);
+ continue;
+ }
+
+ if (!listener->replied) {
+ listener->callback(proxy, properties, NULL, listener->user_data);
+ listener->replied = TRUE;
+ } else {
+ g_warning("Odd, we've already replied to the listener on ID %d", id);
+ }
+ }
+
+ /* Provide errors for those who we can't */
+ GError * localerror = NULL;
+ for (i = 0; i < listeners->len; i++) {
+ properties_listener_t * listener = &g_array_index(listeners, properties_listener_t, i);
+ if (!listener->replied) {
+ if (localerror == NULL) {
+ g_set_error_literal(&localerror, error_domain(), 0, "Error getting properties for ID");
+ }
+ listener->callback(proxy, NULL, localerror, listener->user_data);
+ }
+ }
+ if (localerror != NULL) {
+ g_error_free(localerror);
+ }
+
+ /* Clean up */
+ g_array_free(listeners, TRUE);
+
+ return;
+}
+
+/* Idle handler to send out all of our property requests as one big
+ lovely property request. */
+static gboolean
+get_properties_idle (gpointer user_data)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(user_data);
+ //org_ayatana_dbusmenu_get_properties_async(priv->menuproxy, id, properties, callback, user_data);
+
+ if (priv->delayed_property_listeners->len == 0) {
+ g_warning("Odd, idle func got no listeners.");
+ return FALSE;
+ }
+
+ /* Build up an ID list to pass */
+ GArray * idlist = g_array_new(FALSE, FALSE, sizeof(gint));
+ gint i;
+ for (i = 0; i < priv->delayed_property_listeners->len; i++) {
+ g_array_append_val(idlist, g_array_index(priv->delayed_property_listeners, properties_listener_t, i).id);
+ }
+
+ org_ayatana_dbusmenu_get_group_properties_async(priv->menuproxy, idlist, (const gchar **)priv->delayed_property_list->data, get_properties_callback, priv->delayed_property_listeners);
+
+ /* Free ID List */
+ g_array_free(idlist, TRUE);
+
+ /* Free properties */
+ gchar ** dataregion = (gchar **)g_array_free(priv->delayed_property_list, FALSE);
+ if (dataregion != NULL) {
+ g_strfreev(dataregion);
+ }
+ priv->delayed_property_list = g_array_new(TRUE, FALSE, sizeof(gchar *));
+
+ /* Rebuild the listeners */
+ priv->delayed_property_listeners = g_array_new(FALSE, FALSE, sizeof(properties_listener_t));
+
+ /* Make sure we set for a new idle */
+ priv->delayed_idle = 0;
+
+ return FALSE;
+}
+
+/* Forces a call out to start getting properties with the menu items
+ that we have queued up already. */
+static void
+get_properties_flush (DbusmenuClient * client)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+
+ if (priv->delayed_idle == 0) {
+ return;
+ }
+
+ g_source_remove(priv->delayed_idle);
+ priv->delayed_idle = 0;
+
+ get_properties_idle(client);
+
+ dbus_g_connection_flush(priv->session_bus);
+
+ return;
+}
+
+/* A function to group all the get_properties commands to make them
+ more efficient over dbus. */
+static void
+get_properties_globber (DbusmenuClient * client, gint id, const gchar ** properties, org_ayatana_dbusmenu_get_properties_reply callback, gpointer user_data)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ if (find_listener(priv->delayed_property_listeners, 0, id) != NULL) {
+ g_warning("Asking for properties from same ID twice: %d", id);
+ GError * localerror = NULL;
+ g_set_error_literal(&localerror, error_domain(), 0, "ID already queued");
+ callback(priv->menuproxy, NULL, localerror, user_data);
+ g_error_free(localerror);
+ return;
+ }
+
+ if (properties == NULL || properties[0] == NULL) {
+ /* get all case */
+ if (priv->delayed_property_list->len != 0) {
+ /* If there are entries in the list, then we'll need to
+ remove them all, and start over */
+ gchar ** dataregion = (gchar **)g_array_free(priv->delayed_property_list, FALSE);
+ if (dataregion != NULL) {
+ g_strfreev(dataregion);
+ }
+ priv->delayed_property_list = g_array_new(TRUE, FALSE, sizeof(gchar *));
+ }
+ } else {
+ /* there could be a list we care about */
+ /* TODO: No one uses this today */
+ /* TODO: Copy them into the list */
+ }
+
+ properties_listener_t listener = {0};
+ listener.id = id;
+ listener.callback = callback;
+ listener.user_data = user_data;
+ listener.replied = FALSE;
+
+ g_array_append_val(priv->delayed_property_listeners, listener);
+
+ if (priv->delayed_idle == 0) {
+ priv->delayed_idle = g_idle_add(get_properties_idle, client);
+ }
+
+ return;
+}
+
/* Annoying little wrapper to make the right function update */
static void
layout_update (DBusGProxy * proxy, guint revision, gint parent, DbusmenuClient * client)
@@ -367,10 +639,9 @@ id_update (DBusGProxy * proxy, gint id, DbusmenuClient * client)
DbusmenuMenuitem * menuitem = dbusmenu_menuitem_find_id(priv->root, id);
g_return_if_fail(menuitem != NULL);
- gchar * properties[1] = {NULL}; /* This gets them all */
g_debug("Getting properties");
g_object_ref(menuitem);
- org_ayatana_dbusmenu_get_properties_async(proxy, id, (const gchar **)properties, menuitem_get_properties_cb, menuitem);
+ get_properties_globber(client, id, NULL, menuitem_get_properties_cb, menuitem);
return;
}
@@ -655,16 +926,19 @@ menuitem_get_properties_replace_cb (DBusGProxy * proxy, GHashTable * properties,
static void
menuitem_get_properties_new_cb (DBusGProxy * proxy, GHashTable * properties, GError * error, gpointer data)
{
+ g_return_if_fail(data != NULL);
+ newItemPropData * propdata = (newItemPropData *)data;
+
if (error != NULL) {
g_warning("Error getting properties on a new menuitem: %s", error->message);
- g_object_unref(data);
+ g_object_unref(propdata->item);
+ g_free(data);
return;
}
- g_return_if_fail(data != NULL);
- newItemPropData * propdata = (newItemPropData *)data;
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(propdata->client);
+ /* Extra ref as get_properties will unref once itself */
g_object_ref(propdata->item);
menuitem_get_properties_cb (proxy, properties, error, propdata->item);
@@ -784,11 +1058,51 @@ dbusmenu_client_send_about_to_show(DbusmenuClient * client, gint id, void (*cb)(
return;
}
+/* Builds a new child with property requests and everything
+ else to clean up the code a bit */
+static DbusmenuMenuitem *
+parse_layout_new_child (gint id, DbusmenuClient * client, DbusmenuMenuitem * parent)
+{
+ DbusmenuMenuitem * item = NULL;
+
+ /* Build a new item */
+ item = DBUSMENU_MENUITEM(dbusmenu_client_menuitem_new(id, client));
+ if (parent == NULL) {
+ dbusmenu_menuitem_set_root(item, TRUE);
+ }
+
+ /* Get the properties queued up for this item */
+ /* Not happy allocating about this, but I need these :( */
+ newItemPropData * propdata = g_new0(newItemPropData, 1);
+ if (propdata != NULL) {
+ propdata->client = client;
+ propdata->item = item;
+ propdata->parent = parent;
+
+ g_object_ref(item);
+ get_properties_globber(client, id, NULL, menuitem_get_properties_new_cb, propdata);
+ } else {
+ g_warning("Unable to allocate memory to get properties for menuitem. This menuitem will never be realized.");
+ }
+
+ return item;
+}
+
+/* Refresh the properties on this item */
+static void
+parse_layout_update (DbusmenuMenuitem * item, DbusmenuClient * client)
+{
+ g_object_ref(item);
+ get_properties_globber(client, dbusmenu_menuitem_get_id(item), NULL, menuitem_get_properties_replace_cb, item);
+ return;
+}
+
/* Parse recursively through the XML and make it into
objects as need be */
static DbusmenuMenuitem *
parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, DBusGProxy * proxy)
{
+ /* First verify and figure out what we've got */
gint id = parse_node_get_id(node);
if (id < 0) {
return NULL;
@@ -796,48 +1110,18 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it
#ifdef MASSIVEDEBUGGING
g_debug("Client looking at node with id: %d", id);
#endif
- /* If we don't have any item, or the IDs don't match */
- if (item == NULL || dbusmenu_menuitem_get_id(item) != id) {
- if (item != NULL) {
- if (parent != NULL) {
- dbusmenu_menuitem_child_delete(parent, item);
- }
- item = NULL;
- }
-
- /* Build a new item */
- item = DBUSMENU_MENUITEM(dbusmenu_client_menuitem_new(id, client));
- if (parent == NULL) {
- dbusmenu_menuitem_set_root(item, TRUE);
- }
- /* Get the properties queued up for this item */
- /* Not happy about this, but I need these :( */
- newItemPropData * propdata = g_new0(newItemPropData, 1);
- if (propdata != NULL) {
- propdata->client = client;
- propdata->item = item;
- propdata->parent = parent;
-
- gchar * properties[1] = {NULL}; /* This gets them all */
- g_object_ref(item);
- org_ayatana_dbusmenu_get_properties_async(proxy, id, (const gchar **)properties, menuitem_get_properties_new_cb, propdata);
- } else {
- g_warning("Unable to allocate memory to get properties for menuitem. This menuitem will never be realized.");
- }
- } else {
- /* Refresh the properties */
- /* XXX: We shouldn't need to get the properties everytime we reuse an entry */
- gchar * properties[1] = {NULL}; /* This gets them all */
- g_object_ref(item);
- org_ayatana_dbusmenu_get_properties_async(proxy, id, (const gchar **)properties, menuitem_get_properties_replace_cb, item);
- }
+ g_return_val_if_fail(item != NULL, NULL);
+ g_return_val_if_fail(id == dbusmenu_menuitem_get_id(item), NULL);
+ /* Some variables */
xmlNodePtr children;
guint position;
GList * oldchildren = g_list_copy(dbusmenu_menuitem_get_children(item));
/* g_debug("Starting old children: %d", g_list_length(oldchildren)); */
+ /* Go through all the XML Nodes and make sure that we have menuitems
+ to cover those XML nodes. */
for (children = node->children, position = 0; children != NULL; children = children->next, position++) {
/* g_debug("Looking at child: %d", position); */
gint childid = parse_node_get_id(children);
@@ -846,6 +1130,8 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it
}
DbusmenuMenuitem * childmi = NULL;
+ /* First see if we can recycle a node that we've already built
+ on this menu item */
GList * childsearch = NULL;
for (childsearch = oldchildren; childsearch != NULL; childsearch = g_list_next(childsearch)) {
DbusmenuMenuitem * cs_mi = DBUSMENU_MENUITEM(childsearch->data);
@@ -856,20 +1142,20 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it
}
}
- DbusmenuMenuitem * newchildmi = parse_layout_xml(client, children, childmi, item, proxy);
-
- if (newchildmi != childmi) {
- if (childmi != NULL) {
- dbusmenu_menuitem_child_delete(item, childmi);
- }
- dbusmenu_menuitem_child_add_position(item, newchildmi, position);
- g_object_unref(newchildmi);
+ if (childmi == NULL) {
+ /* If we can't recycle, then we build a new one */
+ childmi = parse_layout_new_child(childid, client, item);
+ dbusmenu_menuitem_child_add_position(item, childmi, position);
+ g_object_unref(childmi);
} else {
+ /* If we can recycle, make sure it's in the right place */
dbusmenu_menuitem_child_reorder(item, childmi, position);
+ parse_layout_update(childmi, client);
}
}
- /* g_debug("Stopping old children: %d", g_list_length(oldchildren)); */
+ /* Remove any children that are no longer used by this version of
+ the layout. */
GList * oldchildleft = NULL;
for (oldchildleft = oldchildren; oldchildleft != NULL; oldchildleft = g_list_next(oldchildleft)) {
DbusmenuMenuitem * oldmi = DBUSMENU_MENUITEM(oldchildleft->data);
@@ -880,6 +1166,27 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it
}
g_list_free(oldchildren);
+ /* We've got everything built up at this node and reconcilled */
+
+ /* Flush the properties requests */
+ get_properties_flush(client);
+
+ /* now it's time to recurse down the tree. */
+ children = node->children;
+ GList * childmis = dbusmenu_menuitem_get_children(item);
+ while (children != NULL && childmis != NULL) {
+ parse_layout_xml(client, children, DBUSMENU_MENUITEM(childmis->data), item, proxy);
+
+ children = children->next;
+ childmis = g_list_next(childmis);
+ }
+ if (children != NULL) {
+ g_warning("Sync failed, now we've got extra XML nodes.");
+ }
+ if (childmis != NULL) {
+ g_warning("Sync failed, now we've got extra menu items.");
+ }
+
return item;
}
@@ -906,6 +1213,12 @@ parse_layout (DbusmenuClient * client, const gchar * layout)
DbusmenuMenuitem * oldroot = priv->root;
+ if (priv->root == NULL) {
+ priv->root = parse_layout_new_child(0, client, NULL);
+ } else {
+ parse_layout_update(priv->root, client);
+ }
+
priv->root = parse_layout_xml(client, root, priv->root, NULL, priv->menuproxy);
xmlFreeDoc(xmldoc);
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/dbusmenu-server.h b/libdbusmenu-glib/dbusmenu-server.h
index dcc65a3..905c3f6 100644
--- a/libdbusmenu-glib/dbusmenu-server.h
+++ b/libdbusmenu-glib/dbusmenu-server.h
@@ -381,7 +381,7 @@ const DBusGObjectInfo dbus_glib__dbusmenu_server_object_info = {
dbus_glib__dbusmenu_server_methods,
7,
"org.ayatana.dbusmenu\0GetLayout\0S\0parentId\0I\0i\0revision\0O\0F\0N\0u\0layout\0O\0F\0N\0s\0\0org.ayatana.dbusmenu\0GetGroupProperties\0S\0ids\0I\0ai\0propertyNames\0I\0as\0properties\0O\0F\0N\0a(ia{sv})\0\0org.ayatana.dbusmenu\0GetChildren\0S\0id\0I\0i\0propertyNames\0I\0as\0properties\0O\0F\0N\0a(ia{sv})\0\0org.ayatana.dbusmenu\0GetProperty\0S\0id\0I\0i\0name\0I\0s\0value\0O\0F\0N\0v\0\0org.ayatana.dbusmenu\0GetProperties\0S\0id\0I\0i\0propertyNames\0I\0as\0properties\0O\0F\0N\0a{sv}\0\0org.ayatana.dbusmenu\0Event\0S\0id\0I\0i\0eventId\0I\0s\0data\0I\0v\0timestamp\0I\0u\0\0org.ayatana.dbusmenu\0AboutToShow\0S\0id\0I\0i\0needUpdate\0O\0F\0N\0b\0\0\0",
-"org.ayatana.dbusmenu\0ItemPropertyUpdated\0org.ayatana.dbusmenu\0ItemUpdated\0org.ayatana.dbusmenu\0LayoutUpdated\0\0",
+"org.ayatana.dbusmenu\0ItemPropertyUpdated\0org.ayatana.dbusmenu\0ItemUpdated\0org.ayatana.dbusmenu\0LayoutUpdated\0org.ayatana.dbusmenu\0ItemActivationRequested\0\0",
"org.ayatana.dbusmenu\0version\0\0"
};
diff --git a/libdbusmenu-glib/menuitem-marshal.c b/libdbusmenu-glib/menuitem-marshal.c
index d6ac0a6..aa7147a 100644
--- a/libdbusmenu-glib/menuitem-marshal.c
+++ b/libdbusmenu-glib/menuitem-marshal.c
@@ -21,7 +21,6 @@
#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
#define g_marshal_value_peek_object(v) g_value_get_object (v)
-#define g_marshal_value_peek_variant(v) g_value_get_variant (v)
#else /* !G_ENABLE_DEBUG */
/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
* Do not access GValues directly in your code. Instead, use the
@@ -45,7 +44,6 @@
#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
-#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer
#endif /* !G_ENABLE_DEBUG */
diff --git a/libdbusmenu-glib/server-marshal.c b/libdbusmenu-glib/server-marshal.c
index 6730b16..68552f6 100644
--- a/libdbusmenu-glib/server-marshal.c
+++ b/libdbusmenu-glib/server-marshal.c
@@ -125,3 +125,40 @@ _dbusmenu_server_marshal_VOID__UINT_INT (GClosure *closure,
data2);
}
+/* VOID:INT,UINT (./server-marshal.list:3) */
+void
+_dbusmenu_server_marshal_VOID__INT_UINT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__INT_UINT) (gpointer data1,
+ gint arg_1,
+ guint arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__INT_UINT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__INT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_uint (param_values + 2),
+ data2);
+}
+
diff --git a/libdbusmenu-glib/server-marshal.h b/libdbusmenu-glib/server-marshal.h
index 3a5a4d0..a561738 100644
--- a/libdbusmenu-glib/server-marshal.h
+++ b/libdbusmenu-glib/server-marshal.h
@@ -22,6 +22,14 @@ extern void _dbusmenu_server_marshal_VOID__UINT_INT (GClosure *closure,
gpointer invocation_hint,
gpointer marshal_data);
+/* VOID:INT,UINT (./server-marshal.list:3) */
+extern void _dbusmenu_server_marshal_VOID__INT_UINT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
G_END_DECLS
#endif /* ___dbusmenu_server_marshal_MARSHAL_H__ */
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 13c2843..d1b4888 100644
--- a/libdbusmenu-glib/server.c
+++ b/libdbusmenu-glib/server.c
@@ -37,14 +37,19 @@ License version 3 and version 2.1 along with this program. If not, see
/* DBus Prototypes */
static gboolean _dbusmenu_server_get_layout (DbusmenuServer * server, gint parent, guint * revision, gchar ** layout, GError ** error);
static gboolean _dbusmenu_server_get_property (DbusmenuServer * server, gint id, gchar * property, gchar ** value, GError ** error);
-static gboolean _dbusmenu_server_get_properties (DbusmenuServer * server, gint id, GPtrArray * properties, GHashTable ** dict, GError ** error);
-static gboolean _dbusmenu_server_get_group_properties (DbusmenuServer * server, GArray * ids, GArray * properties, GHashTable ** values, GError ** error);
+static gboolean _dbusmenu_server_get_properties (DbusmenuServer * server, gint id, gchar ** properties, GHashTable ** dict, GError ** error);
+static gboolean _dbusmenu_server_get_group_properties (DbusmenuServer * server, GArray * ids, gchar ** properties, GPtrArray ** values, GError ** error);
static gboolean _dbusmenu_server_event (DbusmenuServer * server, gint id, gchar * eventid, GValue * data, guint timestamp, GError ** error);
static gboolean _dbusmenu_server_get_children (DbusmenuServer * server, gint id, GPtrArray * properties, GPtrArray ** output, GError ** error);
static gboolean _dbusmenu_server_about_to_show (DbusmenuServer * server, gint id, gboolean * need_update, GError ** error);
+/* DBus Helpers */
+static void _gvalue_array_append_int(GValueArray *array, gint i);
+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... */
@@ -55,6 +60,7 @@ struct _DbusmenuServerPrivate
DbusmenuMenuitem * root;
gchar * dbusobject;
gint layout_revision;
+ guint layout_idle;
};
#define DBUSMENU_SERVER_GET_PRIVATE(o) \
@@ -65,6 +71,7 @@ enum {
ID_PROP_UPDATE,
ID_UPDATE,
LAYOUT_UPDATED,
+ ITEM_ACTIVATION,
LAST_SIGNAL
};
@@ -165,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,
@@ -196,6 +219,7 @@ dbusmenu_server_init (DbusmenuServer *self)
priv->root = NULL;
priv->dbusobject = NULL;
priv->layout_revision = 1;
+ priv->layout_idle = 0;
return;
}
@@ -205,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);
@@ -257,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();
@@ -302,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)
{
@@ -332,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;
}
@@ -343,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;
}
@@ -368,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;
}
@@ -480,7 +538,7 @@ _dbusmenu_server_get_property (DbusmenuServer * server, gint id, gchar * propert
}
static gboolean
-_dbusmenu_server_get_properties (DbusmenuServer * server, gint id, GPtrArray * properties, GHashTable ** dict, GError ** error)
+_dbusmenu_server_get_properties (DbusmenuServer * server, gint id, gchar ** properties, GHashTable ** dict, GError ** error)
{
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
@@ -501,18 +559,42 @@ _dbusmenu_server_get_properties (DbusmenuServer * server, gint id, GPtrArray * p
return TRUE;
}
+/* Handles getting a bunch of properties from a variety of menu items
+ to make one mega dbus message */
static gboolean
-_dbusmenu_server_get_group_properties (DbusmenuServer * server, GArray * ids, GArray * properties, GHashTable ** values, GError ** error)
+_dbusmenu_server_get_group_properties (DbusmenuServer * server, GArray * ids, gchar ** properties, GPtrArray ** values, GError ** error)
{
- if (error != NULL) {
- g_set_error(error,
- error_quark(),
- NOT_IMPLEMENTED,
- "The GetGroupProperties function is not implemented, sorry.");
+ /* Build an initial pointer array */
+ *values = g_ptr_array_new();
+
+ /* Go through each ID to get that ID's properties */
+ int idcnt;
+ for (idcnt = 0; idcnt < ids->len; idcnt++) {
+ GHashTable * idprops = NULL;
+ GError * error = NULL;
+ gint id = g_array_index(ids, int, idcnt);
+
+ /* Get the properties for this ID the old fashioned way. */
+ if (!_dbusmenu_server_get_properties(server, id, properties, &idprops, &error)) {
+ g_warning("Error getting the properties from ID %d: %s", id, error->message);
+ g_error_free(error);
+ error = NULL;
+ continue;
+ }
+
+ GValueArray * valarray = g_value_array_new(2);
+
+ _gvalue_array_append_int(valarray, id);
+ _gvalue_array_append_hashtable(valarray, idprops);
+
+ g_ptr_array_add(*values, valarray);
}
- return FALSE;
+
+ return TRUE;
}
+/* Allocate a value on the stack for the int and append
+ it to the array. */
static void
_gvalue_array_append_int(GValueArray *array, gint i)
{
@@ -524,6 +606,8 @@ _gvalue_array_append_int(GValueArray *array, gint i)
g_value_unset(&value);
}
+/* Allocate a value on the stack for the hashtable and append
+ it to the array. */
static void
_gvalue_array_append_hashtable(GValueArray *array, GHashTable * dict)
{
@@ -544,7 +628,7 @@ serialize_menuitem(gpointer data, gpointer user_data)
gint id = dbusmenu_menuitem_get_id(mi);
GHashTable * dict = dbusmenu_menuitem_properties_copy(mi);
- GValueArray * item = g_value_array_new(1);
+ GValueArray * item = g_value_array_new(2);
_gvalue_array_append_int(item, id);
_gvalue_array_append_hashtable(item, dict);
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);
};
/**