Merge lp:~ted/libdbusmenu/event-grouping into lp:libdbusmenu/0.6

Proposed by Ted Gould
Status: Merged
Approved by: Charles Kerr
Approved revision: 428
Merged at revision: 400
Proposed branch: lp:~ted/libdbusmenu/event-grouping
Merge into: lp:libdbusmenu/0.6
Diff against target: 1250 lines (+843/-62)
8 files modified
.bzrignore (+2/-0)
docs/libdbusmenu-glib/reference/libdbusmenu-glib-sections.txt (+1/-0)
libdbusmenu-glib/client.c (+446/-45)
libdbusmenu-glib/client.h (+6/-0)
libdbusmenu-glib/dbus-menu.xml (+46/-0)
libdbusmenu-glib/server.c (+176/-17)
tests/Makefile.am (+24/-0)
tests/test-glib-events-nogroup-client.c (+142/-0)
To merge this branch: bzr merge lp:~ted/libdbusmenu/event-grouping
Reviewer Review Type Date Requested Status
Charles Kerr (community) Approve
Review via email: mp+100690@code.launchpad.net

Description of the change

Takes all of the events on a client and puts them into single dbus messages to be sent to the server. It also does the same thing for about-to-show messages. Both sets of messages are reserialized on either side to make callers unable to notice the difference.

To post a comment you must log in.
Revision history for this message
Allison Karlitskaya (desrt) wrote :

In broad strokes, this is pretty much what I had in mind, but the devil is in the details.

A few suggestions:

 - use a GQueue instead of prepend-and-reversing a list

 - the flag to enable/disable event-grouping is awkward. By my read, it gets set according to the remote version but can be manually changed by the user but may change again depending on when we get the version number from the remote? Also: I may want to group open/close but not activations. It might be better (not to mention vastly reducing the lines of code in this patch) to have an explicit API to send a message to a group as specified by the user? I guess it could take a GList of DbusmenuMenuItem instances? In the case that it's not supported, the flattening would be done on the client side. It's not a totally awesome API but it would be a lot less complicated and I'd be willing to do the collecting of like-queries in the HUD.

 - the above API would also allow simplifying the dbus method signature since there would only be the possibility of a single event-type and a single timestamp (and only multiple IDs)

 - when doing mass-dispatching of events I'm unlikely to care about errors. I wouldn't bother reporting them. In fact, the only user of this API (the HUD) will have set the "don't send reply" flag.

 - it's very possible that I might send some events and then unref the client before returning to the mainloop (or by returning to the mainloop to find an event source with a higher priority than your idle that causes the unref).

 - the /* Get the icon theme directories if available */ comment is intensely confusing (copy/paste error?)

 - some of your use of GVariant is a bit odd (although you could argue a style of prefering to avoid varargs use):

   - typically you use g_variant_new("()") for GDBus argument lists -- it helps to document the signature of the method call at the site of use

   - g_variant_builder_add_value(x, g_variant_new_*()) is odd -- this is what g_variant_builder_add() is for

  - creating a tuple builder, loading it up with 4 values, ending it and then packing it inside of another builder can be replaced with a single g_variant_builder_add (x, "(....)", ...) line

 - glad to see you took care to preserve calling gdbus with NULL async callbacks in the don't-care case :)

 - the receiving side looks good except for the questionable use of an idle/timeout(0). Why did you do that? You're already in a rather direct dispatch from the mainloop -- punting to another idle here gains you nothing except for additional complexity and may also introduce ordering issues (ie: the next dbus message to arrive may end up being processed before the idle for your group-of-events that arrived first).

Revision history for this message
Allison Karlitskaya (desrt) wrote :

Totally crazy outside-the-box type of idea (which I regret only having thought of just now): what if we just had a D-Bus API for "show all of the submenus that you know about" and the service side figured out which menus to deliver that to locally...

Revision history for this message
Allison Karlitskaya (desrt) wrote :

The potential race caused by a "new submenu has appeared" signal crossing the bus at the same time as the "show all the submenus" method call presents an annoying problem. The API would rather have to be more like "enter into the always-showing mode" and "leave the always-showing mode" so that new items that got added on the client side would automatically have 'open' delivered to them without additional intervention from the HUD.

Revision history for this message
Ted Gould (ted) wrote :
Download full text (3.7 KiB)

On Wed, 2012-04-04 at 00:41 +0000, Ryan Lortie wrote:
> - use a GQueue instead of prepend-and-reversing a list

Eh, okay. r427

> - the flag to enable/disable event-grouping is awkward. By my read,
> it gets set according to the remote version but can be manually
> changed by the user but may change again depending on when we get the
> version number from the remote?

The flag is only there so that the test suite can test both cases to
ensure there are no regressions. Since it is possible someone would
want to use it, I saw no reason to keep it private. There's really no
use-case to send singular events if the grouping is available.

> - when doing mass-dispatching of events I'm unlikely to care about errors.
> I wouldn't bother reporting them. In fact, the only user of this API (the
> HUD) will have set the "don't send reply" flag.

Sure, but we need to maintain API compatibility no matter how someone
called the API it needs to work the same. It should be transparent to
upper level applications whether there is grouping or not. It's a
technical detail that can, and is, hidden in the library.

> - it's very possible that I might send some events and then unref the
> client before returning to the mainloop (or by returning to the
> mainloop to find an event source with a higher priority than your idle
> that causes the unref).

I'm unsure of what you're saying here. We do keep a ref to the client
so I think that case is taken care of... do you see a case where that's
not true?

> - the /* Get the icon theme directories if available */ comment is
> intensely confusing (copy/paste error?)

Fixed. r428

> - some of your use of GVariant is a bit odd (although you could argue
> a style of prefering to avoid varargs use):

Exactly. And I will :-) I like it when the compiler tells me when I've
done thing wrong not bug reports from apport :-)

> - the receiving side looks good except for the questionable use of an
> idle/timeout(0). Why did you do that? You're already in a rather
> direct dispatch from the mainloop -- punting to another idle here
> gains you nothing except for additional complexity and may also
> introduce ordering issues (ie: the next dbus message to arrive may end
> up being processed before the idle for your group-of-events that
> arrived first).

The problem that we found is that in some cases we weren't giving enough
time for the mainloop to process and this would make application appear
"hung" to some users. Effectively appmenu-gtk is a leach on mainloop
time and we're trying to be as light a pull on that as possible. I
believe this will be especially the case with the number of events the
HUD is sending.

> Totally crazy outside-the-box type of idea (which I regret only having
> thought of just now): what if we just had a D-Bus API for "show all of
> the submenus that you know about" and the service side figured out
> which menus to deliver that to locally...

We kinda need to stay with as much as possible the events that already
exist. Adding a new program flow through all the various dbusmenu
implementations would be difficult and risky.

> The potential race caused by a "new submenu has appeared" signa...

Read more...

lp:~ted/libdbusmenu/event-grouping updated
427. By Ted Gould

Switching to GQueue

428. By Ted Gould

Fixing a cut-and-paste comment

Revision history for this message
Allison Karlitskaya (desrt) wrote :

fwiw, It's possible to use GQueue embedded in the parent struct. This is when it's at its best. I can understand your lack of enthusiasm for switching to it when you have to manually g_queue_new()/g_queue_free() :)

About the API for grouping: I guess I was falsely considering that the user would be responsible for setting the grouping flag for themselves to opt-in to the feature. I didn't conisder that it would be enabled for everyone on the basis of version (which I only discovered after more reading). It sort of makes more sense now that I consider that.

Revision history for this message
Ted Gould (ted) wrote :

On Wed, 2012-04-04 at 12:56 +0000, Ryan Lortie wrote:
> fwiw, It's possible to use GQueue embedded in the parent struct. This
> is when it's at its best. I can understand your lack of enthusiasm
> for switching to it when you have to manually
> g_queue_new()/g_queue_free() :)

The problem there is that a new queue always needs to be created when
the dbus message is sent and the old queue attached to the callback. So
we have to allocate the queues.

