~ken-vandine/libdbusmenu/ubuntu

« back to all changes in this revision

Viewing changes to libdbusmenu-glib/server.c

  • Committer: Ken VanDine
  • Date: 2010-12-10 15:20:10 UTC
  • mfrom: (48.1.207 ubuntu)
  • Revision ID: ken.vandine@canonical.com-20101210152010-7ztwgb1ivsj6mwvc
* New upstream snapshot, r175 
* debian/control
  - New binaries libdbusmenu-gtk3-3, libdbusmenu-jsonloader3, 
    libdbusmenu-gtk3, libdbusmenu-glib3, and gir1.0-dbusmenu-gtk3-0.4
  - Added new build depends for xsltproc
* debian/control: Forgot jsonloader
* Upstream Merge
  * Fixing check items
  * Bumping the library ABI version
* debian/control: Bumping ABI version in package names
* Upstream Merge
  * Porting from dbus-glib to GDBus
* debian/*.install: Changing to be the 0.4 version of the dbusmenu
  API of all the filenames and paths.
* Autogen

Show diffs side-by-side

added added

removed removed

Lines of Context:
30
30
#include "config.h"
31
31
#endif
32
32
 
 
33
#include <gio/gio.h>
 
34
 
33
35
#include "menuitem-private.h"
34
36
#include "server.h"
35
37
#include "server-marshal.h"
36
38
 
37
 
/* DBus Prototypes */
38
 
static gboolean _dbusmenu_server_get_layout (DbusmenuServer * server, gint parent, guint * revision, gchar ** layout, GError ** error);
39
 
static gboolean _dbusmenu_server_get_property (DbusmenuServer * server, gint id, gchar * property, gchar ** value, GError ** error);
40
 
static gboolean _dbusmenu_server_get_properties (DbusmenuServer * server, gint id, gchar ** properties, GHashTable ** dict, GError ** error);
41
 
static gboolean _dbusmenu_server_get_group_properties (DbusmenuServer * server, GArray * ids, gchar ** properties, GPtrArray ** values, GError ** error);
42
 
static gboolean _dbusmenu_server_event (DbusmenuServer * server, gint id, gchar * eventid, GValue * data, guint timestamp, GError ** error);
43
 
static gboolean _dbusmenu_server_get_children (DbusmenuServer * server, gint id, GPtrArray * properties, GPtrArray ** output, GError ** error);
44
 
static gboolean _dbusmenu_server_about_to_show (DbusmenuServer * server, gint id, gboolean * need_update, GError ** error);
45
 
/* DBus Helpers */
46
 
static void _gvalue_array_append_int(GValueArray *array, gint i);
47
 
static void _gvalue_array_append_hashtable(GValueArray *array, GHashTable * dict);
48
 
 
49
 
#include "dbusmenu-server.h"
 
39
#include "dbus-menu-clean.xml.h"
50
40
 
51
41
static void layout_update_signal (DbusmenuServer * server);
52
42
 
53
 
#define DBUSMENU_VERSION_NUMBER  2
 
43
#define DBUSMENU_VERSION_NUMBER    2
 
44
#define DBUSMENU_INTERFACE         "org.ayatana.dbusmenu"
54
45
 
55
46
/* Privates, I'll show you mine... */
56
47
struct _DbusmenuServerPrivate
59
50
        gchar * dbusobject;
60
51
        gint layout_revision;
61
52
        guint layout_idle;
 
53
 
 
54
        GDBusConnection * bus;
 
55
        GCancellable * bus_lookup;
 
56
        guint dbus_registration;
62
57
};
63
58
 
64
59
#define DBUSMENU_SERVER_GET_PRIVATE(o) (DBUSMENU_SERVER(o)->priv)
88
83
        INVALID_PROPERTY_NAME,
89
84
        UNKNOWN_DBUS_ERROR,
90
85
        NOT_IMPLEMENTED,
 
86
        NO_VALID_LAYOUT,
91
87
        LAST_ERROR
92
88
};
93
89
 
 
90
/* Method Table */
 
91
typedef void (*MethodTableFunc) (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation);
 
92
 
 
93
typedef struct _method_table_t method_table_t;
 
94
struct _method_table_t {
 
95
        const gchar * interned_name;
 
96
        MethodTableFunc func;
 
97
};
 
98
 
 
99
enum {
 
100
        METHOD_GET_LAYOUT = 0,
 
101
        METHOD_GET_GROUP_PROPERTIES,
 
102
        METHOD_GET_CHILDREN,
 
103
        METHOD_GET_PROPERTY,
 
104
        METHOD_GET_PROPERTIES,
 
105
        METHOD_EVENT,
 
106
        METHOD_ABOUT_TO_SHOW,
 
107
        /* Counter, do not remove! */
 
108
        METHOD_COUNT
 
109
};
 
