aboutsummaryrefslogtreecommitdiff
path: root/libdbusmenu-glib/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdbusmenu-glib/server.c')
-rw-r--r--libdbusmenu-glib/server.c1474
1 files changed, 1254 insertions, 220 deletions
diff --git a/libdbusmenu-glib/server.c b/libdbusmenu-glib/server.c
index 68afb92..c7057df 100644
--- a/libdbusmenu-glib/server.c
+++ b/libdbusmenu-glib/server.c
@@ -30,41 +30,42 @@ License version 3 and version 2.1 along with this program. If not, see
#include "config.h"
#endif
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+
#include "menuitem-private.h"
#include "server.h"
#include "server-marshal.h"
+#include "enum-types.h"
-/* DBus Prototypes */
-static gboolean _dbusmenu_server_get_layout (DbusmenuServer * server, gint parent, guint * revision, gchar ** layout, GError ** error);
-static gboolean _dbusmenu_server_get_property (DbusmenuServer * server, gint id, gchar * property, gchar ** value, GError ** error);
-static gboolean _dbusmenu_server_get_properties (DbusmenuServer * server, gint id, gchar ** properties, GHashTable ** dict, GError ** error);
-static gboolean _dbusmenu_server_get_group_properties (DbusmenuServer * server, GArray * ids, gchar ** properties, GPtrArray ** values, GError ** error);
-static gboolean _dbusmenu_server_event (DbusmenuServer * server, gint id, gchar * eventid, GValue * data, guint timestamp, GError ** error);
-static gboolean _dbusmenu_server_get_children (DbusmenuServer * server, gint id, GPtrArray * properties, GPtrArray ** output, GError ** error);
-static gboolean _dbusmenu_server_about_to_show (DbusmenuServer * server, gint id, gboolean * need_update, GError ** error);
-/* DBus Helpers */
-static void _gvalue_array_append_int(GValueArray *array, gint i);
-static void _gvalue_array_append_hashtable(GValueArray *array, GHashTable * dict);
-
-#include "dbusmenu-server.h"
+#include "dbus-menu-clean.xml.h"
static void layout_update_signal (DbusmenuServer * server);
-#define DBUSMENU_VERSION_NUMBER 2
+#define DBUSMENU_VERSION_NUMBER 2
+#define DBUSMENU_INTERFACE "com.canonical.dbusmenu"
/* Privates, I'll show you mine... */
-typedef struct _DbusmenuServerPrivate DbusmenuServerPrivate;
-
struct _DbusmenuServerPrivate
{
DbusmenuMenuitem * root;
gchar * dbusobject;
gint layout_revision;
guint layout_idle;
+
+ GDBusConnection * bus;
+ GCancellable * bus_lookup;
+ guint dbus_registration;
+
+ DbusmenuTextDirection text_direction;
+ DbusmenuStatus status;
+ GStrv icon_dirs;
+
+ GArray * prop_array;
+ guint property_idle;
};
-#define DBUSMENU_SERVER_GET_PRIVATE(o) \
-(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_SERVER, DbusmenuServerPrivate))
+#define DBUSMENU_SERVER_GET_PRIVATE(o) (DBUSMENU_SERVER(o)->priv)
/* Signals */
enum {
@@ -82,7 +83,10 @@ enum {
PROP_0,
PROP_DBUS_OBJECT,
PROP_ROOT_NODE,
- PROP_VERSION
+ PROP_VERSION,
+ PROP_TEXT_DIRECTION,
+ PROP_STATUS,
+ PROP_ICON_THEME_DIRS
};
/* Errors */
@@ -91,22 +95,112 @@ enum {
INVALID_PROPERTY_NAME,
UNKNOWN_DBUS_ERROR,
NOT_IMPLEMENTED,
+ NO_VALID_LAYOUT,
LAST_ERROR
};
+/* Method Table */
+typedef void (*MethodTableFunc) (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation);
+
+typedef struct _method_table_t method_table_t;
+struct _method_table_t {
+ const gchar * interned_name;
+ MethodTableFunc func;
+};
+
+enum {
+ METHOD_GET_LAYOUT = 0,
+ METHOD_GET_GROUP_PROPERTIES,
+ METHOD_GET_CHILDREN,
+ METHOD_GET_PROPERTY,
+ METHOD_GET_PROPERTIES,
+ METHOD_EVENT,
+ METHOD_ABOUT_TO_SHOW,
+ /* Counter, do not remove! */
+ METHOD_COUNT
+};
+
/* 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);
-static void menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, GValue * value, DbusmenuServer * server);
-static void menuitem_child_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint pos, DbusmenuServer * server);
-static void menuitem_child_removed (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, DbusmenuServer * server);
-static void menuitem_signals_create (DbusmenuMenuitem * mi, gpointer data);
-static void menuitem_signals_remove (DbusmenuMenuitem * mi, gpointer data);
-static GQuark error_quark (void);
+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);
+static void default_text_direction (DbusmenuServer * server);
+static void register_object (DbusmenuServer * server);
+static void bus_got_cb (GObject * obj,
+ GAsyncResult * result,
+ gpointer user_data);
+static void bus_method_call (GDBusConnection * connection,
+ const gchar * sender,
+ const gchar * path,
+ const gchar * interface,
+ const gchar * method,
+ GVariant * params,
+ GDBusMethodInvocation * invocation,
+ gpointer user_data);
+static GVariant * bus_get_prop (GDBusConnection * connection,
+ const gchar * sender,
+ const gchar * path,
+ const gchar * interface,
+ const gchar * property,
+ GError ** error,
+ gpointer user_data);
+static void menuitem_property_changed (DbusmenuMenuitem * mi,
+ gchar * property,
+ GVariant * variant,
+ DbusmenuServer * server);
+static void menuitem_child_added (DbusmenuMenuitem * parent,
+ DbusmenuMenuitem * child,
+ guint pos,
+ DbusmenuServer * server);
+static void menuitem_child_removed (DbusmenuMenuitem * parent,
+ DbusmenuMenuitem * child,
+ DbusmenuServer * server);
+static void menuitem_signals_create (DbusmenuMenuitem * mi,
+ gpointer data);
+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);
+static void bus_get_group_properties (DbusmenuServer * server,
+ GVariant * params,
+ GDBusMethodInvocation * invocation);
+static void bus_get_children (DbusmenuServer * server,
+ GVariant * params,
+ GDBusMethodInvocation * invocation);
+static void bus_get_property (DbusmenuServer * server,
+ GVariant * params,
+ GDBusMethodInvocation * invocation);
+static void bus_get_properties (DbusmenuServer * server,
+ GVariant * params,
+ GDBusMethodInvocation * invocation);
+static void bus_event (DbusmenuServer * server,
+ GVariant * params,
+ GDBusMethodInvocation * invocation);
+static void bus_about_to_show (DbusmenuServer * server,
+ GVariant * params,
+ GDBusMethodInvocation * invocation);
+
+/* Globals */
+static GDBusNodeInfo * dbusmenu_node_info = NULL;
+static GDBusInterfaceInfo * dbusmenu_interface_info = NULL;
+static const GDBusInterfaceVTable dbusmenu_interface_table = {
+ method_call: bus_method_call,
+ get_property: bus_get_prop,
+ set_property: NULL /* No properties that can be set */
+};
+static method_table_t dbusmenu_method_table[METHOD_COUNT];
G_DEFINE_TYPE (DbusmenuServer, dbusmenu_server, G_TYPE_OBJECT);
@@ -137,8 +231,8 @@ dbusmenu_server_class_init (DbusmenuServerClass *class)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(DbusmenuServerClass, id_prop_update),
NULL, NULL,
- _dbusmenu_server_marshal_VOID__INT_STRING_POINTER,
- G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_STRING, G_TYPE_VALUE);
+ _dbusmenu_server_marshal_VOID__INT_STRING_VARIANT,
+ G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_STRING, G_TYPE_VARIANT);
/**
DbusmenuServer::id-update:
@arg0: The #DbusmenuServer emitting the signal.
@@ -193,7 +287,7 @@ dbusmenu_server_class_init (DbusmenuServerClass *class)
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/ayatana/dbusmenu",
+ "/com/canonical/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",
@@ -205,8 +299,56 @@ dbusmenu_server_class_init (DbusmenuServerClass *class)
"The version of the DBusmenu API that we're implementing.",
DBUSMENU_VERSION_NUMBER, DBUSMENU_VERSION_NUMBER, DBUSMENU_VERSION_NUMBER,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_TEXT_DIRECTION,
+ g_param_spec_enum(DBUSMENU_SERVER_PROP_TEXT_DIRECTION, "The default direction of text",
+ "The object that represents this set of menus on DBus",
+ DBUSMENU_TYPE_TEXT_DIRECTION, DBUSMENU_TEXT_DIRECTION_NONE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_STATUS,
+ g_param_spec_enum(DBUSMENU_SERVER_PROP_STATUS, "Status of viewing the menus",
+ "Exports over DBus whether the menus should be given special visuals",
+ DBUSMENU_TYPE_STATUS, DBUSMENU_STATUS_NORMAL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ if (dbusmenu_node_info == NULL) {
+ GError * error = NULL;
+
+ dbusmenu_node_info = g_dbus_node_info_new_for_xml(dbus_menu_clean_xml, &error);
+ if (error != NULL) {
+ g_error("Unable to parse DBusmenu Interface description: %s", error->message);
+ g_error_free(error);
+ }
+ }
+
+ if (dbusmenu_interface_info == NULL) {
+ dbusmenu_interface_info = g_dbus_node_info_lookup_interface(dbusmenu_node_info, DBUSMENU_INTERFACE);
+
+ if (dbusmenu_interface_info == NULL) {
+ g_error("Unable to find interface '" DBUSMENU_INTERFACE "'");
+ }
+ }
+
+ /* Building our Method table :( */
+ dbusmenu_method_table[METHOD_GET_LAYOUT].interned_name = g_intern_static_string("GetLayout");
+ dbusmenu_method_table[METHOD_GET_LAYOUT].func = bus_get_layout;
+
+ dbusmenu_method_table[METHOD_GET_GROUP_PROPERTIES].interned_name = g_intern_static_string("GetGroupProperties");
+ dbusmenu_method_table[METHOD_GET_GROUP_PROPERTIES].func = bus_get_group_properties;
- dbus_g_object_type_install_info(DBUSMENU_TYPE_SERVER, &dbus_glib__dbusmenu_server_object_info);
+ dbusmenu_method_table[METHOD_GET_CHILDREN].interned_name = g_intern_static_string("GetChildren");
+ dbusmenu_method_table[METHOD_GET_CHILDREN].func = bus_get_children;
+
+ dbusmenu_method_table[METHOD_GET_PROPERTY].interned_name = g_intern_static_string("GetProperty");
+ dbusmenu_method_table[METHOD_GET_PROPERTY].func = bus_get_property;
+
+ dbusmenu_method_table[METHOD_GET_PROPERTIES].interned_name = g_intern_static_string("GetProperties");
+ dbusmenu_method_table[METHOD_GET_PROPERTIES].func = bus_get_properties;
+
+ dbusmenu_method_table[METHOD_EVENT].interned_name = g_intern_static_string("Event");
+ dbusmenu_method_table[METHOD_EVENT].func = bus_event;
+
+ dbusmenu_method_table[METHOD_ABOUT_TO_SHOW].interned_name = g_intern_static_string("AboutToShow");
+ dbusmenu_method_table[METHOD_ABOUT_TO_SHOW].func = bus_about_to_show;
return;
}
@@ -214,12 +356,21 @@ dbusmenu_server_class_init (DbusmenuServerClass *class)
static void
dbusmenu_server_init (DbusmenuServer *self)
{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), DBUSMENU_TYPE_SERVER, DbusmenuServerPrivate);
+
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(self);
priv->root = NULL;
priv->dbusobject = NULL;
priv->layout_revision = 1;
priv->layout_idle = 0;
+ priv->bus = NULL;
+ priv->bus_lookup = NULL;
+ priv->dbus_registration = 0;
+
+ default_text_direction(self);
+ priv->status = DBUSMENU_STATUS_NORMAL;
+ priv->icon_dirs = NULL;
return;
}
@@ -231,6 +382,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) {
@@ -238,6 +400,27 @@ dbusmenu_server_dispose (GObject *object)
g_object_unref(priv->root);
}
+ if (priv->dbus_registration != 0) {
+ g_dbus_connection_unregister_object(priv->bus, priv->dbus_registration);
+ priv->dbus_registration = 0;
+ }
+
+ if (priv->bus != NULL) {
+ g_object_unref(priv->bus);
+ priv->bus = NULL;
+ }
+
+ if (priv->bus_lookup != NULL) {
+ if (!g_cancellable_is_cancelled(priv->bus_lookup)) {
+ /* Note, this may case the async function to run at
+ some point in the future. That's okay, it'll get an
+ error, but just FYI */
+ g_cancellable_cancel(priv->bus_lookup);
+ }
+ g_object_unref(priv->bus_lookup);
+ priv->bus_lookup = NULL;
+ }
+
G_OBJECT_CLASS (dbusmenu_server_parent_class)->dispose (object);
return;
}
@@ -245,6 +428,13 @@ dbusmenu_server_dispose (GObject *object)
static void
dbusmenu_server_finalize (GObject *object)
{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(object);
+
+ if (priv->icon_dirs != NULL) {
+ g_strfreev(priv->icon_dirs);
+ priv->icon_dirs = NULL;
+ }
+
G_OBJECT_CLASS (dbusmenu_server_parent_class)->finalize (object);
return;
}
@@ -253,27 +443,37 @@ static void
set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
{
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(obj);
- GError * error = NULL;
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, &error);
- if (connection == NULL || error != NULL) {
- g_warning("Unable to get session bus: %s", error == NULL ? "No message" : error->message);
- if (error != NULL) { g_error_free(error); }
+ if (priv->bus == NULL) {
+ if (priv->bus_lookup == NULL) {
+ priv->bus_lookup = g_cancellable_new();
+ g_return_if_fail(priv->bus_lookup != NULL);
+ }
+
+ g_object_ref(obj);
+ g_bus_get(G_BUS_TYPE_SESSION, priv->bus_lookup, bus_got_cb, obj);
} else {
- dbus_g_connection_register_g_object(connection,
- priv->dbusobject,
- obj);
+ register_object(DBUSMENU_SERVER(obj));
}
break;
case PROP_ROOT_NODE:
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;
}
@@ -282,25 +482,80 @@ 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");
}
layout_update_signal(DBUSMENU_SERVER(obj));
break;
- default:
- g_return_if_reached();
+ case PROP_TEXT_DIRECTION: {
+ DbusmenuTextDirection indir = g_value_get_enum(value);
+ DbusmenuTextDirection olddir = priv->text_direction;
+
+ /* If being set to none we need to go back to default, otherwise
+ we'll set things the way that we've been told */
+ if (indir == DBUSMENU_TEXT_DIRECTION_NONE) {
+ default_text_direction(DBUSMENU_SERVER(obj));
+ } else {
+ priv->text_direction = indir;
+ }
+
+ /* If the value has changed we need to signal that on DBus */
+ if (priv->text_direction != olddir && priv->bus != NULL && priv->dbusobject != NULL) {
+ GVariantBuilder params;
+ g_variant_builder_init(&params, G_VARIANT_TYPE_TUPLE);
+ g_variant_builder_add_value(&params, g_variant_new_string(DBUSMENU_INTERFACE));
+ GVariant * dict = g_variant_new_dict_entry(g_variant_new_string("TextDirection"), g_variant_new_variant(g_variant_new_string(dbusmenu_text_direction_get_nick(priv->text_direction))));
+ g_variant_builder_add_value(&params, g_variant_new_array(NULL, &dict, 1));
+ g_variant_builder_add_value(&params, g_variant_new_array(G_VARIANT_TYPE_STRING, NULL, 0));
+ GVariant * vparams = g_variant_builder_end(&params);
+
+ g_dbus_connection_emit_signal(priv->bus,
+ NULL,
+ priv->dbusobject,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ vparams,
+ NULL);
+ }
+
break;
}
+ case PROP_STATUS: {
+ DbusmenuStatus instatus = g_value_get_enum(value);
+
+ /* If the value has changed we need to signal that on DBus */
+ if (priv->status != instatus && priv->bus != NULL && priv->dbusobject != NULL) {
+ GVariantBuilder params;
+ g_variant_builder_init(&params, G_VARIANT_TYPE_TUPLE);
+ g_variant_builder_add_value(&params, g_variant_new_string(DBUSMENU_INTERFACE));
+ GVariant * dict = g_variant_new_dict_entry(g_variant_new_string("Status"), g_variant_new_variant(g_variant_new_string(dbusmenu_status_get_nick(instatus))));
+ g_variant_builder_add_value(&params, g_variant_new_array(NULL, &dict, 1));
+ g_variant_builder_add_value(&params, g_variant_new_array(G_VARIANT_TYPE_STRING, NULL, 0));
+ GVariant * vparams = g_variant_builder_end(&params);
+
+ g_dbus_connection_emit_signal(priv->bus,
+ NULL,
+ priv->dbusobject,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ vparams,
+ NULL);
+ }
- return;
-}
-
-static void
-xmlarray_foreach_free (gpointer arrayentry, gpointer userdata)
-{
- if (arrayentry != NULL) {
- /* g_debug("Freeing pointer: %s", (gchar *)arrayentry); */
- g_free(arrayentry);
+ priv->status = instatus;
+ break;
+ }
+ default:
+ g_return_if_reached();
+ break;
}
return;
@@ -321,6 +576,12 @@ get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
case PROP_VERSION:
g_value_set_uint(value, DBUSMENU_VERSION_NUMBER);
break;
+ case PROP_TEXT_DIRECTION:
+ g_value_set_enum(value, priv->text_direction);
+ break;
+ case PROP_STATUS:
+ g_value_set_enum(value, priv->status);
+ break;
default:
g_return_if_reached();
break;
@@ -329,6 +590,192 @@ get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
return;
}
+/* Determines the default text direction */
+static void
+default_text_direction (DbusmenuServer * server)
+{
+ DbusmenuTextDirection dir = DBUSMENU_TEXT_DIRECTION_NONE;
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
+ const gchar * env = g_getenv("DBUSMENU_TEXT_DIRECTION");
+ if (env != NULL) {
+ if (g_strcmp0(env, "ltr") == 0) {
+ dir = DBUSMENU_TEXT_DIRECTION_LTR;
+ } else if (g_strcmp0(env, "rtl") == 0) {
+ dir = DBUSMENU_TEXT_DIRECTION_RTL;
+ } else {
+ g_warning("Value of 'DBUSMENU_TEXT_DIRECTION' is '%s' which is not one of 'rtl' or 'ltr'", env);
+ }
+ }
+
+ if (dir == DBUSMENU_TEXT_DIRECTION_NONE) {
+ /* TRANSLATORS: This is the direction of the text and can
+ either be the value 'ltr' for left-to-right text (English)
+ or 'rtl' for right-to-left (Arabic). */
+ const gchar * default_dir = C_("default text direction", "ltr");
+
+ if (g_strcmp0(default_dir, "ltr") == 0) {
+ dir = DBUSMENU_TEXT_DIRECTION_LTR;
+ } else if (g_strcmp0(default_dir, "rtl") == 0) {
+ dir = DBUSMENU_TEXT_DIRECTION_RTL;
+ } else {
+ g_warning("Translation has an invalid value '%s' for default text direction. Defaulting to left-to-right.", default_dir);
+ dir = DBUSMENU_TEXT_DIRECTION_LTR;
+ }
+ }
+
+ /* Shouldn't happen, but incase future patches make a mistake
+ this'll catch them */
+ g_return_if_fail(dir != DBUSMENU_TEXT_DIRECTION_NONE);
+
+ priv->text_direction = dir;
+
+ return;
+}
+
+/* Register the object on the dbus bus */
+static void
+register_object (DbusmenuServer * server)
+{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
+ /* Object info */
+ g_return_if_fail(priv->bus != NULL);
+ g_return_if_fail(priv->dbusobject != NULL);
+
+ /* Class info */
+ g_return_if_fail(dbusmenu_node_info != NULL);
+ g_return_if_fail(dbusmenu_interface_info != NULL);
+
+ /* We might block on this in the future, but it'd be nice if
+ we could change the object path. Thinking about it... */
+ if (priv->dbus_registration != 0) {
+ g_dbus_connection_unregister_object(priv->bus, priv->dbus_registration);
+ priv->dbus_registration = 0;
+ }
+
+ GError * error = NULL;
+ priv->dbus_registration = g_dbus_connection_register_object(priv->bus,
+ priv->dbusobject,
+ dbusmenu_interface_info,
+ &dbusmenu_interface_table,
+ server,
+ NULL,
+ &error);
+
+ if (error != NULL) {
+ g_warning("Unable to register object on bus: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ /* If we've got it registered let's tell everyone about it */
+ g_signal_emit(G_OBJECT(server), signals[LAYOUT_UPDATED], 0, priv->layout_revision, 0, TRUE);
+ if (priv->dbusobject != NULL && priv->bus != NULL) {
+ g_dbus_connection_emit_signal(priv->bus,
+ NULL,
+ priv->dbusobject,
+ DBUSMENU_INTERFACE,
+ "LayoutUpdated",
+ g_variant_new("(ui)", priv->layout_revision, 0),
+ NULL);
+ }
+
+ return;
+}
+
+/* Callback from asking GIO to get us the session bus */
+static void
+bus_got_cb (GObject * obj, GAsyncResult * result, gpointer user_data)
+{
+ GError * error = NULL;
+
+ GDBusConnection * bus = g_bus_get_finish(result, &error);
+
+ if (error != NULL) {
+ g_warning("Unable to get session bus: %s", error->message);
+ g_error_free(error);
+ g_object_unref(G_OBJECT(user_data));
+ return;
+ }
+
+ /* Note: We're not using the user_data before we check for
+ the error so that in the cancelled case at destruction of
+ the object we don't end up with an invalid object. */
+
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(user_data);
+ priv->bus = bus;
+
+ register_object(DBUSMENU_SERVER(user_data));
+
+ g_object_unref(G_OBJECT(user_data));
+ return;
+}
+
+/* Function for the GDBus vtable to handle all method calls and dish
+ them out the appropriate functions */
+static void
+bus_method_call (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * method, GVariant * params, GDBusMethodInvocation * invocation, gpointer user_data)
+{
+ int i;
+ const gchar * interned_method = g_intern_string(method);
+
+ for (i = 0; i < METHOD_COUNT; i++) {
+ if (dbusmenu_method_table[i].interned_name == interned_method) {
+ if (dbusmenu_method_table[i].func != NULL) {
+ return dbusmenu_method_table[i].func(DBUSMENU_SERVER(user_data), params, invocation);
+ } else {
+ /* If we have a null function we're responding but nothing else. */
+ g_warning("Invalid function call for '%s' with parameters: %s", method, g_variant_print(params, TRUE));
+ g_dbus_method_invocation_return_value(invocation, NULL);
+ return;
+ }
+ }
+ }
+
+ /* We're here because there's an error */
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ NOT_IMPLEMENTED,
+ "Unable to find method '%s'",
+ method);
+ return;
+}
+
+/* For the GDBus vtable but we only have one property so it's pretty
+ simple. */
+static GVariant *
+bus_get_prop (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * property, GError ** error, gpointer user_data)
+{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(user_data);
+
+ /* None of these should happen */
+ g_return_val_if_fail(g_strcmp0(interface, DBUSMENU_INTERFACE) == 0, NULL);
+ g_return_val_if_fail(g_strcmp0(path, priv->dbusobject) == 0, NULL);
+
+ if (g_strcmp0(property, "Version") == 0) {
+ return g_variant_new_uint32(DBUSMENU_VERSION_NUMBER);
+ } else if (g_strcmp0(property, "TextDirection") == 0) {
+ return g_variant_new_string(dbusmenu_text_direction_get_nick(priv->text_direction));
+ } else if (g_strcmp0(property, "IconThemePath") == 0) {
+ GVariant * dirs = NULL;
+
+ if (priv->icon_dirs != NULL) {
+ dirs = g_variant_new_strv((const gchar * const *)priv->icon_dirs, -1);
+ } else {
+ dirs = g_variant_new_array(G_VARIANT_TYPE_STRING, NULL, 0);
+ }
+
+ return dirs;
+ } else if (g_strcmp0(property, "Status") == 0) {
+ return g_variant_new_string(dbusmenu_status_get_nick(priv->status));
+ } else {
+ g_warning("Unknown property '%s'", property);
+ }
+
+ return NULL;
+}
+
/* Handle actually signalling in the idle loop. This way we collect all
the updates. */
static gboolean
@@ -338,6 +785,15 @@ layout_update_idle (gpointer user_data)
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
g_signal_emit(G_OBJECT(server), signals[LAYOUT_UPDATED], 0, priv->layout_revision, 0, TRUE);
+ if (priv->dbusobject != NULL && priv->bus != NULL) {
+ g_dbus_connection_emit_signal(priv->bus,
+ NULL,
+ priv->dbusobject,
+ DBUSMENU_INTERFACE,
+ "LayoutUpdated",
+ g_variant_new("(ui)", priv->layout_revision, 0),
+ NULL);
+ }
priv->layout_idle = 0;
@@ -358,10 +814,280 @@ layout_update_signal (DbusmenuServer * server)
return;
}
+typedef struct _prop_idle_item_t prop_idle_item_t;
+struct _prop_idle_item_t {
+ DbusmenuMenuitem * mi;
+ 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)
+{
+ 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_object_unref(G_OBJECT(iitem->mi));
+ 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);
+
+ /* if it's not exposed we're going to block it's properties
+ from getting into the dbus message */
+ if (dbusmenu_menuitem_exposed(iitem->mi) == FALSE) {
+ continue;
+ }
+
+ 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(dbusmenu_menuitem_get_id(iitem->mi)));
+ 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(dbusmenu_menuitem_get_id(iitem->mi)));
+ 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_variant_builder_add_value(&removeitembuilder, g_variant_builder_end(&tuplebuilder));
+ }
+ }
+
+ /* these are going to be standard references in all code paths and must be unrefed */
+ GVariant * megadata[2];
+ gboolean gotsomething = FALSE;
+
+ if (item_init) {
+ megadata[0] = g_variant_builder_end(&itembuilder);
+ g_variant_ref_sink(megadata[0]);
+ gotsomething = TRUE;
+ } 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);
+ g_variant_ref_sink(megadata[1]);
+ gotsomething = TRUE;
+ } else {
+ GError * error = NULL;
+ megadata[1] = g_variant_parse(G_VARIANT_TYPE("a(ias)"), "[ ]", NULL, NULL, &error);
+
+ if (error != NULL) {
+ g_warning("Unable to parse '[ ]' as a 'a(ias)': %s", error->message);
+ g_error_free(error);
+ }
+ }
+
+ if (gotsomething && priv->dbusobject != NULL && priv->bus != NULL) {
+ g_dbus_connection_emit_signal(priv->bus,
+ NULL,
+ priv->dbusobject,
+ DBUSMENU_INTERFACE,
+ "ItemsPropertiesUpdated",
+ g_variant_new_tuple(megadata, 2),
+ NULL);
+ }
+
+ 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, GValue * value, DbusmenuServer * server)
+menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, GVariant * variant, DbusmenuServer * server)
{
- g_signal_emit(G_OBJECT(server), signals[ID_PROP_UPDATE], 0, dbusmenu_menuitem_get_id(mi), property, value, TRUE);
+ 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->mi == mi) {
+ item = iitem;
+ break;
+ }
+ }
+
+ GArray * properties = NULL;
+ /* If not, we'll need to build ourselves one */
+ if (item == NULL) {
+ prop_idle_item_t myitem;
+ myitem.mi = mi;
+ g_object_ref(G_OBJECT(mi));
+ 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) {
+ if (prop->variant != 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;
}
@@ -412,7 +1138,20 @@ menuitem_child_moved (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint
static void
menuitem_shown (DbusmenuMenuitem * mi, guint timestamp, DbusmenuServer * server)
{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
g_signal_emit(G_OBJECT(server), signals[ITEM_ACTIVATION], 0, dbusmenu_menuitem_get_id(mi), timestamp, TRUE);
+
+ if (priv->dbusobject != NULL && priv->bus != NULL) {
+ g_dbus_connection_emit_signal(priv->bus,
+ NULL,
+ priv->dbusobject,
+ DBUSMENU_INTERFACE,
+ "ItemActivationRequested",
+ g_variant_new("(iu)", dbusmenu_menuitem_get_id(mi), timestamp),
+ NULL);
+ }
+
return;
}
@@ -452,215 +1191,321 @@ error_quark (void)
}
/* DBus interface */
-static gboolean
-_dbusmenu_server_get_layout (DbusmenuServer * server, gint parent, guint * revision, gchar ** layout, GError ** error)
+static void
+bus_get_layout (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
{
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
- *revision = priv->layout_revision;
- GPtrArray * xmlarray = g_ptr_array_new();
+ /* Input */
+ gint32 parent;
+ gint32 recurse;
+ const gchar ** props;
- 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);
+ g_variant_get(params, "(ii^a&s)", &parent, &recurse, &props);
+
+ /* Output */
+ guint revision = priv->layout_revision;
+ GVariant * items = NULL;
+
+ if (priv->root != NULL) {
+ DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, parent);
+
+ if (mi != NULL) {
+ items = dbusmenu_menuitem_build_variant(mi, props, recurse);
}
- } else {
- DbusmenuMenuitem * item = dbusmenu_menuitem_find_id(priv->root, parent);
- if (item == NULL) {
- if (error != NULL) {
- g_set_error(error,
- error_quark(),
- INVALID_MENUITEM_ID,
- "The ID supplied %d does not refer to a menu item we have",
- parent);
- }
- return FALSE;
+ }
+ g_free(props);
+
+ /* 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,
+ "The ID supplied %d does not refer to a menu item we have",
+ parent);
+ return;
}
- dbusmenu_menuitem_buildxml(item, xmlarray);
}
- g_ptr_array_add(xmlarray, NULL);
- /* build string */
- *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);
- return TRUE;
+ 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,
+ retval);
+ return;
}
-static gboolean
-_dbusmenu_server_get_property (DbusmenuServer * server, gint id, gchar * property, gchar ** value, GError ** error)
+/* Get a single property off of a single menuitem */
+static void
+bus_get_property (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
{
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
+ if (priv->root == NULL) {
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ NO_VALID_LAYOUT,
+ "There currently isn't a layout in this server");
+ return;
+ }
+
+ gint32 id;
+ const gchar * property;
+
+ g_variant_get(params, "(i&s)", &id, &property);
+
DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
if (mi == NULL) {
- if (error != NULL) {
- g_set_error(error,
+ g_dbus_method_invocation_return_error(invocation,
error_quark(),
INVALID_MENUITEM_ID,
"The ID supplied %d does not refer to a menu item we have",
id);
- }
- return FALSE;
+ return;
}
- const gchar * prop = dbusmenu_menuitem_property_get(mi, property);
- if (prop == NULL) {
- if (error != NULL) {
- g_set_error(error,
+ GVariant * variant = dbusmenu_menuitem_property_get_variant(mi, property);
+ if (variant == NULL) {
+ g_dbus_method_invocation_return_error(invocation,
error_quark(),
INVALID_PROPERTY_NAME,
"The property '%s' does not exist on menuitem with ID of %d",
property,
id);
- }
- return FALSE;
+ return;
}
- if (value == NULL) {
- if (error != NULL) {
- g_set_error(error,
- error_quark(),
- UNKNOWN_DBUS_ERROR,
- "Uhm, yeah. We didn't get anywhere to put the value, that's really weird. Seems impossible really.");
- }
- return FALSE;
- }
-
- *value = g_strdup(prop);
-
- return TRUE;
+ g_dbus_method_invocation_return_value(invocation, g_variant_new("(v)", variant));
+ return;
}
-static gboolean
-_dbusmenu_server_get_properties (DbusmenuServer * server, gint id, gchar ** properties, GHashTable ** dict, GError ** error)
+/* Get some properties off of a single menuitem */
+static void
+bus_get_properties (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
{
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
+ if (priv->root == NULL) {
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ NO_VALID_LAYOUT,
+ "There currently isn't a layout in this server");
+ return;
+ }
+
+ gint32 id;
+ g_variant_get(params, "(i)", &id);
+
DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
if (mi == NULL) {
- if (error != NULL) {
- g_set_error(error,
+ g_dbus_method_invocation_return_error(invocation,
error_quark(),
INVALID_MENUITEM_ID,
"The ID supplied %d does not refer to a menu item we have",
id);
- }
- return FALSE;
+ return;
}
- *dict = dbusmenu_menuitem_properties_copy(mi);
+ GVariant * dict = dbusmenu_menuitem_properties_variant(mi, NULL);
+
+ g_dbus_method_invocation_return_value(invocation, g_variant_new("(a{sv})", dict));
- return TRUE;
+ return;
}
/* Handles getting a bunch of properties from a variety of menu items
to make one mega dbus message */
-static gboolean
-_dbusmenu_server_get_group_properties (DbusmenuServer * server, GArray * ids, gchar ** properties, GPtrArray ** values, GError ** error)
+static void
+bus_get_group_properties (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
{
- /* Build an initial pointer array */
- *values = g_ptr_array_new();
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
- /* Go through each ID to get that ID's properties */
- int idcnt;
- for (idcnt = 0; idcnt < ids->len; idcnt++) {
- GHashTable * idprops = NULL;
- GError * error = NULL;
- gint id = g_array_index(ids, int, idcnt);
+ if (priv->root == NULL) {
+ /* Allow a request for just id 0 when root is null. Return no properties.
+ So that a request always returns a valid structure no matter the
+ state of the structure in the server.
+ */
+ GVariant * idlist = g_variant_get_child_value(params, 0);
+ if (g_variant_n_children(idlist) == 1) {
- /* Get the properties for this ID the old fashioned way. */
- if (!_dbusmenu_server_get_properties(server, id, properties, &idprops, &error)) {
- g_warning("Error getting the properties from ID %d: %s", id, error->message);
- g_error_free(error);
- error = NULL;
- continue;
- }
+ GVariant *id_v = g_variant_get_child_value(idlist, 0);
+ gint32 id = g_variant_get_int32(id_v);
+ g_variant_unref(id_v);
- GValueArray * valarray = g_value_array_new(2);
+ if (id == 0) {
- _gvalue_array_append_int(valarray, id);
- _gvalue_array_append_hashtable(valarray, idprops);
+ GVariant * final = g_variant_parse(G_VARIANT_TYPE("(a(ia{sv}))"), "([(0, {})],)", NULL, NULL, NULL);
+ g_dbus_method_invocation_return_value(invocation, final);
+ g_variant_unref(final);
+ }
+ } else {
- g_ptr_array_add(*values, valarray);
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ NO_VALID_LAYOUT,
+ "There currently isn't a layout in this server");
+ }
+ g_variant_unref(idlist);
+ return;
}
- return TRUE;
-}
+ GVariantIter *ids;
+ g_variant_get(params, "(aias)", &ids, NULL);
+ /* TODO: implementation ignores propertyNames declared in XML */
-/* Allocate a value on the stack for the int and append
- it to the array. */
-static void
-_gvalue_array_append_int(GValueArray *array, gint i)
-{
- GValue value = {0};
+ GVariantBuilder builder;
+ gboolean builder_init = FALSE;
- g_value_init(&value, G_TYPE_INT);
- g_value_set_int(&value, i);
- g_value_array_append(array, &value);
- g_value_unset(&value);
-}
+ gint32 id;
+ while (g_variant_iter_loop(ids, "i", &id)) {
+ DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
+ if (mi == NULL) continue;
-/* Allocate a value on the stack for the hashtable and append
- it to the array. */
-static void
-_gvalue_array_append_hashtable(GValueArray *array, GHashTable * dict)
-{
- GValue value = {0};
+ if (!builder_init) {
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
+ builder_init = TRUE;
+ }
+
+ 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, NULL);
+
+ if (props == NULL) {
+ GError * error = NULL;
+ props = g_variant_parse(G_VARIANT_TYPE("a{sv}"), "{}", NULL, NULL, &error);
+ if (error != NULL) {
+ g_warning("Unable to parse '{}' as a 'a{sv}': %s", error->message);
+ g_error_free(error);
+ props = NULL;
+ }
+ }
+
+ g_variant_builder_add_value(&wbuilder, props);
+ GVariant * mi_data = g_variant_builder_end(&wbuilder);
+
+ g_variant_builder_add_value(&builder, mi_data);
+ }
+ g_variant_iter_free(ids);
+
+ /* a standard reference that must be unrefed */
+ GVariant * ret = NULL;
+
+ if (builder_init) {
+ ret = g_variant_builder_end(&builder);
+ g_variant_ref_sink(ret);
+ } else {
+ GError * error = NULL;
+ ret = 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);
+ }
+ }
+
+ GVariant * final = NULL;
+ if (ret != NULL) {
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE);
+ g_variant_builder_add_value(&builder, ret);
+ g_variant_unref(ret);
+ final = g_variant_builder_end(&builder);
+ } else {
+ g_warning("Error building property list, final variant is NULL");
+ }
+
+ g_dbus_method_invocation_return_value(invocation, final);
- g_value_init(&value, dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE));
- g_value_set_boxed(&value, dict);
- g_value_array_append(array, &value);
- g_value_unset(&value);
+ return;
}
+/* Turn a menuitem into an variant and attach it to the
+ VariantBuilder we passed in */
static void
serialize_menuitem(gpointer data, gpointer user_data)
{
DbusmenuMenuitem * mi = DBUSMENU_MENUITEM(data);
- GPtrArray * output = (GPtrArray *)(user_data);
+ GVariantBuilder * builder = (GVariantBuilder *)(user_data);
+ GVariantBuilder tuple;
+
+ g_variant_builder_init(&tuple, G_VARIANT_TYPE_TUPLE);
gint id = dbusmenu_menuitem_get_id(mi);
- GHashTable * dict = dbusmenu_menuitem_properties_copy(mi);
-
- GValueArray * item = g_value_array_new(2);
- _gvalue_array_append_int(item, id);
- _gvalue_array_append_hashtable(item, dict);
+ g_variant_builder_add_value(&tuple, g_variant_new_int32(id));
- g_ptr_array_add(output, item);
+ GVariant * props = dbusmenu_menuitem_properties_variant(mi, NULL);
+ g_variant_builder_add_value(&tuple, props);
- g_hash_table_unref(dict);
+ g_variant_builder_add_value(builder, g_variant_builder_end(&tuple));
return;
}
-static gboolean
-_dbusmenu_server_get_children (DbusmenuServer * server, gint id, GPtrArray * properties, GPtrArray ** output, GError ** error)
+/* Gets the children and their properties of the ID that is
+ passed into the function */
+static void
+bus_get_children (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
{
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+ gint32 id;
+ g_variant_get(params, "(i)", &id);
+
+ if (priv->root == NULL) {
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ NO_VALID_LAYOUT,
+ "There currently isn't a layout in this server");
+ return;
+ }
+
DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
if (mi == NULL) {
- if (error != NULL) {
- g_set_error(error,
- error_quark(),
- INVALID_MENUITEM_ID,
- "The ID supplied %d does not refer to a menu item we have",
- id);
- }
- return FALSE;
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ INVALID_MENUITEM_ID,
+ "The ID supplied %d does not refer to a menu item we have",
+ id);
+ return;
}
- *output = g_ptr_array_new();
GList * children = dbusmenu_menuitem_get_children(mi);
- g_list_foreach(children, serialize_menuitem, *output);
+ GVariant * ret = NULL;
+
+ if (children != NULL) {
+ GVariantBuilder builder;
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
+
+ g_list_foreach(children, serialize_menuitem, &builder);
+
+ GVariant * end = g_variant_builder_end(&builder);
+ ret = g_variant_new_tuple(&end, 1);
+ } else {
+ GError * error = NULL;
+ ret = g_variant_parse(G_VARIANT_TYPE("(a(ia{sv}))"), "([(0, {})],)", NULL, NULL, &error);
+ if (error != NULL) {
+ g_warning("Unable to parse '([(0, {})],)' as a '(a(ia{sv}))': %s", error->message);
+ g_error_free(error);
+ ret = NULL;
+ }
+ }
- return TRUE;
+ g_dbus_method_invocation_return_value(invocation, ret);
+ return;
}
/* Structure for holding the event data for the idle function
@@ -669,7 +1514,7 @@ typedef struct _idle_event_t idle_event_t;
struct _idle_event_t {
DbusmenuMenuitem * mi;
gchar * eventid;
- GValue data;
+ GVariant * variant;
guint timestamp;
};
@@ -680,66 +1525,96 @@ event_local_handler (gpointer user_data)
{
idle_event_t * data = (idle_event_t *)user_data;
- dbusmenu_menuitem_handle_event(data->mi, data->eventid, &data->data, data->timestamp);
+ dbusmenu_menuitem_handle_event(data->mi, data->eventid, data->variant, data->timestamp);
g_object_unref(data->mi);
g_free(data->eventid);
- g_value_unset(&data->data);
+ g_variant_unref(data->variant);
g_free(data);
return FALSE;
}
-/* Handles the even coming off of DBus */
-static gboolean
-_dbusmenu_server_event (DbusmenuServer * server, gint id, gchar * eventid, GValue * data, guint timestamp, GError ** error)
+/* Handles the events coming off of DBus */
+static void
+bus_event (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
{
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
- DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
- if (mi == NULL) {
- if (error != NULL) {
- g_set_error(error,
+ if (priv->root == NULL) {
+ g_dbus_method_invocation_return_error(invocation,
error_quark(),
- INVALID_MENUITEM_ID,
- "The ID supplied %d does not refer to a menu item we have",
- id);
- }
- return FALSE;
+ NO_VALID_LAYOUT,
+ "There currently isn't a layout in this server");
+ return;
}
- idle_event_t * event_data = g_new0(idle_event_t, 1);
- event_data->mi = mi;
- g_object_ref(event_data->mi);
- event_data->eventid = g_strdup(eventid);
- event_data->timestamp = timestamp;
- g_value_init(&(event_data->data), G_VALUE_TYPE(data));
- g_value_copy(data, &(event_data->data));
+ gint32 id;
+ gchar *etype;
+ GVariant *data;
+ guint32 ts;
- g_timeout_add(0, event_local_handler, event_data);
- return TRUE;
+ g_variant_get(params, "(isvu)", &id, &etype, &data, &ts);
+
+ DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
+
+ if (mi == NULL) {
+
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ INVALID_MENUITEM_ID,
+ "The ID supplied %d does not refer to a menu item we have",
+ id);
+ g_free(etype);
+ g_variant_unref(data);
+
+ } else {
+
+ idle_event_t * event_data = g_new0(idle_event_t, 1);
+ event_data->mi = g_object_ref(mi);
+ event_data->eventid = etype;
+ event_data->timestamp = ts;
+ event_data->variant = data; /* give away our reference */
+
+ g_timeout_add(0, event_local_handler, event_data);
+ }
+
+ g_dbus_method_invocation_return_value(invocation, NULL);
+ return;
}
/* Recieve the About To Show function. Pass it to our menu item. */
-static gboolean
-_dbusmenu_server_about_to_show (DbusmenuServer * server, gint id, gboolean * need_update, GError ** error)
+static void
+bus_about_to_show (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
{
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
+ if (priv->root == NULL) {
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ NO_VALID_LAYOUT,
+ "There currently isn't a layout in this server");
+ return;
+ }
+
+ gint32 id;
+ g_variant_get(params, "(i)", &id);
DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
if (mi == NULL) {
- if (error != NULL) {
- g_set_error(error,
- error_quark(),
- INVALID_MENUITEM_ID,
- "The ID supplied %d does not refer to a menu item we have",
- id);
- }
- return FALSE;
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ INVALID_MENUITEM_ID,
+ "The ID supplied %d does not refer to a menu item we have",
+ id);
+ return;
}
+ dbusmenu_menuitem_send_about_to_show(mi, NULL, NULL);
+
/* GTK+ does not support about-to-show concept for now */
- *need_update = FALSE;
- return TRUE;
+ g_dbus_method_invocation_return_value(invocation,
+ g_variant_new("(b)", FALSE));
+ return;
}
/* Public Interface */
@@ -750,7 +1625,7 @@ _dbusmenu_server_about_to_show (DbusmenuServer * server, gint id, gboolean * nee
Creates a new #DbusmenuServer object with a specific object
path on DBus. If @object is set to NULL the default object
- name of "/org/ayatana/dbusmenu" will be used.
+ name of "/com/canonical/dbusmenu" will be used.
Return value: A brand new #DbusmenuServer
*/
@@ -758,7 +1633,7 @@ DbusmenuServer *
dbusmenu_server_new (const gchar * object)
{
if (object == NULL) {
- object = "/org/ayatana/dbusmenu";
+ object = "/com/canonical/dbusmenu";
}
DbusmenuServer * self = g_object_new(DBUSMENU_TYPE_SERVER,
@@ -792,5 +1667,164 @@ dbusmenu_server_set_root (DbusmenuServer * self, DbusmenuMenuitem * root)
return;
}
+/**
+ dbusmenu_server_get_text_direction:
+ @server: The #DbusmenuServer object to get the text direction from
+
+ Returns the value of the text direction that is being exported
+ over DBus for this server. It should relate to the direction
+ of the labels and other text fields that are being exported by
+ this server.
+
+ Return value: Text direction exported for this server.
+*/
+DbusmenuTextDirection
+dbusmenu_server_get_text_direction (DbusmenuServer * server)
+{
+ g_return_val_if_fail(DBUSMENU_IS_SERVER(server), DBUSMENU_TEXT_DIRECTION_NONE);
+
+ GValue val = {0};
+ g_value_init(&val, DBUSMENU_TYPE_TEXT_DIRECTION);
+ g_object_get_property(G_OBJECT(server), DBUSMENU_SERVER_PROP_TEXT_DIRECTION, &val);
+
+ DbusmenuTextDirection retval = g_value_get_enum(&val);
+ g_value_unset(&val);
+
+ return retval;
+}
+
+/**
+ dbusmenu_server_set_text_direction:
+ @server: The #DbusmenuServer object to set the text direction on
+ @dir: Direction of the text
+
+ Sets the text direction that should be exported over DBus for
+ this server. If the value is set to #DBUSMENU_TEXT_DIRECTION_NONE
+ the default detection will be used for setting the value and
+ exported over DBus.
+*/
+void
+dbusmenu_server_set_text_direction (DbusmenuServer * server, DbusmenuTextDirection dir)
+{
+ g_return_if_fail(DBUSMENU_IS_SERVER(server));
+ g_return_if_fail(dir == DBUSMENU_TEXT_DIRECTION_NONE || dir == DBUSMENU_TEXT_DIRECTION_LTR || dir == DBUSMENU_TEXT_DIRECTION_RTL);
+
+ GValue newval = {0};
+ g_value_init(&newval, DBUSMENU_TYPE_TEXT_DIRECTION);
+ g_value_set_enum(&newval, dir);
+ g_object_set_property(G_OBJECT(server), DBUSMENU_SERVER_PROP_TEXT_DIRECTION, &newval);
+ g_value_unset(&newval);
+ return;
+}
+
+/**
+ dbusmenu_server_get_status:
+ @server: The #DbusmenuServer to get the status from
+
+ Gets the current statust hat the server is sending out over
+ DBus.
+
+ Return value: The current status the server is sending
+*/
+DbusmenuStatus
+dbusmenu_server_get_status (DbusmenuServer * server)
+{
+ g_return_val_if_fail(DBUSMENU_IS_SERVER(server), DBUSMENU_STATUS_NORMAL);
+
+ GValue val = {0};
+ g_value_init(&val, DBUSMENU_TYPE_STATUS);
+ g_object_get_property(G_OBJECT(server), DBUSMENU_SERVER_PROP_STATUS, &val);
+
+ DbusmenuStatus retval = g_value_get_enum(&val);
+ g_value_unset(&val);
+
+ return retval;
+}
+
+/**
+ dbusmenu_server_set_status:
+ @server: The #DbusmenuServer to set the status on
+ @status: Status value to set on the server
+
+ Changes the status of the server.
+*/
+void
+dbusmenu_server_set_status (DbusmenuServer * server, DbusmenuStatus status)
+{
+ g_return_if_fail(DBUSMENU_IS_SERVER(server));
+
+ GValue val = {0};
+ g_value_init(&val, DBUSMENU_TYPE_STATUS);
+ g_value_set_enum(&val, status);
+ g_object_set_property(G_OBJECT(server), DBUSMENU_SERVER_PROP_STATUS, &val);
+ g_value_unset(&val);
+
+ return;
+}
+
+/**
+ * dbusmenu_server_get_icon_paths:
+ * @server: The #DbusmenuServer to get the icon paths from
+ *
+ * Gets the stored and exported icon paths from the server.
+ *
+ * Return value: (transfer none): A NULL-terminated list of icon paths with
+ * memory managed by the server. Duplicate if you want
+ * to keep them.
+ */
+const GStrv
+dbusmenu_server_get_icon_paths (DbusmenuServer * server)
+{
+ g_return_val_if_fail(DBUSMENU_IS_SERVER(server), NULL);
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+ return priv->icon_dirs;
+}
+
+/**
+ dbusmenu_server_set_icon_paths:
+ @server: The #DbusmenuServer to set the icon paths on
+
+ Sets the icon paths for the server. This will replace previously
+ set icon theme paths.
+*/
+void
+dbusmenu_server_set_icon_paths (DbusmenuServer * server, GStrv icon_paths)
+{
+ g_return_if_fail(DBUSMENU_IS_SERVER(server));
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
+ if (priv->icon_dirs != NULL) {
+ g_strfreev(priv->icon_dirs);
+ priv->icon_dirs = NULL;
+ }
+
+ if (icon_paths != NULL) {
+ priv->icon_dirs = g_strdupv(icon_paths);
+ }
+ if (priv->bus != NULL && priv->dbusobject != NULL) {
+ GVariantBuilder params;
+ g_variant_builder_init(&params, G_VARIANT_TYPE_TUPLE);
+ g_variant_builder_add_value(&params, g_variant_new_string(DBUSMENU_INTERFACE));
+ GVariant * items = NULL;
+ if (priv->icon_dirs != NULL) {
+ GVariant * dict = g_variant_new_dict_entry(g_variant_new_string("IconThemePath"), g_variant_new_variant(g_variant_new_strv((const gchar * const *)priv->icon_dirs, -1)));
+ items = g_variant_new_array(NULL, &dict, 1);
+ } else {
+ items = g_variant_new_array(G_VARIANT_TYPE("{sv}"), NULL, 0);
+ }
+ g_variant_builder_add_value(&params, items);
+ g_variant_builder_add_value(&params, g_variant_new_array(G_VARIANT_TYPE_STRING, NULL, 0));
+ GVariant * vparams = g_variant_builder_end(&params);
+
+ g_dbus_connection_emit_signal(priv->bus,
+ NULL,
+ priv->dbusobject,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ vparams,
+ NULL);
+ }
+ return;
+}