diff options
author | Martin Pitt <martin@piware.de> | 2011-02-21 13:12:33 +0100 |
---|---|---|
committer | Martin Pitt <martin@piware.de> | 2011-02-21 13:12:33 +0100 |
commit | ded5bb1e7c5bf42c24ed25a66c7e73ae8eaa54e0 (patch) | |
tree | eeac4c7bace411463570b6d0b06b18264d09afe8 /libdbusmenu-glib | |
parent | 3f3db4e1ea5e5b2321a7266edf018bb510fcdfb4 (diff) | |
parent | 87703b408d42ce97b5a64142b0c33951d366890a (diff) | |
download | libdbusmenu-ded5bb1e7c5bf42c24ed25a66c7e73ae8eaa54e0.tar.gz libdbusmenu-ded5bb1e7c5bf42c24ed25a66c7e73ae8eaa54e0.tar.bz2 libdbusmenu-ded5bb1e7c5bf42c24ed25a66c7e73ae8eaa54e0.zip |
merge trunk
Diffstat (limited to 'libdbusmenu-glib')
-rw-r--r-- | libdbusmenu-glib/client.c | 186 | ||||
-rw-r--r-- | libdbusmenu-glib/dbus-menu.xml | 99 | ||||
-rw-r--r-- | libdbusmenu-glib/menuitem-private.h | 5 | ||||
-rw-r--r-- | libdbusmenu-glib/menuitem.c | 78 | ||||
-rw-r--r-- | libdbusmenu-glib/menuitem.h | 11 | ||||
-rw-r--r-- | libdbusmenu-glib/server.c | 359 |
6 files changed, 524 insertions, 214 deletions
diff --git a/libdbusmenu-glib/client.c b/libdbusmenu-glib/client.c index 8a1d213..0848294 100644 --- a/libdbusmenu-glib/client.c +++ b/libdbusmenu-glib/client.c @@ -32,9 +32,6 @@ License version 3 and version 2.1 along with this program. If not, see #include <gio/gio.h> -#include <libxml/parser.h> -#include <libxml/tree.h> - #include "client.h" #include "menuitem.h" #include "menuitem-private.h" @@ -151,9 +148,8 @@ static void layout_update (GDBusProxy * proxy, guint revision, gint parent, Dbus static void id_prop_update (GDBusProxy * proxy, gint id, gchar * property, GVariant * value, DbusmenuClient * client); static void id_update (GDBusProxy * proxy, gint id, DbusmenuClient * client); static void build_proxies (DbusmenuClient * client); -static gint parse_node_get_id (xmlNodePtr node); -static DbusmenuMenuitem * parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, GDBusProxy * proxy); -static gint parse_layout (DbusmenuClient * client, const gchar * layout); +static DbusmenuMenuitem * parse_layout_xml(DbusmenuClient * client, GVariant * layout, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, GDBusProxy * proxy); +static gint parse_layout (DbusmenuClient * client, GVariant * layout); static void update_layout_cb (GObject * proxy, GAsyncResult * res, gpointer data); static void update_layout (DbusmenuClient * client); static void menuitem_get_properties_cb (GVariant * properties, GError * error, gpointer data); @@ -1030,11 +1026,60 @@ menuproxy_signal_cb (GDBusProxy * proxy, gchar * sender, gchar * signal, GVarian { g_return_if_fail(DBUSMENU_IS_CLIENT(user_data)); DbusmenuClient * client = DBUSMENU_CLIENT(user_data); + DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client); if (g_strcmp0(signal, "LayoutUpdated") == 0) { guint revision; gint parent; g_variant_get(params, "(ui)", &revision, &parent); layout_update(proxy, revision, parent, client); + } else if (priv->root == NULL) { + /* Drop out here, all the rest of these really need to have a root + node so we can just ignore them if there isn't one. */ + } else if (g_strcmp0(signal, "ItemPropertiesUpdated") == 0) { + /* Remove before adding just incase there is a duplicate, against the + rules, but we can handle it so let's do it. */ + GVariantIter ritems; + g_variant_iter_init(&ritems, g_variant_get_child_value(params, 1)); + + GVariant * ritem; + while ((ritem = g_variant_iter_next_value(&ritems)) != NULL) { + gint id = g_variant_get_int32(g_variant_get_child_value(ritem, 0)); + DbusmenuMenuitem * menuitem = dbusmenu_menuitem_find_id(priv->root, id); + + if (menuitem == NULL) { + continue; + } + + GVariantIter properties; + g_variant_iter_init(&properties, g_variant_get_child_value(ritem, 1)); + gchar * property; + + while (g_variant_iter_next(&properties, "s", &property)) { + g_debug("Removing property '%s' on %d", property, id); + dbusmenu_menuitem_property_remove(menuitem, property); + } + } + + GVariantIter items; + g_variant_iter_init(&items, g_variant_get_child_value(params, 0)); + + GVariant * item; + while ((item = g_variant_iter_next_value(&items)) != NULL) { + gint id = g_variant_get_int32(g_variant_get_child_value(item, 0)); + GVariantIter properties; + g_variant_iter_init(&properties, g_variant_get_child_value(item, 1)); + gchar * property; + GVariant * value; + + while (g_variant_iter_next(&properties, "{sv}", &property, &value)) { + GVariant * internalvalue = value; + if (G_LIKELY(g_variant_is_of_type(value, G_VARIANT_TYPE_VARIANT))) { + /* Unboxing if needed */ + internalvalue = g_variant_get_variant(value); + } + id_prop_update(proxy, id, property, internalvalue, client); + } + } } else if (g_strcmp0(signal, "ItemPropertyUpdated") == 0) { gint id; gchar * property; GVariant * value; g_variant_get(params, "(isv)", &id, &property, &value); @@ -1054,40 +1099,6 @@ menuproxy_signal_cb (GDBusProxy * proxy, gchar * sender, gchar * signal, GVarian return; } -/* Get the ID attribute of the node, parse it and - return it. Also we're checking to ensure the node - is a 'menu' here. */ -static gint -parse_node_get_id (xmlNodePtr node) -{ - if (node == NULL) { - return -1; - } - if (node->type != XML_ELEMENT_NODE) { - return -1; - } - if (g_strcmp0((gchar *)node->name, "menu") != 0) { - /* This kills some nodes early */ - g_warning("XML Node is not 'menu' it is '%s'", node->name); - return -1; - } - - xmlAttrPtr attrib; - for (attrib = node->properties; attrib != NULL; attrib = attrib->next) { - if (g_strcmp0((gchar *)attrib->name, "id") == 0) { - if (attrib->children != NULL) { - gint id = (guint)g_ascii_strtoll((gchar *)attrib->children->content, NULL, 10); - /* g_debug ("Found ID: %d", id); */ - return id; - } - break; - } - } - - g_warning("Unable to find an ID on the node"); - return -1; -} - /* This is the callback for the properties on a menu item. There should be all of them in the Hash, and they we use foreach to copy them into the menuitem. @@ -1265,7 +1276,7 @@ dbusmenu_client_send_event (DbusmenuClient * client, gint id, const gchar * name edata->event = g_strdup(name); edata->timestamp = timestamp; edata->variant = variant; - g_variant_ref(variant); + g_variant_ref_sink(variant); g_dbus_proxy_call(priv->menuproxy, "Event", @@ -1390,10 +1401,14 @@ parse_layout_update (DbusmenuMenuitem * item, DbusmenuClient * client) /* Parse recursively through the XML and make it into objects as need be */ static DbusmenuMenuitem * -parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, GDBusProxy * proxy) +parse_layout_xml(DbusmenuClient * client, GVariant * layout, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, GDBusProxy * proxy) { + if (layout == NULL) { + return NULL; + } + /* First verify and figure out what we've got */ - gint id = parse_node_get_id(node); + gint id = g_variant_get_int32(g_variant_get_child_value(layout, 0)); if (id < 0) { return NULL; } @@ -1405,20 +1420,26 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it g_return_val_if_fail(id == dbusmenu_menuitem_get_id(item), NULL); /* Some variables */ - xmlNodePtr children; - guint position; + GVariantIter children; + g_variant_iter_init(&children, g_variant_get_child_value(layout, 2)); + GVariant * child; + + guint position = 0; GList * oldchildren = g_list_copy(dbusmenu_menuitem_get_children(item)); /* g_debug("Starting old children: %d", g_list_length(oldchildren)); */ /* Go through all the XML Nodes and make sure that we have menuitems to cover those XML nodes. */ - for (children = node->children, position = 0; children != NULL; children = children->next, position++) { + while ((child = g_variant_iter_next_value(&children)) != NULL) { /* g_debug("Looking at child: %d", position); */ - gint childid = parse_node_get_id(children); + if (g_variant_is_of_type(child, G_VARIANT_TYPE_VARIANT)) { + child = g_variant_get_variant(child); + } + + gint childid = g_variant_get_int32(g_variant_get_child_value(child, 0)); if (childid < 0) { /* Don't increment the position when there isn't a valid node in the XML tree. It's probably a comment. */ - position--; continue; } DbusmenuMenuitem * childmi = NULL; @@ -1451,6 +1472,8 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it dbusmenu_menuitem_child_reorder(item, childmi, position); parse_layout_update(childmi, client); } + + position++; } /* Remove any children that are no longer used by this version of @@ -1473,14 +1496,20 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it } /* now it's time to recurse down the tree. */ - children = node->children; + g_variant_iter_init(&children, g_variant_get_child_value(layout, 2)); + + child = g_variant_iter_next_value(&children); GList * childmis = dbusmenu_menuitem_get_children(item); - while (children != NULL && childmis != NULL) { - gint xmlid = parse_node_get_id(children); + while (child != NULL && childmis != NULL) { + if (g_variant_is_of_type(child, G_VARIANT_TYPE_VARIANT)) { + child = g_variant_get_variant(child); + } + + gint xmlid = g_variant_get_int32(g_variant_get_child_value(child, 0)); /* If this isn't a valid menu item we need to move on until we have one. This avoids things like comments. */ if (xmlid < 0) { - children = children->next; + child = g_variant_iter_next_value(&children); continue; } @@ -1489,13 +1518,14 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it g_debug("Recursing parse_layout_xml. XML ID: %d MI ID: %d", xmlid, miid); #endif - parse_layout_xml(client, children, DBUSMENU_MENUITEM(childmis->data), item, proxy); + parse_layout_xml(client, child, DBUSMENU_MENUITEM(childmis->data), item, proxy); - children = children->next; + child = g_variant_iter_next_value(&children); childmis = g_list_next(childmis); } - if (children != NULL) { - g_warning("Sync failed, now we've got extra XML nodes."); + + if (child != NULL) { + g_warning("Sync failed, now we've got extra layout nodes."); } if (childmis != NULL) { g_warning("Sync failed, now we've got extra menu items."); @@ -1507,7 +1537,7 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it /* Take the layout passed to us over DBus and turn it into a set of beautiful objects */ static gint -parse_layout (DbusmenuClient * client, const gchar * layout) +parse_layout (DbusmenuClient * client, GVariant * layout) { #ifdef MASSIVEDEBUGGING g_debug("Client Parsing a new layout"); @@ -1515,17 +1545,6 @@ parse_layout (DbusmenuClient * client, const gchar * layout) DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client); - xmlDocPtr xmldoc; - - /* No one should need more characters than this! */ - xmldoc = xmlReadMemory(layout, g_utf8_strlen(layout, 1024*1024), "dbusmenu.xml", NULL, 0); - - xmlNodePtr root = xmlDocGetRootElement(xmldoc); - - if (root == NULL) { - g_warning("Unable to get root node of menu XML"); - } - DbusmenuMenuitem * oldroot = priv->root; if (priv->root == NULL) { @@ -1534,11 +1553,10 @@ parse_layout (DbusmenuClient * client, const gchar * layout) parse_layout_update(priv->root, client); } - priv->root = parse_layout_xml(client, root, priv->root, NULL, priv->menuproxy); - xmlFreeDoc(xmldoc); + priv->root = parse_layout_xml(client, layout, priv->root, NULL, priv->menuproxy); if (priv->root == NULL) { - g_warning("Unable to parse layout on client %s object %s: %s", priv->dbus_name, priv->dbus_object, layout); + g_warning("Unable to parse layout on client %s object %s: %s", priv->dbus_name, priv->dbus_object, g_variant_print(layout, TRUE)); } if (priv->root != oldroot) { @@ -1579,14 +1597,10 @@ update_layout_cb (GObject * proxy, GAsyncResult * res, gpointer data) goto out; } - guint rev; - gchar * xml; - - g_variant_get(params, "(us)", &rev, &xml); - g_variant_unref(params); + guint rev = g_variant_get_uint32(g_variant_get_child_value(params, 0)); + GVariant * layout = g_variant_get_child_value(params, 1); - guint parseable = parse_layout(client, xml); - g_free(xml); + guint parseable = parse_layout(client, layout); if (parseable == 0) { g_warning("Unable to parse layout!"); @@ -1612,6 +1626,10 @@ out: priv->layoutcall = NULL; } + if (params != NULL) { + g_variant_unref(params); + } + g_object_unref(G_OBJECT(client)); return; } @@ -1639,10 +1657,20 @@ update_layout (DbusmenuClient * client) priv->layoutcall = g_cancellable_new(); + GVariantBuilder tupleb; + g_variant_builder_init(&tupleb, G_VARIANT_TYPE_TUPLE); + + g_variant_builder_add_value(&tupleb, g_variant_new_int32(0)); // root + g_variant_builder_add_value(&tupleb, g_variant_new_int32(-1)); // recurse + g_variant_builder_add_value(&tupleb, g_variant_new_array(G_VARIANT_TYPE_STRING, NULL, 0)); // props + + GVariant * args = g_variant_builder_end(&tupleb); + // g_debug("Args (type: %s): %s", g_variant_get_type_string(args), g_variant_print(args, TRUE)); + g_object_ref(G_OBJECT(client)); g_dbus_proxy_call(priv->menuproxy, "GetLayout", - g_variant_new("(i)", 0), /* root */ + args, G_DBUS_CALL_FLAGS_NONE, -1, /* timeout */ priv->layoutcall, /* cancellable */ diff --git a/libdbusmenu-glib/dbus-menu.xml b/libdbusmenu-glib/dbus-menu.xml index 042a24c..da14c63 100644 --- a/libdbusmenu-glib/dbus-menu.xml +++ b/libdbusmenu-glib/dbus-menu.xml @@ -174,34 +174,38 @@ License version 3 and version 2.1 along with this program. If not, see <!-- Functions --> <method name="GetLayout"> - <dox:d><![CDATA[ - Provides an XML representation of the menu hierarchy - - XML syntax: - - @verbatim -<menu id="0"> # Root container - <menu id="1"> # First level menu, for example "File" - <menu id="2"/> ~ Second level menu, for example "Open" - <menu id="3"/> - ... - </menu> - <menu id="4"> # Another first level menu, say "Edit" - ... - </menu> - ... -</menu> - @endverbatim - ]]></dox:d> + <dox:d> + Provides the layout and propertiers that are attached to the entries + that are in the layout. It only gives the items that are children + of the item that is specified in @parentId. It will return all of the + properties or specific ones depending of the value in @propertyNames. + + The format is recursive, where the second 'v' is in the same format + as the original 'a(ia(sv)a(v))'. If the @recursive flag is set to + less than one then the second array will have zero entries. + </dox:d> <arg type="i" name="parentId" direction="in"> <dox:d>The ID of the parent node for the layout. For grabbing the layout from the root node use zero.</dox:d> </arg> + <arg type="i" name="recurse" direction="in"> + <dox:d> + The amount of levels of recursion to use. -1, as value would + deliver all the items under the @parentId. + </dox:d> + </arg> + <arg type="as" name="propertyNames" direction="in" > + <dox:d> + The list of item properties we are + interested in. If there are no entries in the list all of + the properties will be sent. + </dox:d> + </arg> <arg type="u" name="revision" direction="out"> <dox:d>The revision number of the layout. For matching with layoutUpdated signals.</dox:d> </arg> - <arg type="s" name="layout" direction="out"> + <arg type="(ia{sv}av)" name="layout" direction="out"> <dox:d>The layout as an XML string of IDs.</dox:d> </arg> </method> @@ -236,33 +240,21 @@ License version 3 and version 2.1 along with this program. If not, see </arg> </method> - <method name="GetChildren"> - <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="DBusMenuItemList"/> - <arg type="i" name="id" direction="in" /> - <arg type="as" name="propertyNames" direction="in" /> - <arg type="a(ia{sv})" name="properties" direction="out" /> - </method> - <method name="GetProperty"> - <arg type="i" name="id" direction="in" /> - <arg type="s" name="name" direction="in" /> - <arg type="v" name="value" direction="out" /> - </method> - - <method name="GetProperties"> <dox:d> - Returns multiple properties in one call. This is more efficient than - GetProperty. - + Get a signal property on a single item. This is not useful if you're + going to implement this interface, it should only be used if you're + debugging via a commandline tool. </dox:d> - <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QVariantMap"/> - <arg type="i" name="id" direction="in" > - <dox:d>The item whose properties we want to retrieve.</dox:d> + <arg type="i" name="id" direction="in"> + <dox:d>the id of the item which received the event</dox:d> </arg> - <arg type="as" name="propertyNames" direction="in" > - <dox:d>List of string name of the properties we want. If the list contains no entries, all properties are sent.</dox:d> + <arg type="s" name="name" direction="in"> + <dox:d>the name of the property to get</dox:d> + </arg> + <arg type="v" name="value" direction="out"> + <dox:d>the value of the property</dox:d> </arg> - <arg type="a{sv}" name="properties" direction="out" /> </method> <method name="Event"> @@ -309,25 +301,16 @@ License version 3 and version 2.1 along with this program. If not, see </method> <!-- Signals --> - <signal name="ItemPropertyUpdated"> - <dox:d> - Triggered by the application to notify the applet that the property @a property - from item @a id has changed to @a value. - </dox:d> - <arg type="i" name="id" direction="out" /> - <arg type="s" name="prop" direction="out" /> - <arg type="v" name="value" direction="out" /> - </signal> - - <signal name="ItemUpdated"> + <signal name="ItemsPropertiesUpdated"> <dox:d> - Triggered by the application to notify the applet that all properties of item + Triggered when there are lots of property updates across many items + so they all get grouped into a single dbus message. The format is + the ID of the item with a hashtable of names and values for those + properties. </dox:d> - <arg type="i" name="id" direction="out" > - <dox:d>id which should be considered outdated</dox:d> - </arg> + <arg type="a(ia(sv))" name="props" direction="out" /> + <arg type="a(ia(s))" name="props_removed" direction="out" /> </signal> - <signal name="LayoutUpdated"> <dox:d> Triggered by the application to notify display of a layout update, up to diff --git a/libdbusmenu-glib/menuitem-private.h b/libdbusmenu-glib/menuitem-private.h index 2028464..89319dc 100644 --- a/libdbusmenu-glib/menuitem-private.h +++ b/libdbusmenu-glib/menuitem-private.h @@ -33,10 +33,11 @@ License version 3 and version 2.1 along with this program. If not, see G_BEGIN_DECLS -void dbusmenu_menuitem_buildxml (DbusmenuMenuitem * mi, GPtrArray * array); +GVariant * dbusmenu_menuitem_build_variant (DbusmenuMenuitem * mi, const gchar ** properties, gint recurse); gboolean dbusmenu_menuitem_realized (DbusmenuMenuitem * mi); void dbusmenu_menuitem_set_realized (DbusmenuMenuitem * mi); -GVariant * dbusmenu_menuitem_properties_variant (DbusmenuMenuitem * mi); +GVariant * dbusmenu_menuitem_properties_variant (DbusmenuMenuitem * mi, const gchar ** properties); +gboolean dbusmenu_menuitem_property_is_default (DbusmenuMenuitem * mi, const gchar * property); G_END_DECLS diff --git a/libdbusmenu-glib/menuitem.c b/libdbusmenu-glib/menuitem.c index b5a87a4..c994130 100644 --- a/libdbusmenu-glib/menuitem.c +++ b/libdbusmenu-glib/menuitem.c @@ -1009,7 +1009,7 @@ dbusmenu_menuitem_property_set_variant (DbusmenuMenuitem * mi, const gchar * pro if (value != NULL) { gchar * lprop = g_strdup(property); - g_variant_ref(value); + g_variant_ref_sink(value); if (currentval == NULL || !g_variant_equal((GVariant*)currentval, value)) { g_hash_table_replace(priv->properties, lprop, value); @@ -1214,7 +1214,7 @@ copy_helper (gpointer in_key, gpointer in_value, gpointer in_data) GHashTable * table = (GHashTable *)in_data; gchar * key = (gchar *)in_key; GVariant * value = (GVariant *)in_value; - g_variant_ref(value); + g_variant_ref_sink(value); g_hash_table_insert(table, g_strdup(key), value); return; } @@ -1251,7 +1251,9 @@ dbusmenu_menuitem_properties_copy (DbusmenuMenuitem * mi) static void variant_helper (gpointer in_key, gpointer in_value, gpointer user_data) { - g_variant_builder_add((GVariantBuilder *)user_data, "{sv}", in_key, in_value); + GVariant * value = g_variant_new_dict_entry(g_variant_new_string((gchar *)in_key), + g_variant_new_variant((GVariant *)in_value)); + g_variant_builder_add_value((GVariantBuilder *)user_data, value); return; } @@ -1265,7 +1267,7 @@ variant_helper (gpointer in_key, gpointer in_value, gpointer user_data) * Return Value: (transfer full): A GVariant of type "a{sv}" or NULL on error. */ GVariant * -dbusmenu_menuitem_properties_variant (DbusmenuMenuitem * mi) +dbusmenu_menuitem_properties_variant (DbusmenuMenuitem * mi, const gchar ** properties) { g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL); @@ -1275,7 +1277,7 @@ dbusmenu_menuitem_properties_variant (DbusmenuMenuitem * mi) if (g_hash_table_size(priv->properties) > 0) { GVariantBuilder builder; - g_variant_builder_init(&builder, g_variant_type_new("a{sv}")); + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); g_hash_table_foreach(priv->properties, variant_helper, &builder); @@ -1323,38 +1325,62 @@ dbusmenu_menuitem_get_root (DbusmenuMenuitem * mi) /** - * dbusmenu_menuitem_buildxml: - * @mi: #DbusmenuMenuitem to represent in XML - * @array: (element-type utf8): A list of string that will be turned into an XML file + * dbusmenu_menuitem_buildvariant: + * @mi: #DbusmenuMenuitem to represent in a variant + * @properties: (element-type utf8): A list of string that will be put into + * a variant * - * This function will add strings to the array @array. It will put - * at least one entry if this menu item has no children. If it has - * children it will put two for this entry, one representing the + * This function will put at least one entry if this menu item has no children. + * If it has children it will put two for this entry, one representing the * start tag and one that is a closing tag. It will allow it's * children to place their own tags in the array in between those two. - */ -void -dbusmenu_menuitem_buildxml (DbusmenuMenuitem * mi, GPtrArray * array) + * + * Return value: (transfer full): Variant representing @properties +*/ +GVariant * +dbusmenu_menuitem_build_variant (DbusmenuMenuitem * mi, const gchar ** properties, gint recurse) { - g_return_if_fail(DBUSMENU_IS_MENUITEM(mi)); + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL); gint id = 0; if (!dbusmenu_menuitem_get_root(mi)) { id = dbusmenu_menuitem_get_id(mi); } + /* This is the tuple that'll build up being a representation of + this entry */ + GVariantBuilder tupleb; + g_variant_builder_init(&tupleb, G_VARIANT_TYPE_TUPLE); + + /* Add our ID */ + g_variant_builder_add_value(&tupleb, g_variant_new_int32(id)); + + /* Figure out the properties */ + GVariant * props = dbusmenu_menuitem_properties_variant(mi, properties); + if (props != NULL) { + g_variant_builder_add_value(&tupleb, props); + } else { + g_variant_builder_add_value(&tupleb, g_variant_parse(G_VARIANT_TYPE("a{sv}"), "[ ]", NULL, NULL, NULL)); + } + + /* Pillage the children */ GList * children = dbusmenu_menuitem_get_children(mi); - if (children == NULL) { - g_ptr_array_add(array, g_strdup_printf("<menu id=\"%d\"/>", id)); + if (children == NULL && recurse != 0) { + g_variant_builder_add_value(&tupleb, g_variant_new_array(G_VARIANT_TYPE_VARIANT, NULL, 0)); } else { - g_ptr_array_add(array, g_strdup_printf("<menu id=\"%d\">", id)); + GVariantBuilder childrenbuilder; + g_variant_builder_init(&childrenbuilder, G_VARIANT_TYPE_ARRAY); + for ( ; children != NULL; children = children->next) { - dbusmenu_menuitem_buildxml(DBUSMENU_MENUITEM(children->data), array); + GVariant * child = dbusmenu_menuitem_build_variant(DBUSMENU_MENUITEM(children->data), properties, recurse - 1); + + g_variant_builder_add_value(&childrenbuilder, g_variant_new_variant(child)); } - g_ptr_array_add(array, g_strdup("</menu>")); + + g_variant_builder_add_value(&tupleb, g_variant_builder_end(&childrenbuilder)); } - return; + return g_variant_builder_end(&tupleb); } typedef struct { @@ -1474,3 +1500,13 @@ dbusmenu_menuitem_show_to_user (DbusmenuMenuitem * mi, guint timestamp) return; } + +/* Checks to see if the value of this property is unique or just the + default value. */ +/* TODO: Implement this */ +gboolean +dbusmenu_menuitem_property_is_default (DbusmenuMenuitem * mi, const gchar * property) +{ + /* No defaults system yet */ + return FALSE; +} diff --git a/libdbusmenu-glib/menuitem.h b/libdbusmenu-glib/menuitem.h index 9b538f6..afd6084 100644 --- a/libdbusmenu-glib/menuitem.h +++ b/libdbusmenu-glib/menuitem.h @@ -111,14 +111,15 @@ struct _DbusmenuMenuitem typedef void (*dbusmenu_menuitem_about_to_show_cb) (DbusmenuMenuitem * mi, gpointer user_data); /** - * dbusmenu_menuitem_buildxml_slot_t: + * dbusmenu_menuitem_buildvariant_slot_t: * @mi: (in): Menu item that should be built from - * @stringarray: (inout) (transfer none) (array) (element-type utf8): An array of strings that can be combined into an XML file. * * This is the function that is called to represent this menu item - * as an XML fragment. Should call it's own children. + * as a variant. Should call it's own children. + * + * Return value: (transfer full) A variant representing this item and it's children */ -typedef void (*dbusmenu_menuitem_buildxml_slot_t) (DbusmenuMenuitem * mi, GPtrArray* stringarray); +typedef GVariant * (*dbusmenu_menuitem_buildvariant_slot_t) (DbusmenuMenuitem * mi, gchar ** properties); /** * DbusmenuMenuitemClass: @@ -155,7 +156,7 @@ struct _DbusmenuMenuitemClass void (*realized) (void); /* Virtual functions */ - dbusmenu_menuitem_buildxml_slot_t buildxml; + dbusmenu_menuitem_buildvariant_slot_t buildvariant; void (*handle_event) (DbusmenuMenuitem * mi, const gchar * name, GVariant * variant, guint timestamp); void (*send_about_to_show) (DbusmenuMenuitem * mi, void (*cb) (DbusmenuMenuitem * mi, gpointer user_data), gpointer cb_data); diff --git a/libdbusmenu-glib/server.c b/libdbusmenu-glib/server.c index 777e4ef..aa39991 100644 --- a/libdbusmenu-glib/server.c +++ b/libdbusmenu-glib/server.c @@ -54,6 +54,9 @@ struct _DbusmenuServerPrivate GDBusConnection * bus; GCancellable * bus_lookup; guint dbus_registration; + + GArray * prop_array; + guint property_idle; }; #define DBUSMENU_SERVER_GET_PRIVATE(o) (DBUSMENU_SERVER(o)->priv) @@ -156,6 +159,7 @@ static void menuitem_signals_create (DbusmenuMenuitem * mi, static void menuitem_signals_remove (DbusmenuMenuitem * mi, gpointer data); static GQuark error_quark (void); +static void prop_array_teardown (GArray * prop_array); static void bus_get_layout (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation); @@ -354,6 +358,17 @@ dbusmenu_server_dispose (GObject *object) if (priv->layout_idle != 0) { g_source_remove(priv->layout_idle); + priv->layout_idle = 0; + } + + if (priv->property_idle != 0) { + g_source_remove(priv->property_idle); + priv->property_idle = 0; + } + + if (priv->prop_array != NULL) { + prop_array_teardown(priv->prop_array); + priv->prop_array = NULL; } if (priv->root != NULL) { @@ -418,6 +433,15 @@ 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); + + GList * properties = dbusmenu_menuitem_properties_list(priv->root); + GList * iter; + for (iter = properties; iter != NULL; iter = g_list_next(iter)) { + gchar * property = (gchar *)iter->data; + menuitem_property_changed(priv->root, property, NULL, DBUSMENU_SERVER(obj)); + } + g_list_free(properties); + g_object_unref(G_OBJECT(priv->root)); priv->root = NULL; } @@ -426,6 +450,14 @@ set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec) g_object_ref(G_OBJECT(priv->root)); dbusmenu_menuitem_set_root(priv->root, TRUE); dbusmenu_menuitem_foreach(priv->root, menuitem_signals_create, obj); + + GList * properties = dbusmenu_menuitem_properties_list(priv->root); + GList * iter; + for (iter = properties; iter != NULL; iter = g_list_next(iter)) { + gchar * property = (gchar *)iter->data; + menuitem_property_changed(priv->root, property, dbusmenu_menuitem_property_get_variant(priv->root, property), DBUSMENU_SERVER(obj)); + } + g_list_free(properties); } else { g_debug("Setting root node to NULL"); } @@ -440,17 +472,6 @@ set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec) } static void -xmlarray_foreach_free (gpointer arrayentry, gpointer userdata) -{ - if (arrayentry != NULL) { - /* g_debug("Freeing pointer: %s", (gchar *)arrayentry); */ - g_free(arrayentry); - } - - return; -} - -static void get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec) { DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(obj); @@ -633,22 +654,264 @@ layout_update_signal (DbusmenuServer * server) return; } -static void -menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, GVariant * variant, DbusmenuServer * server) +typedef struct _prop_idle_item_t prop_idle_item_t; +struct _prop_idle_item_t { + gint id; + GArray * array; +}; + +typedef struct _prop_idle_prop_t prop_idle_prop_t; +struct _prop_idle_prop_t { + gchar * property; + GVariant * variant; +}; + +/* Takes appart our data structure so we don't leak any + memory or references. */ +static void +prop_array_teardown (GArray * prop_array) { - DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server); + int i, j; + + for (i = 0; i < prop_array->len; i++) { + prop_idle_item_t * iitem = &g_array_index(prop_array, prop_idle_item_t, i); + + for (j = 0; j < iitem->array->len; j++) { + prop_idle_prop_t * iprop = &g_array_index(iitem->array, prop_idle_prop_t, j); + + g_free(iprop->property); + + if (iprop->variant != NULL) { + g_variant_unref(iprop->variant); + } + } + + g_array_free(iitem->array, TRUE); + } + + g_array_free(prop_array, TRUE); + + return; +} + +/* Works in the idle to send a set of property updates so that they'll + all update in a single dbus message. */ +static gboolean +menuitem_property_idle (gpointer user_data) +{ + DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(user_data); + + /* Source will get removed as we return */ + priv->property_idle = 0; + + /* If there are no items, let's just not signal */ + if (priv->prop_array == NULL) { + return FALSE; + } + + int i, j; + GVariantBuilder itembuilder; + gboolean item_init = FALSE; + + GVariantBuilder removeitembuilder; + gboolean removeitem_init = FALSE; + + for (i = 0; i < priv->prop_array->len; i++) { + prop_idle_item_t * iitem = &g_array_index(priv->prop_array, prop_idle_item_t, i); + + GVariantBuilder dictbuilder; + gboolean dictinit = FALSE; + + GVariantBuilder removedictbuilder; + gboolean removedictinit = FALSE; + + /* Go throught each item and see if it should go in the removal list + or the additive list. */ + for (j = 0; j < iitem->array->len; j++) { + prop_idle_prop_t * iprop = &g_array_index(iitem->array, prop_idle_prop_t, j); + + if (iprop->variant != NULL) { + if (!dictinit) { + g_variant_builder_init(&dictbuilder, G_VARIANT_TYPE_DICTIONARY); + dictinit = TRUE; + } + + GVariant * entry = g_variant_new_dict_entry(g_variant_new_string(iprop->property), + g_variant_new_variant(iprop->variant)); + + g_variant_builder_add_value(&dictbuilder, entry); + } else { + if (!removedictinit) { + g_variant_builder_init(&removedictbuilder, G_VARIANT_TYPE_ARRAY); + removedictinit = TRUE; + } + + g_variant_builder_add_value(&removedictbuilder, g_variant_new_string(iprop->property)); + } + } + + /* If we've got new values that are real values we need to add that + to the list of items to send the value of */ + if (dictinit) { + GVariantBuilder tuplebuilder; + g_variant_builder_init(&tuplebuilder, G_VARIANT_TYPE_TUPLE); + + g_variant_builder_add_value(&tuplebuilder, g_variant_new_int32(iitem->id)); + g_variant_builder_add_value(&tuplebuilder, g_variant_builder_end(&dictbuilder)); + + if (!item_init) { + g_variant_builder_init(&itembuilder, G_VARIANT_TYPE_ARRAY); + item_init = TRUE; + } + + g_variant_builder_add_value(&itembuilder, g_variant_builder_end(&tuplebuilder)); + } + + /* If we've got properties that have been removed then we need to add + them to the list of removed items */ + if (removedictinit) { + GVariantBuilder tuplebuilder; + g_variant_builder_init(&tuplebuilder, G_VARIANT_TYPE_TUPLE); + + g_variant_builder_add_value(&tuplebuilder, g_variant_new_int32(iitem->id)); + g_variant_builder_add_value(&tuplebuilder, g_variant_builder_end(&removedictbuilder)); + + if (!removeitem_init) { + g_variant_builder_init(&removeitembuilder, G_VARIANT_TYPE_ARRAY); + removeitem_init = TRUE; + } - g_signal_emit(G_OBJECT(server), signals[ID_PROP_UPDATE], 0, dbusmenu_menuitem_get_id(mi), property, variant, TRUE); + g_variant_builder_add_value(&removeitembuilder, g_variant_builder_end(&tuplebuilder)); + } + } + + GVariant * megadata[2]; + + if (item_init) { + megadata[0] = g_variant_builder_end(&itembuilder); + } else { + GError * error = NULL; + megadata[0] = g_variant_parse(G_VARIANT_TYPE("a(ia{sv})"), "[ ]", NULL, NULL, &error); + + if (error != NULL) { + g_warning("Unable to parse '[ ]' as a 'a(ia{sv})': %s", error->message); + g_error_free(error); + } + } + + if (removeitem_init) { + megadata[1] = g_variant_builder_end(&removeitembuilder); + } else { + GError * error = NULL; + megadata[1] = g_variant_parse(G_VARIANT_TYPE("a(ia(s))"), "[ ]", NULL, NULL, &error); + + if (error != NULL) { + g_warning("Unable to parse '[ ]' as a 'a(ia(s))': %s", error->message); + g_error_free(error); + } + } if (priv->dbusobject != NULL && priv->bus != NULL) { g_dbus_connection_emit_signal(priv->bus, NULL, priv->dbusobject, DBUSMENU_INTERFACE, - "ItemPropertyUpdated", - g_variant_new("(isv)", dbusmenu_menuitem_get_id(mi), property, variant), + "ItemPropertiesUpdated", + g_variant_new_tuple(megadata, 2), NULL); + } else { + g_variant_unref(megadata[0]); + g_variant_unref(megadata[1]); + } + + /* Clean everything up */ + prop_array_teardown(priv->prop_array); + priv->prop_array = NULL; + + return FALSE; +} + +static void +menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, GVariant * variant, DbusmenuServer * server) +{ + int i; + gint item_id; + + DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server); + + item_id = dbusmenu_menuitem_get_id(mi); + + g_signal_emit(G_OBJECT(server), signals[ID_PROP_UPDATE], 0, item_id, property, variant, TRUE); + + /* See if we have a property array, if not, we need to + build one of these suckers */ + if (priv->prop_array == NULL) { + priv->prop_array = g_array_new(FALSE, FALSE, sizeof(prop_idle_item_t)); } + + /* Look to see if we already have this item in the list + and use it if so */ + prop_idle_item_t * item = NULL; + for (i = 0; i < priv->prop_array->len; i++) { + prop_idle_item_t * iitem = &g_array_index(priv->prop_array, prop_idle_item_t, i); + if (iitem->id == item_id) { + item = iitem; + break; + } + } + + GArray * properties = NULL; + /* If not, we'll need to build ourselves one */ + if (item == NULL) { + prop_idle_item_t myitem; + myitem.id = item_id; + myitem.array = g_array_new(FALSE, FALSE, sizeof(prop_idle_prop_t)); + + g_array_append_val(priv->prop_array, myitem); + properties = myitem.array; + } else { + properties = item->array; + } + + /* Check to see if this property is in the list */ + prop_idle_prop_t * prop = NULL; + for (i = 0; i < properties->len; i++) { + prop_idle_prop_t * iprop = &g_array_index(properties, prop_idle_prop_t, i); + if (g_strcmp0(iprop->property, property) == 0) { + prop = iprop; + break; + } + } + + /* If it's the default value we want to treat it like a clearing + of the value so that it doesn't get sent over dbus and waste + bandwidth */ + if (dbusmenu_menuitem_property_is_default(mi, property)) { + variant = NULL; + } + + /* If so, we need to swap the value */ + if (prop != NULL) { + g_variant_unref(prop->variant); + prop->variant = variant; + } else { + /* else we need to add it */ + prop_idle_prop_t myprop; + myprop.property = g_strdup(property); + myprop.variant = variant; + + g_array_append_val(properties, myprop); + } + if (variant != NULL) { + g_variant_ref_sink(variant); + } + + /* Check to see if the idle is already queued, and queue it + if not. */ + if (priv->property_idle == 0) { + priv->property_idle = g_idle_add(menuitem_property_idle, server); + } + return; } @@ -757,26 +1020,28 @@ bus_get_layout (DbusmenuServer * server, GVariant * params, GDBusMethodInvocatio { DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server); - gint parent = 0; - g_variant_get(params, "(i)", &parent); + /* Input */ + gint parent = g_variant_get_int32(g_variant_get_child_value(params, 0)); + gint recurse = g_variant_get_int32(g_variant_get_child_value(params, 1)); + const gchar ** props = g_variant_get_strv(g_variant_get_child_value(params, 2), NULL); + /* Output */ guint revision = priv->layout_revision; - GPtrArray * xmlarray = g_ptr_array_new(); + GVariant * items = NULL; - if (parent == 0) { - if (priv->root == NULL) { - /* g_debug("Getting layout without root node!"); */ - g_ptr_array_add(xmlarray, g_strdup("<menu id=\"0\"/>")); - } else { - dbusmenu_menuitem_buildxml(priv->root, xmlarray); - } - } else { - DbusmenuMenuitem * item = NULL; - if (priv->root != NULL) { - item = dbusmenu_menuitem_find_id(priv->root, parent); - } + if (priv->root != NULL) { + items = dbusmenu_menuitem_build_variant(priv->root, props, recurse); + } - if (item == NULL) { + /* What happens if we don't have anything? */ + if (items == NULL) { + if (parent == 0) { + /* We should always have a root, so we'll make up one for + right now. */ + items = g_variant_parse(G_VARIANT_TYPE("(ia{sv}av)"), "(0, [], [])", NULL, NULL, NULL); + } else { + /* If we were looking for a specific ID that's an error that + we should send back, so let's do that. */ g_dbus_method_invocation_return_error(invocation, error_quark(), INVALID_MENUITEM_ID, @@ -784,23 +1049,19 @@ bus_get_layout (DbusmenuServer * server, GVariant * params, GDBusMethodInvocatio parent); return; } - dbusmenu_menuitem_buildxml(item, xmlarray); } - g_ptr_array_add(xmlarray, NULL); - /* build string */ - gchar * layout = g_strjoinv("", (gchar **)xmlarray->pdata); + /* Build the final variant tuple */ + GVariantBuilder tuplebuilder; + g_variant_builder_init(&tuplebuilder, G_VARIANT_TYPE_TUPLE); - g_ptr_array_foreach(xmlarray, xmlarray_foreach_free, NULL); - g_ptr_array_free(xmlarray, TRUE); + g_variant_builder_add_value(&tuplebuilder, g_variant_new_uint32(revision)); + g_variant_builder_add_value(&tuplebuilder, items); + GVariant * retval = g_variant_builder_end(&tuplebuilder); + // g_debug("Sending layout type: %s", g_variant_get_type_string(retval)); g_dbus_method_invocation_return_value(invocation, - g_variant_new("(us)", - revision, - layout)); - - g_free(layout); - + retval); return; } @@ -874,7 +1135,7 @@ bus_get_properties (DbusmenuServer * server, GVariant * params, GDBusMethodInvoc return; } - GVariant * dict = dbusmenu_menuitem_properties_variant(mi); + GVariant * dict = dbusmenu_menuitem_properties_variant(mi, NULL); g_dbus_method_invocation_return_value(invocation, g_variant_new("(a{sv})", dict)); @@ -922,7 +1183,7 @@ bus_get_group_properties (DbusmenuServer * server, GVariant * params, GDBusMetho GVariantBuilder wbuilder; g_variant_builder_init(&wbuilder, G_VARIANT_TYPE_TUPLE); g_variant_builder_add(&wbuilder, "i", id); - GVariant * props = dbusmenu_menuitem_properties_variant(mi); + GVariant * props = dbusmenu_menuitem_properties_variant(mi, NULL); if (props == NULL) { GError * error = NULL; @@ -982,7 +1243,7 @@ serialize_menuitem(gpointer data, gpointer user_data) gint id = dbusmenu_menuitem_get_id(mi); g_variant_builder_add_value(&tuple, g_variant_new_int32(id)); - GVariant * props = dbusmenu_menuitem_properties_variant(mi); + GVariant * props = dbusmenu_menuitem_properties_variant(mi, NULL); g_variant_builder_add_value(&tuple, props); g_variant_builder_add_value(builder, g_variant_builder_end(&tuple)); @@ -1105,7 +1366,7 @@ bus_event (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * i event_data->variant = g_variant_get_variant(event_data->variant); } - g_variant_ref(event_data->variant); + g_variant_ref_sink(event_data->variant); g_timeout_add(0, event_local_handler, event_data); |