110
 
94
111
/* Prototype */
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,
 
117
                                               guint id,
 
118
                                               const GValue * value,
 
119
                                               GParamSpec * pspec);
 
120
static void       get_property                (GObject * obj,
 
121
                                               guint id,
 
122
                                               GValue * value,
 
123
                                               GParamSpec * pspec);
 
124
static void       register_object             (DbusmenuServer * server);
 
125
static void       bus_got_cb                  (GObject * obj,
 
126
                                               GAsyncResult * result,
 
127
                                               gpointer user_data);
 
128
static void       bus_method_call             (GDBusConnection * connection,
 
129
                                               const gchar * sender,
 
130
                                               const gchar * path,
 
131
                                               const gchar * interface,
 
132
                                               const gchar * method,
 
133
                                               GVariant * params,
 
134
                                               GDBusMethodInvocation * invocation,
 
135
                                               gpointer user_data);
 
136
static GVariant * bus_get_prop                (GDBusConnection * connection,
 
137
                                               const gchar * sender,
 
138
                                               const gchar * path,
 
139
                                               const gchar * interface,
 
140
                                               const gchar * property,
 
141
                                               GError ** error,
 
142
                                               gpointer user_data);
 
143
static void       menuitem_property_changed   (DbusmenuMenuitem * mi,
 
144
                                               gchar * property,
 
145
                                               GVariant * variant,
 
146
                                               DbusmenuServer * server);
 
147
static void       menuitem_child_added        (DbusmenuMenuitem * parent,
 
148
                                               DbusmenuMenuitem * child,
 
149
                                               guint pos,
 
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,
 
155
                                               gpointer data);
 
156
static void       menuitem_signals_remove     (DbusmenuMenuitem * mi,
 
157
                                               gpointer data);
 
158
static GQuark     error_quark                 (void);
 
159
static void       bus_get_layout              (DbusmenuServer * server,
 
160
                                               GVariant * params,
 
161
                                               GDBusMethodInvocation * invocation);
 
162
static void       bus_get_group_properties    (DbusmenuServer * server,
 
163
                                               GVariant * params,
 
164
                                               GDBusMethodInvocation * invocation);
 
165
static void       bus_get_children            (DbusmenuServer * server,
 
166
                                               GVariant * params,
 
167
                                               GDBusMethodInvocation * invocation);
 
168
static void       bus_get_property            (DbusmenuServer * server,
 
169
                                               GVariant * params,
 
170
                                               GDBusMethodInvocation * invocation);
 
171
static void       bus_get_properties          (DbusmenuServer * server,
 
172
                                               GVariant * params,
 
173
                                               GDBusMethodInvocation * invocation);
 
174
static void       bus_event                   (DbusmenuServer * server,
 
175
                                               GVariant * params,
 
176
                                               GDBusMethodInvocation * invocation);
 
177
static void       bus_about_to_show           (DbusmenuServer * server,
 
178
                                               GVariant * params,
 
179
                                               GDBusMethodInvocation * invocation);
 
180
 
 
181
/* Globals */
 
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 */
 
188
};
 
189
static method_table_t             dbusmenu_method_table[METHOD_COUNT];
107
190
 
108
191
G_DEFINE_TYPE (DbusmenuServer, dbusmenu_server, G_TYPE_OBJECT);
109
192
 
134
217
                                                 G_SIGNAL_RUN_LAST,
135
218
                                                 G_STRUCT_OFFSET(DbusmenuServerClass, id_prop_update),
136
219
                                                 NULL, NULL,
137
 
                                                 _dbusmenu_server_marshal_VOID__INT_STRING_POINTER,
138
 
                                                 G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_STRING, G_TYPE_VALUE);
 
220
                                                 _dbusmenu_server_marshal_VOID__INT_STRING_VARIANT,
 
221
                                                 G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_STRING, G_TYPE_VARIANT);
139
222
        /**
140
223
                DbusmenuServer::id-update:
141
224
                @arg0: The #DbusmenuServer emitting the signal.
203
286
                                                      DBUSMENU_VERSION_NUMBER, DBUSMENU_VERSION_NUMBER, DBUSMENU_VERSION_NUMBER,
204
287
                                                      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
205
288
 
206
 
        dbus_g_object_type_install_info(DBUSMENU_TYPE_SERVER, &dbus_glib__dbusmenu_server_object_info);
 
289
        if (dbusmenu_node_info == NULL) {
 
290
                GError * error = NULL;
 
291
 
 
292
                dbusmenu_node_info = g_dbus_node_info_new_for_xml(dbus_menu_clean_xml, &error);
 
293
                if (error != NULL) {
 
294
                        g_error("Unable to parse DBusmenu Interface description: %s", error->message);
 
295
                        g_error_free(error);
 
296
                }
 
297
        }
 
298
 
 
299
        if (dbusmenu_interface_info == NULL) {
 
300
                dbusmenu_interface_info = g_dbus_node_info_lookup_interface(dbusmenu_node_info, DBUSMENU_INTERFACE);
 
301
 
 
302
                if (dbusmenu_interface_info == NULL) {
 
303
                        g_error("Unable to find interface '" DBUSMENU_INTERFACE "'");
 
304
                }
 
305
        }
 
306
 
 
307
        /* Building our Method table :( */
 
308
        dbusmenu_method_table[METHOD_GET_LAYOUT].interned_name = g_intern_static_string("GetLayout");
 
309
        dbusmenu_method_table[METHOD_GET_LAYOUT].func          = bus_get_layout;
 
310
 
 
311
        dbusmenu_method_table[METHOD_GET_GROUP_PROPERTIES].interned_name = g_intern_static_string("GetGroupProperties");
 
312
        dbusmenu_method_table[METHOD_GET_GROUP_PROPERTIES].func          = bus_get_group_properties;
 
313
 
 
314
        dbusmenu_method_table[METHOD_GET_CHILDREN].interned_name = g_intern_static_string("GetChildren");
 
315
        dbusmenu_method_table[METHOD_GET_CHILDREN].func          = bus_get_children;
 
316
 
 
317
        dbusmenu_method_table[METHOD_GET_PROPERTY].interned_name = g_intern_static_string("GetProperty");
 
318
        dbusmenu_method_table[METHOD_GET_PROPERTY].func          = bus_get_property;
 
319
 
 
320
        dbusmenu_method_table[METHOD_GET_PROPERTIES].interned_name = g_intern_static_string("GetProperties");
 
321
        dbusmenu_method_table[METHOD_GET_PROPERTIES].func          = bus_get_properties;
 
322
 
 
323
        dbusmenu_method_table[METHOD_EVENT].interned_name = g_intern_static_string("Event");
 
324
        dbusmenu_method_table[METHOD_EVENT].func          = bus_event;
 
325
 
 
326
        dbusmenu_method_table[METHOD_ABOUT_TO_SHOW].interned_name = g_intern_static_string("AboutToShow");
 
327
        dbusmenu_method_table[METHOD_ABOUT_TO_SHOW].func          = bus_about_to_show;
207
328
 
208
329
        return;
209
330
}
219
340
        priv->dbusobject = NULL;
220
341
        priv->layout_revision = 1;
221
342
        priv->layout_idle = 0;
 
343
        priv->bus = NULL;
 
344
        priv->bus_lookup = NULL;
 
345
        priv->dbus_registration = 0;
222
346
 
223
347
        return;
224
348
}
237
361
                g_object_unref(priv->root);
238
362
        }
239
363
 
 
364
        if (priv->dbus_registration != 0) {
 
365
                g_dbus_connection_unregister_object(priv->bus, priv->dbus_registration);
 
366
                priv->dbus_registration = 0;
 
367
        }
 
368
 
 
369
        if (priv->bus != NULL) {
 
370
                g_object_unref(priv->bus);
 
371
                priv->bus = NULL;
 
372
        }
 
373
 
 
374
        if (priv->bus_lookup != NULL) {
 
375
                if (!g_cancellable_is_cancelled(priv->bus_lookup)) {
 
376
                        /* Note, this may case the async function to run at
 
377
                           some point in the future.  That's okay, it'll get an
 
378
                           error, but just FYI */
 
379
                        g_cancellable_cancel(priv->bus_lookup);
 
380
                }
 
381
                g_object_unref(priv->bus_lookup);
 
382
                priv->bus_lookup = NULL;
 
383
        }
 
384
 
240
385
        G_OBJECT_CLASS (dbusmenu_server_parent_class)->dispose (object);
241
386
        return;
242
387
}
252
397
set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
253
398
{
254
399
        DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(obj);
255
 
        GError * error = NULL;
256
400
 
257
401
        switch (id) {
258
402
        case PROP_DBUS_OBJECT:
259
403
                g_return_if_fail(priv->dbusobject == NULL);
260
404
                priv->dbusobject = g_value_dup_string(value);
261
 
                DBusGConnection * connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
262
 
 
263
 
                if (connection == NULL || error != NULL) {
264
 
                        g_warning("Unable to get session bus: %s", error == NULL ? "No message" : error->message);
265
 
                        if (error != NULL) { g_error_free(error); }
 
405
 
 
406
                if (priv->bus == NULL) {
 
407
                        if (priv->bus_lookup == NULL) {
 
408
                                priv->bus_lookup = g_cancellable_new();
 
409
                                g_return_if_fail(priv->bus_lookup != NULL);
 
410
                        }
 
411
 
 
412
                        g_bus_get(G_BUS_TYPE_SESSION, priv->bus_lookup, bus_got_cb, obj);
266
413
                } else {
267
 
                        dbus_g_connection_register_g_object(connection,
268
 
                                                            priv->dbusobject,
269
 
                                                            obj);
 
414
                        register_object(DBUSMENU_SERVER(obj));
270
415
                }
271
416
                break;
272
417
        case PROP_ROOT_NODE:
328
473
        return;
329
474
}
330
475
 
 
476
/* Register the object on the dbus bus */
 
477
static void
 
478
register_object (DbusmenuServer * server)
 
479
{
 
480
        DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
 
481
 
 
482
        /* Object info */
 
483
        g_return_if_fail(priv->bus != NULL);
 
484
        g_return_if_fail(priv->dbusobject != NULL);
 
485
 
 
486
        /* Class info */
 
487
        g_return_if_fail(dbusmenu_node_info != NULL);
 
488
        g_return_if_fail(dbusmenu_interface_info != NULL);
 
489
 
 
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;
 
495
        }
 
496
 
 
497
        GError * error = NULL;
 
498
        priv->dbus_registration = g_dbus_connection_register_object(priv->bus,
 
499
                                                                    priv->dbusobject,
 
500
                                                                    dbusmenu_interface_info,
 
501
                                                                    &dbusmenu_interface_table,
 
502
                                                                    server,
 
503
                                                                    NULL,
 
504
                                                                    &error);
 
505
 
 
506
        if (error != NULL) {
 
507
                g_warning("Unable to register object on bus: %s", error->message);
 
508
                g_error_free(error);
 
509
        }
 
510
 
 
511
        return;
 
512
}
 
513
 
 
514
/* Callback from asking GIO to get us the session bus */
 
515
static void
 
516
bus_got_cb (GObject * obj, GAsyncResult * result, gpointer user_data)
 
517
{
 
518
        GError * error = NULL;
 
519
 
 
520
        GDBusConnection * bus = g_bus_get_finish(result, &error);
 
521
 
 
522
        if (error != NULL) {
 
523
                g_warning("Unable to get session bus: %s", error->message);
 
524
                g_error_free(error);
 
525
                return;
 
526
        }
 
527
 
 
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. */
 
531
 
 
532
        DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(user_data);
 
533
        priv->bus = bus;
 
534
 
 
535
        register_object(DBUSMENU_SERVER(user_data));
 
536
 
 
537
        return;
 
538
}
 
539
 
 
540
/* Function for the GDBus vtable to handle all method calls and dish
 
541
   them out the appropriate functions */
 
542
static void
 
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)
 
544
{
 
545
        int i;
 
546
        const gchar * interned_method = g_intern_string(method);
 
547
 
 
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);
 
552
                        } else {
 
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);
 
556
                                return;
 
557
                        }
 
558
                }
 
559
        }
 
560
 
 
561
        /* We're here because there's an error */
 
562
        g_dbus_method_invocation_return_error(invocation,
 
563
                                              error_quark(),
 
564
                                              NOT_IMPLEMENTED,
 
565
                                              "Unable to find method '%s'",
 
566
                                              method);
 
567
        return;
 
568
}
 
569
 
 
570
/* For the GDBus vtable but we only have one property so it's pretty
 
571
   simple. */
 
572
static GVariant *
 
573
bus_get_prop (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * property, GError ** error, gpointer user_data)
 
574
{
 
575
        DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(user_data);
 
576
 
 
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);
 
581
 
 
582
        return g_variant_new_uint32(DBUSMENU_VERSION_NUMBER);
 
583
}
 
584
 
331
585
/* Handle actually signalling in the idle loop.  This way we collect all
332
586
   the updates. */
333
587
static gboolean
337
591
        DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
338
592
 
339
593
        g_signal_emit(G_OBJECT(server), signals[LAYOUT_UPDATED], 0, priv->layout_revision, 0, TRUE);
 
594
        if (priv->dbusobject != NULL && priv->bus != NULL) {
 
595
                g_dbus_connection_emit_signal(priv->bus,
 
596
                                              NULL,
 
597
                                              priv->dbusobject,
 
598
                                              DBUSMENU_INTERFACE,
 
599
                                              "LayoutUpdated",
 
600
                                              g_variant_new("(ui)", priv->layout_revision, 0),
 
601
                                              NULL);
 
602
        }
340
603
 
341
604
        priv->layout_idle = 0;
342
605
 
358
621
}
359
622
 
360
623
static void 
361
 
menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, GValue * value, DbusmenuServer * server)
 
624
menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, GVariant * variant, DbusmenuServer * server)
362
625
{
363
 
        g_signal_emit(G_OBJECT(server), signals[ID_PROP_UPDATE], 0, dbusmenu_menuitem_get_id(mi), property, value, TRUE);
 
626
        DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
 
627
 
 
628
        g_signal_emit(G_OBJECT(server), signals[ID_PROP_UPDATE], 0, dbusmenu_menuitem_get_id(mi), property, variant, TRUE);
 
629
 
 
630
        if (priv->dbusobject != NULL && priv->bus != NULL) {
 
631
                g_dbus_connection_emit_signal(priv->bus,
 
632
                                              NULL,
 
633
                                              priv->dbusobject,
 
634
                                              DBUSMENU_INTERFACE,
 
635
                                              "ItemPropertyUpdated",
 
636
                                              g_variant_new("(isv)", dbusmenu_menuitem_get_id(mi), property, variant),
 
637
                                              NULL);
 
638
        }
364
639
        return;
365
640
}
366
641
 
411
686
static void 
412
687
menuitem_shown (DbusmenuMenuitem * mi, guint timestamp, DbusmenuServer * server)
413
688
{
 
689
        DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
 
690
 
414
691
        g_signal_emit(G_OBJECT(server), signals[ITEM_ACTIVATION], 0, dbusmenu_menuitem_get_id(mi), timestamp, TRUE);
 
692
 
 
693
        if (priv->dbusobject != NULL && priv->bus != NULL) {
 
694
                g_dbus_connection_emit_signal(priv->bus,
 
695
                                              NULL,
 
696
                                              priv->dbusobject,
 
697
                                              DBUSMENU_INTERFACE,
 
698
                                              "ItemActivationRequested",
 
699
                                              g_variant_new("(iu)", dbusmenu_menuitem_get_id(mi), timestamp),
 
700
                                              NULL);
 
701
        }
 
702
 
415
703
        return;
416
704
}
417
705
 
451
739
}
452
740
 
453
741
/* DBus interface */
454
 
static gboolean
455
 
_dbusmenu_server_get_layout (DbusmenuServer * server, gint parent, guint * revision, gchar ** layout, GError ** error)
 
742
static void
 
743
bus_get_layout (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
456
744
{
457
745
        DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
458
746
 
459
 
        *revision = priv->layout_revision;
 
747
        gint parent = 0;
 
748
        g_variant_get(params, "(i)", &parent);
 
749
 
 
750
        guint revision = priv->layout_revision;
460
751
        GPtrArray * xmlarray = g_ptr_array_new();
461
752
 
462
753
        if (parent == 0) {
467
758
                        dbusmenu_menuitem_buildxml(priv->root, xmlarray);
468
759
                }
469
760
        } else {
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);
 
764
                }
 
765
 
471
766
                if (item == NULL) {
472
 
                        if (error != NULL) {
473
 
                                g_set_error(error,
474
 
                                            error_quark(),
475
 
                                            INVALID_MENUITEM_ID,
476
 
                                            "The ID supplied %d does not refer to a menu item we have",
477
 
                                            parent);
478
 
                        }
479
 
                        return FALSE;
 
767
                        g_dbus_method_invocation_return_error(invocation,
 
768
                                                                  error_quark(),
 
769
                                                                  INVALID_MENUITEM_ID,
 
770
                                                                  "The ID supplied %d does not refer to a menu item we have",
 
771
                                                                  parent);
 
772
                        return;
480
773
                }
481
774
                dbusmenu_menuitem_buildxml(item, xmlarray);
482
775
        }
483
776
        g_ptr_array_add(xmlarray, NULL);
484
777
 
485
778
        /* build string */
486
 
        *layout = g_strjoinv("", (gchar **)xmlarray->pdata);
 
779
        gchar * layout = g_strjoinv("", (gchar **)xmlarray->pdata);
487
780
 
488
781
        g_ptr_array_foreach(xmlarray, xmlarray_foreach_free, NULL);
489
782
        g_ptr_array_free(xmlarray, TRUE);
490
783
 
491
 
        return TRUE;
 
784
        g_dbus_method_invocation_return_value(invocation,
 
785
                                              g_variant_new("(us)",
 
786
                                                            revision,
 
787
                                                            layout));
 
788
 
 
789
        g_free(layout);
 
790
 
 
791
        return;
492
792
}
493
793
 
494
 
static gboolean 
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 */
 
795
static void
 