Revision history for this message
Charles Kerr (charlesk) wrote :

I agree with some of the discussion here but the remaining refinements aren't blockers for today's release.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file '.bzrignore'
--- .bzrignore 2011-08-22 19:31:31 +0000
+++ .bzrignore 2012-04-04 04:28:21 +0000
@@ -257,3 +257,5 @@
257genericmenuitem-enum-types.c257genericmenuitem-enum-types.c
258genericmenuitem-enum-types.h258genericmenuitem-enum-types.h
259libdbusmenu_gtk3_la-genericmenuitem-enum-types.lo259libdbusmenu_gtk3_la-genericmenuitem-enum-types.lo
260test-glib-events-nogroup
261test-glib-events-nogroup-client
260262
=== modified file 'docs/libdbusmenu-glib/reference/libdbusmenu-glib-sections.txt'
--- docs/libdbusmenu-glib/reference/libdbusmenu-glib-sections.txt 2012-01-31 04:56:12 +0000
+++ docs/libdbusmenu-glib/reference/libdbusmenu-glib-sections.txt 2012-04-04 04:28:21 +0000
@@ -9,6 +9,7 @@
9DBUSMENU_CLIENT_SIGNAL_ICON_THEME_DIRS_CHANGED9DBUSMENU_CLIENT_SIGNAL_ICON_THEME_DIRS_CHANGED
10DBUSMENU_CLIENT_PROP_DBUS_NAME10DBUSMENU_CLIENT_PROP_DBUS_NAME
11DBUSMENU_CLIENT_PROP_DBUS_OBJECT11DBUSMENU_CLIENT_PROP_DBUS_OBJECT
12DBUSMENU_CLIENT_PROP_GROUP_EVENTS
12DBUSMENU_CLIENT_PROP_STATUS13DBUSMENU_CLIENT_PROP_STATUS
13DBUSMENU_CLIENT_PROP_TEXT_DIRECTION14DBUSMENU_CLIENT_PROP_TEXT_DIRECTION
14DBUSMENU_CLIENT_TYPES_DEFAULT15DBUSMENU_CLIENT_TYPES_DEFAULT
1516
=== modified file 'libdbusmenu-glib/client.c'
--- libdbusmenu-glib/client.c 2012-03-16 17:26:58 +0000
+++ libdbusmenu-glib/client.c 2012-04-04 04:28:21 +0000
@@ -52,7 +52,8 @@
52 PROP_DBUSOBJECT,52 PROP_DBUSOBJECT,
53 PROP_DBUSNAME,53 PROP_DBUSNAME,
54 PROP_STATUS,54 PROP_STATUS,
55 PROP_TEXT_DIRECTION55 PROP_TEXT_DIRECTION,
56 PROP_GROUP_EVENTS
56};57};
5758
58/* Signals */59/* Signals */
@@ -66,6 +67,12 @@
66 LAST_SIGNAL67 LAST_SIGNAL
67};68};
6869
70/* Errors */
71enum {
72 ERROR_DISPOSAL,
73 ERROR_ID_NOT_FOUND
74};
75
69typedef void (*properties_func) (GVariant * properties, GError * error, gpointer user_data);76typedef void (*properties_func) (GVariant * properties, GError * error, gpointer user_data);
7077
71static guint signals[LAST_SIGNAL] = { 0 };78static guint signals[LAST_SIGNAL] = { 0 };
@@ -100,6 +107,13 @@
100 DbusmenuTextDirection text_direction;107 DbusmenuTextDirection text_direction;
101 DbusmenuStatus status;108 DbusmenuStatus status;
102 GStrv icon_dirs;109 GStrv icon_dirs;
110
111 gboolean group_events;
112 guint event_idle;
113 GQueue * events_to_go; /* type: event_data_t * */
114
115 guint about_to_show_idle;
116 GQueue * about_to_show_to_go; /* type: about_to_show_t * */
103};117};
104118
105typedef struct _newItemPropData newItemPropData;119typedef struct _newItemPropData newItemPropData;
@@ -120,6 +134,7 @@
120134
121typedef struct _event_data_t event_data_t;135typedef struct _event_data_t event_data_t;
122struct _event_data_t {136struct _event_data_t {
137 gint id;
123 DbusmenuClient * client;138 DbusmenuClient * client;
124 DbusmenuMenuitem * menuitem;139 DbusmenuMenuitem * menuitem;
125 gchar * event;140 gchar * event;
@@ -171,6 +186,8 @@
171static void menuproxy_name_changed_cb (GObject * object, GParamSpec * pspec, gpointer user_data);186static void menuproxy_name_changed_cb (GObject * object, GParamSpec * pspec, gpointer user_data);
172static void menuproxy_signal_cb (GDBusProxy * proxy, gchar * sender, gchar * signal, GVariant * params, gpointer user_data);187static void menuproxy_signal_cb (GDBusProxy * proxy, gchar * sender, gchar * signal, GVariant * params, gpointer user_data);
173static void type_handler_destroy (gpointer user_data);188static void type_handler_destroy (gpointer user_data);
189static void event_data_end (event_data_t * eventd, GError * error);
190static void about_to_show_finish_pntr (gpointer data, gpointer user_data);
174191
175/* Globals */192/* Globals */
176static GDBusNodeInfo * dbusmenu_node_info = NULL;193static GDBusNodeInfo * dbusmenu_node_info = NULL;
@@ -309,6 +326,10 @@
309 "Signals which direction the default text direction is for the menus",326 "Signals which direction the default text direction is for the menus",
310 DBUSMENU_TYPE_TEXT_DIRECTION, DBUSMENU_TEXT_DIRECTION_NONE,327 DBUSMENU_TYPE_TEXT_DIRECTION, DBUSMENU_TEXT_DIRECTION_NONE,
311 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));328 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
329 g_object_class_install_property (object_class, PROP_GROUP_EVENTS,
330 g_param_spec_boolean(DBUSMENU_CLIENT_PROP_GROUP_EVENTS, "Whether or not multiple events should be grouped",
331 "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.",
332 FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
312333
313 if (dbusmenu_node_info == NULL) {334 if (dbusmenu_node_info == NULL) {
314 GError * error = NULL;335 GError * error = NULL;
@@ -380,6 +401,13 @@
380 priv->status = DBUSMENU_STATUS_NORMAL;401 priv->status = DBUSMENU_STATUS_NORMAL;
381 priv->icon_dirs = NULL;402 priv->icon_dirs = NULL;
382403
404 priv->group_events = FALSE;
405 priv->event_idle = 0;
406 priv->events_to_go = NULL;
407
408 priv->about_to_show_idle = 0;
409 priv->about_to_show_to_go = NULL;
410
383 return;411 return;
384}412}
385413
@@ -393,6 +421,32 @@
393 priv->delayed_idle = 0;421 priv->delayed_idle = 0;
394 }422 }
395423
424 if (priv->event_idle != 0) {
425 g_source_remove(priv->event_idle);
426 priv->event_idle = 0;
427 }
428
429 if (priv->about_to_show_idle != 0) {
430 g_source_remove(priv->about_to_show_idle);
431 priv->about_to_show_idle = 0;
432 }
433
434 if (priv->events_to_go != NULL) {
435 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.");
436 GError * error = g_error_new_literal(error_domain(), ERROR_DISPOSAL, "Client disposed before event signal returned");
437 g_queue_foreach(priv->events_to_go, (GFunc)event_data_end, error);
438 g_queue_free(priv->events_to_go);
439 priv->events_to_go = NULL;
440 g_error_free(error);
441 }
442
443 if (priv->about_to_show_to_go != NULL) {
444 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.");
445 g_queue_foreach(priv->about_to_show_to_go, about_to_show_finish_pntr, GINT_TO_POINTER(FALSE));
446 g_queue_free(priv->about_to_show_to_go);
447 priv->about_to_show_to_go = NULL;
448 }
449
396 /* Only used for queueing up a new command, so we can450 /* Only used for queueing up a new command, so we can
397 just drop this array. */451 just drop this array. */
398 if (priv->delayed_property_list != NULL) {452 if (priv->delayed_property_list != NULL) {
@@ -517,6 +571,9 @@
517 build_proxies(DBUSMENU_CLIENT(obj));571 build_proxies(DBUSMENU_CLIENT(obj));
518 }572 }
519 break;573 break;
574 case PROP_GROUP_EVENTS:
575 priv->group_events = g_value_get_boolean(value);
576 break;
520 default:577 default:
521 g_warning("Unknown property %d.", id);578 g_warning("Unknown property %d.", id);
522 return;579 return;
@@ -543,6 +600,9 @@
543 case PROP_TEXT_DIRECTION:600 case PROP_TEXT_DIRECTION:
544 g_value_set_enum(value, priv->text_direction);601 g_value_set_enum(value, priv->text_direction);
545 break;602 break;
603 case PROP_GROUP_EVENTS:
604 g_value_set_boolean(value, priv->group_events);
605 break;
546 default:606 default:
547 g_warning("Unknown property %d.", id);607 g_warning("Unknown property %d.", id);
548 return;608 return;
@@ -649,7 +709,7 @@
649 for (i = 0; i < listeners->len; i++) {709 for (i = 0; i < listeners->len; i++) {
650 properties_listener_t * listener = &g_array_index(listeners, properties_listener_t, i);710 properties_listener_t * listener = &g_array_index(listeners, properties_listener_t, i);
651 if (!listener->replied) {711 if (!listener->replied) {
652 g_warning("Generating properties error for: %d", listener->id);712 g_debug("Generating properties error for: %d", listener->id);
653 if (localerror == NULL) {713 if (localerror == NULL) {
654 g_set_error_literal(&localerror, error_domain(), 0, "Error getting properties for ID");714 g_set_error_literal(&localerror, error_domain(), 0, "Error getting properties for ID");
655 }715 }
@@ -1086,6 +1146,7 @@
1086 g_object_notify(G_OBJECT(user_data), DBUSMENU_CLIENT_PROP_TEXT_DIRECTION);1146 g_object_notify(G_OBJECT(user_data), DBUSMENU_CLIENT_PROP_TEXT_DIRECTION);
10871147
1088 g_variant_unref(textdir);1148 g_variant_unref(textdir);
1149 textdir = NULL;
1089 }1150 }
10901151
1091 /* Check the status if available */1152 /* Check the status if available */
@@ -1101,6 +1162,7 @@
1101 g_object_notify(G_OBJECT(user_data), DBUSMENU_CLIENT_PROP_STATUS);1162 g_object_notify(G_OBJECT(user_data), DBUSMENU_CLIENT_PROP_STATUS);
11021163
1103 g_variant_unref(status);1164 g_variant_unref(status);
1165 status = NULL;
1104 }1166 }
11051167
1106 /* Get the icon theme directories if available */1168 /* Get the icon theme directories if available */
@@ -1115,6 +1177,33 @@
1115 g_signal_emit(G_OBJECT(client), signals[ICON_THEME_DIRS], 0, priv->icon_dirs, TRUE);1177 g_signal_emit(G_OBJECT(client), signals[ICON_THEME_DIRS], 0, priv->icon_dirs, TRUE);
11161178
1117 g_variant_unref(icon_dirs);1179 g_variant_unref(icon_dirs);
1180 icon_dirs = NULL;
1181 }
1182
1183 /* Get the dbusmenu protocol version if available */
1184 GVariant * version = g_dbus_proxy_get_cached_property(priv->menuproxy, "Version");
1185 if (version != NULL) {
1186 guint32 remote_version = 0;
1187
1188 if (g_variant_is_of_type(version, G_VARIANT_TYPE_UINT32)) {
1189 remote_version = g_variant_get_uint32(version);
1190 }
1191
1192 gboolean old_group = priv->group_events;
1193 /* Figure out if we can group the events or not */
1194 if (remote_version >= 3) {
1195 priv->group_events = TRUE;
1196 } else {
1197 priv->group_events = FALSE;
1198 }
1199
1200 /* Notify listeners if we changed the value */
1201 if (old_group != priv->group_events) {
1202 g_object_notify(G_OBJECT(client), DBUSMENU_CLIENT_PROP_GROUP_EVENTS);
1203 }
1204
1205 g_variant_unref(version);
1206 version = NULL;
1118 }1207 }
11191208
1120 /* If we get here, we don't need the DBus proxy */1209 /* If we get here, we don't need the DBus proxy */
@@ -1196,6 +1285,19 @@
1196 priv->icon_dirs = g_variant_dup_strv(value, NULL);1285 priv->icon_dirs = g_variant_dup_strv(value, NULL);
1197 dirs_changed = TRUE;1286 dirs_changed = TRUE;
1198 }1287 }
1288 if (g_strcmp0(key, "Version") == 0) {
1289 guint32 remote_version = 0;
1290
1291 if (g_variant_is_of_type(value, G_VARIANT_TYPE_UINT32)) {
1292 remote_version = g_variant_get_uint32(value);
1293 }
1294
1295 if (remote_version >= 3) {
1296 priv->group_events = TRUE;
1297 } else {
1298 priv->group_events = FALSE;
1299 }
1300 }
1199 }1301 }
12001302
1201 if (olddir != priv->text_direction) {1303 if (olddir != priv->text_direction) {
@@ -1441,7 +1543,7 @@
1441 newItemPropData * propdata = (newItemPropData *)data;1543 newItemPropData * propdata = (newItemPropData *)data;
14421544
1443 if (error != NULL) {1545 if (error != NULL) {
1444 g_warning("Error getting properties on a new menuitem: %s", error->message);1546 g_debug("Error getting properties on a new menuitem: %s", error->message);
1445 goto out;1547 goto out;
1446 }1548 }
14471549
@@ -1488,21 +1590,11 @@
1488 return;1590 return;
1489}1591}
14901592
1491/* Respond to the call function to make sure that the other side1593/* A function to work with an event_data_t and make sure it gets
1492 got it, or print a warning. */1594 free'd and in a terminal state. */
1493static void1595static void
1494menuitem_call_cb (GObject * proxy, GAsyncResult * res, gpointer userdata)1596event_data_end (event_data_t * edata, GError * error)
1495{1597{
1496 GError * error = NULL;
1497 event_data_t * edata = (event_data_t *)userdata;
1498 GVariant * params;
1499
1500 params = g_dbus_proxy_call_finish(G_DBUS_PROXY(proxy), res, &error);
1501
1502 if (error != NULL) {
1503 g_warning("Unable to call event '%s' on menu item %d: %s", edata->event, dbusmenu_menuitem_get_id(edata->menuitem), error->message);
1504 }
1505
1506 g_signal_emit(edata->client, signals[EVENT_RESULT], 0, edata->menuitem, edata->event, edata->variant, edata->timestamp, error, TRUE);1598 g_signal_emit(edata->client, signals[EVENT_RESULT], 0, edata->menuitem, edata->event, edata->variant, edata->timestamp, error, TRUE);
15071599
1508 g_variant_unref(edata->variant);1600 g_variant_unref(edata->variant);
@@ -1511,6 +1603,26 @@
1511 g_object_unref(edata->client);1603 g_object_unref(edata->client);
1512 g_free(edata);1604 g_free(edata);
15131605
1606 return;
1607}
1608
1609/* Respond to the call function to make sure that the other side
1610 got it, or print a warning. */
1611static void
1612menuitem_call_cb (GObject * proxy, GAsyncResult * res, gpointer userdata)
1613{
1614 GError * error = NULL;
1615 event_data_t * edata = (event_data_t *)userdata;
1616 GVariant * params;
1617
1618 params = g_dbus_proxy_call_finish(G_DBUS_PROXY(proxy), res, &error);
1619
1620 if (error != NULL) {
1621 g_warning("Unable to call event '%s' on menu item %d: %s", edata->event, dbusmenu_menuitem_get_id(edata->menuitem), error->message);
1622 }
1623
1624 event_data_end(edata, error);
1625
1514 if (G_UNLIKELY(error != NULL)) {1626 if (G_UNLIKELY(error != NULL)) {
1515 g_error_free(error);1627 g_error_free(error);
1516 }1628 }
@@ -1521,6 +1633,128 @@
1521 return;1633 return;
1522}1634}
15231635
1636/* Looks at event_data_t structs to match an ID */
1637gint
1638event_data_find (gconstpointer data, gconstpointer user_data)
1639{
1640 event_data_t * edata = (event_data_t *)data;
1641 gint id = GPOINTER_TO_INT(user_data);
1642
1643 if (edata->id == id) {
1644 return 0;
1645 } else {
1646 return -1;
1647 }
1648}
1649
1650/* The callback from the dbus message to pass events to the
1651 to the server en masse */
1652static void
1653event_group_cb (GObject * proxy, GAsyncResult * res, gpointer user_data)
1654{
1655 GQueue * events = (GQueue *)user_data;
1656
1657 GError * error = NULL;
1658 GVariant * params;
1659 params = g_dbus_proxy_call_finish(G_DBUS_PROXY(proxy), res, &error);
1660
1661 if (error != NULL) {
1662 /* If we got an actual DBus error, we should just pass that
1663 along and finish up */
1664 g_queue_foreach(events, (GFunc)event_data_end, error);
1665 g_queue_free(events);
1666 events = NULL;
1667 return;
1668 }
1669
1670 gint id = 0;
1671 GVariant * array = g_variant_get_child_value(params, 0);
1672 GVariantIter iter;
1673 g_variant_iter_init(&iter, array);
1674
1675 while (g_variant_iter_loop(&iter, "i", &id)) {
1676 GList * item = g_queue_find_custom(events, GINT_TO_POINTER(id), event_data_find);
1677
1678 if (item != NULL) {
1679 GError * iderror = g_error_new(error_domain(), ERROR_ID_NOT_FOUND, "Unable to find ID: %d", id);
1680 event_data_end((event_data_t *)item->data, iderror);
1681 g_queue_delete_link(events, item);
1682 g_error_free(iderror);
1683 }
1684 }
1685
1686 g_variant_unref(array);
1687 g_variant_unref(params);
1688
1689 /* If we have any left send non-error responses */
1690 g_queue_foreach(events, (GFunc)event_data_end, NULL);
1691 g_queue_free(events);
1692 return;
1693}
1694
1695/* Turn an event structure into the variant builder form */
1696static void
1697events_to_builder (gpointer data, gpointer user_data)
1698{
1699 event_data_t * edata = (event_data_t *)data;
1700 GVariantBuilder * builder = (GVariantBuilder *)user_data;
1701
1702 GVariantBuilder tuple;
1703 g_variant_builder_init(&tuple, G_VARIANT_TYPE_TUPLE);
1704
1705 g_variant_builder_add_value(&tuple, g_variant_new_int32(edata->id));
1706 g_variant_builder_add_value(&tuple, g_variant_new_string(edata->event));
1707 g_variant_builder_add_value(&tuple, g_variant_new_variant(edata->variant));
1708 g_variant_builder_add_value(&tuple, g_variant_new_uint32(edata->timestamp));
1709
1710 GVariant * vtuple = g_variant_builder_end(&tuple);
1711 g_variant_builder_add_value(builder, vtuple);
1712 return;
1713}
1714
1715/* Group all the events into a single Dbus message and send
1716 that out */
1717static gboolean
1718event_idle_cb (gpointer user_data)
1719{
1720 g_return_val_if_fail(DBUSMENU_IS_CLIENT(user_data), FALSE);
1721 DbusmenuClient * client = DBUSMENU_CLIENT(user_data);
1722 DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(user_data);
1723
1724 /* We use prepend for speed, but now we want to have them
1725 in the order they were called incase that matters. */
1726 GQueue * levents = priv->events_to_go;
1727 priv->events_to_go = NULL;
1728 priv->event_idle = 0;
1729
1730 GVariantBuilder array;
1731 g_variant_builder_init(&array, G_VARIANT_TYPE("a(isvu)"));
1732 g_queue_foreach(levents, events_to_builder, &array);
1733 GVariant * vevents = g_variant_builder_end(&array);
1734
1735 if (g_signal_has_handler_pending (client, signals[EVENT_RESULT], 0, TRUE)) {
1736 g_dbus_proxy_call(priv->menuproxy,
1737 "EventGroup",
1738 g_variant_new_tuple(&vevents, 1),
1739 G_DBUS_CALL_FLAGS_NONE,
1740 1000, /* timeout */
1741 NULL, /* cancellable */
1742 event_group_cb, levents);
1743 } else {
1744 g_dbus_proxy_call(priv->menuproxy,
1745 "EventGroup",
1746 g_variant_new_tuple(&vevents, 1),
1747 G_DBUS_CALL_FLAGS_NONE,
1748 1000, /* timeout */
1749 NULL, /* cancellable */
1750 NULL, NULL);
1751 g_queue_foreach(levents, (GFunc)event_data_end, NULL);
1752 g_queue_free(levents);
1753 }
1754
1755 return FALSE;
1756}
1757
1524/* Sends the event over DBus to the server on the other side1758/* Sends the event over DBus to the server on the other side
1525 of the bus. */1759 of the bus. */
1526void1760void
@@ -1541,7 +1775,7 @@
1541 }1775 }
15421776
1543 /* Don't bother with the reply handling if nobody is watching... */1777 /* Don't bother with the reply handling if nobody is watching... */
1544 if (!g_signal_has_handler_pending (client, signals[EVENT_RESULT], 0, TRUE)) {1778 if (!priv->group_events && !g_signal_has_handler_pending (client, signals[EVENT_RESULT], 0, TRUE)) {
1545 g_dbus_proxy_call(priv->menuproxy,1779 g_dbus_proxy_call(priv->menuproxy,
1546 "Event",1780 "Event",
1547 g_variant_new("(isvu)", id, name, variant, timestamp),1781 g_variant_new("(isvu)", id, name, variant, timestamp),
@@ -1553,6 +1787,7 @@
1553 }1787 }
15541788
1555 event_data_t * edata = g_new0(event_data_t, 1);1789 event_data_t * edata = g_new0(event_data_t, 1);
1790 edata->id = id;
1556 edata->client = client;1791 edata->client = client;
1557 g_object_ref(client);1792 g_object_ref(client);
1558 edata->menuitem = mi;1793 edata->menuitem = mi;
@@ -1562,25 +1797,181 @@
1562 edata->variant = variant;1797 edata->variant = variant;
1563 g_variant_ref_sink(variant);1798 g_variant_ref_sink(variant);
15641799
1565 g_dbus_proxy_call(priv->menuproxy,1800 if (!priv->group_events) {
1566 "Event",1801 g_dbus_proxy_call(priv->menuproxy,
1567 g_variant_new("(isvu)", id, name, variant, timestamp),1802 "Event",
1568 G_DBUS_CALL_FLAGS_NONE,1803 g_variant_new("(isvu)", id, name, variant, timestamp),
1569 1000, /* timeout */1804 G_DBUS_CALL_FLAGS_NONE,
1570 NULL, /* cancellable */1805 1000, /* timeout */
1571 menuitem_call_cb,1806 NULL, /* cancellable */
1572 edata);1807 menuitem_call_cb,
1808 edata);
1809 } else {
1810 if (priv->events_to_go == NULL) {
1811 priv->events_to_go = g_queue_new();
1812 }
1813
1814 g_queue_push_tail(priv->events_to_go, edata);
1815
1816 if (priv->event_idle == 0) {
1817 priv->event_idle = g_idle_add(event_idle_cb, client);
1818 }
1819 }
15731820
1574 return;1821 return;
1575}1822}
15761823
1577typedef struct _about_to_show_t about_to_show_t;1824typedef struct _about_to_show_t about_to_show_t;
1578struct _about_to_show_t {1825struct _about_to_show_t {
1826 gint id;
1579 DbusmenuClient * client;1827 DbusmenuClient * client;
1580 void (*cb) (gpointer data);1828 void (*cb) (gpointer data);
1581 gpointer cb_data;1829 gpointer cb_data;
1582};1830};
15831831
1832/* Takes an about_to_show_t structure and calls the callback correctly
1833 and updates the layout if needed. */
1834static void
1835about_to_show_finish (about_to_show_t * data, gboolean need_update)
1836{
1837 /* If we need to update, do that first. */
1838 if (need_update) {
1839 update_layout(data->client);
1840 }
1841
1842 if (data->cb != NULL) {
1843 data->cb(data->cb_data);
1844 }
1845
1846 g_object_unref(data->client);
1847 g_free(data);
1848
1849 return;
1850}
1851
1852/* A little function to match prototypes and make sure to convert from
1853 a pointer to an int correctly */
1854static void
1855about_to_show_finish_pntr (gpointer data, gpointer user_data)
1856{
1857 return about_to_show_finish((about_to_show_t *)data, GPOINTER_TO_INT(user_data));
1858}
1859
1860/* Respond to the DBus message from sending a bunch of about-to-show events
1861 to the server */
1862static void
1863about_to_show_group_cb (GObject * proxy, GAsyncResult * res, gpointer userdata)
1864{
1865 GError * error = NULL;
1866 GQueue * showers = (GQueue *)userdata;
1867 GVariant * params = NULL;
1868
1869 params = g_dbus_proxy_call_finish(G_DBUS_PROXY(proxy), res, &error);
1870
1871 if (error != NULL) {
1872 g_warning("Unable to send about_to_show_group: %s", error->message);
1873 /* Note: we're just ensuring only the callback gets called */
1874 g_error_free(error);
1875 error = NULL;
1876 } else {
1877 GVariant * updates = g_variant_get_child_value(params, 0);
1878 GVariantIter iter;
1879
1880 /* Okay, so this is kinda interesting. We actually don't care which
1881 entries asked us to update the structure, as it's quite simply a
1882 single structure. So if we have any ask, we get the update once to
1883 avoid itterating through all the structures. */
1884 if (g_variant_iter_init(&iter, updates) > 0) {
1885 about_to_show_t * first = (about_to_show_t *)g_queue_peek_head(showers);
1886 update_layout(first->client);
1887 }
1888
1889 g_variant_unref(updates);
1890 g_variant_unref(params);
1891 params = NULL;
1892 }
1893
1894 g_queue_foreach(showers, about_to_show_finish_pntr, GINT_TO_POINTER(FALSE));
1895 g_queue_free(showers);
1896
1897 return;
1898}
1899
1900/* Check to see if this about to show entry has a callback associated
1901 with it */
1902static void
1903about_to_show_idle_callbacks (gpointer data, gpointer user_data)
1904{
1905 about_to_show_t * abts = (about_to_show_t *)data;
1906 gboolean * got_callbacks = (gboolean *)user_data;
1907
1908 if (abts->cb != NULL) {
1909 *got_callbacks = TRUE;
1910 }
1911
1912 return;
1913}
1914
1915/* Take the ID out of the about to show structure and put it into the
1916 variant builder */
1917static void
1918about_to_show_idle_ids (gpointer data, gpointer user_data)
1919{
1920 about_to_show_t * abts = (about_to_show_t *)data;
1921 GVariantBuilder * builder = (GVariantBuilder *)user_data;
1922
1923 g_variant_builder_add_value(builder, g_variant_new_int32(abts->id));
1924
1925 return;
1926}
1927
1928/* Function that gets called with all the queued about_to_show messages, let's
1929 get these guys on the bus! */
1930static gboolean
1931about_to_show_idle (gpointer user_data)
1932{
1933 DbusmenuClient * client = DBUSMENU_CLIENT(user_data);
1934 DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
1935
1936 /* Reset our object global props and take ownership of these entries */
1937 priv->about_to_show_idle = 0;
1938 GQueue * showers = priv->about_to_show_to_go;
1939 priv->about_to_show_to_go = NULL;
1940
1941 /* Figure out if we've got any callbacks */
1942 gboolean got_callbacks = FALSE;
1943 g_queue_foreach(showers, about_to_show_idle_callbacks, &got_callbacks);
1944
1945 /* Build a list of the IDs */
1946 GVariantBuilder idarray;
1947 g_variant_builder_init(&idarray, G_VARIANT_TYPE("ai"));
1948 g_queue_foreach(showers, about_to_show_idle_ids, &idarray);
1949 GVariant * ids = g_variant_builder_end(&idarray);
1950
1951 /* Setup our callbacks */
1952 GAsyncReadyCallback cb = NULL;
1953 gpointer cb_data = NULL;
1954 if (got_callbacks) {
1955 cb = about_to_show_group_cb;
1956 cb_data = showers;
1957 } else {
1958 g_queue_foreach(showers, about_to_show_finish_pntr, GINT_TO_POINTER(FALSE));
1959 g_queue_free(showers);
1960 }
1961
1962 /* Let's call it! */
1963 g_dbus_proxy_call(priv->menuproxy,
1964 "AboutToShowGroup",
1965 g_variant_new_tuple(&ids, 1),
1966 G_DBUS_CALL_FLAGS_NONE,
1967 -1, /* timeout */
1968 NULL, /* cancellable */
1969 cb,
1970 cb_data);
1971
1972 return FALSE;
1973}
1974
1584/* Reports errors and responds to update request that were a result1975/* Reports errors and responds to update request that were a result
1585 of sending the about to show signal. */1976 of sending the about to show signal. */
1586static void1977static void
@@ -1604,18 +1995,7 @@
1604 g_variant_unref(params);1995 g_variant_unref(params);
1605 }1996 }
16061997
1607 /* If we need to update, do that first. */1998 about_to_show_finish(data, need_update);
1608 if (need_update) {
1609 update_layout(data->client);
1610 }
1611
1612 if (data->cb != NULL) {
1613 data->cb(data->cb_data);
1614 }
1615
1616 g_object_unref(data->client);
1617 g_free(data);
1618
1619 return;1999 return;
1620}2000}
16212001
@@ -1631,19 +2011,40 @@
1631 g_return_if_fail(priv != NULL);2011 g_return_if_fail(priv != NULL);
16322012
1633 about_to_show_t * data = g_new0(about_to_show_t, 1);2013 about_to_show_t * data = g_new0(about_to_show_t, 1);
2014 data->id = id;
1634 data->client = client;2015 data->client = client;
1635 data->cb = cb;2016 data->cb = cb;
1636 data->cb_data = cb_data;2017 data->cb_data = cb_data;
1637 g_object_ref(client);2018 g_object_ref(client);
16382019
1639 g_dbus_proxy_call(priv->menuproxy,2020 if (priv->group_events) {
1640 "AboutToShow",2021 if (priv->about_to_show_to_go == NULL) {
1641 g_variant_new("(i)", id),2022 priv->about_to_show_to_go = g_queue_new();
1642 G_DBUS_CALL_FLAGS_NONE,2023 }
1643 -1, /* timeout */2024
1644 NULL, /* cancellable */2025 g_queue_push_tail(priv->about_to_show_to_go, data);
1645 about_to_show_cb,2026
1646 data);2027 if (priv->about_to_show_idle == 0) {
2028 priv->about_to_show_idle = g_idle_add(about_to_show_idle, client);
2029 }
2030 } else {
2031 /* If there's no callback we don't need this data, let's
2032 clean it up in a consistent way */
2033 if (cb == NULL) {
2034 about_to_show_finish(data, FALSE);
2035 data = NULL;
2036 }
2037
2038 g_dbus_proxy_call(priv->menuproxy,
2039 "AboutToShow",
2040 g_variant_new("(i)", id),
2041 G_DBUS_CALL_FLAGS_NONE,
2042 -1, /* timeout */
2043 NULL, /* cancellable */
2044 about_to_show_cb,
2045 data);
2046 }
2047
1647 return;2048 return;
1648}2049}
16492050
16502051
=== modified file 'libdbusmenu-glib/client.h'
--- libdbusmenu-glib/client.h 2012-02-10 14:26:02 +0000
+++ libdbusmenu-glib/client.h 2012-04-04 04:28:21 +0000
@@ -105,6 +105,12 @@
105 * String to access property #DbusmenuClient:text-direction105 * String to access property #DbusmenuClient:text-direction
106 */106 */
107#define DBUSMENU_CLIENT_PROP_TEXT_DIRECTION "text-direction"107#define DBUSMENU_CLIENT_PROP_TEXT_DIRECTION "text-direction"
108/**
109 * DBUSMENU_CLIENT_PROP_GROUP_EVENTS:
110 *
111 * String to access property #DbusmenuClient:group-events
112 */
113#define DBUSMENU_CLIENT_PROP_GROUP_EVENTS "group-events"
108114
109/**115/**
110 * DBUSMENU_CLIENT_TYPES_DEFAULT:116 * DBUSMENU_CLIENT_TYPES_DEFAULT:
111117
=== modified file 'libdbusmenu-glib/dbus-menu.xml'
--- libdbusmenu-glib/dbus-menu.xml 2011-08-22 16:35:02 +0000
+++ libdbusmenu-glib/dbus-menu.xml 2012-04-04 04:28:21 +0000
@@ -326,6 +326,26 @@
326 </arg>326 </arg>
327 </method>327 </method>
328328
329 <method name="EventGroup">
330 <dox:d>
331 Used to pass a set of events as a single message for possibily several
332 different menuitems. This is done to optimize DBus traffic.
333 </dox:d>
334 <arg type="a(isvu)" name="events" direction="in">
335 <dox:d>
336 An array of all the events that should be passed. This tuple should
337 match the parameters of the 'Event' signal. Which is roughly:
338 id, eventID, data and timestamp.
339 </dox:d>
340 </arg>
341 <arg type="ai" name="idErrors" direction="out">
342 <dox:d>
343 I list of menuitem IDs that couldn't be found. If none of the ones
344 in the list can be found, a DBus error is returned.
345 </dox:d>
346 </arg>
347 </method>
348
329 <method name="AboutToShow">349 <method name="AboutToShow">
330 <dox:d>350 <dox:d>
331 This is called by the applet to notify the application that it is about351 This is called by the applet to notify the application that it is about
@@ -343,6 +363,32 @@
343 </arg>363 </arg>
344 </method>364 </method>
345365
366 <method name="AboutToShowGroup">
367 <dox:d>
368 A function to tell several menus being shown that they are about to
369 be shown to the user. This is likely only useful for programitc purposes
370 so while the return values are returned, in general, the singular function
371 should be used in most user interacation scenarios.
372 </dox:d>
373 <arg type="ai" name="ids" direction="in">
374 <dox:d>
375 The IDs of the menu items who's submenus are being shown.
376 </dox:d>
377 </arg>
378 <arg type="ai" name="updatesNeeded" direction="out">
379 <dox:d>
380 The IDs of the menus that need updates. Note: if no update information
381 is needed the DBus message should set the no reply flag.
382 </dox:d>
383 </arg>
384 <arg type="ai" name="idErrors" direction="out">
385 <dox:d>
386 I list of menuitem IDs that couldn't be found. If none of the ones
387 in the list can be found, a DBus error is returned.
388 </dox:d>
389 </arg>
390 </method>
391
346<!-- Signals -->392<!-- Signals -->
347 <signal name="ItemsPropertiesUpdated">393 <signal name="ItemsPropertiesUpdated">
348 <dox:d>394 <dox:d>
349395
=== modified file 'libdbusmenu-glib/server.c'
--- libdbusmenu-glib/server.c 2012-03-28 14:07:30 +0000
+++ libdbusmenu-glib/server.c 2012-04-04 04:28:21 +0000
@@ -42,7 +42,7 @@
4242
43static void layout_update_signal (DbusmenuServer * server);43static void layout_update_signal (DbusmenuServer * server);
4444
45#define DBUSMENU_VERSION_NUMBER 245#define DBUSMENU_VERSION_NUMBER 3
46#define DBUSMENU_INTERFACE "com.canonical.dbusmenu"46#define DBUSMENU_INTERFACE "com.canonical.dbusmenu"
4747
48/* Privates, I'll show you mine... */48/* Privates, I'll show you mine... */
@@ -118,7 +118,9 @@
118 METHOD_GET_PROPERTY,118 METHOD_GET_PROPERTY,
119 METHOD_GET_PROPERTIES,119 METHOD_GET_PROPERTIES,
120 METHOD_EVENT,120 METHOD_EVENT,
121 METHOD_EVENT_GROUP,
121 METHOD_ABOUT_TO_SHOW,122 METHOD_ABOUT_TO_SHOW,
123 METHOD_ABOUT_TO_SHOW_GROUP,
122 /* Counter, do not remove! */124 /* Counter, do not remove! */
123 METHOD_COUNT125 METHOD_COUNT
124};126};
@@ -191,9 +193,15 @@
191static void bus_event (DbusmenuServer * server,193static void bus_event (DbusmenuServer * server,
192 GVariant * params,194 GVariant * params,
193 GDBusMethodInvocation * invocation);195 GDBusMethodInvocation * invocation);
196static void bus_event_group (DbusmenuServer * server,
197 GVariant * params,
198 GDBusMethodInvocation * invocation);
194static void bus_about_to_show (DbusmenuServer * server,199static void bus_about_to_show (DbusmenuServer * server,
195 GVariant * params,200 GVariant * params,
196 GDBusMethodInvocation * invocation);201 GDBusMethodInvocation * invocation);
202static void bus_about_to_show_group (DbusmenuServer * server,
203 GVariant * params,
204 GDBusMethodInvocation * invocation);
197static void find_servers_cb (GDBusConnection * connection,205static void find_servers_cb (GDBusConnection * connection,
198 const gchar * sender,206 const gchar * sender,
199 const gchar * path,207 const gchar * path,
@@ -358,9 +366,15 @@
358 dbusmenu_method_table[METHOD_EVENT].interned_name = g_intern_static_string("Event");366 dbusmenu_method_table[METHOD_EVENT].interned_name = g_intern_static_string("Event");
359 dbusmenu_method_table[METHOD_EVENT].func = bus_event;367 dbusmenu_method_table[METHOD_EVENT].func = bus_event;
360368
369 dbusmenu_method_table[METHOD_EVENT_GROUP].interned_name = g_intern_static_string("EventGroup");
370 dbusmenu_method_table[METHOD_EVENT_GROUP].func = bus_event_group;
371
361 dbusmenu_method_table[METHOD_ABOUT_TO_SHOW].interned_name = g_intern_static_string("AboutToShow");372 dbusmenu_method_table[METHOD_ABOUT_TO_SHOW].interned_name = g_intern_static_string("AboutToShow");
362 dbusmenu_method_table[METHOD_ABOUT_TO_SHOW].func = bus_about_to_show;373 dbusmenu_method_table[METHOD_ABOUT_TO_SHOW].func = bus_about_to_show;
363374
375 dbusmenu_method_table[METHOD_ABOUT_TO_SHOW_GROUP].interned_name = g_intern_static_string("AboutToShowGroup");
376 dbusmenu_method_table[METHOD_ABOUT_TO_SHOW_GROUP].func = bus_about_to_show_group;
377
364 return;378 return;
365}379}
366380
@@ -1633,6 +1647,28 @@
1633 return FALSE;1647 return FALSE;
1634}1648}
16351649
1650/* The core menu finding and doing the work part of the two
1651 event functions */
1652static gboolean
1653bus_event_core (DbusmenuServer * server, gint32 id, gchar * event_type, GVariant * data, guint32 timestamp)
1654{
1655 DbusmenuMenuitem * mi = lookup_menuitem_by_id(server, id);
1656
1657 if (mi == NULL) {
1658 return FALSE;
1659 }
1660
1661 idle_event_t * event_data = g_new0(idle_event_t, 1);
1662 event_data->mi = g_object_ref(mi);
1663 event_data->eventid = g_strdup(event_type);
1664 event_data->timestamp = timestamp;
1665 event_data->variant = g_variant_ref(data);
1666
1667 g_timeout_add(0, event_local_handler, event_data);
1668
1669 return TRUE;
1670}
1671
1636/* Handles the events coming off of DBus */1672/* Handles the events coming off of DBus */
1637static void1673static void
1638bus_event (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)1674bus_event (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
@@ -1654,32 +1690,92 @@
16541690
1655 g_variant_get(params, "(isvu)", &id, &etype, &data, &ts);1691 g_variant_get(params, "(isvu)", &id, &etype, &data, &ts);
16561692
1657 DbusmenuMenuitem * mi = lookup_menuitem_by_id(server, id);1693 if (!bus_event_core(server, id, etype, data, ts)) {
1658
1659 if (mi == NULL) {
1660 g_dbus_method_invocation_return_error(invocation,1694 g_dbus_method_invocation_return_error(invocation,
1661 error_quark(),1695 error_quark(),
1662 INVALID_MENUITEM_ID,1696 INVALID_MENUITEM_ID,
1663 "The ID supplied %d does not refer to a menu item we have",1697 "The ID supplied %d does not refer to a menu item we have",
1664 id);1698 id);
1665 g_free(etype);
1666 g_variant_unref(data);
1667
1668 } else {1699 } else {
1669 idle_event_t * event_data = g_new0(idle_event_t, 1);
1670 event_data->mi = g_object_ref(mi);
1671 event_data->eventid = etype; /* give away our allocation */
1672 event_data->timestamp = ts;
1673 event_data->variant = data; /* give away our reference */
1674
1675 g_timeout_add(0, event_local_handler, event_data);
1676
1677 if (~g_dbus_message_get_flags (g_dbus_method_invocation_get_message (invocation)) & G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED) {1700 if (~g_dbus_message_get_flags (g_dbus_method_invocation_get_message (invocation)) & G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED) {
1678 g_dbus_method_invocation_return_value(invocation, NULL);1701 g_dbus_method_invocation_return_value(invocation, NULL);
1679 }1702 }
1680 }1703 }
16811704
1682 return;1705 g_free(etype);
1706 g_variant_unref(data);
1707
1708 return;
1709}
1710
1711/* Respond to the event group method that will send events to a
1712 variety of menuitems */
1713static void
1714bus_event_group (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
1715{
1716 DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
1717
1718 if (priv->root == NULL) {
1719 g_dbus_method_invocation_return_error(invocation,
1720 error_quark(),
1721 NO_VALID_LAYOUT,
1722 "There currently isn't a layout in this server");
1723 return;
1724 }
1725
1726 GVariant * events = g_variant_get_child_value(params, 0);
1727 gint32 id;
1728 gchar *etype;
1729 GVariant *data;
1730 guint32 ts;
1731 GVariantIter iter;
1732 GVariantBuilder builder;
1733
1734 g_variant_iter_init(&iter, events);
1735 g_variant_builder_init(&builder, G_VARIANT_TYPE("ai"));
1736 gboolean gotone = FALSE;
1737
1738 while (g_variant_iter_loop(&iter, "(isvu)", &id, &etype, &data, &ts)) {
1739 if (bus_event_core(server, id, etype, data, ts)) {
1740 gotone = TRUE;
1741 } else {
1742 g_variant_builder_add_value(&builder, g_variant_new_int32(id));
1743 }
1744 }
1745
1746 GVariant * errors = g_variant_builder_end(&builder);
1747 g_variant_ref_sink(errors);
1748
1749 if (gotone) {
1750 if (~g_dbus_message_get_flags (g_dbus_method_invocation_get_message (invocation)) & G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED) {
1751 g_dbus_method_invocation_return_value(invocation, g_variant_new_tuple(&errors, 1));
1752 }
1753 } else {
1754 gchar * ids = g_variant_print(errors, FALSE);
1755 g_dbus_method_invocation_return_error(invocation,
1756 error_quark(),
1757 INVALID_MENUITEM_ID,
1758 "The IDs supplied '%s' do not refer to any menu items we have",
1759 ids);
1760 g_free(ids);
1761 }
1762
1763 g_variant_unref(errors);
1764 g_variant_unref(events);
1765
1766 return;
1767}
1768
1769/* Does the about-to-show in an idle loop so we don't block things */
1770/* NOTE: this only works so easily as we don't return the value, if we
1771 were to do that it would get more complex. */
1772static gboolean
1773bus_about_to_show_idle (gpointer user_data)
1774{
1775 DbusmenuMenuitem * mi = DBUSMENU_MENUITEM(user_data);
1776 dbusmenu_menuitem_send_about_to_show(mi, NULL, NULL);
1777 g_object_unref(mi);
1778 return FALSE;
1683}1779}
16841780
1685/* Recieve the About To Show function. Pass it to our menu item. */1781/* Recieve the About To Show function. Pass it to our menu item. */
@@ -1709,7 +1805,7 @@
1709 return;1805 return;
1710 }1806 }
17111807
1712 dbusmenu_menuitem_send_about_to_show(mi, NULL, NULL);1808 g_timeout_add(0, bus_about_to_show_idle, g_object_ref(mi));
17131809
1714 /* GTK+ does not support about-to-show concept for now */1810 /* GTK+ does not support about-to-show concept for now */
1715 g_dbus_method_invocation_return_value(invocation,1811 g_dbus_method_invocation_return_value(invocation,
@@ -1717,6 +1813,69 @@
1717 return;1813 return;
1718}1814}
17191815
1816/* Handle the about to show on a set of menus and tell all of them that
1817 we love them */
1818static void
1819bus_about_to_show_group (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
1820{
1821 DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
1822
1823 if (priv->root == NULL) {
1824 g_dbus_method_invocation_return_error(invocation,
1825 error_quark(),
1826 NO_VALID_LAYOUT,
1827 "There currently isn't a layout in this server");
1828 return;
1829 }
1830
1831 gint32 id;
1832 GVariantIter iter;
1833 GVariantBuilder builder;
1834
1835 g_variant_iter_init(&iter, params);
1836 g_variant_builder_init(&builder, G_VARIANT_TYPE("ai"));
1837 gboolean gotone = FALSE;
1838
1839 while (g_variant_iter_loop(&iter, "(i)", &id)) {
1840 DbusmenuMenuitem * mi = lookup_menuitem_by_id(server, id);
1841 if (mi != NULL) {
1842 g_timeout_add(0, bus_about_to_show_idle, g_object_ref(mi));
1843 gotone = TRUE;
1844 } else {
1845 g_variant_builder_add_value(&builder, g_variant_new_int32(id));
1846 }
1847 }
1848
1849 GVariant * errors = g_variant_builder_end(&builder);
1850 g_variant_ref_sink(errors);
1851
1852 if (gotone) {
1853 if (~g_dbus_message_get_flags (g_dbus_method_invocation_get_message (invocation)) & G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED) {
1854 GVariantBuilder tuple;
1855 g_variant_builder_init(&tuple, G_VARIANT_TYPE_TUPLE);
1856
1857 /* Updates needed */
1858 g_variant_builder_add_value(&tuple, g_variant_new_array(G_VARIANT_TYPE_INT32, NULL, 0));
1859 /* Errors */
1860 g_variant_builder_add_value(&tuple, errors);
1861
1862 g_dbus_method_invocation_return_value(invocation, g_variant_builder_end(&tuple));
1863 }
1864 } else {
1865 gchar * ids = g_variant_print(errors, FALSE);
1866 g_dbus_method_invocation_return_error(invocation,
1867 error_quark(),
1868 INVALID_MENUITEM_ID,
1869 "The IDs supplied '%s' do not refer to any menu items we have",
1870 ids);
1871 g_free(ids);
1872 }
1873
1874 g_variant_unref(errors);
1875
1876 return;
1877}
1878
1720/* Public Interface */1879/* Public Interface */
1721/**1880/**
1722 dbusmenu_server_new:1881 dbusmenu_server_new:
17231882
=== modified file 'tests/Makefile.am'
--- tests/Makefile.am 2012-04-02 14:25:13 +0000
+++ tests/Makefile.am 2012-04-04 04:28:21 +0000
@@ -8,6 +8,7 @@
8TESTS = \8TESTS = \
9 test-glib-objects-test \9 test-glib-objects-test \
10 test-glib-events \10 test-glib-events \
11 test-glib-events-nogroup \
11 test-glib-layout \12 test-glib-layout \
12 test-glib-properties \13 test-glib-properties \
13 test-glib-proxy \14 test-glib-proxy \
@@ -45,6 +46,7 @@
45 test-glib-objects \46 test-glib-objects \
46 test-glib-events-client \47 test-glib-events-client \
47 test-glib-events-server \48 test-glib-events-server \
49 test-glib-events-nogroup-client \
48 test-glib-layout-client \50 test-glib-layout-client \
49 test-glib-layout-server \51 test-glib-layout-server \
50 test-glib-properties-client \52 test-glib-properties-client \
@@ -200,6 +202,28 @@
200 ../libdbusmenu-glib/libdbusmenu-glib.la \202 ../libdbusmenu-glib/libdbusmenu-glib.la \
201 $(DBUSMENUGLIB_LIBS)203 $(DBUSMENUGLIB_LIBS)
202204
205################################
206# Test Glib Events No Grouping
207################################
208
209test-glib-events-nogroup: test-glib-events-nogroup-client test-glib-events-server Makefile.am
210 @echo "#!/bin/bash" > $@
211 @echo export UBUNTU_MENUPROXY="" >> $@
212 @echo export G_DEBUG=fatal_criticals >> $@
213 @echo $(DBUS_RUNNER) --task ./test-glib-events-nogroup-client --task-name Client --task ./test-glib-events-server --task-name Server --ignore-return >> $@
214 @chmod +x $@
215
216test_glib_events_nogroup_client_SOURCES = \
217 test-glib-events-nogroup-client.c
218
219test_glib_events_nogroup_client_CFLAGS = \
220 -I $(srcdir)/.. \
221 $(DBUSMENUGLIB_CFLAGS) -Wall -Werror
222
223test_glib_events_nogroup_client_LDADD = \
224 ../libdbusmenu-glib/libdbusmenu-glib.la \
225 $(DBUSMENUGLIB_LIBS)
226
203######################227######################
204# Test JSON228# Test JSON
205######################229######################
206230
=== added file 'tests/test-glib-events-nogroup-client.c'
--- tests/test-glib-events-nogroup-client.c 1970-01-01 00:00:00 +0000
+++ tests/test-glib-events-nogroup-client.c 2012-04-04 04:28:21 +0000
@@ -0,0 +1,142 @@
1/*
2A test for libdbusmenu to ensure its quality.
3
4Copyright 2009 Canonical Ltd.
5
6Authors:
7 Ted Gould <ted@canonical.com>
8
9This program is free software: you can redistribute it and/or modify it
10under the terms of the GNU General Public License version 3, as published
11by the Free Software Foundation.
12
13This program is distributed in the hope that it will be useful, but
14WITHOUT ANY WARRANTY; without even the implied warranties of
15MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
16PURPOSE. See the GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License along
19with this program. If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#include <glib.h>
23
24#include <libdbusmenu-glib/client.h>
25#include <libdbusmenu-glib/menuitem.h>
26
27#include "test-glib-submenu.h"
28
29#define TIMESTAMP_VALUE 54
30#define DATA_VALUE 32
31#define USER_VALUE 76
32
33static GMainLoop * mainloop = NULL;
34static gboolean passed = TRUE;
35static gboolean first = TRUE;
36
37static void
38event_status (DbusmenuClient * client, DbusmenuMenuitem * item, gchar * name, GVariant * data, guint timestamp, GError * error, gpointer user_data)
39{
40 g_debug("Event status: %s", error == NULL ? "Sent" : "Error");
41
42 if (timestamp != TIMESTAMP_VALUE) {
43 g_debug("Timestamp value pass fail got: %d", timestamp);
44 passed = FALSE;
45 g_main_loop_quit(mainloop);
46 return;
47 }
48
49 if (g_variant_get_int32(data) != DATA_VALUE) {
50 g_debug("Data value pass fail got: %d", g_variant_get_int32(g_variant_get_child_value(data, 0)));
51 passed = FALSE;
52 g_main_loop_quit(mainloop);
53 return;
54 }
55
56 if (GPOINTER_TO_INT(user_data) != USER_VALUE) {
57 g_debug("User value pass fail got: %d", GPOINTER_TO_INT(user_data));
58 passed = FALSE;
59 g_main_loop_quit(mainloop);
60 return;
61 }
62
63 if (first && error != NULL) {
64 passed = FALSE;
65 g_debug("First signal back failed.");
66 g_main_loop_quit(mainloop);
67 return;
68 }
69
70 if (!first && error == NULL) {
71 passed = FALSE;
72 g_debug("Second signal didn't fail.");
73 g_main_loop_quit(mainloop);
74 return;
75 }
76
77 if (!first && error != NULL) {
78 g_debug("Second signal failed: pass.");
79 g_main_loop_quit(mainloop);
80 return;
81 }
82
83 first = FALSE;
84 dbusmenu_menuitem_handle_event(item, "clicked", data, timestamp);
85 return;
86}
87
88static void
89layout_updated (DbusmenuClient * client, gpointer user_data)
90{
91 g_debug("Layout Updated");
92
93 DbusmenuMenuitem * menuroot = dbusmenu_client_get_root(client);
94 if (menuroot == NULL) {
95 g_debug("Root is NULL?");
96 return;
97 }
98
99 g_object_set(G_OBJECT(client),
100 DBUSMENU_CLIENT_PROP_GROUP_EVENTS, FALSE,
101 NULL);
102
103 GVariant * data = g_variant_new_int32(DATA_VALUE);
104 dbusmenu_menuitem_handle_event(menuroot, "clicked", data, TIMESTAMP_VALUE);
105
106 return;
107}
108
109static gboolean
110timer_func (gpointer data)
111{
112 g_debug("Death timer. Oops.");
113 passed = FALSE;
114 g_main_loop_quit(mainloop);
115 return FALSE;
116}
117
118int
119main (int argc, char ** argv)
120{
121 g_type_init();
122
123 DbusmenuClient * client = dbusmenu_client_new("org.dbusmenu.test", "/org/test");
124 g_signal_connect(G_OBJECT(client), DBUSMENU_CLIENT_SIGNAL_LAYOUT_UPDATED, G_CALLBACK(layout_updated), NULL);
125 g_signal_connect(G_OBJECT(client), DBUSMENU_CLIENT_SIGNAL_EVENT_RESULT, G_CALLBACK(event_status), GINT_TO_POINTER(USER_VALUE));
126
127 g_timeout_add_seconds(5, timer_func, client);
128
129 mainloop = g_main_loop_new(NULL, FALSE);
130 g_main_loop_run(mainloop);
131
132 g_debug("Main loop complete");
133 g_object_unref(G_OBJECT(client));
134
135 if (passed) {
136 g_debug("Quiting");
137 return 0;
138 } else {
139 g_debug("Quiting as we're a failure");
140 return 1;
141 }
142}

Subscribers

People subscribed via source and target branches

to all changes: