88
83
INVALID_PROPERTY_NAME,
89
84
UNKNOWN_DBUS_ERROR,
91
typedef void (*MethodTableFunc) (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation);
93
typedef struct _method_table_t method_table_t;
94
struct _method_table_t {
95
const gchar * interned_name;
100
METHOD_GET_LAYOUT = 0,
101
METHOD_GET_GROUP_PROPERTIES,
104
METHOD_GET_PROPERTIES,
106
METHOD_ABOUT_TO_SHOW,
107
/* Counter, do not remove! */
95
static void dbusmenu_server_class_init (DbusmenuServerClass *class);
96
static void dbusmenu_server_init (DbusmenuServer *self);
97
static void dbusmenu_server_dispose (GObject *object);
98
static void dbusmenu_server_finalize (GObject *object);
99
static void set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec);
100
static void get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec);
101
static void menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, GValue * value, DbusmenuServer * server);
102
static void menuitem_child_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint pos, DbusmenuServer * server);
103
static void menuitem_child_removed (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, DbusmenuServer * server);
104
static void menuitem_signals_create (DbusmenuMenuitem * mi, gpointer data);
105
static void menuitem_signals_remove (DbusmenuMenuitem * mi, gpointer data);
106
static GQuark error_quark (void);
112
static void dbusmenu_server_class_init (DbusmenuServerClass *class);
113
static void dbusmenu_server_init (DbusmenuServer *self);
114
static void dbusmenu_server_dispose (GObject *object);
115
static void dbusmenu_server_finalize (GObject *object);
116
static void set_property (GObject * obj,
118
const GValue * value,
120
static void get_property (GObject * obj,
124
static void register_object (DbusmenuServer * server);
125
static void bus_got_cb (GObject * obj,
126
GAsyncResult * result,
128
static void bus_method_call (GDBusConnection * connection,
129
const gchar * sender,
131
const gchar * interface,
132
const gchar * method,
134
GDBusMethodInvocation * invocation,
136
static GVariant * bus_get_prop (GDBusConnection * connection,
137
const gchar * sender,
139
const gchar * interface,
140
const gchar * property,
143
static void menuitem_property_changed (DbusmenuMenuitem * mi,
146
DbusmenuServer * server);
147
static void menuitem_child_added (DbusmenuMenuitem * parent,
148
DbusmenuMenuitem * child,
150
DbusmenuServer * server);
151
static void menuitem_child_removed (DbusmenuMenuitem * parent,
152
DbusmenuMenuitem * child,
153
DbusmenuServer * server);
154
static void menuitem_signals_create (DbusmenuMenuitem * mi,
156
static void menuitem_signals_remove (DbusmenuMenuitem * mi,
158
static GQuark error_quark (void);
159
static void bus_get_layout (DbusmenuServer * server,
161
GDBusMethodInvocation * invocation);
162
static void bus_get_group_properties (DbusmenuServer * server,
164
GDBusMethodInvocation * invocation);
165
static void bus_get_children (DbusmenuServer * server,
167
GDBusMethodInvocation * invocation);
168
static void bus_get_property (DbusmenuServer * server,
170
GDBusMethodInvocation * invocation);
171
static void bus_get_properties (DbusmenuServer * server,
173
GDBusMethodInvocation * invocation);
174
static void bus_event (DbusmenuServer * server,
176
GDBusMethodInvocation * invocation);
177
static void bus_about_to_show (DbusmenuServer * server,
179
GDBusMethodInvocation * invocation);
182
static GDBusNodeInfo * dbusmenu_node_info = NULL;
183
static GDBusInterfaceInfo * dbusmenu_interface_info = NULL;
184
static const GDBusInterfaceVTable dbusmenu_interface_table = {
185
method_call: bus_method_call,
186
get_property: bus_get_prop,
187
set_property: NULL /* No properties that can be set */
189
static method_table_t dbusmenu_method_table[METHOD_COUNT];
108
191
G_DEFINE_TYPE (DbusmenuServer, dbusmenu_server, G_TYPE_OBJECT);
476
/* Register the object on the dbus bus */
478
register_object (DbusmenuServer * server)
480
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
483
g_return_if_fail(priv->bus != NULL);
484
g_return_if_fail(priv->dbusobject != NULL);
487
g_return_if_fail(dbusmenu_node_info != NULL);
488
g_return_if_fail(dbusmenu_interface_info != NULL);
490
/* We might block on this in the future, but it'd be nice if
491
we could change the object path. Thinking about it... */
492
if (priv->dbus_registration != 0) {
493
g_dbus_connection_unregister_object(priv->bus, priv->dbus_registration);
494
priv->dbus_registration = 0;
497
GError * error = NULL;
498
priv->dbus_registration = g_dbus_connection_register_object(priv->bus,
500
dbusmenu_interface_info,
501
&dbusmenu_interface_table,
507
g_warning("Unable to register object on bus: %s", error->message);
514
/* Callback from asking GIO to get us the session bus */
516
bus_got_cb (GObject * obj, GAsyncResult * result, gpointer user_data)
518
GError * error = NULL;
520
GDBusConnection * bus = g_bus_get_finish(result, &error);
523
g_warning("Unable to get session bus: %s", error->message);
528
/* Note: We're not using the user_data before we check for
529
the error so that in the cancelled case at destruction of
530
the object we don't end up with an invalid object. */
532
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(user_data);
535
register_object(DBUSMENU_SERVER(user_data));
540
/* Function for the GDBus vtable to handle all method calls and dish
541
them out the appropriate functions */
543
bus_method_call (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * method, GVariant * params, GDBusMethodInvocation * invocation, gpointer user_data)
546
const gchar * interned_method = g_intern_string(method);
548
for (i = 0; i < METHOD_COUNT; i++) {
549
if (dbusmenu_method_table[i].interned_name == interned_method) {
550
if (dbusmenu_method_table[i].func != NULL) {
551
return dbusmenu_method_table[i].func(DBUSMENU_SERVER(user_data), params, invocation);
553
/* If we have a null function we're responding but nothing else. */
554
g_warning("Invalid function call for '%s' with parameters: %s", method, g_variant_print(params, TRUE));
555
g_dbus_method_invocation_return_value(invocation, NULL);
561
/* We're here because there's an error */
562
g_dbus_method_invocation_return_error(invocation,
565
"Unable to find method '%s'",
570
/* For the GDBus vtable but we only have one property so it's pretty
573
bus_get_prop (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * property, GError ** error, gpointer user_data)
575
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(user_data);
577
/* None of these should happen */
578
g_return_val_if_fail(g_strcmp0(interface, DBUSMENU_INTERFACE) == 0, NULL);
579
g_return_val_if_fail(g_strcmp0(path, priv->dbusobject) == 0, NULL);
580
g_return_val_if_fail(g_strcmp0(property, "version") == 0, NULL);
582
return g_variant_new_uint32(DBUSMENU_VERSION_NUMBER);
331
585
/* Handle actually signalling in the idle loop. This way we collect all
467
758
dbusmenu_menuitem_buildxml(priv->root, xmlarray);
470
DbusmenuMenuitem * item = dbusmenu_menuitem_find_id(priv->root, parent);
761
DbusmenuMenuitem * item = NULL;
762
if (priv->root != NULL) {
763
item = dbusmenu_menuitem_find_id(priv->root, parent);
471
766
if (item == NULL) {
476
"The ID supplied %d does not refer to a menu item we have",
767
g_dbus_method_invocation_return_error(invocation,
770
"The ID supplied %d does not refer to a menu item we have",
481
774
dbusmenu_menuitem_buildxml(item, xmlarray);
483
776
g_ptr_array_add(xmlarray, NULL);
485
778
/* build string */
486
*layout = g_strjoinv("", (gchar **)xmlarray->pdata);
779
gchar * layout = g_strjoinv("", (gchar **)xmlarray->pdata);
488
781
g_ptr_array_foreach(xmlarray, xmlarray_foreach_free, NULL);
489
782
g_ptr_array_free(xmlarray, TRUE);
784
g_dbus_method_invocation_return_value(invocation,
785
g_variant_new("(us)",
495
_dbusmenu_server_get_property (DbusmenuServer * server, gint id, gchar * property, gchar ** value, GError ** error)
794
/* Get a single property off of a single menuitem */
796
bus_get_property (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
497
798
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
800
if (priv->root == NULL) {
801
g_dbus_method_invocation_return_error(invocation,
804
"There currently isn't a layout in this server");
808
gint id = g_variant_get_int32(g_variant_get_child_value(params, 0));
809
const gchar * property = g_variant_get_string(g_variant_get_child_value(params, 1), NULL);
498
811
DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
500
813
if (mi == NULL) {
814
g_dbus_method_invocation_return_error(invocation,
504
816
INVALID_MENUITEM_ID,
505
817
"The ID supplied %d does not refer to a menu item we have",
511
const gchar * prop = dbusmenu_menuitem_property_get(mi, property);
822
GVariant * variant = dbusmenu_menuitem_property_get_variant(mi, property);
823
if (variant == NULL) {
824
g_dbus_method_invocation_return_error(invocation,
516
826
INVALID_PROPERTY_NAME,
517
827
"The property '%s' does not exist on menuitem with ID of %d",
529
"Uhm, yeah. We didn't get anywhere to put the value, that's really weird. Seems impossible really.");
534
*value = g_strdup(prop);
833
g_dbus_method_invocation_return_value(invocation, g_variant_new("(v)", variant));
540
_dbusmenu_server_get_properties (DbusmenuServer * server, gint id, gchar ** properties, GHashTable ** dict, GError ** error)
837
/* Get some properties off of a single menuitem */
839
bus_get_properties (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
542
841
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
843
if (priv->root == NULL) {
844
g_dbus_method_invocation_return_error(invocation,
847
"There currently isn't a layout in this server");
851
gint id = g_variant_get_int32(g_variant_get_child_value(params, 0));
543
853
DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
545
855
if (mi == NULL) {
856
g_dbus_method_invocation_return_error(invocation,
549
858
INVALID_MENUITEM_ID,
550
859
"The ID supplied %d does not refer to a menu item we have",
556
*dict = dbusmenu_menuitem_properties_copy(mi);
864
GVariant * dict = dbusmenu_menuitem_properties_variant(mi);
866
g_dbus_method_invocation_return_value(invocation, g_variant_new("(a{sv})", dict));
561
871
/* Handles getting a bunch of properties from a variety of menu items
562
872
to make one mega dbus message */
564
_dbusmenu_server_get_group_properties (DbusmenuServer * server, GArray * ids, gchar ** properties, GPtrArray ** values, GError ** error)
566
/* Build an initial pointer array */
567
*values = g_ptr_array_new();
569
/* Go through each ID to get that ID's properties */
571
for (idcnt = 0; idcnt < ids->len; idcnt++) {
572
GHashTable * idprops = NULL;
573
GError * error = NULL;
574
gint id = g_array_index(ids, int, idcnt);
576
/* Get the properties for this ID the old fashioned way. */
577
if (!_dbusmenu_server_get_properties(server, id, properties, &idprops, &error)) {
578
g_warning("Error getting the properties from ID %d: %s", id, error->message);
584
GValueArray * valarray = g_value_array_new(2);
586
_gvalue_array_append_int(valarray, id);
587
_gvalue_array_append_hashtable(valarray, idprops);
589
g_ptr_array_add(*values, valarray);
595
/* Allocate a value on the stack for the int and append
598
_gvalue_array_append_int(GValueArray *array, gint i)
602
g_value_init(&value, G_TYPE_INT);
603
g_value_set_int(&value, i);
604
g_value_array_append(array, &value);
605
g_value_unset(&value);
608
/* Allocate a value on the stack for the hashtable and append
611
_gvalue_array_append_hashtable(GValueArray *array, GHashTable * dict)
615
g_value_init(&value, dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE));
616
g_value_set_boxed(&value, dict);
617
g_value_array_append(array, &value);
618
g_value_unset(&value);
874
bus_get_group_properties (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
876
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
878
if (priv->root == NULL) {
879
GVariant * idlist = g_variant_get_child_value(params, 0);
880
if (g_variant_n_children(idlist) == 1 && g_variant_get_int32(g_variant_get_child_value(idlist, 0)) == 0) {
881
GVariant * final = g_variant_parse(g_variant_type_new("(a(ia{sv}))"), "([(0, {})],)", NULL, NULL, NULL);
882
g_dbus_method_invocation_return_value(invocation, final);
886
g_dbus_method_invocation_return_error(invocation,
889
"There currently isn't a layout in this server");
894
g_variant_iter_init(&ids, g_variant_get_child_value(params, 0));
896
GVariantBuilder builder;
897
g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
900
while (g_variant_iter_next(&ids, "i", &id)) {
901
DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
902
if (mi == NULL) continue;
904
GVariantBuilder wbuilder;
905
g_variant_builder_init(&wbuilder, G_VARIANT_TYPE_TUPLE);
906
g_variant_builder_add(&wbuilder, "i", id);
907
GVariant * props = dbusmenu_menuitem_properties_variant(mi);
910
props = g_variant_parse(g_variant_type_new("a{sv}"), "{}", NULL, NULL, NULL);
913
g_variant_builder_add_value(&wbuilder, props);
914
GVariant * mi_data = g_variant_builder_end(&wbuilder);
916
g_variant_builder_add_value(&builder, mi_data);
919
GVariant * ret = g_variant_builder_end(&builder);
921
g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE);
922
g_variant_builder_add_value(&builder, ret);
923
GVariant * final = g_variant_builder_end(&builder);
925
g_dbus_method_invocation_return_value(invocation, final);
930
/* Turn a menuitem into an variant and attach it to the
931
VariantBuilder we passed in */
622
933
serialize_menuitem(gpointer data, gpointer user_data)
624
935
DbusmenuMenuitem * mi = DBUSMENU_MENUITEM(data);
625
GPtrArray * output = (GPtrArray *)(user_data);
936
GVariantBuilder * builder = (GVariantBuilder *)(user_data);
627
938
gint id = dbusmenu_menuitem_get_id(mi);
628
GHashTable * dict = dbusmenu_menuitem_properties_copy(mi);
630
GValueArray * item = g_value_array_new(2);
631
_gvalue_array_append_int(item, id);
632
_gvalue_array_append_hashtable(item, dict);
634
g_ptr_array_add(output, item);
636
g_hash_table_unref(dict);
939
GVariant * props = dbusmenu_menuitem_properties_variant(mi);
941
g_variant_builder_add(builder, "ia{sv}", id, props);
642
_dbusmenu_server_get_children (DbusmenuServer * server, gint id, GPtrArray * properties, GPtrArray ** output, GError ** error)
946
/* Gets the children and their properties of the ID that is
947
passed into the function */
949
bus_get_children (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
644
951
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
952
gint id = g_variant_get_int32(g_variant_get_child_value(params, 0));
954
if (priv->root == NULL) {
955
g_dbus_method_invocation_return_error(invocation,
958
"There currently isn't a layout in this server");
645
962
DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
647
964
if (mi == NULL) {
652
"The ID supplied %d does not refer to a menu item we have",
965
g_dbus_method_invocation_return_error(invocation,
968
"The ID supplied %d does not refer to a menu item we have",
658
*output = g_ptr_array_new();
659
973
GList * children = dbusmenu_menuitem_get_children(mi);
660
g_list_foreach(children, serialize_menuitem, *output);
974
GVariant * ret = NULL;
976
if (children != NULL) {
977
GVariantBuilder builder;
978
g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
980
g_list_foreach(children, serialize_menuitem, &builder);
982
ret = g_variant_new("(a(ia{svg}))", g_variant_builder_end(&builder));
984
ret = g_variant_parse(g_variant_type_new("(a(ia{sv}))"), "([(0, {})],)", NULL, NULL, NULL);
987
g_dbus_method_invocation_return_value(invocation, ret);
665
991
/* Structure for holding the event data for the idle function
680
1006
idle_event_t * data = (idle_event_t *)user_data;
682
dbusmenu_menuitem_handle_event(data->mi, data->eventid, &data->data, data->timestamp);
1008
dbusmenu_menuitem_handle_event(data->mi, data->eventid, data->variant, data->timestamp);
684
1010
g_object_unref(data->mi);
685
1011
g_free(data->eventid);
686
g_value_unset(&data->data);
1012
g_variant_unref(data->variant);
691
/* Handles the even coming off of DBus */
693
_dbusmenu_server_event (DbusmenuServer * server, gint id, gchar * eventid, GValue * data, guint timestamp, GError ** error)
1017
/* Handles the events coming off of DBus */
1019
bus_event (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
695
1021
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
1023
if (priv->root == NULL) {
1024
g_dbus_method_invocation_return_error(invocation,
1027
"There currently isn't a layout in this server");
1031
gint id = g_variant_get_int32(g_variant_get_child_value(params, 0));
696
1032
DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
698
1034
if (mi == NULL) {
703
"The ID supplied %d does not refer to a menu item we have",
1035
g_dbus_method_invocation_return_error(invocation,
1037
INVALID_MENUITEM_ID,
1038
"The ID supplied %d does not refer to a menu item we have",
709
1043
idle_event_t * event_data = g_new0(idle_event_t, 1);
710
1044
event_data->mi = mi;
711
1045
g_object_ref(event_data->mi);
712
event_data->eventid = g_strdup(eventid);
713
event_data->timestamp = timestamp;
714
g_value_init(&(event_data->data), G_VALUE_TYPE(data));
715
g_value_copy(data, &(event_data->data));
1046
event_data->eventid = g_strdup(g_variant_get_string(g_variant_get_child_value(params, 1), NULL));
1047
event_data->timestamp = g_variant_get_uint32(g_variant_get_child_value(params, 3));
1048
event_data->variant = g_variant_get_child_value(params, 2);
717
1050
g_timeout_add(0, event_local_handler, event_data);
1052
g_dbus_method_invocation_return_value(invocation, NULL);
721
1056
/* Recieve the About To Show function. Pass it to our menu item. */
723
_dbusmenu_server_about_to_show (DbusmenuServer * server, gint id, gboolean * need_update, GError ** error)
1058
bus_about_to_show (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
725
1060
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
1062
if (priv->root == NULL) {
1063
g_dbus_method_invocation_return_error(invocation,
1066
"There currently isn't a layout in this server");
1070
gint id = g_variant_get_int32(g_variant_get_child_value(params, 0));
726
1071
DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
728
1073
if (mi == NULL) {
733
"The ID supplied %d does not refer to a menu item we have",
1074
g_dbus_method_invocation_return_error(invocation,
1076
INVALID_MENUITEM_ID,
1077
"The ID supplied %d does not refer to a menu item we have",
739
1082
/* GTK+ does not support about-to-show concept for now */
740
*need_update = FALSE;
1083
g_dbus_method_invocation_return_value(invocation,
1084
g_variant_new("(b)", FALSE));
744
1088
/* Public Interface */