796
bus_get_property (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
496
797
{
497
798
        DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
 
799
 
 
800
        if (priv->root == NULL) {
 
801
                g_dbus_method_invocation_return_error(invocation,
 
802
                                    error_quark(),
 
803
                                    NO_VALID_LAYOUT,
 
804
                                    "There currently isn't a layout in this server");
 
805
                return;
 
806
        }
 
807
        
 
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);
 
810
 
498
811
        DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
499
812
 
500
813
        if (mi == NULL) {
501
 
                if (error != NULL) {
502
 
                        g_set_error(error,
 
814
                g_dbus_method_invocation_return_error(invocation,
503
815
                                    error_quark(),
504
816
                                    INVALID_MENUITEM_ID,
505
817
                                    "The ID supplied %d does not refer to a menu item we have",
506
818
                                    id);
507
 
                }
508
 
                return FALSE;
 
819
                return;
509
820
        }
510
821
 
511
 
        const gchar * prop = dbusmenu_menuitem_property_get(mi, property);
512
 
        if (prop == NULL) {
513
 
                if (error != NULL) {
514
 
                        g_set_error(error,
 
822
        GVariant * variant = dbusmenu_menuitem_property_get_variant(mi, property);
 
823
        if (variant == NULL) {
 
824
                g_dbus_method_invocation_return_error(invocation,
515
825
                                    error_quark(),
516
826
                                    INVALID_PROPERTY_NAME,
517
827
                                    "The property '%s' does not exist on menuitem with ID of %d",
518
828
                                    property,
519
829
                                    id);
520
 
                }
521
 
                return FALSE;
522
 
        }
523
 
 
524
 
        if (value == NULL) {
525
 
                if (error != NULL) {
526
 
                        g_set_error(error,
527
 
                                    error_quark(),
528
 
                                    UNKNOWN_DBUS_ERROR,
529
 
                                    "Uhm, yeah.  We didn't get anywhere to put the value, that's really weird.  Seems impossible really.");
530
 
                }
531
 
                return FALSE;
532
 
        }
533
 
 
534
 
        *value = g_strdup(prop);
535
 
 
536
 
        return TRUE;
 
830
                return;
 
831
        }
 
832
 
 
833
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(v)", variant));
 
834
        return;
537
835
}
538
836
 
539
 
static gboolean
540
 
_dbusmenu_server_get_properties (DbusmenuServer * server, gint id, gchar ** properties, GHashTable ** dict, GError ** error)
 
837
/* Get some properties off of a single menuitem */
 
838
static void
 
839
bus_get_properties (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
541
840
{
542
841
        DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
 
842
        
 
843
        if (priv->root == NULL) {
 
844
                g_dbus_method_invocation_return_error(invocation,
 
845
                                    error_quark(),
 
846
                                    NO_VALID_LAYOUT,
 
847
                                    "There currently isn't a layout in this server");
 
848
                return;
 
849
        }
 
850
 
 
851
        gint id = g_variant_get_int32(g_variant_get_child_value(params, 0));
 
852
 
543
853
        DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
544
854
 
545
855
        if (mi == NULL) {
546
 
                if (error != NULL) {
547
 
                        g_set_error(error,
 
856
                g_dbus_method_invocation_return_error(invocation,
548
857
                                    error_quark(),
549
858
                                    INVALID_MENUITEM_ID,
550
859
                                    "The ID supplied %d does not refer to a menu item we have",
551
860
                                    id);
552
 
                }
553
 
                return FALSE;
 
861
                return;
554
862
        }
555
863
 
556
 
        *dict = dbusmenu_menuitem_properties_copy(mi);
557
 
 
558
 
        return TRUE;
 
864
        GVariant * dict = dbusmenu_menuitem_properties_variant(mi);
 
865
 
 
866
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(a{sv})", dict));
 
867
 
 
868
        return;
559
869
}
560
870
 
561
871
/* Handles getting a bunch of properties from a variety of menu items
562
872
   to make one mega dbus message */
563
 
static gboolean
564
 
_dbusmenu_server_get_group_properties (DbusmenuServer * server, GArray * ids, gchar ** properties, GPtrArray ** values, GError ** error)
565
 
{
566
 
        /* Build an initial pointer array */
567
 
        *values = g_ptr_array_new();
568
 
 
569
 
        /* Go through each ID to get that ID's properties */
570
 
        int idcnt;
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);
575
 
 
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);
579
 
                        g_error_free(error);
580
 
                        error = NULL;
581
 
                        continue;
582
 
                }
583
 
 
584
 
                GValueArray * valarray = g_value_array_new(2);
585
 
 
586
 
                _gvalue_array_append_int(valarray, id);
587
 
                _gvalue_array_append_hashtable(valarray, idprops);
588
 
 
589
 
                g_ptr_array_add(*values, valarray);
590
 
        }
591
 
 
592
 
        return TRUE;
593
 
}
594
 
 
595
 
/* Allocate a value on the stack for the int and append
596
 
   it to the array. */
597
 
static void
598
 
_gvalue_array_append_int(GValueArray *array, gint i)
599
 
{
600
 
        GValue value = {0};
601
 
 
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);
606
 
}
607
 
 
608
 
/* Allocate a value on the stack for the hashtable and append
609
 
   it to the array. */
610
 
static void
611
 
_gvalue_array_append_hashtable(GValueArray *array, GHashTable * dict)
612
 
{
613
 
        GValue value = {0};
614
 
 
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);
619
 
}
620
 
 
 
873
static void
 
874
bus_get_group_properties (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
 
875
{
 
876
        DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
 
877
 
 
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);
 
883
                        return;
 
884
                }
 
885
 
 
886
                g_dbus_method_invocation_return_error(invocation,
 
887
                                    error_quark(),
 
888
                                    NO_VALID_LAYOUT,
 
889
                                    "There currently isn't a layout in this server");
 
890
                return;
 
891
        }
 
892
 
 
893
        GVariantIter ids;
 
894
        g_variant_iter_init(&ids, g_variant_get_child_value(params, 0));
 
895
 
 
896
        GVariantBuilder builder;
 
897
        g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
 
898
 
 
899
        gint id;
 
900
        while (g_variant_iter_next(&ids, "i", &id)) {
 
901
                DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
 
902
                if (mi == NULL) continue;
 
903
 
 
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);
 
908
 
 
909
                if (props == NULL) {
 
910
                        props = g_variant_parse(g_variant_type_new("a{sv}"), "{}", NULL, NULL, NULL);
 
911
                }
 
912
 
 
913
                g_variant_builder_add_value(&wbuilder, props);
 
914
                GVariant * mi_data = g_variant_builder_end(&wbuilder);
 
915
 
 
916
                g_variant_builder_add_value(&builder, mi_data);
 
917
        }
 
918
 
 
919
        GVariant * ret = g_variant_builder_end(&builder);
 
920
 
 
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);
 
924
 
 
925
        g_dbus_method_invocation_return_value(invocation, final);
 
926
 
 
927
        return;
 
928
}
 
929
 
 
930
/* Turn a menuitem into an variant and attach it to the
 
931
   VariantBuilder we passed in */
621
932
static void
622
933
serialize_menuitem(gpointer data, gpointer user_data)
623
934
{
624
935
        DbusmenuMenuitem * mi = DBUSMENU_MENUITEM(data);
625
 
        GPtrArray * output = (GPtrArray *)(user_data);
 
936
        GVariantBuilder * builder = (GVariantBuilder *)(user_data);
626
937
 
627
938
        gint id = dbusmenu_menuitem_get_id(mi);
628
 
        GHashTable * dict = dbusmenu_menuitem_properties_copy(mi);
629
 
 
630
 
        GValueArray * item = g_value_array_new(2);
631
 
        _gvalue_array_append_int(item, id);
632
 
        _gvalue_array_append_hashtable(item, dict);
633
 
 
634
 
        g_ptr_array_add(output, item);
635
 
 
636
 
        g_hash_table_unref(dict);
 
939
        GVariant * props = dbusmenu_menuitem_properties_variant(mi);
 
940
 
 
941
        g_variant_builder_add(builder, "ia{sv}", id, props);
637
942
 
638
943
        return;
639
944
}
640
945
 
641
 
static gboolean
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 */
 
948
static void
 
949
bus_get_children (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
643
950
{
644
951
        DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
 
952
        gint id = g_variant_get_int32(g_variant_get_child_value(params, 0));
 
953
 
 
954
        if (priv->root == NULL) {
 
955
                g_dbus_method_invocation_return_error(invocation,
 
956
                                    error_quark(),
 
957
                                    NO_VALID_LAYOUT,
 
958
                                    "There currently isn't a layout in this server");
 
959
                return;
 
960
        }
 
961
 
645
962
        DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
646
963
 
647
964
        if (mi == NULL) {
648
 
                if (error != NULL) {
649
 
                        g_set_error(error,
650
 
                                    error_quark(),
651
 
                                    INVALID_MENUITEM_ID,
652
 
                                    "The ID supplied %d does not refer to a menu item we have",
653
 
                                    id);
654
 
                }
655
 
                return FALSE;
 
965
                g_dbus_method_invocation_return_error(invocation,
 
966
                                                          error_quark(),
 
967
                                                          INVALID_MENUITEM_ID,
 
968
                                                          "The ID supplied %d does not refer to a menu item we have",
 
969
                                                          id);
 
970
                return;
656
971
        }
657
972
 
658
 
        *output = g_ptr_array_new();
659
973
        GList * children = dbusmenu_menuitem_get_children(mi);
660
 
        g_list_foreach(children, serialize_menuitem, *output);
661
 
 
662
 
        return TRUE;
 
974
        GVariant * ret = NULL;
 
975
 
 
976
        if (children != NULL) {
 
977
                GVariantBuilder builder;
 
978
                g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); 
 
979
 
 
980
                g_list_foreach(children, serialize_menuitem, &builder);
 
981
 
 
982
                ret = g_variant_new("(a(ia{svg}))", g_variant_builder_end(&builder));
 
983
        } else {
 
984
                ret = g_variant_parse(g_variant_type_new("(a(ia{sv}))"), "([(0, {})],)", NULL, NULL, NULL);
 
985
        }
 
986
 
 
987
        g_dbus_method_invocation_return_value(invocation, ret);
 
988
        return;
663
989
}
664
990
 
