2
A service which aggregates all the SyncClients' data together
3
for consumption by the Sync Indicator
5
Copyright 2012 Canonical Ltd.
8
Charles Kerr <charles.kerr@canonical.com>
10
This program is free software: you can redistribute it and/or modify it
11
under the terms of the GNU General Public License version 3,
12
as published by the Free Software Foundation.
14
This program is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranties of
16
MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
17
See the GNU General Public License for more details.
19
You should have received a copy of the GNU General Public License along
20
with this program. If not, see <http://www.gnu.org/licenses/>.
31
#include <glib/gi18n.h>
33
#include <libdbusmenu-glib/client.h>
34
#include <libdbusmenu-glib/menuitem.h>
35
#include <libdbusmenu-glib/server.h>
37
#include <libindicator/indicator-service.h>
39
#include "app-menu-item.h"
40
#include "dbus-shared.h"
41
#include "sync-client.h"
42
#include "sync-client-dbus.h"
43
#include "sync-enum.h"
44
#include "sync-service-dbus.h"
50
/* bookkeeping for each SyncClient */
51
typedef struct ClientEntry
55
AppMenuItem * app_menu_item;
57
DbusmenuClient * menu_client;
58
gulong menu_client_root_handler_id;
60
DbusSyncClient * sync_client;
61
gulong sync_client_menu_handler_id;
62
gulong sync_client_state_handler_id;
63
gulong sync_client_paused_handler_id;
67
/* the main service struct */
68
typedef struct SyncService
72
IndicatorService * indicator_service;
73
DbusmenuServer * menu_server;
74
DbusSyncService * skeleton;
75
guint signal_subscription;
77
GSList * client_entries;
81
static SyncService sync_service;
83
static void entry_clear_menu_client (ClientEntry * entry);
87
***** Various ways to look up a ClientEntry
90
typedef gint (entry_compare_func)(const ClientEntry * entry, gconstpointer key);
93
entry_compare_to_object_path (const ClientEntry * entry, gconstpointer path)
95
GDBusProxy * proxy = G_DBUS_PROXY (entry->sync_client);
97
return g_strcmp0 (g_dbus_proxy_get_object_path(proxy), path);
100
entry_compare_to_dbus_name (const ClientEntry * entry, gconstpointer name)
102
GDBusProxy * proxy = G_DBUS_PROXY (entry->sync_client);
104
return g_strcmp0 (g_dbus_proxy_get_name(proxy), name);
107
entry_compare_to_menu_client (const ClientEntry * entry, gconstpointer client)
109
const ptrdiff_t diff = (gconstpointer)(entry->menu_client) - client;
110
if (diff < 0) return -1;
111
if (diff > 0) return 1;
122
entry_compare_to_name_and_path (const ClientEntry * entry, gconstpointer key)
125
const struct name_and_path * nap = key;
128
if (( i = entry_compare_to_dbus_name (entry, nap->name)))
132
return entry_compare_to_object_path (entry, nap->path);
136
entry_find (SyncService * service, entry_compare_func func, gconstpointer key)
139
ClientEntry * match = NULL;
141
for (l=service->client_entries; l!=NULL; l=l->next)
143
if (func (l->data, key) == 0)
153
entry_find_from_dbus_name (SyncService * service, const gchar * name)
155
return entry_find (service, entry_compare_to_dbus_name, name);
158
entry_find_from_menu_client (SyncService * service, DbusmenuClient * client)
160
return entry_find (service, entry_compare_to_menu_client, client);
163
entry_find_from_name_and_path (SyncService * service,
167
struct name_and_path nap;
170
return entry_find (service, entry_compare_to_name_and_path, &nap);
174
entry_compare_by_appname (gconstpointer ga, gconstpointer gb)
176
const ClientEntry * const a = ga;
177
const ClientEntry * const b = gb;
178
return g_strcmp0 (app_menu_item_get_name(a->app_menu_item),
179
app_menu_item_get_name(b->app_menu_item));
183
entry_get_state (ClientEntry * entry)
185
g_return_val_if_fail (entry->sync_client != NULL, SYNC_STATE_IDLE);
187
return dbus_sync_client_get_state (entry->sync_client);
191
entry_get_paused (ClientEntry * entry)
193
g_return_val_if_fail (entry->sync_client != NULL, FALSE);
195
return dbus_sync_client_get_paused (entry->sync_client);
204
menuitem_set_visible (DbusmenuMenuitem * mi, gboolean v)
206
dbusmenu_menuitem_property_set_bool (mi, DBUSMENU_MENUITEM_PROP_VISIBLE, v);
210
menuitem_unparent (DbusmenuMenuitem * mi)
212
DbusmenuMenuitem * parent = dbusmenu_menuitem_get_parent (mi);
215
dbusmenu_menuitem_child_delete (parent, mi);
220
service_menu_append_client_menu (DbusmenuMenuitem * root,
221
DbusmenuClient * menu_client)
223
g_return_if_fail (menu_client != NULL);
225
DbusmenuMenuitem * mi = dbusmenu_client_get_root (menu_client);
229
GList * children = dbusmenu_menuitem_get_children (mi);
230
if (children == NULL)
232
mi = DBUSMENU_MENUITEM(dbusmenu_menuitem_proxy_new (mi));
233
dbusmenu_menuitem_child_append (root, mi);
235
else for (l=children; l!=NULL; l=l->next)
237
mi = DBUSMENU_MENUITEM(l->data);
238
mi = DBUSMENU_MENUITEM(dbusmenu_menuitem_proxy_new (mi));
239
dbusmenu_menuitem_child_append (root, mi);
245
service_refresh_menu (SyncService * service)
249
g_debug (G_STRLOC" rebuilding the menu");
251
/* get an alphabetically sorted list of SyncClients */
252
entries = g_slist_copy (service->client_entries);
253
entries = g_slist_sort (entries, entry_compare_by_appname);
255
/* build the new menu */
256
DbusmenuMenuitem * root = dbusmenu_menuitem_new ();
257
for (l=entries; l!=NULL; l=l->next)
259
ClientEntry * entry = l->data;
261
/* add the client's app menuitem */
262
DbusmenuMenuitem * mi = DBUSMENU_MENUITEM (entry->app_menu_item);
263
menuitem_unparent (mi);
264
menuitem_set_visible (mi, TRUE);
265
dbusmenu_menuitem_child_append (root, mi);
267
/* add the client's custom menuitems */
268
service_menu_append_client_menu (root, entry->menu_client);
270
/* add a separator before the next client */
273
DbusmenuMenuitem * sep = dbusmenu_menuitem_new ();
274
menuitem_set_visible (mi, TRUE);
275
dbusmenu_menuitem_property_set (sep,
276
DBUSMENU_MENUITEM_PROP_TYPE,
277
DBUSMENU_CLIENT_TYPES_SEPARATOR);
278
dbusmenu_menuitem_child_append (root, sep);
282
dbusmenu_server_set_root (service->menu_server, root);
285
g_slist_free (entries);
286
g_object_unref (root);
290
service_calculate_state (SyncService * service)
294
/* if any service is in error state... */
295
for (l=service->client_entries; l!=NULL; l=l->next)
296
if (entry_get_state (l->data) == SYNC_STATE_ERROR)
297
return SYNC_STATE_ERROR;
299
/* otherwise if any service is syncing... */
300
for (l=service->client_entries; l!=NULL; l=l->next)
301
if (entry_get_state (l->data) == SYNC_STATE_SYNCING)
302
return SYNC_STATE_SYNCING;
304
return SYNC_STATE_IDLE;
308
service_refresh_state (SyncService * service)
310
const SyncState new_state = service_calculate_state (service);
312
dbus_sync_service_set_state (service->skeleton, new_state);
316
service_calculate_paused (SyncService * service)
320
for (l=service->client_entries; l!=NULL; l=l->next)
321
if (entry_get_paused (l->data))
328
service_refresh_paused (SyncService * service)
330
const gboolean new_paused = service_calculate_paused (service);
332
dbus_sync_service_set_paused (service->skeleton, new_paused);
336
service_refresh_count (SyncService * service)
338
const guint new_count = g_slist_length (service->client_entries);
340
dbus_sync_service_set_client_count (service->skeleton, new_count);
344
service_refresh (SyncService * service)
346
service_refresh_menu (service);
347
service_refresh_state (service);
348
service_refresh_paused (service);
349
service_refresh_count (service);
357
signal_handler_clear (gpointer instance, gulong * handler_id)
359
g_return_if_fail (handler_id != NULL);
361
if ((instance != NULL) && *handler_id)
363
g_signal_handler_disconnect (instance, *handler_id);
370
entry_free (ClientEntry * entry)
372
g_debug (G_STRLOC " freeing entry %p", entry);
374
if (entry->watch_id != 0)
376
g_bus_unwatch_name (entry->watch_id);
380
signal_handler_clear (entry->sync_client, &entry->sync_client_menu_handler_id);
381
signal_handler_clear (entry->sync_client, &entry->sync_client_state_handler_id);
382
signal_handler_clear (entry->sync_client, &entry->sync_client_paused_handler_id);
383
g_clear_object (&entry->sync_client);
385
entry_clear_menu_client (entry);
391
service_remove_entry (SyncService * service, ClientEntry * entry)
393
g_return_if_fail (service != NULL);
394
g_return_if_fail (entry != NULL);
396
service->client_entries = g_slist_remove (service->client_entries, entry);
397
service_refresh (service);
403
service_add_entry (SyncService * service, ClientEntry * entry)
405
service->client_entries = g_slist_prepend (service->client_entries, entry);
406
service_refresh (service);
410
on_client_menu_root_changed (DbusmenuClient * client,
411
DbusmenuMenuitem * newroot G_GNUC_UNUSED,
414
SyncService * service = user_data;
415
ClientEntry * entry = entry_find_from_menu_client (service, client);
416
g_return_if_fail (entry != NULL);
418
g_debug (G_STRLOC " SyncClient %s changed its menu root",
419
app_menu_item_get_name(entry->app_menu_item));
420
service_refresh_menu (service);
424
entry_create_menu_client (SyncService * service,
427
g_return_if_fail (entry != NULL);
428
g_return_if_fail (entry->sync_client != NULL);
430
const gchar * name = g_dbus_proxy_get_name (G_DBUS_PROXY(entry->sync_client));
431
const gchar * path = dbus_sync_client_get_menu_path (entry->sync_client);
432
entry->menu_client = dbusmenu_client_new (name, path);
434
entry->menu_client_root_handler_id = g_signal_connect (
436
DBUSMENU_CLIENT_SIGNAL_ROOT_CHANGED,
437
G_CALLBACK(on_client_menu_root_changed),
442
entry_clear_menu_client (ClientEntry * entry)
444
signal_handler_clear (entry->menu_client, &entry->menu_client_root_handler_id);
445
g_clear_object (&entry->menu_client);
449
on_sync_client_state_changed (GObject * o, GParamSpec * ps, gpointer service)
451
service_refresh_state (service);
455
on_sync_client_paused_changed (GObject * o, GParamSpec * ps, gpointer service)
457
service_refresh_paused (service);
461
on_sync_client_menu_path_changed (GObject * o, GParamSpec * ps, gpointer gentry)
463
SyncService * service = &sync_service;
464
DbusSyncClient * sync_client = DBUS_SYNC_CLIENT(o);
465
ClientEntry * entry = gentry;
466
g_return_if_fail (sync_client != NULL);
468
entry_clear_menu_client (entry);
469
entry_create_menu_client (service, entry);
470
service_refresh_menu (service);
474
on_sync_client_vanished (GDBusConnection * connection G_GNUC_UNUSED,
475
const gchar * dbus_name,
479
SyncService * self = user_data;
481
while ((entry = entry_find_from_dbus_name (self, dbus_name)))
483
service_remove_entry (self, entry);
488
entry_new (SyncService * service, DbusSyncClient * sync_client)
490
GDBusProxy * proxy = G_DBUS_PROXY(sync_client);
491
ClientEntry * entry = g_new0 (ClientEntry, 1);
493
entry->sync_client = sync_client;
495
entry->app_menu_item = app_menu_item_new (proxy);
497
entry->sync_client_menu_handler_id = g_signal_connect (
498
proxy, "notify::menu-path",
499
G_CALLBACK(on_sync_client_menu_path_changed), entry);
501
entry->sync_client_state_handler_id = g_signal_connect (
502
proxy, "notify::state",
503
G_CALLBACK(on_sync_client_state_changed), service);
505
entry->sync_client_paused_handler_id = g_signal_connect (
506
proxy, "notify::paused",
507
G_CALLBACK(on_sync_client_paused_changed), service);
509
entry_create_menu_client (service, entry);
511
entry->watch_id = g_bus_watch_name_on_connection (
512
g_dbus_proxy_get_connection (proxy),
513
g_dbus_proxy_get_name (proxy),
514
G_BUS_NAME_WATCHER_FLAGS_NONE,
515
NULL, on_sync_client_vanished,
518
g_debug (G_STRLOC" created a new proxy for '%s', watch id is %d",
519
g_dbus_proxy_get_name(proxy), entry->watch_id);
525
on_sync_client_exists (GDBusConnection * connection,
526
const gchar * sender,
527
const gchar * object,
528
const gchar * interface,
529
const gchar * signal G_GNUC_UNUSED,
530
GVariant * params G_GNUC_UNUSED,
533
g_debug (G_STRLOC" got an Exists signal from"
537
sender, object, interface);
539
SyncService * service = user_data;
541
if (entry_find_from_name_and_path (service, sender, object) != NULL)
543
g_debug (G_STRLOC" ...which we're already tracking");
548
g_debug (G_STRLOC" ...which is new to us! Let's add it to our list.");
549
DbusSyncClient * proxy = dbus_sync_client_proxy_new_sync (
551
G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
558
service_add_entry (service, entry_new (service, proxy));
562
g_warning ("couldn't create a proxy for '%s': %s", object, err->message);
563
g_clear_error (&err);
572
on_got_bus (GObject * o, GAsyncResult * res, gpointer user_data)
575
SyncService * service = user_data;
576
GDBusConnection * connection = g_bus_get_finish (res, &err);
580
g_error ("unable to get bus: %s", err->message);
581
g_clear_error (&err);
585
g_dbus_interface_skeleton_export (
586
G_DBUS_INTERFACE_SKELETON(service->skeleton),
588
SYNC_SERVICE_DBUS_OBJECT,
593
g_error ("unable to get bus: %s", err->message);
594
g_clear_error (&err);
597
/* listen for SyncClients to show up on the bus */
598
g_debug (G_STRLOC" listening to Exists from %s", SYNC_CLIENT_DBUS_IFACE);
599
service->signal_subscription = g_dbus_connection_signal_subscribe (
602
SYNC_CLIENT_DBUS_IFACE,
606
G_DBUS_SIGNAL_FLAGS_NONE,
607
on_sync_client_exists,
609
NULL); /* destroy notify */
612
g_object_unref (connection);
617
service_shutdown (IndicatorService * service G_GNUC_UNUSED, gpointer user_data)
619
SyncService * sync_service = user_data;
620
g_debug ("sync-service shutting down by request");
621
g_main_loop_quit (sync_service->mainloop);
625
main (int argc, char ** argv)
627
memset (&sync_service, 0, sizeof(sync_service));
629
setlocale (LC_ALL, "");
630
bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
631
textdomain (GETTEXT_PACKAGE);
635
sync_service.indicator_service =
636
indicator_service_new_version (SYNC_SERVICE_DBUS_NAME, 1);
638
g_signal_connect (sync_service.indicator_service,
639
INDICATOR_SERVICE_SIGNAL_SHUTDOWN,
640
G_CALLBACK(service_shutdown),
643
sync_service.skeleton = dbus_sync_service_skeleton_new ();
645
g_bus_get (G_BUS_TYPE_SESSION, NULL, on_got_bus, &sync_service);
647
sync_service.menu_server = dbusmenu_server_new(SYNC_SERVICE_DBUS_MENU_OBJECT);
650
sync_service.mainloop = g_main_loop_new (NULL, FALSE);
651
g_main_loop_run (sync_service.mainloop);
654
g_clear_pointer (&sync_service.mainloop, g_main_loop_unref);
655
g_slist_free_full (sync_service.client_entries, (GDestroyNotify)entry_free);
656
g_clear_object (&sync_service.menu_server);
657
g_clear_object (&sync_service.skeleton);
658
g_clear_object (&sync_service.indicator_service);