diff options
author | Ted Gould <ted@canonical.com> | 2009-05-12 11:23:10 -0500 |
---|---|---|
committer | Ted Gould <ted@canonical.com> | 2009-05-12 11:23:10 -0500 |
commit | 3d0e0276fd7856831dcc845a24a252ad304b3bad (patch) | |
tree | 5ec06325d34b0fdab498045aa911fb534eb2ca48 /libdbusmenu-glib | |
parent | c5024e812fb046f6bccceafaf46ebbd966f465cf (diff) | |
parent | 53b4a13ca4541bcdf85ca3ee28cb6ffc81e6f5b0 (diff) | |
download | libdbusmenu-3d0e0276fd7856831dcc845a24a252ad304b3bad.tar.gz libdbusmenu-3d0e0276fd7856831dcc845a24a252ad304b3bad.tar.bz2 libdbusmenu-3d0e0276fd7856831dcc845a24a252ad304b3bad.zip |
Merging in my working branch that brings in basic object functionality passing across DBus.
Diffstat (limited to 'libdbusmenu-glib')
-rw-r--r-- | libdbusmenu-glib/Makefile.am | 52 | ||||
-rw-r--r-- | libdbusmenu-glib/client.c | 499 | ||||
-rw-r--r-- | libdbusmenu-glib/client.h | 116 | ||||
-rw-r--r-- | libdbusmenu-glib/dbus-menu.xml | 70 | ||||
-rw-r--r-- | libdbusmenu-glib/menuitem.c | 424 | ||||
-rw-r--r-- | libdbusmenu-glib/menuitem.h | 135 | ||||
-rw-r--r-- | libdbusmenu-glib/server-marshal.list | 1 | ||||
-rw-r--r-- | libdbusmenu-glib/server.c | 332 | ||||
-rw-r--r-- | libdbusmenu-glib/server.h | 120 | ||||
-rw-r--r-- | libdbusmenu-glib/test.c | 4 | ||||
-rw-r--r-- | libdbusmenu-glib/test.h | 2 |
11 files changed, 1744 insertions, 11 deletions
diff --git a/libdbusmenu-glib/Makefile.am b/libdbusmenu-glib/Makefile.am index c94231b..b273555 100644 --- a/libdbusmenu-glib/Makefile.am +++ b/libdbusmenu-glib/Makefile.am @@ -1,6 +1,7 @@ EXTRA_DIST = \ - dbusmenu-glib.pc.in + dbusmenu-glib.pc.in \ + dbus-menu.xml lib_LTLIBRARIES = \ libdbusmenu-glib.la @@ -8,10 +9,21 @@ lib_LTLIBRARIES = \ libdbusmenu_glibincludedir=$(includedir)/libdbusmenu-0.1/libdbusmenu-glib/ libdbusmenu_glibinclude_HEADERS = \ - test.h + menuitem.h \ + server.h \ + client.h libdbusmenu_glib_la_SOURCES = \ - test.c + dbusmenu-server.h \ + dbusmenu-client.h \ + menuitem.h \ + menuitem.c \ + server.h \ + server.c \ + server-marshal.h \ + server-marshal.c \ + client.h \ + client.c libdbusmenu_glib_la_LDFLAGS = \ -version-info $(LIBDBUSMENU_CURRENT):$(LIBDBUSMENU_REVISION):$(LIBDBUSMENU_AGE) \ @@ -19,11 +31,41 @@ libdbusmenu_glib_la_LDFLAGS = \ -export-symbols-regex "^[^_].*" libdbusmenu_glib_la_CFLAGS = \ - $(LIBDBUSMENU_GLIB_CFLAGS) + $(DBUSMENUGLIB_CFLAGS) -Wall -Werror libdbusmenu_glib_la_LIBADD = \ - $(LIBDBUSMENU_GLIB_LIBS) + $(DBUSMENUGLIB_LIBS) pkgconfig_DATA = dbusmenu-glib.pc pkgconfigdir = $(libdir)/pkgconfig +BUILT_SOURCES = \ + dbusmenu-client.h \ + dbusmenu-server.h \ + server-marshal.h \ + server-marshal.c + +dbusmenu-server.h: dbus-menu.xml + dbus-binding-tool \ + --prefix=_dbusmenu_server \ + --mode=glib-server \ + --output=dbusmenu-server.h \ + $(srcdir)/dbus-menu.xml + +dbusmenu-client.h: dbus-menu.xml + dbus-binding-tool \ + --prefix=_dbusmenu_client \ + --mode=glib-client \ + --output=dbusmenu-client.h \ + $(srcdir)/dbus-menu.xml + +server-marshal.h: $(srcdir)/server-marshal.list + glib-genmarshal --header \ + --prefix=_dbusmenu_server_marshal $(srcdir)/server-marshal.list \ + > server-marshal.h + +server-marshal.c: $(srcdir)/server-marshal.list + glib-genmarshal --body \ + --prefix=_dbusmenu_server_marshal $(srcdir)/server-marshal.list \ + > server-marshal.c + diff --git a/libdbusmenu-glib/client.c b/libdbusmenu-glib/client.c new file mode 100644 index 0000000..b0b1157 --- /dev/null +++ b/libdbusmenu-glib/client.c @@ -0,0 +1,499 @@ +/* +A library to communicate a menu object set accross DBus and +track updates and maintain consistency. + +Copyright 2009 Canonical Ltd. + +Authors: + Ted Gould <ted@canonical.com> + +This program is free software: you can redistribute it and/or modify it +under the terms of either or both of the following licenses: + +1) the GNU Lesser General Public License version 3, as published by the +Free Software Foundation; and/or +2) the GNU Lesser General Public License version 2.1, as published by +the Free Software Foundation. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranties of +MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR +PURPOSE. See the applicable version of the GNU Lesser General Public +License for more details. + +You should have received a copy of both the GNU Lesser General Public +License version 3 and version 2.1 along with this program. If not, see +<http://www.gnu.org/licenses/> +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <libxml/parser.h> +#include <libxml/tree.h> + +#include "client.h" +#include "dbusmenu-client.h" + +/* Properties */ +enum { + PROP_0, + PROP_DBUSOBJECT, + PROP_DBUSNAME +}; + +/* Signals */ +enum { + LAYOUT_UPDATED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef struct _DbusmenuClientPrivate DbusmenuClientPrivate; +struct _DbusmenuClientPrivate +{ + DbusmenuMenuitem * root; + + gchar * dbus_object; + gchar * dbus_name; + + DBusGConnection * session_bus; + DBusGProxy * menuproxy; + DBusGProxy * propproxy; + DBusGProxyCall * layoutcall; +}; + +#define DBUSMENU_CLIENT_GET_PRIVATE(o) \ +(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_CLIENT, DbusmenuClientPrivate)) + +/* GObject Stuff */ +static void dbusmenu_client_class_init (DbusmenuClientClass *klass); +static void dbusmenu_client_init (DbusmenuClient *self); +static void dbusmenu_client_dispose (GObject *object); +static void dbusmenu_client_finalize (GObject *object); +static void set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec); +static void get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec); +/* Private Funcs */ +static void layout_update (DBusGProxy * proxy, DbusmenuClient * client); +static void build_proxies (DbusmenuClient * client); +static guint parse_node_get_id (xmlNodePtr node); +static DbusmenuMenuitem * parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent); +static void parse_layout (DbusmenuClient * client, const gchar * layout); +static void update_layout_cb (DBusGProxy * proxy, DBusGProxyCall * call, void * data); +static void update_layout (DbusmenuClient * client); + +/* Build a type */ +G_DEFINE_TYPE (DbusmenuClient, dbusmenu_client, G_TYPE_OBJECT); + +static void +dbusmenu_client_class_init (DbusmenuClientClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (DbusmenuClientPrivate)); + + object_class->dispose = dbusmenu_client_dispose; + object_class->finalize = dbusmenu_client_finalize; + object_class->set_property = set_property; + object_class->get_property = get_property; + + /** + DbusmenuClient::layout-update: + @arg0: The #DbusmenuClient object + + Tells that the layout has been updated and parsed by + this object and is ready for grabbing by the calling + application. + */ + signals[LAYOUT_UPDATED] = g_signal_new(DBUSMENU_CLIENT_SIGNAL_LAYOUT_UPDATED, + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (DbusmenuClientClass, layout_updated), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0, G_TYPE_NONE); + + g_object_class_install_property (object_class, PROP_DBUSOBJECT, + g_param_spec_string(DBUSMENU_CLIENT_PROP_DBUS_OBJECT, "DBus Object we represent", + "The Object on the client that we're getting our data from.", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_DBUSNAME, + g_param_spec_string(DBUSMENU_CLIENT_PROP_DBUS_NAME, "DBus Client we connect to", + "Name of the DBus client we're connecting to.", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + return; +} + +static void +dbusmenu_client_init (DbusmenuClient *self) +{ + DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(self); + + priv->root = NULL; + + priv->dbus_object = NULL; + priv->dbus_name = NULL; + + priv->session_bus = NULL; + priv->menuproxy = NULL; + priv->propproxy = NULL; + priv->layoutcall = NULL; + + return; +} + +static void +dbusmenu_client_dispose (GObject *object) +{ + DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(object); + + if (priv->layoutcall != NULL) { + dbus_g_proxy_cancel_call(priv->propproxy, priv->layoutcall); + priv->layoutcall = NULL; + } + if (priv->menuproxy != NULL) { + g_object_unref(G_OBJECT(priv->menuproxy)); + priv->menuproxy = NULL; + } + if (priv->propproxy != NULL) { + g_object_unref(G_OBJECT(priv->propproxy)); + priv->propproxy = NULL; + } + priv->session_bus = NULL; + + G_OBJECT_CLASS (dbusmenu_client_parent_class)->dispose (object); + return; +} + +static void +dbusmenu_client_finalize (GObject *object) +{ + DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(object); + + g_free(priv->dbus_name); + g_free(priv->dbus_object); + + G_OBJECT_CLASS (dbusmenu_client_parent_class)->finalize (object); + return; +} + +static void +set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec) +{ + DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(obj); + + switch (id) { + case PROP_DBUSNAME: + priv->dbus_name = g_value_dup_string(value); + if (priv->dbus_name != NULL && priv->dbus_object != NULL) { + build_proxies(DBUSMENU_CLIENT(obj)); + } + break; + case PROP_DBUSOBJECT: + priv->dbus_object = g_value_dup_string(value); + if (priv->dbus_name != NULL && priv->dbus_object != NULL) { + build_proxies(DBUSMENU_CLIENT(obj)); + } + break; + default: + g_warning("Unknown property %d.", id); + return; + } + + return; +} + +static void +get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec) +{ + DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(obj); + + switch (id) { + case PROP_DBUSNAME: + g_value_set_string(value, priv->dbus_name); + break; + case PROP_DBUSOBJECT: + g_value_set_string(value, priv->dbus_object); + break; + default: + g_warning("Unknown property %d.", id); + return; + } + + return; +} + +/* Internal funcs */ + +/* Annoying little wrapper to make the right function update */ +static void +layout_update (DBusGProxy * proxy, DbusmenuClient * client) +{ + update_layout(client); + return; +} + +/* When we have a name and an object, build the two proxies and get the + first version of the layout */ +static void +build_proxies (DbusmenuClient * client) +{ + DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client); + GError * error = NULL; + + g_return_if_fail(priv->dbus_object != NULL); + g_return_if_fail(priv->dbus_name != NULL); + + priv->session_bus = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + if (error != NULL) { + g_error("Unable to get session bus: %s", error->message); + g_error_free(error); + return; + } + + priv->propproxy = dbus_g_proxy_new_for_name_owner(priv->session_bus, + priv->dbus_name, + priv->dbus_object, + DBUS_INTERFACE_PROPERTIES, + &error); + if (error != NULL) { + g_error("Unable to get property proxy for %s on %s: %s", priv->dbus_name, priv->dbus_object, error->message); + g_error_free(error); + return; + } + + priv->menuproxy = dbus_g_proxy_new_for_name_owner(priv->session_bus, + priv->dbus_name, + priv->dbus_object, + "org.freedesktop.dbusmenu", + &error); + if (error != NULL) { + g_error("Unable to get dbusmenu proxy for %s on %s: %s", priv->dbus_name, priv->dbus_object, error->message); + g_error_free(error); + return; + } + + dbus_g_proxy_add_signal(priv->menuproxy, "LayoutUpdate", G_TYPE_INVALID); + dbus_g_proxy_connect_signal(priv->menuproxy, "LayoutUpdate", G_CALLBACK(layout_update), client, NULL); + + 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 guint +parse_node_get_id (xmlNodePtr node) +{ + 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 0; + } + + xmlAttrPtr attrib; + for (attrib = node->properties; attrib != NULL; attrib = attrib->next) { + if (g_strcmp0((gchar *)attrib->name, "id") == 0) { + if (attrib->children != NULL) { + guint id = (guint)g_ascii_strtoull((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 0; +} + +/* Parse recursively through the XML and make it into + objects as need be */ +static DbusmenuMenuitem * +parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent) +{ + guint id = parse_node_get_id(node); + g_debug("Looking at node with id: %d", id); + if (item == NULL || dbusmenu_menuitem_get_id(item) != id || id == 0) { + if (item != NULL) { + if (parent != NULL) { + dbusmenu_menuitem_child_delete(parent, item); + } + g_object_unref(G_OBJECT(item)); + } + + if (id == 0) { + g_warning("ID from XML file is zero"); + return NULL; + } + + /* Build a new item */ + item = dbusmenu_menuitem_new_with_id(id); + } + + xmlNodePtr children; + guint position; + GList * oldchildren = dbusmenu_menuitem_take_children(item); + + for (children = node->children, position = 0; children != NULL; children = children->next, position++) { + g_debug("Looking at child: %d", position); + guint childid = parse_node_get_id(children); + DbusmenuMenuitem * childmi = NULL; + + GList * childsearch = NULL; + for (childsearch = oldchildren; childsearch != NULL; childsearch = g_list_next(childsearch)) { + DbusmenuMenuitem * cs_mi = DBUSMENU_MENUITEM(childsearch->data); + if (childid == dbusmenu_menuitem_get_id(cs_mi)) { + oldchildren = g_list_remove(oldchildren, cs_mi); + childmi = cs_mi; + break; + } + } + + childmi = parse_layout_xml(children, childmi, item); + dbusmenu_menuitem_child_add_position(item, childmi, position); + } + + GList * oldchildleft = NULL; + for (oldchildleft = oldchildren; oldchildleft != NULL; oldchildleft = g_list_next(oldchildleft)) { + DbusmenuMenuitem * oldmi = DBUSMENU_MENUITEM(oldchildleft->data); + g_object_unref(G_OBJECT(oldmi)); + } + g_list_free(oldchildren); + + return item; +} + +/* Take the layout passed to us over DBus and turn it into + a set of beautiful objects */ +static void +parse_layout (DbusmenuClient * client, const gchar * layout) +{ + DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client); + + xmlDocPtr xmldoc; + + xmldoc = xmlReadMemory(layout, g_utf8_strlen(layout, 16*1024), "dbusmenu.xml", NULL, 0); + + xmlNodePtr root = xmlDocGetRootElement(xmldoc); + + priv->root = parse_layout_xml(root, priv->root, NULL); + if (priv->root == NULL) { + g_warning("Unable to parse layout on client %s object %s: %s", priv->dbus_name, priv->dbus_object, layout); + } + + xmlFreeDoc(xmldoc); + return; +} + +/* When the layout property returns, here's where we take care of that. */ +static void +update_layout_cb (DBusGProxy * proxy, DBusGProxyCall * call, void * data) +{ + DbusmenuClient * client = DBUSMENU_CLIENT(data); + DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client); + + GError * error = NULL; + GValue value = {0}; + + if (!dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_VALUE, &value, G_TYPE_INVALID)) { + g_warning("Getting layout failed on client %s object %s: %s", priv->dbus_name, priv->dbus_object, error->message); + priv->layoutcall = NULL; + g_error_free(error); + return; + } + + const gchar * xml = g_value_get_string(&value); + g_debug("Got layout string: %s", xml); + parse_layout(client, xml); + + priv->layoutcall = NULL; + g_debug("Root is now: 0x%X", (unsigned int)priv->root); + g_signal_emit(G_OBJECT(client), signals[LAYOUT_UPDATED], 0, TRUE); + + return; +} + +/* Call the property on the server we're connected to and set it up to + be async back to _update_layout_cb */ +static void +update_layout (DbusmenuClient * client) +{ + DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client); + + if (priv->layoutcall != NULL) { + return; + } + + priv->layoutcall = dbus_g_proxy_begin_call (priv->propproxy, + "Get", + update_layout_cb, + client, + NULL, + G_TYPE_STRING, "org.freedesktop.dbusmenu", + G_TYPE_STRING, "layout", + G_TYPE_INVALID, G_TYPE_VALUE, G_TYPE_INVALID); + + return; +} + +/* Public API */ +/** + dbusmenu_client_new: + @name: The DBus name for the server to connect to + @object: The object on the server to monitor + + This function creates a new client that connects to a specific + server on DBus. That server is at a specific location sharing + a known object. The interface is assumed by the code to be + the DBus menu interface. The newly created client will start + sending out events as it syncs up with the server. + + Return value: A brand new #DbusmenuClient +*/ +DbusmenuClient * +dbusmenu_client_new (const gchar * name, const gchar * object) +{ + DbusmenuClient * self = g_object_new(DBUSMENU_TYPE_CLIENT, + DBUSMENU_CLIENT_PROP_DBUS_NAME, name, + DBUSMENU_CLIENT_PROP_DBUS_OBJECT, object, + NULL); + update_layout(self); + + return self; +} + +/** + dbusmenu_client_get_root: + @client: The #DbusmenuClient to get the root node from + + Grabs the root node for the specified client @client. This + function may block. It will block if there is currently a + call to update the layout, it will block on that layout + updated and then return the newly updated layout. Chances + are that this update is in the queue for the mainloop as + it would have been requested some time ago, but in theory + it could block longer. + + Return value: A #DbusmenuMenuitem representing the root of + menu on the server. +*/ +DbusmenuMenuitem * +dbusmenu_client_get_root (DbusmenuClient * client) +{ + g_return_val_if_fail(DBUSMENU_IS_CLIENT(client), NULL); + + DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client); + + if (priv->layoutcall != NULL) { + /* Will end the current call and block on it's completion */ + update_layout_cb(priv->propproxy, priv->layoutcall, client); + } + + return priv->root; +} diff --git a/libdbusmenu-glib/client.h b/libdbusmenu-glib/client.h new file mode 100644 index 0000000..d591ebb --- /dev/null +++ b/libdbusmenu-glib/client.h @@ -0,0 +1,116 @@ +/* +A library to communicate a menu object set accross DBus and +track updates and maintain consistency. + +Copyright 2009 Canonical Ltd. + +Authors: + Ted Gould <ted@canonical.com> + +This program is free software: you can redistribute it and/or modify it +under the terms of either or both of the following licenses: + +1) the GNU Lesser General Public License version 3, as published by the +Free Software Foundation; and/or +2) the GNU Lesser General Public License version 2.1, as published by +the Free Software Foundation. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranties of +MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR +PURPOSE. See the applicable version of the GNU Lesser General Public +License for more details. + +You should have received a copy of both the GNU Lesser General Public +License version 3 and version 2.1 along with this program. If not, see +<http://www.gnu.org/licenses/> +*/ + +#ifndef __DBUSMENU_CLIENT_H__ +#define __DBUSMENU_CLIENT_H__ + +#include <glib.h> +#include <glib-object.h> + +#include "menuitem.h" + +G_BEGIN_DECLS + +#define DBUSMENU_TYPE_CLIENT (dbusmenu_client_get_type ()) +#define DBUSMENU_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DBUSMENU_TYPE_CLIENT, DbusmenuClient)) +#define DBUSMENU_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DBUSMENU_TYPE_CLIENT, DbusmenuClientClass)) +#define DBUSMENU_IS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DBUSMENU_TYPE_CLIENT)) +#define DBUSMENU_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_TYPE_CLIENT)) +#define DBUSMENU_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_TYPE_CLIENT, DbusmenuClientClass)) + +#define DBUSMENU_CLIENT_SIGNAL_LAYOUT_UPDATED "layout-updated" + +#define DBUSMENU_CLIENT_PROP_DBUS_NAME "dbus-name" +#define DBUSMENU_CLIENT_PROP_DBUS_OBJECT "dbus-object" + +/** + DbusmenuClientClass: + @parent_class: #GObjectClass + @layout_updated: Slot for #DbusmenuClient::layout-updated. + @reserved1: Reserved for future use. + @reserved2: Reserved for future use. + @reserved3: Reserved for future use. + @reserved4: Reserved for future use. + + A simple class that takes all of the information from a + #DbusmenuServer over DBus and makes the same set of + #DbusmenuMenuitem objects appear on the other side. +*/ +typedef struct _DbusmenuClientClass DbusmenuClientClass; +struct _DbusmenuClientClass { + GObjectClass parent_class; + + void (*layout_updated)(void); + + /* Reserved for future use */ + void (*reserved1) (void); + void (*reserved2) (void); + void (*reserved3) (void); + void (*reserved4) (void); +}; + +/** + DbusmenuClient: + @parent: #GObject. + + The client for a #DbusmenuServer creating a shared + object set of #DbusmenuMenuitem objects. +*/ +typedef struct _DbusmenuClient DbusmenuClient; +struct _DbusmenuClient { + GObject parent; +}; + +GType dbusmenu_client_get_type (void); +DbusmenuClient * dbusmenu_client_new (const gchar * name, const gchar * object); +DbusmenuMenuitem * dbusmenu_client_get_root (DbusmenuClient * client); + +/** + SECTION:client + @short_description: The catcher of all the server traffic + @stability: Unstable + @include: libdbusmenu-glib/client.h + + The client exists as a mirror to the server. For most folks + all they will do with a client is set it up to connect to + a server and then watch as the menu items on their side + of the bus change. This is all they should need to know about + the client, that it magically makes their menuitems dance. + + It does this by setting up signal watchers and adjusting + the menuitems appropriately. Most users should watch the + menu items and the signal #DbusmenuClient::layout-changed for + larger events so that they can be optimized. It is possible + with that signal that even the root node would change. If + that doesn't happen the normal signals on the individual + nodes should be enough for most users. +*/ + +G_END_DECLS + +#endif diff --git a/libdbusmenu-glib/dbus-menu.xml b/libdbusmenu-glib/dbus-menu.xml new file mode 100644 index 0000000..7c41ac2 --- /dev/null +++ b/libdbusmenu-glib/dbus-menu.xml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +A library to allow applictions to provide simple indications of +information to be displayed to users of the application through the +interface shell. + +Copyright 2009 Canonical Ltd. + +Authors: + Ted Gould <ted@canonical.com> + +This program is free software: you can redistribute it and/or modify it +under the terms of either or both of the following licenses: + +1) the GNU Lesser General Public License version 3, as published by the +Free Software Foundation; and/or +2) the GNU Lesser General Public License version 2.1, as published by +the Free Software Foundation. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranties of +MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR +PURPOSE. See the applicable version of the GNU Lesser General Public +License for more details. + +You should have received a copy of both the GNU Lesser General Public +License version 3 and version 2.1 along with this program. If not, see +<http://www.gnu.org/licenses/> +--> +<node name="/"> + <interface name="org.freedesktop.dbusmenu"> + +<!-- Properties --> + <property name="layout" type="s" access="read"/> + +<!-- Functions --> + <method name="GetProperty"> + <arg type="u" name="id" direction="in" /> + <arg type="s" name="property" direction="in" /> + <arg type="s" name="value" direction="out" /> + </method> + <method name="GetProperties"> + <arg type="u" name="id" direction="in" /> + <arg type="as" name="property" direction="in" /> + <arg type="a(ss)" name="value" direction="out" /> + </method> + <method name="Call"> + <arg type="u" name="id" direction="in" /> + </method> + <method name="ListProperties"> + <arg type="u" name="id" direction="in" /> + <arg type="as" name="properties" direction="out" /> + </method> + +<!-- Signals --> + <signal name="IdPropUpdate"> + <arg type="u" name="id" direction="out" /> + <arg type="s" name="prop" direction="out" /> + <arg type="s" name="value" direction="out" /> + </signal> + <signal name="IdUpdate"> + <arg type="u" name="id" direction="out" /> + </signal> + <signal name="LayoutUpdate"> + </signal> + +<!-- End of interesting stuff --> + + </interface> +</node> diff --git a/libdbusmenu-glib/menuitem.c b/libdbusmenu-glib/menuitem.c new file mode 100644 index 0000000..0894d2c --- /dev/null +++ b/libdbusmenu-glib/menuitem.c @@ -0,0 +1,424 @@ +/* +A library to communicate a menu object set accross DBus and +track updates and maintain consistency. + +Copyright 2009 Canonical Ltd. + +Authors: + Ted Gould <ted@canonical.com> + +This program is free software: you can redistribute it and/or modify it +under the terms of either or both of the following licenses: + +1) the GNU Lesser General Public License version 3, as published by the +Free Software Foundation; and/or +2) the GNU Lesser General Public License version 2.1, as published by +the Free Software Foundation. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranties of +MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR +PURPOSE. See the applicable version of the GNU Lesser General Public +License for more details. + +You should have received a copy of both the GNU Lesser General Public +License version 3 and version 2.1 along with this program. If not, see +<http://www.gnu.org/licenses/> +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "menuitem.h" + +/* Private */ +/** + DbusmenuMenuitemPrivate: + @id: The ID of this menu item + @children: A list of #DbusmenuMenuitem objects that are + children to this one. + + These are the little secrets that we don't want getting + out of data that we have. They can still be gotten using + accessor functions, but are protected appropriately. +*/ +typedef struct _DbusmenuMenuitemPrivate DbusmenuMenuitemPrivate; +struct _DbusmenuMenuitemPrivate +{ + guint id; + GList * children; +}; + +/* Properties */ +enum { + PROP_0, + PROP_ID, +}; + +#define DBUSMENU_MENUITEM_GET_PRIVATE(o) \ +(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_MENUITEM, DbusmenuMenuitemPrivate)) + +/* Prototypes */ +static void dbusmenu_menuitem_class_init (DbusmenuMenuitemClass *klass); +static void dbusmenu_menuitem_init (DbusmenuMenuitem *self); +static void dbusmenu_menuitem_dispose (GObject *object); +static void dbusmenu_menuitem_finalize (GObject *object); +static void set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec); +static void get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec); + +/* GObject stuff */ +G_DEFINE_TYPE (DbusmenuMenuitem, dbusmenu_menuitem, G_TYPE_OBJECT); + +static void +dbusmenu_menuitem_class_init (DbusmenuMenuitemClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (DbusmenuMenuitemPrivate)); + + object_class->dispose = dbusmenu_menuitem_dispose; + object_class->finalize = dbusmenu_menuitem_finalize; + object_class->set_property = set_property; + object_class->get_property = get_property; + + g_object_class_install_property (object_class, PROP_ID, + g_param_spec_uint("id", "ID for the menu item", + "This is a unique indentifier for the menu item.", + 0, 30000, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + return; +} + +static guint menuitem_next_id = 1; + +static void +dbusmenu_menuitem_init (DbusmenuMenuitem *self) +{ + DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(self); + + priv->id = 0; + priv->children = NULL; + + return; +} + +static void +dbusmenu_menuitem_dispose (GObject *object) +{ + + G_OBJECT_CLASS (dbusmenu_menuitem_parent_class)->dispose (object); + return; +} + +static void +dbusmenu_menuitem_finalize (GObject *object) +{ + + G_OBJECT_CLASS (dbusmenu_menuitem_parent_class)->finalize (object); + return; +} + +static void +set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec) +{ + DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(obj); + + switch (id) { + case PROP_ID: + priv->id = g_value_get_uint(value); + if (priv->id > menuitem_next_id) { + menuitem_next_id = priv->id; + } + break; + } + + return; +} + +static void +get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec) +{ + DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(obj); + + switch (id) { + case PROP_ID: + if (priv->id == 0) { + priv->id = menuitem_next_id++; + } + g_value_set_uint(value, priv->id); + break; + } + + return; +} + + +/* Public interface */ + +/** + dbusmenu_menuitem_new: + + Create a new #DbusmenuMenuitem with all default values. + + Return value: A newly allocated #DbusmenuMenuitem. +*/ +DbusmenuMenuitem * +dbusmenu_menuitem_new (void) +{ + return g_object_new(DBUSMENU_TYPE_MENUITEM, NULL); +} + +/** + dbusmenu_menuitem_new_with_id: + @id: ID to use for this menuitem + + This creates a blank #DbusmenuMenuitem with a specific ID. + + Return value: A newly allocated #DbusmenuMenuitem. +*/ +DbusmenuMenuitem * +dbusmenu_menuitem_new_with_id (guint id) +{ + DbusmenuMenuitem * mi = g_object_new(DBUSMENU_TYPE_MENUITEM, "id", id, NULL); + g_debug("New Menuitem id %d goal id %d", dbusmenu_menuitem_get_id(mi), id); + return mi; +} + +/** + dbusmenu_menuitem_get_id: + @mi: The #DbusmenuMenuitem to query. + + Gets the unique ID for @mi. + + Return value: The ID of the @mi. +*/ +guint +dbusmenu_menuitem_get_id (DbusmenuMenuitem * mi) +{ + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), 0); + + GValue retval = {0}; + g_value_init(&retval, G_TYPE_UINT); + g_object_get_property(G_OBJECT(mi), "id", &retval); + return g_value_get_uint(&retval); +} + +/** + dbusmenu_menuitem_get_children: + @mi: The #DbusmenuMenuitem to query. + + Returns simply the list of children that this menu item + has. The list is valid until another child related function + is called, where it might be changed. + + Return value: A #GList of pointers to #DbusmenuMenuitem objects. +*/ +GList * +dbusmenu_menuitem_get_children (DbusmenuMenuitem * mi) +{ + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL); + + DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi); + return priv->children; +} + +/** + dbusmenu_menuitem_take_children: + @mi: The #DbusmenMenuitem to take the children from. + + While the name sounds devious that's exactly what this function + does. It takes the list of children from the @mi and clears the + internal list. The calling function is no in charge of the ref's + on the children it has taken. A lot of responsibility involved + in taking children. + + Return value: A #GList of pointers to #DbusmenuMenuitem objects. +*/ +GList * +dbusmenu_menuitem_take_children (DbusmenuMenuitem * mi) +{ + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL); + + DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi); + GList * children = priv->children; + priv->children = NULL; + return children; +} + +/** + dbusmenu_menuitem_get_position: + @mi: The #DbusmenuMenuitem to find the position of + @parent: The #DbusmenuMenuitem who's children contain @mi + + This function returns the position of the menu item @mi + in the children of @parent. It will return zero if the + menu item can't be found. + + Return value: The position of @mi in the children of @parent. +*/ +guint +dbusmenu_menuitem_get_position (DbusmenuMenuitem * mi, DbusmenuMenuitem * parent) +{ + /* TODO: I'm not too happy returning zeros here. But that's all I've got */ + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), 0); + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(parent), 0); + + GList * childs = dbusmenu_menuitem_get_children(parent); + if (childs == NULL) return 0; + guint count = 0; + for ( ; childs != NULL; childs = childs->next, count++) { + if (childs->data == mi) break; + } + + if (childs == NULL) return 0; + + return count; +} + +/** + dbusmenu_menuitem_child_append: + @mi: The #DbusmenuMenuitem which will become a new parent + @child: The #DbusmenMenuitem that will be a child + + This function adds @child to the list of children on @mi at + the end of that list. + + Return value: Whether the child has been added successfully. +*/ +gboolean +dbusmenu_menuitem_child_append (DbusmenuMenuitem * mi, DbusmenuMenuitem * child) +{ + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE); + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(child), FALSE); + + DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi); + priv->children = g_list_append(priv->children, child); + return TRUE; +} + +/** + dbusmenu_menuitem_child_delete: + @mi: The #DbusmenuMenuitem which has @child as a child + @child: The child #DbusmenuMenuitem that you want to no longer + be a child of @mi. + + This function removes @child from the children list of @mi. It does + not call #g_object_unref on @child. + + Return value: If we were able to delete @child. +*/ +gboolean +dbusmenu_menuitem_child_delete (DbusmenuMenuitem * mi, DbusmenuMenuitem * child) +{ + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE); + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(child), FALSE); + + DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi); + priv->children = g_list_remove(priv->children, child); + return TRUE; +} + +/** + dbusmenu_menuitem_child_add_position: + @mi: The #DbusmenuMenuitem that we're adding the child @child to. + @child: The #DbusmenuMenuitem to make a child of @mi. + @position: Where in @mi object's list of chidren @child should be placed. + + Puts @child in the list of children for @mi at the location + specified in @position. If there is not enough entires available + then @child will be placed at the end of the list. + + Return value: Whether @child was added successfully. +*/ +gboolean +dbusmenu_menuitem_child_add_position (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position) +{ + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE); + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(child), FALSE); + + DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi); + priv->children = g_list_insert(priv->children, child, position); + return TRUE; +} + +/** + dbusmenu_menuitem_child_find: + @mi: The #DbusmenuMenuitem who's children to look on + @id: The ID of the child that we're looking for. + + Search the children of @mi to find one with the ID of @id. + If it doesn't exist then we return #NULL. + + Return value: The menu item with the ID @id or #NULL if it + can't be found. +*/ +DbusmenuMenuitem * +dbusmenu_menuitem_child_find (DbusmenuMenuitem * mi, guint id) +{ + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL); + + GList * childs = dbusmenu_menuitem_get_children(mi); + if (childs == NULL) return NULL; + + for ( ; childs == NULL; childs = g_list_next(childs)) { + DbusmenuMenuitem * lmi = DBUSMENU_MENUITEM(childs->data); + if (id == dbusmenu_menuitem_get_id(lmi)) { + return lmi; + } + } + + return NULL; +} + +gboolean +dbusmenu_menuitem_property_set (DbusmenuMenuitem * mi, const gchar * property, const gchar * value) +{ + + return FALSE; +} + +const gchar * +dbusmenu_menuitem_property_get (DbusmenuMenuitem * mi, const gchar * property) +{ + + return NULL; +} + +gboolean +dbusmenu_menuitem_property_exist (DbusmenuMenuitem * mi, const gchar * property) +{ + + return FALSE; +} + +/** + dbusmenu_menuitem_buildxml: + @mi: #DbusmenuMenuitem to represent in XML + @array: A list of string that will be turned into an XML file + + 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 + 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) +{ + g_return_if_fail(DBUSMENU_IS_MENUITEM(mi)); + + GList * children = dbusmenu_menuitem_get_children(mi); + if (children == NULL) { + g_ptr_array_add(array, g_strdup_printf("<menu id=\"%d\" />", dbusmenu_menuitem_get_id(mi))); + } else { + g_ptr_array_add(array, g_strdup_printf("<menu id=\"%d\">", dbusmenu_menuitem_get_id(mi))); + for ( ; children != NULL; children = children->next) { + dbusmenu_menuitem_buildxml(DBUSMENU_MENUITEM(children->data), array); + } + g_ptr_array_add(array, g_strdup("</menu>")); + } + + return; +} + diff --git a/libdbusmenu-glib/menuitem.h b/libdbusmenu-glib/menuitem.h new file mode 100644 index 0000000..a604e7a --- /dev/null +++ b/libdbusmenu-glib/menuitem.h @@ -0,0 +1,135 @@ +/* +A library to communicate a menu object set accross DBus and +track updates and maintain consistency. + +Copyright 2009 Canonical Ltd. + +Authors: + Ted Gould <ted@canonical.com> + +This program is free software: you can redistribute it and/or modify it +under the terms of either or both of the following licenses: + +1) the GNU Lesser General Public License version 3, as published by the +Free Software Foundation; and/or +2) the GNU Lesser General Public License version 2.1, as published by +the Free Software Foundation. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranties of +MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR +PURPOSE. See the applicable version of the GNU Lesser General Public +License for more details. + +You should have received a copy of both the GNU Lesser General Public +License version 3 and version 2.1 along with this program. If not, see +<http://www.gnu.org/licenses/> +*/ + +#ifndef __DBUSMENU_MENUITEM_H__ +#define __DBUSMENU_MENUITEM_H__ + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define DBUSMENU_TYPE_MENUITEM (dbusmenu_menuitem_get_type ()) +#define DBUSMENU_MENUITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DBUSMENU_TYPE_MENUITEM, DbusmenuMenuitem)) +#define DBUSMENU_MENUITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DBUSMENU_TYPE_MENUITEM, DbusmenuMenuitemClass)) +#define DBUSMENU_IS_MENUITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DBUSMENU_TYPE_MENUITEM)) +#define DBUSMENU_IS_MENUITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_TYPE_MENUITEM)) +#define DBUSMENU_MENUITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_TYPE_MENUITEM, DbusmenuMenuitemClass)) + + +#define DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED "property-changed" +#define DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED "item-activated" + +/** + DbusmenuMenuitemClass: + @property_changed: Slot for #DbusmenuMenuitem::property-changed. + @item_activated: Slot for #DbusmenuMenuitem::item-activated. + @buildxml: Virtual function that appends the strings required + to represent this menu item in the menu XML file. + @reserved1: Reserved for future use. + @reserved2: Reserved for future use. + @reserved3: Reserved for future use. + @reserved4: Reserved for future use. +*/ +typedef struct _DbusmenuMenuitemClass DbusmenuMenuitemClass; +struct _DbusmenuMenuitemClass +{ + GObjectClass parent_class; + + /* Signals */ + void (*property_changed) (gchar * property); + void (*item_activated) (void); + + /* Virtual functions */ + void (*buildxml) (GPtrArray * stringarray); + + void (*reserved1) (void); + void (*reserved2) (void); + void (*reserved3) (void); + void (*reserved4) (void); +}; + +/** + DbusmenuMenuitem: + + This is the #GObject based object that represents a menu + item. It gets created the same on both the client and + the server side and libdbusmenu-glib does the work of making + this object model appear on both sides of DBus. Simple + really, though through updates and people coming on and off + the bus it can lead to lots of fun complex scenarios. +*/ +typedef struct _DbusmenuMenuitem DbusmenuMenuitem; +struct _DbusmenuMenuitem +{ + GObject parent; +}; + +GType dbusmenu_menuitem_get_type (void); + +DbusmenuMenuitem * dbusmenu_menuitem_new (void); +DbusmenuMenuitem * dbusmenu_menuitem_new_with_id (guint id); +guint dbusmenu_menuitem_get_id (DbusmenuMenuitem * mi); + +GList * dbusmenu_menuitem_get_children (DbusmenuMenuitem * mi); +GList * dbusmenu_menuitem_take_children (DbusmenuMenuitem * mi); +guint dbusmenu_menuitem_get_position (DbusmenuMenuitem * mi, DbusmenuMenuitem * parent); + +gboolean dbusmenu_menuitem_child_append (DbusmenuMenuitem * mi, DbusmenuMenuitem * child); +gboolean dbusmenu_menuitem_child_delete (DbusmenuMenuitem * mi, DbusmenuMenuitem * child); +gboolean dbusmenu_menuitem_child_add_position (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position); +DbusmenuMenuitem * dbusmenu_menuitem_child_find (DbusmenuMenuitem * mi, guint id); + +gboolean dbusmenu_menuitem_property_set (DbusmenuMenuitem * mi, const gchar * property, const gchar * value); +const gchar * dbusmenu_menuitem_property_get (DbusmenuMenuitem * mi, const gchar * property); +gboolean dbusmenu_menuitem_property_exist (DbusmenuMenuitem * mi, const gchar * property); + +void dbusmenu_menuitem_buildxml (DbusmenuMenuitem * mi, GPtrArray * array); + +/** + SECTION:menuitem + @short_description: A lowlevel represenation of a menuitem + @stability: Unstable + @include: libdbusmenu-glib/menuitem.h + + A #DbusmenuMenuitem is the lowest level of represenation of a + single item in a menu. It gets created on the server side + and copied over to the client side where it gets rendered. As + the server starts to change it, and grow it, and do all kinds + of fun stuff that information is transfered over DBus and the + client updates it's understanding of the object model. + + Most people using either the client or the server should be + able to deal mostly with #DbusmenuMenuitem objects. These + are simple, but then they can be attached to more complex + objects and handled appropriately. +*/ + +G_END_DECLS + +#endif diff --git a/libdbusmenu-glib/server-marshal.list b/libdbusmenu-glib/server-marshal.list new file mode 100644 index 0000000..950fc9d --- /dev/null +++ b/libdbusmenu-glib/server-marshal.list @@ -0,0 +1 @@ +VOID: UINT, STRING, STRING diff --git a/libdbusmenu-glib/server.c b/libdbusmenu-glib/server.c new file mode 100644 index 0000000..3db6db0 --- /dev/null +++ b/libdbusmenu-glib/server.c @@ -0,0 +1,332 @@ +/* +A library to communicate a menu object set accross DBus and +track updates and maintain consistency. + +Copyright 2009 Canonical Ltd. + +Authors: + Ted Gould <ted@canonical.com> + +This program is free software: you can redistribute it and/or modify it +under the terms of either or both of the following licenses: + +1) the GNU Lesser General Public License version 3, as published by the +Free Software Foundation; and/or +2) the GNU Lesser General Public License version 2.1, as published by +the Free Software Foundation. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranties of +MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR +PURPOSE. See the applicable version of the GNU Lesser General Public +License for more details. + +You should have received a copy of both the GNU Lesser General Public +License version 3 and version 2.1 along with this program. If not, see +<http://www.gnu.org/licenses/> +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "server.h" +#include "server-marshal.h" + +/* DBus Prototypes */ +static gboolean _dbusmenu_server_get_property (void); +static gboolean _dbusmenu_server_get_properties (void); +static gboolean _dbusmenu_server_call (void); +static gboolean _dbusmenu_server_list_properties (void); + +#include "dbusmenu-server.h" + +/* Privates, I'll show you mine... */ +typedef struct _DbusmenuServerPrivate DbusmenuServerPrivate; + +struct _DbusmenuServerPrivate +{ + DbusmenuMenuitem * root; + gchar * dbusobject; +}; + +#define DBUSMENU_SERVER_GET_PRIVATE(o) \ +(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_SERVER, DbusmenuServerPrivate)) + +/* Signals */ +enum { + ID_PROP_UPDATE, + ID_UPDATE, + LAYOUT_UPDATE, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +/* Properties */ +enum { + PROP_0, + PROP_DBUS_OBJECT, + PROP_ROOT_NODE, + PROP_LAYOUT +}; + +/* Prototype */ +static void dbusmenu_server_class_init (DbusmenuServerClass *class); +static void dbusmenu_server_init (DbusmenuServer *self); +static void dbusmenu_server_dispose (GObject *object); +static void dbusmenu_server_finalize (GObject *object); +static void set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec); +static void get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec); + +G_DEFINE_TYPE (DbusmenuServer, dbusmenu_server, G_TYPE_OBJECT); + +static void +dbusmenu_server_class_init (DbusmenuServerClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + g_type_class_add_private (class, sizeof (DbusmenuServerPrivate)); + + object_class->dispose = dbusmenu_server_dispose; + object_class->finalize = dbusmenu_server_finalize; + object_class->set_property = set_property; + object_class->get_property = get_property; + + signals[ID_PROP_UPDATE] = g_signal_new(DBUSMENU_SERVER_SIGNAL_ID_PROP_UPDATE, + G_TYPE_FROM_CLASS(class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(DbusmenuServerClass, id_prop_update), + NULL, NULL, + _dbusmenu_server_marshal_VOID__UINT_STRING_STRING, + G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING); + signals[ID_UPDATE] = g_signal_new(DBUSMENU_SERVER_SIGNAL_ID_UPDATE, + G_TYPE_FROM_CLASS(class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(DbusmenuServerClass, id_update), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + signals[LAYOUT_UPDATE] = g_signal_new(DBUSMENU_SERVER_SIGNAL_LAYOUT_UPDATE, + G_TYPE_FROM_CLASS(class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(DbusmenuServerClass, layout_update), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + + g_object_class_install_property (object_class, PROP_DBUS_OBJECT, + g_param_spec_string(DBUSMENU_SERVER_PROP_DBUS_OBJECT, "DBus object path", + "The object that represents this set of menus on DBus", + "/org/freedesktop/dbusmenu", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_ROOT_NODE, + g_param_spec_object(DBUSMENU_SERVER_PROP_ROOT_NODE, "Root menu node", + "The base object of the menus that are served", + DBUSMENU_TYPE_MENUITEM, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_LAYOUT, + g_param_spec_string(DBUSMENU_SERVER_PROP_LAYOUT, "XML Layout of the menus", + "A simple XML string that describes the layout of the menus", + "<menu />", + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + dbus_g_object_type_install_info(DBUSMENU_TYPE_SERVER, &dbus_glib__dbusmenu_server_object_info); + + return; +} + +static void +dbusmenu_server_init (DbusmenuServer *self) +{ + DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(self); + + priv->root = NULL; + priv->dbusobject = NULL; + + return; +} + +static void +dbusmenu_server_dispose (GObject *object) +{ + G_OBJECT_CLASS (dbusmenu_server_parent_class)->dispose (object); + return; +} + +static void +dbusmenu_server_finalize (GObject *object) +{ + G_OBJECT_CLASS (dbusmenu_server_parent_class)->finalize (object); + return; +} + +static void +set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec) +{ + DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(obj); + + switch (id) { + case PROP_DBUS_OBJECT: + g_return_if_fail(priv->dbusobject == NULL); + priv->dbusobject = g_value_dup_string(value); + DBusGConnection * connection = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); + dbus_g_connection_register_g_object(connection, + priv->dbusobject, + obj); + break; + case PROP_ROOT_NODE: + if (priv->root != NULL) { + g_object_unref(G_OBJECT(priv->root)); + priv->root = NULL; + } + priv->root = DBUSMENU_MENUITEM(g_value_get_object(value)); + if (priv->root != NULL) { + g_object_ref(G_OBJECT(priv->root)); + } else { + g_debug("Setting root node to NULL"); + } + g_signal_emit(obj, signals[LAYOUT_UPDATE], 0, TRUE); + break; + case PROP_LAYOUT: + /* Can't set this, fall through to error */ + g_warning("Can not set property: layout"); + default: + g_return_if_reached(); + break; + } + + return; +} + +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); + + switch (id) { + case PROP_DBUS_OBJECT: + g_value_set_string(value, priv->dbusobject); + break; + case PROP_ROOT_NODE: + g_value_set_object(value, priv->root); + break; + case PROP_LAYOUT: { + GPtrArray * xmlarray = g_ptr_array_new(); + if (priv->root == NULL) { + g_debug("Getting layout without root node!"); + g_ptr_array_add(xmlarray, g_strdup("<menu />")); + } else { + dbusmenu_menuitem_buildxml(priv->root, xmlarray); + } + g_ptr_array_add(xmlarray, NULL); + + /* build string */ + gchar * finalstring = g_strjoinv("", (gchar **)xmlarray->pdata); + g_value_take_string(value, finalstring); + g_debug("Final string: %s", finalstring); + + g_ptr_array_foreach(xmlarray, xmlarray_foreach_free, NULL); + g_ptr_array_free(xmlarray, TRUE); + break; + } + default: + g_return_if_reached(); + break; + } + + return; +} + +/* DBus interface */ +static gboolean +_dbusmenu_server_get_property (void) +{ + + return TRUE; +} + +static gboolean +_dbusmenu_server_get_properties (void) +{ + + return TRUE; +} + +static gboolean +_dbusmenu_server_call (void) +{ + + return TRUE; +} + +static gboolean +_dbusmenu_server_list_properties (void) +{ + + return TRUE; +} + +/* Public Interface */ +/** + dbusmenu_server_new: + @object: The object name to show for this menu structure + on DBus. May be NULL. + + Creates a new #DbusmenuServer object with a specific object + path on DBus. If @object is set to NULL the default object + name of "/org/freedesktop/dbusmenu" will be used. + + Return value: A brand new #DbusmenuServer +*/ +DbusmenuServer * +dbusmenu_server_new (const gchar * object) +{ + if (object == NULL) { + object = "/org/freedesktop/dbusmenu"; + } + + DbusmenuServer * self = g_object_new(DBUSMENU_TYPE_SERVER, + DBUSMENU_SERVER_PROP_DBUS_OBJECT, object, + NULL); + + return self; +} + +/** + dbusmenu_server_set_root: + @self: The #DbusmenuServer object to set the root on + @root: The new root #DbusmenuMenuitem tree + + This function contains all of the #GValue wrapping + required to set the property #DbusmenuServer:root-node + on the server @self. +*/ +void +dbusmenu_server_set_root (DbusmenuServer * self, DbusmenuMenuitem * root) +{ + g_return_if_fail(DBUSMENU_IS_SERVER(self)); + g_return_if_fail(DBUSMENU_IS_MENUITEM(root)); + + g_debug("Setting root object: 0x%X", (unsigned int)root); + GValue rootvalue = {0}; + g_value_init(&rootvalue, G_TYPE_OBJECT); + g_value_set_object(&rootvalue, root); + g_object_set_property(G_OBJECT(self), DBUSMENU_SERVER_PROP_ROOT_NODE, &rootvalue); + return; +} + + + diff --git a/libdbusmenu-glib/server.h b/libdbusmenu-glib/server.h new file mode 100644 index 0000000..2c6c817 --- /dev/null +++ b/libdbusmenu-glib/server.h @@ -0,0 +1,120 @@ +/* +A library to communicate a menu object set accross DBus and +track updates and maintain consistency. + +Copyright 2009 Canonical Ltd. + +Authors: + Ted Gould <ted@canonical.com> + +This program is free software: you can redistribute it and/or modify it +under the terms of either or both of the following licenses: + +1) the GNU Lesser General Public License version 3, as published by the +Free Software Foundation; and/or +2) the GNU Lesser General Public License version 2.1, as published by +the Free Software Foundation. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranties of +MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR +PURPOSE. See the applicable version of the GNU Lesser General Public +License for more details. + +You should have received a copy of both the GNU Lesser General Public +License version 3 and version 2.1 along with this program. If not, see +<http://www.gnu.org/licenses/> +*/ + +#ifndef __DBUSMENU_SERVER_H__ +#define __DBUSMENU_SERVER_H__ + +#include <glib.h> +#include <glib-object.h> + +#include "menuitem.h" + +G_BEGIN_DECLS + +#define DBUSMENU_TYPE_SERVER (dbusmenu_server_get_type ()) +#define DBUSMENU_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DBUSMENU_TYPE_SERVER, DbusmenuServer)) +#define DBUSMENU_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DBUSMENU_TYPE_SERVER, DbusmenuServerClass)) +#define DBUSMENU_IS_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DBUSMENU_TYPE_SERVER)) +#define DBUSMENU_IS_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_TYPE_SERVER)) +#define DBUSMENU_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_TYPE_SERVER, DbusmenuServerClass)) + +#define DBUSMENU_SERVER_SIGNAL_ID_PROP_UPDATE "id-prop-update" +#define DBUSMENU_SERVER_SIGNAL_ID_UPDATE "id-update" +#define DBUSMENU_SERVER_SIGNAL_LAYOUT_UPDATE "layout-update" + +#define DBUSMENU_SERVER_PROP_DBUS_OBJECT "dbus-object" +#define DBUSMENU_SERVER_PROP_ROOT_NODE "root-node" +#define DBUSMENU_SERVER_PROP_LAYOUT "layout" + +/** + DbusmenuServerClass: + @parent_class: #GObjectClass + @id_prop_update: Slot for #DbusmenuServer::id-prop-update. + @id_update: Slot for #DbusmenuServer::id-update. + @layout_update: Slot for #DbusmenuServer::layout-update. + @dbusmenu_server_reserved1: Reserved for future use. + @dbusmenu_server_reserved2: Reserved for future use. + @dbusmenu_server_reserved3: Reserved for future use. + @dbusmenu_server_reserved4: Reserved for future use. + + The class implementing the virtual functions for #DbusmenuServer. +*/ +typedef struct _DbusmenuServerClass DbusmenuServerClass; +struct _DbusmenuServerClass { + GObjectClass parent_class; + + /* Signals */ + void (*id_prop_update)(guint id, gchar * property, gchar * value); + void (*id_update)(guint id); + void (*layout_update)(void); + + /* Reserved */ + void (*dbusmenu_server_reserved1)(void); + void (*dbusmenu_server_reserved2)(void); + void (*dbusmenu_server_reserved3)(void); + void (*dbusmenu_server_reserved4)(void); +}; + +/** + DbusmenuServer: + @parent: #GObject + + A server which represents a sharing of a set of + #DbusmenuMenuitems across DBus to a #DbusmenuClient. +*/ +typedef struct _DbusmenuServer DbusmenuServer; +struct _DbusmenuServer { + GObject parent; +}; + +GType dbusmenu_server_get_type (void); +DbusmenuServer * dbusmenu_server_new (const gchar * object); +void dbusmenu_server_set_root (DbusmenuServer * server, DbusmenuMenuitem * root); + +/** + SECIONT:server + @short_description: The server signals changed and + updates on a tree of #DbusmenuMenuitem objecs. + @stability: Unstable + @include: libdbusmenu-glib/server.h + + A #DbusmenuServer is the object that represents the local + tree of #DbusmenuMenuitem objects on DBus. It watches the + various signals that those objects emit and correctly + represents them across DBus to a #DbusmenuClient so that + the same tree can be maintained in another process. + + The server needs to have the root set of #DbusmenuMenuitem + objects set via #dbusmenu_server_set_root but it will query + all of the objects in that tree automatically. After setting + the root there should be no other maintence required by + users of the server class. +*/ +G_END_DECLS + +#endif diff --git a/libdbusmenu-glib/test.c b/libdbusmenu-glib/test.c deleted file mode 100644 index 8ebb3f7..0000000 --- a/libdbusmenu-glib/test.c +++ /dev/null @@ -1,4 +0,0 @@ - -void mysymbol (void) { - return; -} diff --git a/libdbusmenu-glib/test.h b/libdbusmenu-glib/test.h deleted file mode 100644 index ad000af..0000000 --- a/libdbusmenu-glib/test.h +++ /dev/null @@ -1,2 +0,0 @@ - -void mysymbol (void); |