665
991
/* Structure for holding the event data for the idle function
668
994
struct _idle_event_t {
669
995
        DbusmenuMenuitem * mi;
670
996
        gchar * eventid;
671
 
        GValue data;
 
997
        GVariant * variant;
672
998
        guint timestamp;
673
999
};
674
1000
 
679
1005
{
680
1006
        idle_event_t * data = (idle_event_t *)user_data;
681
1007
 
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);
683
1009
 
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);
687
1013
        g_free(data);
688
1014
        return FALSE;
689
1015
}
690
1016
 
691
 
/* Handles the even coming off of DBus */
692
 
static gboolean
693
 
_dbusmenu_server_event (DbusmenuServer * server, gint id, gchar * eventid, GValue * data, guint timestamp, GError ** error)
 
1017
/* Handles the events coming off of DBus */
 
1018
static void
 
1019
bus_event (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
694
1020
{
695
1021
        DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
 
1022
 
 
1023
        if (priv->root == NULL) {
 
1024
                g_dbus_method_invocation_return_error(invocation,
 
1025
                                    error_quark(),
 
1026
                                    NO_VALID_LAYOUT,
 
1027
                                    "There currently isn't a layout in this server");
 
1028
                return;
 
1029
        }
 
1030
 
 
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);
697
1033
 
698
1034
        if (mi == NULL) {
699
 
                if (error != NULL) {
700
 
                        g_set_error(error,
701
 
                                    error_quark(),
702
 
                                    INVALID_MENUITEM_ID,
703
 
                                    "The ID supplied %d does not refer to a menu item we have",
704
 
                                    id);
705
 
                }
706
 
                return FALSE;
 
1035
                g_dbus_method_invocation_return_error(invocation,
 
1036
                                                          error_quark(),
 
1037
                                                          INVALID_MENUITEM_ID,
 
1038
                                                          "The ID supplied %d does not refer to a menu item we have",
 
1039
                                                          id);
 
1040
                return;
707
1041
        }
708
1042
 
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);
716
1049
 
717
1050
        g_timeout_add(0, event_local_handler, event_data);
718
 
        return TRUE;
 
1051
 
 
1052
        g_dbus_method_invocation_return_value(invocation, NULL);
 
1053
        return;
719
1054
}
720
1055
 
721
1056
/* Recieve the About To Show function.  Pass it to our menu item. */
722
 
static gboolean
723
 
_dbusmenu_server_about_to_show (DbusmenuServer * server, gint id, gboolean * need_update, GError ** error)
 
1057
static void
 
1058
bus_about_to_show (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
724
1059
{
725
1060
        DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
 
1061
 
 
1062
        if (priv->root == NULL) {
 
1063
                g_dbus_method_invocation_return_error(invocation,
 
1064
                                    error_quark(),
 
1065
                                    NO_VALID_LAYOUT,
 
1066
                                    "There currently isn't a layout in this server");
 
1067
                return;
 
1068
        }
 
1069
 
 
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);
727
1072
 
728
1073
        if (mi == NULL) {
729
 
                if (error != NULL) {
730
 
                        g_set_error(error,
731
 
                                    error_quark(),
732
 
                                    INVALID_MENUITEM_ID,
733
 
                                    "The ID supplied %d does not refer to a menu item we have",
734
 
                                    id);
735
 
                }
736
 
                return FALSE;
 
1074
                g_dbus_method_invocation_return_error(invocation,
 
1075
                                                          error_quark(),
 
1076
                                                          INVALID_MENUITEM_ID,
 
1077
                                                          "The ID supplied %d does not refer to a menu item we have",
 
1078
                                                          id);
 
1079
                return;
737
1080
        }
738
1081
 
739
1082
        /* GTK+ does not support about-to-show concept for now */
740
 
        *need_update = FALSE;
741
 
        return TRUE;
 
1083
        g_dbus_method_invocation_return_value(invocation,
 
1084
                                              g_variant_new("(b)", FALSE));
 
1085
        return;
742
1086
}
743
1087
 
744
1088
/* Public Interface */