aboutsummaryrefslogtreecommitdiff
path: root/libdbusmenu-glib
diff options
context:
space:
mode:
authorTed Gould <ted@gould.cx>2012-04-09 09:15:17 -0500
committerTed Gould <ted@gould.cx>2012-04-09 09:15:17 -0500
commit475110aa2fe3a47cf6331fbd5941f34a9461fd81 (patch)
treebf2318e8b94cf0b74ea17b496e9d5331a3c065ec /libdbusmenu-glib
parente0eb750e8c36a4116247ea80b4d055a9f821459a (diff)
parent2d60549a2394ce7d73907abcaca22b48553d6c5b (diff)
downloadlibdbusmenu-475110aa2fe3a47cf6331fbd5941f34a9461fd81.tar.gz
libdbusmenu-475110aa2fe3a47cf6331fbd5941f34a9461fd81.tar.bz2
libdbusmenu-475110aa2fe3a47cf6331fbd5941f34a9461fd81.zip
Catching up to trunk
Diffstat (limited to 'libdbusmenu-glib')
-rw-r--r--libdbusmenu-glib/Makefile.am10
-rw-r--r--libdbusmenu-glib/client.c573
-rw-r--r--libdbusmenu-glib/client.h6
-rw-r--r--libdbusmenu-glib/dbus-menu.xml46
-rw-r--r--libdbusmenu-glib/menuitem.c6
-rw-r--r--libdbusmenu-glib/server.c251
6 files changed, 788 insertions, 104 deletions
diff --git a/libdbusmenu-glib/Makefile.am b/libdbusmenu-glib/Makefile.am
index 6fc3fb8..b2e796c 100644
--- a/libdbusmenu-glib/Makefile.am
+++ b/libdbusmenu-glib/Makefile.am
@@ -6,6 +6,7 @@ EXTRA_DIST = \
clean-namespaces.xslt \
dbusmenu-glib-0.4.pc.in \
dbus-menu.xml \
+ dbus-menu-clean.xml \
client-marshal.list \
menuitem-marshal.list \
server-marshal.list
@@ -57,12 +58,14 @@ libdbusmenu_glib_la_SOURCES = \
client.c
libdbusmenu_glib_la_LDFLAGS = \
+ $(COVERAGE_LDFLAGS) \
-version-info $(LIBDBUSMENU_CURRENT):$(LIBDBUSMENU_REVISION):$(LIBDBUSMENU_AGE) \
-no-undefined \
-export-symbols-regex "^[^_].*"
libdbusmenu_glib_la_CFLAGS = \
$(DBUSMENUGLIB_CFLAGS) \
+ $(COVERAGE_CFLAGS) \
-Wall -Werror -Wno-error=deprecated-declarations \
-DG_LOG_DOMAIN="\"LIBDBUSMENU-GLIB\""
@@ -180,7 +183,12 @@ introspection_sources = \
Dbusmenu-0.4.gir: libdbusmenu-glib.la
Dbusmenu_0_4_gir_INCLUDES = \
GObject-2.0
-Dbusmenu_0_4_gir_CFLAGS = $(DBUSMENUGLIB_CFLAGS) -I$(top_srcdir)
+Dbusmenu_0_4_gir_CFLAGS = \
+ $(DBUSMENUGLIB_CFLAGS) \
+ $(COVERAGE_CFLAGS) \
+ -I$(top_srcdir)
+Dbusmenu_0_4_gir_LDFLAGS = \
+ $(COVERAGE_LDFLAGS)
Dbusmenu_0_4_gir_LIBS = libdbusmenu-glib.la
Dbusmenu_0_4_gir_FILES = $(introspection_sources)
Dbusmenu_0_4_gir_NAMESPACE = Dbusmenu
diff --git a/libdbusmenu-glib/client.c b/libdbusmenu-glib/client.c
index e64d923..34f8b8d 100644
--- a/libdbusmenu-glib/client.c
+++ b/libdbusmenu-glib/client.c
@@ -52,7 +52,8 @@ enum {
PROP_DBUSOBJECT,
PROP_DBUSNAME,
PROP_STATUS,
- PROP_TEXT_DIRECTION
+ PROP_TEXT_DIRECTION,
+ PROP_GROUP_EVENTS
};
/* Signals */
@@ -66,6 +67,12 @@ enum {
LAST_SIGNAL
};
+/* Errors */
+enum {
+ ERROR_DISPOSAL,
+ ERROR_ID_NOT_FOUND
+};
+
typedef void (*properties_func) (GVariant * properties, GError * error, gpointer user_data);
static guint signals[LAST_SIGNAL] = { 0 };
@@ -100,6 +107,13 @@ struct _DbusmenuClientPrivate
DbusmenuTextDirection text_direction;
DbusmenuStatus status;
GStrv icon_dirs;
+
+ gboolean group_events;
+ guint event_idle;
+ GQueue * events_to_go; /* type: event_data_t * */
+
+ guint about_to_show_idle;
+ GQueue * about_to_show_to_go; /* type: about_to_show_t * */
};
typedef struct _newItemPropData newItemPropData;
@@ -120,6 +134,7 @@ struct _properties_listener_t {
typedef struct _event_data_t event_data_t;
struct _event_data_t {
+ gint id;
DbusmenuClient * client;
DbusmenuMenuitem * menuitem;
gchar * event;
@@ -171,6 +186,8 @@ static void menuproxy_prop_changed_cb (GDBusProxy * proxy, GVariant * properties
static void menuproxy_name_changed_cb (GObject * object, GParamSpec * pspec, gpointer user_data);
static void menuproxy_signal_cb (GDBusProxy * proxy, gchar * sender, gchar * signal, GVariant * params, gpointer user_data);
static void type_handler_destroy (gpointer user_data);
+static void event_data_end (event_data_t * eventd, GError * error);
+static void about_to_show_finish_pntr (gpointer data, gpointer user_data);
/* Globals */
static GDBusNodeInfo * dbusmenu_node_info = NULL;
@@ -309,6 +326,10 @@ dbusmenu_client_class_init (DbusmenuClientClass *klass)
"Signals which direction the default text direction is for the menus",
DBUSMENU_TYPE_TEXT_DIRECTION, DBUSMENU_TEXT_DIRECTION_NONE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_GROUP_EVENTS,
+ g_param_spec_boolean(DBUSMENU_CLIENT_PROP_GROUP_EVENTS, "Whether or not multiple events should be grouped",
+ "Event grouping lowers the number of messages on DBus and will be set automatically based on the version to optimize traffic. It can be disabled for testing or other purposes.",
+ FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
if (dbusmenu_node_info == NULL) {
GError * error = NULL;
@@ -380,6 +401,13 @@ dbusmenu_client_init (DbusmenuClient *self)
priv->status = DBUSMENU_STATUS_NORMAL;
priv->icon_dirs = NULL;
+ priv->group_events = FALSE;
+ priv->event_idle = 0;
+ priv->events_to_go = NULL;
+
+ priv->about_to_show_idle = 0;
+ priv->about_to_show_to_go = NULL;
+
return;
}
@@ -393,6 +421,32 @@ dbusmenu_client_dispose (GObject *object)
priv->delayed_idle = 0;
}
+ if (priv->event_idle != 0) {
+ g_source_remove(priv->event_idle);
+ priv->event_idle = 0;
+ }
+
+ if (priv->about_to_show_idle != 0) {
+ g_source_remove(priv->about_to_show_idle);
+ priv->about_to_show_idle = 0;
+ }
+
+ if (priv->events_to_go != NULL) {
+ g_warning("Getting to client dispose with events pending. This is odd. Probably there's a ref count problem somewhere, but we're going to be cool about it now and clean up. But there's probably a bug.");
+ GError * error = g_error_new_literal(error_domain(), ERROR_DISPOSAL, "Client disposed before event signal returned");
+ g_queue_foreach(priv->events_to_go, (GFunc)event_data_end, error);
+ g_queue_free(priv->events_to_go);
+ priv->events_to_go = NULL;
+ g_error_free(error);
+ }
+
+ if (priv->about_to_show_to_go != NULL) {
+ g_warning("Getting to client dispose with about_to_show's pending. This is odd. Probably there's a ref count problem somewhere, but we're going to be cool about it now and clean up. But there's probably a bug.");
+ g_queue_foreach(priv->about_to_show_to_go, about_to_show_finish_pntr, GINT_TO_POINTER(FALSE));
+ g_queue_free(priv->about_to_show_to_go);
+ priv->about_to_show_to_go = NULL;
+ }
+
/* Only used for queueing up a new command, so we can
just drop this array. */
if (priv->delayed_property_list != NULL) {
@@ -517,6 +571,9 @@ set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
build_proxies(DBUSMENU_CLIENT(obj));
}
break;
+ case PROP_GROUP_EVENTS:
+ priv->group_events = g_value_get_boolean(value);
+ break;
default:
g_warning("Unknown property %d.", id);
return;
@@ -543,6 +600,9 @@ get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
case PROP_TEXT_DIRECTION:
g_value_set_enum(value, priv->text_direction);
break;
+ case PROP_GROUP_EVENTS:
+ g_value_set_boolean(value, priv->group_events);
+ break;
default:
g_warning("Unknown property %d.", id);
return;
@@ -601,64 +661,66 @@ get_properties_callback (GObject *obj, GAsyncResult * res, gpointer user_data)
listener->callback(NULL, error, listener->user_data);
}
g_error_free(error);
- goto out;
}
/* Callback all the folks we can find */
- GVariant * parent = g_variant_get_child_value(params, 0);
- GVariantIter iter;
- g_variant_iter_init(&iter, parent);
- GVariant * child;
- while ((child = g_variant_iter_next_value(&iter)) != NULL) {
- if (g_strcmp0(g_variant_get_type_string(child), "(ia{sv})") != 0) {
- g_warning("Properties return signature is not '(ia{sv})' it is '%s'", g_variant_get_type_string(child));
- g_variant_unref(child);
- continue;
- }
+ if (error == NULL) {
+ GVariant * parent = g_variant_get_child_value(params, 0);
+ GVariantIter iter;
+ g_variant_iter_init(&iter, parent);
+ GVariant * child;
+ while ((child = g_variant_iter_next_value(&iter)) != NULL) {
+ if (g_strcmp0(g_variant_get_type_string(child), "(ia{sv})") != 0) {
+ g_warning("Properties return signature is not '(ia{sv})' it is '%s'", g_variant_get_type_string(child));
+ g_variant_unref(child);
+ continue;
+ }
- GVariant * idv = g_variant_get_child_value(child, 0);
- gint id = g_variant_get_int32(idv);
- g_variant_unref(idv);
+ GVariant * idv = g_variant_get_child_value(child, 0);
+ gint id = g_variant_get_int32(idv);
+ g_variant_unref(idv);
- GVariant * properties = g_variant_get_child_value(child, 1);
+ GVariant * properties = g_variant_get_child_value(child, 1);
- properties_listener_t * listener = find_listener(listeners, 0, id);
- if (listener == NULL) {
- g_warning("Unable to find listener for ID %d", id);
+ properties_listener_t * listener = find_listener(listeners, 0, id);
+ if (listener == NULL) {
+ g_warning("Unable to find listener for ID %d", id);
+ g_variant_unref(properties);
+ g_variant_unref(child);
+ continue;
+ }
+
+ if (!listener->replied) {
+ listener->callback(properties, NULL, listener->user_data);
+ listener->replied = TRUE;
+ } else {
+ g_warning("Odd, we've already replied to the listener on ID %d", id);
+ }
g_variant_unref(properties);
g_variant_unref(child);
- continue;
}
-
- if (!listener->replied) {
- listener->callback(properties, NULL, listener->user_data);
- listener->replied = TRUE;
- } else {
- g_warning("Odd, we've already replied to the listener on ID %d", id);
- }
- g_variant_unref(properties);
- g_variant_unref(child);
+ g_variant_unref(parent);
+ g_variant_unref(params);
}
- g_variant_unref(parent);
- g_variant_unref(params);
/* 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) {
- g_warning("Generating properties error for: %d", listener->id);
- if (localerror == NULL) {
- g_set_error_literal(&localerror, error_domain(), 0, "Error getting properties for ID");
+ if (error == NULL && listeners->len > 0) {
+ 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) {
+ g_debug("Generating properties error for: %d", listener->id);
+ if (localerror == NULL) {
+ g_set_error_literal(&localerror, error_domain(), 0, "Error getting properties for ID");
+ }
+ listener->callback(NULL, localerror, listener->user_data);
}
- listener->callback(NULL, localerror, listener->user_data);
}
- }
- if (localerror != NULL) {
- g_error_free(localerror);
+ if (localerror != NULL) {
+ g_error_free(localerror);
+ }
}
-out:
/* Clean up */
g_array_free(listeners, TRUE);
g_object_unref(cbdata->client);
@@ -1084,6 +1146,7 @@ menuproxy_build_cb (GObject * object, GAsyncResult * res, gpointer user_data)
g_object_notify(G_OBJECT(user_data), DBUSMENU_CLIENT_PROP_TEXT_DIRECTION);
g_variant_unref(textdir);
+ textdir = NULL;
}
/* Check the status if available */
@@ -1099,6 +1162,7 @@ menuproxy_build_cb (GObject * object, GAsyncResult * res, gpointer user_data)
g_object_notify(G_OBJECT(user_data), DBUSMENU_CLIENT_PROP_STATUS);
g_variant_unref(status);
+ status = NULL;
}
/* Get the icon theme directories if available */
@@ -1113,6 +1177,33 @@ menuproxy_build_cb (GObject * object, GAsyncResult * res, gpointer user_data)
g_signal_emit(G_OBJECT(client), signals[ICON_THEME_DIRS], 0, priv->icon_dirs, TRUE);
g_variant_unref(icon_dirs);
+ icon_dirs = NULL;
+ }
+
+ /* Get the dbusmenu protocol version if available */
+ GVariant * version = g_dbus_proxy_get_cached_property(priv->menuproxy, "Version");
+ if (version != NULL) {
+ guint32 remote_version = 0;
+
+ if (g_variant_is_of_type(version, G_VARIANT_TYPE_UINT32)) {
+ remote_version = g_variant_get_uint32(version);
+ }
+
+ gboolean old_group = priv->group_events;
+ /* Figure out if we can group the events or not */
+ if (remote_version >= 3) {
+ priv->group_events = TRUE;
+ } else {
+ priv->group_events = FALSE;
+ }
+
+ /* Notify listeners if we changed the value */
+ if (old_group != priv->group_events) {
+ g_object_notify(G_OBJECT(client), DBUSMENU_CLIENT_PROP_GROUP_EVENTS);
+ }
+
+ g_variant_unref(version);
+ version = NULL;
}
/* If we get here, we don't need the DBus proxy */
@@ -1194,6 +1285,19 @@ menuproxy_prop_changed_cb (GDBusProxy * proxy, GVariant * properties, GStrv inva
priv->icon_dirs = g_variant_dup_strv(value, NULL);
dirs_changed = TRUE;
}
+ if (g_strcmp0(key, "Version") == 0) {
+ guint32 remote_version = 0;
+
+ if (g_variant_is_of_type(value, G_VARIANT_TYPE_UINT32)) {
+ remote_version = g_variant_get_uint32(value);
+ }
+
+ if (remote_version >= 3) {
+ priv->group_events = TRUE;
+ } else {
+ priv->group_events = FALSE;
+ }
+ }
}
if (olddir != priv->text_direction) {
@@ -1439,7 +1543,7 @@ menuitem_get_properties_new_cb (GVariant * properties, GError * error, gpointer
newItemPropData * propdata = (newItemPropData *)data;
if (error != NULL) {
- g_warning("Error getting properties on a new menuitem: %s", error->message);
+ g_debug("Error getting properties on a new menuitem: %s", error->message);
goto out;
}
@@ -1486,6 +1590,22 @@ out:
return;
}
+/* A function to work with an event_data_t and make sure it gets
+ free'd and in a terminal state. */
+static void
+event_data_end (event_data_t * edata, GError * error)
+{
+ g_signal_emit(edata->client, signals[EVENT_RESULT], 0, edata->menuitem, edata->event, edata->variant, edata->timestamp, error, TRUE);
+
+ g_variant_unref(edata->variant);
+ g_free(edata->event);
+ g_object_unref(edata->menuitem);
+ g_object_unref(edata->client);
+ g_free(edata);
+
+ return;
+}
+
/* Respond to the call function to make sure that the other side
got it, or print a warning. */
static void
@@ -1501,13 +1621,7 @@ menuitem_call_cb (GObject * proxy, GAsyncResult * res, gpointer userdata)
g_warning("Unable to call event '%s' on menu item %d: %s", edata->event, dbusmenu_menuitem_get_id(edata->menuitem), error->message);
}
- g_signal_emit(edata->client, signals[EVENT_RESULT], 0, edata->menuitem, edata->event, edata->variant, edata->timestamp, error, TRUE);
-
- g_variant_unref(edata->variant);
- g_free(edata->event);
- g_object_unref(edata->menuitem);
- g_object_unref(edata->client);
- g_free(edata);
+ event_data_end(edata, error);
if (G_UNLIKELY(error != NULL)) {
g_error_free(error);
@@ -1519,6 +1633,128 @@ menuitem_call_cb (GObject * proxy, GAsyncResult * res, gpointer userdata)
return;
}
+/* Looks at event_data_t structs to match an ID */
+gint
+event_data_find (gconstpointer data, gconstpointer user_data)
+{
+ event_data_t * edata = (event_data_t *)data;
+ gint id = GPOINTER_TO_INT(user_data);
+
+ if (edata->id == id) {
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+/* The callback from the dbus message to pass events to the
+ to the server en masse */
+static void
+event_group_cb (GObject * proxy, GAsyncResult * res, gpointer user_data)
+{
+ GQueue * events = (GQueue *)user_data;
+
+ GError * error = NULL;
+ GVariant * params;
+ params = g_dbus_proxy_call_finish(G_DBUS_PROXY(proxy), res, &error);
+
+ if (error != NULL) {
+ /* If we got an actual DBus error, we should just pass that
+ along and finish up */
+ g_queue_foreach(events, (GFunc)event_data_end, error);
+ g_queue_free(events);
+ events = NULL;
+ return;
+ }
+
+ gint id = 0;
+ GVariant * array = g_variant_get_child_value(params, 0);
+ GVariantIter iter;
+ g_variant_iter_init(&iter, array);
+
+ while (g_variant_iter_loop(&iter, "i", &id)) {
+ GList * item = g_queue_find_custom(events, GINT_TO_POINTER(id), event_data_find);
+
+ if (item != NULL) {
+ GError * iderror = g_error_new(error_domain(), ERROR_ID_NOT_FOUND, "Unable to find ID: %d", id);
+ event_data_end((event_data_t *)item->data, iderror);
+ g_queue_delete_link(events, item);
+ g_error_free(iderror);
+ }
+ }
+
+ g_variant_unref(array);
+ g_variant_unref(params);
+
+ /* If we have any left send non-error responses */
+ g_queue_foreach(events, (GFunc)event_data_end, NULL);
+ g_queue_free(events);
+ return;
+}
+
+/* Turn an event structure into the variant builder form */
+static void
+events_to_builder (gpointer data, gpointer user_data)
+{
+ event_data_t * edata = (event_data_t *)data;
+ GVariantBuilder * builder = (GVariantBuilder *)user_data;
+
+ GVariantBuilder tuple;
+ g_variant_builder_init(&tuple, G_VARIANT_TYPE_TUPLE);
+
+ g_variant_builder_add_value(&tuple, g_variant_new_int32(edata->id));
+ g_variant_builder_add_value(&tuple, g_variant_new_string(edata->event));
+ g_variant_builder_add_value(&tuple, g_variant_new_variant(edata->variant));
+ g_variant_builder_add_value(&tuple, g_variant_new_uint32(edata->timestamp));
+
+ GVariant * vtuple = g_variant_builder_end(&tuple);
+ g_variant_builder_add_value(builder, vtuple);
+ return;
+}
+
+/* Group all the events into a single Dbus message and send
+ that out */
+static gboolean
+event_idle_cb (gpointer user_data)
+{
+ g_return_val_if_fail(DBUSMENU_IS_CLIENT(user_data), FALSE);
+ DbusmenuClient * client = DBUSMENU_CLIENT(user_data);
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(user_data);
+
+ /* We use prepend for speed, but now we want to have them
+ in the order they were called incase that matters. */
+ GQueue * levents = priv->events_to_go;
+ priv->events_to_go = NULL;
+ priv->event_idle = 0;
+
+ GVariantBuilder array;
+ g_variant_builder_init(&array, G_VARIANT_TYPE("a(isvu)"));
+ g_queue_foreach(levents, events_to_builder, &array);
+ GVariant * vevents = g_variant_builder_end(&array);
+
+ if (g_signal_has_handler_pending (client, signals[EVENT_RESULT], 0, TRUE)) {
+ g_dbus_proxy_call(priv->menuproxy,
+ "EventGroup",
+ g_variant_new_tuple(&vevents, 1),
+ G_DBUS_CALL_FLAGS_NONE,
+ 1000, /* timeout */
+ NULL, /* cancellable */
+ event_group_cb, levents);
+ } else {
+ g_dbus_proxy_call(priv->menuproxy,
+ "EventGroup",
+ g_variant_new_tuple(&vevents, 1),
+ G_DBUS_CALL_FLAGS_NONE,
+ 1000, /* timeout */
+ NULL, /* cancellable */
+ NULL, NULL);
+ g_queue_foreach(levents, (GFunc)event_data_end, NULL);
+ g_queue_free(levents);
+ }
+
+ return FALSE;
+}
+
/* Sends the event over DBus to the server on the other side
of the bus. */
void
@@ -1538,7 +1774,20 @@ dbusmenu_client_send_event (DbusmenuClient * client, gint id, const gchar * name
variant = g_variant_new_int32(0);
}
+ /* Don't bother with the reply handling if nobody is watching... */
+ if (!priv->group_events && !g_signal_has_handler_pending (client, signals[EVENT_RESULT], 0, TRUE)) {
+ g_dbus_proxy_call(priv->menuproxy,
+ "Event",
+ g_variant_new("(isvu)", id, name, variant, timestamp),
+ G_DBUS_CALL_FLAGS_NONE,
+ 1000, /* timeout */
+ NULL, /* cancellable */
+ NULL, NULL);
+ return;
+ }
+
event_data_t * edata = g_new0(event_data_t, 1);
+ edata->id = id;
edata->client = client;
g_object_ref(client);
edata->menuitem = mi;
@@ -1548,25 +1797,181 @@ dbusmenu_client_send_event (DbusmenuClient * client, gint id, const gchar * name
edata->variant = variant;
g_variant_ref_sink(variant);
- g_dbus_proxy_call(priv->menuproxy,
- "Event",
- g_variant_new("(isvu)", id, name, variant, timestamp),
- G_DBUS_CALL_FLAGS_NONE,
- 1000, /* timeout */
- NULL, /* cancellable */
- menuitem_call_cb,
- edata);
+ if (!priv->group_events) {
+ g_dbus_proxy_call(priv->menuproxy,
+ "Event",
+ g_variant_new("(isvu)", id, name, variant, timestamp),
+ G_DBUS_CALL_FLAGS_NONE,
+ 1000, /* timeout */
+ NULL, /* cancellable */
+ menuitem_call_cb,
+ edata);
+ } else {
+ if (priv->events_to_go == NULL) {
+ priv->events_to_go = g_queue_new();
+ }
+
+ g_queue_push_tail(priv->events_to_go, edata);
+
+ if (priv->event_idle == 0) {
+ priv->event_idle = g_idle_add(event_idle_cb, client);
+ }
+ }
return;
}
typedef struct _about_to_show_t about_to_show_t;
struct _about_to_show_t {
+ gint id;
DbusmenuClient * client;
void (*cb) (gpointer data);
gpointer cb_data;
};
+/* Takes an about_to_show_t structure and calls the callback correctly
+ and updates the layout if needed. */
+static void
+about_to_show_finish (about_to_show_t * data, gboolean need_update)
+{
+ /* If we need to update, do that first. */
+ if (need_update) {
+ update_layout(data->client);
+ }
+
+ if (data->cb != NULL) {
+ data->cb(data->cb_data);
+ }
+
+ g_object_unref(data->client);
+ g_free(data);
+
+ return;
+}
+
+/* A little function to match prototypes and make sure to convert from
+ a pointer to an int correctly */
+static void
+about_to_show_finish_pntr (gpointer data, gpointer user_data)
+{
+ return about_to_show_finish((about_to_show_t *)data, GPOINTER_TO_INT(user_data));
+}
+
+/* Respond to the DBus message from sending a bunch of about-to-show events
+ to the server */
+static void
+about_to_show_group_cb (GObject * proxy, GAsyncResult * res, gpointer userdata)
+{
+ GError * error = NULL;
+ GQueue * showers = (GQueue *)userdata;
+ GVariant * params = NULL;
+
+ params = g_dbus_proxy_call_finish(G_DBUS_PROXY(proxy), res, &error);
+
+ if (error != NULL) {
+ g_warning("Unable to send about_to_show_group: %s", error->message);
+ /* Note: we're just ensuring only the callback gets called */
+ g_error_free(error);
+ error = NULL;
+ } else {
+ GVariant * updates = g_variant_get_child_value(params, 0);
+ GVariantIter iter;
+
+ /* Okay, so this is kinda interesting. We actually don't care which
+ entries asked us to update the structure, as it's quite simply a
+ single structure. So if we have any ask, we get the update once to
+ avoid itterating through all the structures. */
+ if (g_variant_iter_init(&iter, updates) > 0) {
+ about_to_show_t * first = (about_to_show_t *)g_queue_peek_head(showers);
+ update_layout(first->client);
+ }
+
+ g_variant_unref(updates);
+ g_variant_unref(params);
+ params = NULL;
+ }
+
+ g_queue_foreach(showers, about_to_show_finish_pntr, GINT_TO_POINTER(FALSE));
+ g_queue_free(showers);
+
+ return;
+}
+
+/* Check to see if this about to show entry has a callback associated
+ with it */
+static void
+about_to_show_idle_callbacks (gpointer data, gpointer user_data)
+{
+ about_to_show_t * abts = (about_to_show_t *)data;
+ gboolean * got_callbacks = (gboolean *)user_data;
+
+ if (abts->cb != NULL) {
+ *got_callbacks = TRUE;
+ }
+
+ return;
+}
+
+/* Take the ID out of the about to show structure and put it into the
+ variant builder */
+static void
+about_to_show_idle_ids (gpointer data, gpointer user_data)
+{
+ about_to_show_t * abts = (about_to_show_t *)data;
+ GVariantBuilder * builder = (GVariantBuilder *)user_data;
+
+ g_variant_builder_add_value(builder, g_variant_new_int32(abts->id));
+
+ return;
+}
+
+/* Function that gets called with all the queued about_to_show messages, let's
+ get these guys on the bus! */
+static gboolean
+about_to_show_idle (gpointer user_data)
+{
+ DbusmenuClient * client = DBUSMENU_CLIENT(user_data);
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+
+ /* Reset our object global props and take ownership of these entries */
+ priv->about_to_show_idle = 0;
+ GQueue * showers = priv->about_to_show_to_go;
+ priv->about_to_show_to_go = NULL;
+
+ /* Figure out if we've got any callbacks */
+ gboolean got_callbacks = FALSE;
+ g_queue_foreach(showers, about_to_show_idle_callbacks, &got_callbacks);
+
+ /* Build a list of the IDs */
+ GVariantBuilder idarray;
+ g_variant_builder_init(&idarray, G_VARIANT_TYPE("ai"));
+ g_queue_foreach(showers, about_to_show_idle_ids, &idarray);
+ GVariant * ids = g_variant_builder_end(&idarray);
+
+ /* Setup our callbacks */
+ GAsyncReadyCallback cb = NULL;
+ gpointer cb_data = NULL;
+ if (got_callbacks) {
+ cb = about_to_show_group_cb;
+ cb_data = showers;
+ } else {
+ g_queue_foreach(showers, about_to_show_finish_pntr, GINT_TO_POINTER(FALSE));
+ g_queue_free(showers);
+ }
+
+ /* Let's call it! */
+ g_dbus_proxy_call(priv->menuproxy,
+ "AboutToShowGroup",
+ g_variant_new_tuple(&ids, 1),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, /* timeout */
+ NULL, /* cancellable */
+ cb,
+ cb_data);
+
+ return FALSE;
+}
+
/* Reports errors and responds to update request that were a result
of sending the about to show signal. */
static void
@@ -1590,18 +1995,7 @@ about_to_show_cb (GObject * proxy, GAsyncResult * res, gpointer userdata)
g_variant_unref(params);
}
- /* If we need to update, do that first. */
- if (need_update) {
- update_layout(data->client);
- }
-
- if (data->cb != NULL) {
- data->cb(data->cb_data);
- }
-
- g_object_unref(data->client);
- g_free(data);
-
+ about_to_show_finish(data, need_update);
return;
}
@@ -1617,19 +2011,40 @@ dbusmenu_client_send_about_to_show(DbusmenuClient * client, gint id, void (*cb)(
g_return_if_fail(priv != NULL);
about_to_show_t * data = g_new0(about_to_show_t, 1);
+ data->id = id;
data->client = client;
data->cb = cb;
data->cb_data = cb_data;
g_object_ref(client);
- g_dbus_proxy_call(priv->menuproxy,
- "AboutToShow",
- g_variant_new("(i)", id),
- G_DBUS_CALL_FLAGS_NONE,
- -1, /* timeout */
- NULL, /* cancellable */
- about_to_show_cb,
- data);
+ if (priv->group_events) {
+ if (priv->about_to_show_to_go == NULL) {
+ priv->about_to_show_to_go = g_queue_new();
+ }
+
+ g_queue_push_tail(priv->about_to_show_to_go, data);
+
+ if (priv->about_to_show_idle == 0) {
+ priv->about_to_show_idle = g_idle_add(about_to_show_idle, client);
+ }
+ } else {
+ /* If there's no callback we don't need this data, let's
+ clean it up in a consistent way */
+ if (cb == NULL) {
+ about_to_show_finish(data, FALSE);
+ data = NULL;
+ }
+
+ g_dbus_proxy_call(priv->menuproxy,
+ "AboutToShow",
+ g_variant_new("(i)", id),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, /* timeout */
+ NULL, /* cancellable */
+ about_to_show_cb,
+ data);
+ }
+
return;
}
diff --git a/libdbusmenu-glib/client.h b/libdbusmenu-glib/client.h
index d0fca32..ae22863 100644
--- a/libdbusmenu-glib/client.h
+++ b/libdbusmenu-glib/client.h
@@ -105,6 +105,12 @@ G_BEGIN_DECLS
* String to access property #DbusmenuClient:text-direction
*/
#define DBUSMENU_CLIENT_PROP_TEXT_DIRECTION "text-direction"
+/**
+ * DBUSMENU_CLIENT_PROP_GROUP_EVENTS:
+ *
+ * String to access property #DbusmenuClient:group-events
+ */
+#define DBUSMENU_CLIENT_PROP_GROUP_EVENTS "group-events"
/**
* DBUSMENU_CLIENT_TYPES_DEFAULT:
diff --git a/libdbusmenu-glib/dbus-menu.xml b/libdbusmenu-glib/dbus-menu.xml
index 4b5a5d8..de6868c 100644
--- a/libdbusmenu-glib/dbus-menu.xml
+++ b/libdbusmenu-glib/dbus-menu.xml
@@ -326,6 +326,26 @@ License version 3 and version 2.1 along with this program. If not, see
</arg>
</method>
+ <method name="EventGroup">
+ <dox:d>
+ Used to pass a set of events as a single message for possibily several
+ different menuitems. This is done to optimize DBus traffic.
+ </dox:d>
+ <arg type="a(isvu)" name="events" direction="in">
+ <dox:d>
+ An array of all the events that should be passed. This tuple should
+ match the parameters of the 'Event' signal. Which is roughly:
+ id, eventID, data and timestamp.
+ </dox:d>
+ </arg>
+ <arg type="ai" name="idErrors" direction="out">
+ <dox:d>
+ I list of menuitem IDs that couldn't be found. If none of the ones
+ in the list can be found, a DBus error is returned.
+ </dox:d>
+ </arg>
+ </method>
+
<method name="AboutToShow">
<dox:d>
This is called by the applet to notify the application that it is about
@@ -343,6 +363,32 @@ License version 3 and version 2.1 along with this program. If not, see
</arg>
</method>
+ <method name="AboutToShowGroup">
+ <dox:d>
+ A function to tell several menus being shown that they are about to
+ be shown to the user. This is likely only useful for programitc purposes
+ so while the return values are returned, in general, the singular function
+ should be used in most user interacation scenarios.
+ </dox:d>
+ <arg type="ai" name="ids" direction="in">
+ <dox:d>
+ The IDs of the menu items who's submenus are being shown.
+ </dox:d>
+ </arg>
+ <arg type="ai" name="updatesNeeded" direction="out">
+ <dox:d>
+ The IDs of the menus that need updates. Note: if no update information
+ is needed the DBus message should set the no reply flag.
+ </dox:d>
+ </arg>
+ <arg type="ai" name="idErrors" direction="out">
+ <dox:d>
+ I list of menuitem IDs that couldn't be found. If none of the ones
+ in the list can be found, a DBus error is returned.
+ </dox:d>
+ </arg>
+ </method>
+
<!-- Signals -->
<signal name="ItemsPropertiesUpdated">
<dox:d>
diff --git a/libdbusmenu-glib/menuitem.c b/libdbusmenu-glib/menuitem.c
index 18db4ef..c81c36e 100644
--- a/libdbusmenu-glib/menuitem.c
+++ b/libdbusmenu-glib/menuitem.c
@@ -1253,7 +1253,7 @@ dbusmenu_menuitem_property_set_variant (DbusmenuMenuitem * mi, const gchar * pro
becuse it has been unref'd when replaced in the hash
table. But the fact that there was a value is
the imporant part. */
- if (!inhash || replaced) {
+ if (replaced) {
GVariant * signalval = value;
if (signalval == NULL) {
@@ -1462,8 +1462,8 @@ dbusmenu_menuitem_property_remove (DbusmenuMenuitem * mi, const gchar * property
* by the menuitem but the list is not and should be freed using
* g_list_free() when the calling function is done with it.
*
- * Return value: (transfer container): A list of strings or NULL if there are
- * none.
+ * Return value: (transfer container) (element-type utf8): A list of
+ * strings or NULL if there are none.
*/
GList *
dbusmenu_menuitem_properties_list (DbusmenuMenuitem * mi)
diff --git a/libdbusmenu-glib/server.c b/libdbusmenu-glib/server.c
index 524e777..091b243 100644
--- a/libdbusmenu-glib/server.c
+++ b/libdbusmenu-glib/server.c
@@ -42,7 +42,7 @@ License version 3 and version 2.1 along with this program. If not, see
static void layout_update_signal (DbusmenuServer * server);
-#define DBUSMENU_VERSION_NUMBER 2
+#define DBUSMENU_VERSION_NUMBER 3
#define DBUSMENU_INTERFACE "com.canonical.dbusmenu"
/* Privates, I'll show you mine... */
@@ -64,6 +64,8 @@ struct _DbusmenuServerPrivate
GArray * prop_array;
guint property_idle;
+
+ GHashTable * lookup_cache;
};
#define DBUSMENU_SERVER_GET_PRIVATE(o) (DBUSMENU_SERVER(o)->priv)
@@ -116,7 +118,9 @@ enum {
METHOD_GET_PROPERTY,
METHOD_GET_PROPERTIES,
METHOD_EVENT,
+ METHOD_EVENT_GROUP,
METHOD_ABOUT_TO_SHOW,
+ METHOD_ABOUT_TO_SHOW_GROUP,
/* Counter, do not remove! */
METHOD_COUNT
};
@@ -189,9 +193,15 @@ static void bus_get_properties (DbusmenuServer * server,
static void bus_event (DbusmenuServer * server,
GVariant * params,
GDBusMethodInvocation * invocation);
+static void bus_event_group (DbusmenuServer * server,
+ GVariant * params,
+ GDBusMethodInvocation * invocation);
static void bus_about_to_show (DbusmenuServer * server,
GVariant * params,
GDBusMethodInvocation * invocation);
+static void bus_about_to_show_group (DbusmenuServer * server,
+ GVariant * params,
+ GDBusMethodInvocation * invocation);
static void find_servers_cb (GDBusConnection * connection,
const gchar * sender,
const gchar * path,
@@ -356,9 +366,15 @@ dbusmenu_server_class_init (DbusmenuServerClass *class)
dbusmenu_method_table[METHOD_EVENT].interned_name = g_intern_static_string("Event");
dbusmenu_method_table[METHOD_EVENT].func = bus_event;
+ dbusmenu_method_table[METHOD_EVENT_GROUP].interned_name = g_intern_static_string("EventGroup");
+ dbusmenu_method_table[METHOD_EVENT_GROUP].func = bus_event_group;
+
dbusmenu_method_table[METHOD_ABOUT_TO_SHOW].interned_name = g_intern_static_string("AboutToShow");
dbusmenu_method_table[METHOD_ABOUT_TO_SHOW].func = bus_about_to_show;
+ dbusmenu_method_table[METHOD_ABOUT_TO_SHOW_GROUP].interned_name = g_intern_static_string("AboutToShowGroup");
+ dbusmenu_method_table[METHOD_ABOUT_TO_SHOW_GROUP].func = bus_about_to_show_group;
+
return;
}
@@ -378,6 +394,8 @@ dbusmenu_server_init (DbusmenuServer *self)
priv->find_server_signal = 0;
priv->dbus_registration = 0;
+ priv->lookup_cache = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_object_unref);
+
default_text_direction(self);
priv->status = DBUSMENU_STATUS_NORMAL;
priv->icon_dirs = NULL;
@@ -450,10 +468,50 @@ dbusmenu_server_finalize (GObject *object)
priv->icon_dirs = NULL;
}
+ if (priv->lookup_cache) {
+ g_hash_table_destroy(priv->lookup_cache);
+ priv->lookup_cache = NULL;
+ }
+
G_OBJECT_CLASS (dbusmenu_server_parent_class)->finalize (object);
return;
}
+static DbusmenuMenuitem *
+lookup_menuitem_by_id (DbusmenuServer * server, gint id)
+{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
+ DbusmenuMenuitem *res = (DbusmenuMenuitem *) g_hash_table_lookup(priv->lookup_cache, GINT_TO_POINTER(id));
+ if (!res && id == 0) {
+ return priv->root;
+ }
+
+ return res;
+}
+
+static void
+cache_remove_entries_for_menuitem (GHashTable * cache, DbusmenuMenuitem * item)
+{
+ g_hash_table_remove(cache, GINT_TO_POINTER(dbusmenu_menuitem_get_id(item)));
+
+ GList *child, *children = dbusmenu_menuitem_get_children(item);
+ for (child = children; child != NULL; child = child->next) {
+ cache_remove_entries_for_menuitem(cache, child->data);
+ }
+}
+
+static void
+cache_add_entries_for_menuitem (GHashTable * cache, DbusmenuMenuitem * item)
+{
+ g_hash_table_insert(cache, GINT_TO_POINTER(dbusmenu_menuitem_get_id(item)), g_object_ref(item));
+
+ GList *child, *children = dbusmenu_menuitem_get_children(item);
+ for (child = children; child != NULL; child = child->next) {
+ cache_add_entries_for_menuitem(cache, child->data);
+ }
+}
+
static void
set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
{
@@ -480,6 +538,7 @@ set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
if (priv->root != NULL) {
dbusmenu_menuitem_foreach(priv->root, menuitem_signals_remove, obj);
dbusmenu_menuitem_set_root(priv->root, FALSE);
+ cache_remove_entries_for_menuitem(priv->lookup_cache, priv->root);
GList * properties = dbusmenu_menuitem_properties_list(priv->root);
GList * iter;
@@ -495,6 +554,7 @@ set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
priv->root = DBUSMENU_MENUITEM(g_value_get_object(value));
if (priv->root != NULL) {
g_object_ref(G_OBJECT(priv->root));
+ cache_add_entries_for_menuitem(priv->lookup_cache, priv->root);
dbusmenu_menuitem_set_root(priv->root, TRUE);
dbusmenu_menuitem_foreach(priv->root, menuitem_signals_create, obj);
@@ -1161,6 +1221,7 @@ static void
menuitem_child_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint pos, DbusmenuServer * server)
{
menuitem_signals_create(child, server);
+ cache_add_entries_for_menuitem(server->priv->lookup_cache, child);
g_list_foreach(dbusmenu_menuitem_get_children(child), added_check_children, server);
layout_update_signal(server);
@@ -1171,6 +1232,7 @@ static void
menuitem_child_removed (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, DbusmenuServer * server)
{
menuitem_signals_remove(child, server);
+ cache_remove_entries_for_menuitem(server->priv->lookup_cache, child);
layout_update_signal(server);
return;
}
@@ -1259,7 +1321,7 @@ bus_get_layout (DbusmenuServer * server, GVariant * params, GDBusMethodInvocatio
GVariant * items = NULL;
if (priv->root != NULL) {
- DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, parent);
+ DbusmenuMenuitem * mi = lookup_menuitem_by_id(server, parent);
if (mi != NULL) {
items = dbusmenu_menuitem_build_variant(mi, props, recurse);
@@ -1318,7 +1380,7 @@ bus_get_property (DbusmenuServer * server, GVariant * params, GDBusMethodInvocat
g_variant_get(params, "(i&s)", &id, &property);
- DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
+ DbusmenuMenuitem * mi = lookup_menuitem_by_id(server, id);
if (mi == NULL) {
g_dbus_method_invocation_return_error(invocation,
@@ -1361,7 +1423,7 @@ bus_get_properties (DbusmenuServer * server, GVariant * params, GDBusMethodInvoc
gint32 id;
g_variant_get(params, "(i)", &id);
- DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
+ DbusmenuMenuitem * mi = lookup_menuitem_by_id(server, id);
if (mi == NULL) {
g_dbus_method_invocation_return_error(invocation,
@@ -1424,7 +1486,7 @@ bus_get_group_properties (DbusmenuServer * server, GVariant * params, GDBusMetho
gint32 id;
while (g_variant_iter_loop(ids, "i", &id)) {
- DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
+ DbusmenuMenuitem * mi = lookup_menuitem_by_id(server, id);
if (mi == NULL) continue;
if (!builder_init) {
@@ -1523,7 +1585,7 @@ bus_get_children (DbusmenuServer * server, GVariant * params, GDBusMethodInvocat
return;
}
- DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
+ DbusmenuMenuitem * mi = lookup_menuitem_by_id(server, id);
if (mi == NULL) {
g_dbus_method_invocation_return_error(invocation,
@@ -1585,6 +1647,28 @@ event_local_handler (gpointer user_data)
return FALSE;
}
+/* The core menu finding and doing the work part of the two
+ event functions */
+static gboolean
+bus_event_core (DbusmenuServer * server, gint32 id, gchar * event_type, GVariant * data, guint32 timestamp)
+{
+ DbusmenuMenuitem * mi = lookup_menuitem_by_id(server, id);
+
+ if (mi == NULL) {
+ return FALSE;
+ }
+
+ idle_event_t * event_data = g_new0(idle_event_t, 1);
+ event_data->mi = g_object_ref(mi);
+ event_data->eventid = g_strdup(event_type);
+ event_data->timestamp = timestamp;
+ event_data->variant = g_variant_ref(data);
+
+ g_timeout_add(0, event_local_handler, event_data);
+
+ return TRUE;
+}
+
/* Handles the events coming off of DBus */
static void
bus_event (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
@@ -1606,32 +1690,94 @@ bus_event (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * i
g_variant_get(params, "(isvu)", &id, &etype, &data, &ts);
- DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
-
- if (mi == NULL) {
+ if (!bus_event_core(server, id, etype, data, ts)) {
g_dbus_method_invocation_return_error(invocation,
error_quark(),
INVALID_MENUITEM_ID,
"The ID supplied %d does not refer to a menu item we have",
id);
- g_free(etype);
- g_variant_unref(data);
-
} else {
- idle_event_t * event_data = g_new0(idle_event_t, 1);
- event_data->mi = g_object_ref(mi);
- event_data->eventid = etype; /* give away our allocation */
- event_data->timestamp = ts;
- event_data->variant = data; /* give away our reference */
+ if (~g_dbus_message_get_flags (g_dbus_method_invocation_get_message (invocation)) & G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED) {
+ g_dbus_method_invocation_return_value(invocation, NULL);
+ }
+ }
+
+ g_free(etype);
+ g_variant_unref(data);
+
+ return;
+}
+
+/* Respond to the event group method that will send events to a
+ variety of menuitems */
+static void
+bus_event_group (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
+{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
+ if (priv->root == NULL) {
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ NO_VALID_LAYOUT,
+ "There currently isn't a layout in this server");
+ return;
+ }
+
+ GVariant * events = g_variant_get_child_value(params, 0);
+ gint32 id;
+ gchar *etype;
+ GVariant *data;
+ guint32 ts;
+ GVariantIter iter;
+ GVariantBuilder builder;
- g_timeout_add(0, event_local_handler, event_data);
+ g_variant_iter_init(&iter, events);
+ g_variant_builder_init(&builder, G_VARIANT_TYPE("ai"));
+ gboolean gotone = FALSE;
- g_dbus_method_invocation_return_value(invocation, NULL);
+ while (g_variant_iter_loop(&iter, "(isvu)", &id, &etype, &data, &ts)) {
+ if (bus_event_core(server, id, etype, data, ts)) {
+ gotone = TRUE;
+ } else {
+ g_variant_builder_add_value(&builder, g_variant_new_int32(id));
+ }
+ }
+
+ GVariant * errors = g_variant_builder_end(&builder);
+ g_variant_ref_sink(errors);
+
+ if (gotone) {
+ if (~g_dbus_message_get_flags (g_dbus_method_invocation_get_message (invocation)) & G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED) {
+ g_dbus_method_invocation_return_value(invocation, g_variant_new_tuple(&errors, 1));
+ }
+ } else {
+ gchar * ids = g_variant_print(errors, FALSE);
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ INVALID_MENUITEM_ID,
+ "The IDs supplied '%s' do not refer to any menu items we have",
+ ids);
+ g_free(ids);
}
+ g_variant_unref(errors);
+ g_variant_unref(events);
+
return;
}
+/* Does the about-to-show in an idle loop so we don't block things */
+/* NOTE: this only works so easily as we don't return the value, if we
+ were to do that it would get more complex. */
+static gboolean
+bus_about_to_show_idle (gpointer user_data)
+{
+ DbusmenuMenuitem * mi = DBUSMENU_MENUITEM(user_data);
+ dbusmenu_menuitem_send_about_to_show(mi, NULL, NULL);
+ g_object_unref(mi);
+ return FALSE;
+}
+
/* Recieve the About To Show function. Pass it to our menu item. */
static void
bus_about_to_show (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
@@ -1648,7 +1794,7 @@ bus_about_to_show (DbusmenuServer * server, GVariant * params, GDBusMethodInvoca
gint32 id;
g_variant_get(params, "(i)", &id);
- DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
+ DbusmenuMenuitem * mi = lookup_menuitem_by_id(server, id);
if (mi == NULL) {
g_dbus_method_invocation_return_error(invocation,
@@ -1659,7 +1805,7 @@ bus_about_to_show (DbusmenuServer * server, GVariant * params, GDBusMethodInvoca
return;
}
- dbusmenu_menuitem_send_about_to_show(mi, NULL, NULL);
+ g_timeout_add(0, bus_about_to_show_idle, g_object_ref(mi));
/* GTK+ does not support about-to-show concept for now */
g_dbus_method_invocation_return_value(invocation,
@@ -1667,6 +1813,69 @@ bus_about_to_show (DbusmenuServer * server, GVariant * params, GDBusMethodInvoca
return;
}
+/* Handle the about to show on a set of menus and tell all of them that
+ we love them */
+static void
+bus_about_to_show_group (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
+{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
+ if (priv->root == NULL) {
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ NO_VALID_LAYOUT,
+ "There currently isn't a layout in this server");
+ return;
+ }
+
+ gint32 id;
+ GVariantIter iter;
+ GVariantBuilder builder;
+
+ g_variant_iter_init(&iter, params);
+ g_variant_builder_init(&builder, G_VARIANT_TYPE("ai"));
+ gboolean gotone = FALSE;
+
+ while (g_variant_iter_loop(&iter, "(i)", &id)) {
+ DbusmenuMenuitem * mi = lookup_menuitem_by_id(server, id);
+ if (mi != NULL) {
+ g_timeout_add(0, bus_about_to_show_idle, g_object_ref(mi));
+ gotone = TRUE;
+ } else {
+ g_variant_builder_add_value(&builder, g_variant_new_int32(id));
+ }
+ }
+
+ GVariant * errors = g_variant_builder_end(&builder);
+ g_variant_ref_sink(errors);
+
+ if (gotone) {
+ if (~g_dbus_message_get_flags (g_dbus_method_invocation_get_message (invocation)) & G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED) {
+ GVariantBuilder tuple;
+ g_variant_builder_init(&tuple, G_VARIANT_TYPE_TUPLE);
+
+ /* Updates needed */
+ g_variant_builder_add_value(&tuple, g_variant_new_array(G_VARIANT_TYPE_INT32, NULL, 0));
+ /* Errors */
+ g_variant_builder_add_value(&tuple, errors);
+
+ g_dbus_method_invocation_return_value(invocation, g_variant_builder_end(&tuple));
+ }
+ } else {
+ gchar * ids = g_variant_print(errors, FALSE);
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ INVALID_MENUITEM_ID,
+ "The IDs supplied '%s' do not refer to any menu items we have",
+ ids);
+ g_free(ids);
+ }
+
+ g_variant_unref(errors);
+
+ return;
+}
+
/* Public Interface */
/**
dbusmenu_server_new: