26
26
#include "applet-item.h"
27
27
#include "applet-draw.h"
28
28
#include "applet-host.h"
29
#include "applet-host-kde.h"
30
#include "applet-host-ias.h"
30
32
// our address basename
31
33
#define CD_STATUS_NOTIFIER_HOST_ADDR "org.kde.StatusNotifierHost"
33
// KDE watcher (old names)
34
//~ #define CD_STATUS_NOTIFIER_WATCHER_ADDR2 "org.kde.NotificationItemWatcher"
35
//~ #define CD_STATUS_NOTIFIER_WATCHER_OBJ2 "/NotificationItemWatcher"
36
//~ #define CD_STATUS_NOTIFIER_WATCHER_IFACE2 "org.kde.NotificationItemWatcher"
39
#define CD_STATUS_NOTIFIER_WATCHER_ADDR "org.kde.StatusNotifierWatcher"
40
#define CD_STATUS_NOTIFIER_WATCHER_OBJ "/StatusNotifierWatcher"
41
#define CD_STATUS_NOTIFIER_WATCHER_IFACE "org.kde.StatusNotifierWatcher"
43
// Ubuntu sort-of-high-level-Watcher (new or old address)
44
#if (INDICATOR_OLD_NAMES == 0) // Natty
45
#define CD_INDICATOR_APPLICATION_ADDR "com.canonical.indicator.application"
46
#define CD_INDICATOR_APPLICATION_OBJ "/com/canonical/indicator/application/service"
47
#define CD_INDICATOR_APPLICATION_IFACE "com.canonical.indicator.application.service"
49
#define CD_INDICATOR_APPLICATION_ADDR "org.ayatana.indicator.application"
50
#define CD_INDICATOR_APPLICATION_OBJ "/org/ayatana/indicator/application/service"
51
#define CD_INDICATOR_APPLICATION_IFACE "org.ayatana.indicator.application.service"
54
// Ubuntu Indicator Service
55
#define CD_INDICATOR_SERVICE_INTERFACE "org.ayatana.indicator.service"
56
#define CD_INDICATOR_SERVICE_OBJECT "/org/ayatana/indicator/service"
58
static DBusGProxyCall *s_pDetectWatcherCall = NULL;
59
static DBusGProxyCall *s_pDetectIASCall = NULL;
61
#if (INDICATOR_OLD_NAMES != 0) // Maverick
62
static void _cd_cclosure_marshal_VOID__STRING_INT_STRING_STRING_STRING_STRING_STRING (GClosure *closure,
63
GValue *return_value G_GNUC_UNUSED,
65
const GValue *param_values,
66
gpointer invocation_hint G_GNUC_UNUSED,
67
gpointer marshal_data)
69
//cd_debug ("=== %s ()\n", __func__);
70
typedef void (*GMarshalFunc_VOID__STRING_INT_STRING_STRING_STRING_STRING_STRING) (
80
register GMarshalFunc_VOID__STRING_INT_STRING_STRING_STRING_STRING_STRING callback;
81
register GCClosure *cc = (GCClosure*) closure;
82
register gpointer data1, data2;
83
g_return_if_fail (n_param_values == 8); // return_value est NULL ici, car la callback ne renvoit rien.
85
if (G_CCLOSURE_SWAP_DATA (closure))
87
data1 = closure->data;
88
data2 = g_value_peek_pointer (param_values + 0);
92
data1 = g_value_peek_pointer (param_values + 0);
93
data2 = closure->data;
95
callback = (GMarshalFunc_VOID__STRING_INT_STRING_STRING_STRING_STRING_STRING) (marshal_data ? marshal_data : cc->callback);
98
(char*) g_value_get_string (param_values + 1),
99
g_value_get_int (param_values + 2),
100
(char*) g_value_get_string (param_values + 3),
101
(char*) g_value_get_string (param_values + 4),
102
(char*) g_value_get_string (param_values + 5),
103
(char*) g_value_get_string (param_values + 6),
104
(char*) g_value_get_string (param_values + 7),
108
static void _cd_cclosure_marshal_VOID__STRING_INT_STRING_STRING_STRING_STRING_STRING_STRING (GClosure *closure,
109
GValue *return_value G_GNUC_UNUSED,
110
guint n_param_values,
111
const GValue *param_values,
112
gpointer invocation_hint G_GNUC_UNUSED,
113
gpointer marshal_data)
115
//cd_debug ("=== %s ()\n", __func__);
116
typedef void (*GMarshalFunc_VOID__STRING_INT_STRING_STRING_STRING_STRING_STRING_STRING) (
127
register GMarshalFunc_VOID__STRING_INT_STRING_STRING_STRING_STRING_STRING_STRING callback;
128
register GCClosure *cc = (GCClosure*) closure;
129
register gpointer data1, data2;
130
g_return_if_fail (n_param_values == 9); // return_value est NULL ici, car la callback ne renvoit rien.
132
if (G_CCLOSURE_SWAP_DATA (closure))
134
data1 = closure->data;
135
data2 = g_value_peek_pointer (param_values + 0);
139
data1 = g_value_peek_pointer (param_values + 0);
140
data2 = closure->data;
142
callback = (GMarshalFunc_VOID__STRING_INT_STRING_STRING_STRING_STRING_STRING_STRING) (marshal_data ? marshal_data : cc->callback);
145
(char*) g_value_get_string (param_values + 1),
146
g_value_get_int (param_values + 2),
147
(char*) g_value_get_string (param_values + 3),
148
(char*) g_value_get_boxed (param_values + 4),
149
(char*) g_value_get_string (param_values + 5),
150
(char*) g_value_get_string (param_values + 6),
151
(char*) g_value_get_string (param_values + 7),
152
(char*) g_value_get_string (param_values + 8),
157
36
static CDStatusNotifierItem * _cd_satus_notifier_find_item_from_service (const gchar *cService)
236
115
cd_free_item (pItem);
239
static void on_new_item (DBusGProxy *proxy_watcher, const gchar *cService, CairoDockModuleInstance *myApplet)
242
cd_debug ("=== %s (%s)", __func__, cService);
244
_add_new_item (cService, NULL, -1); // on suppose que leur indicator-application ne mettra jamais -1 comme position.
249
static void on_removed_item (DBusGProxy *proxy_watcher, const gchar *cService, CairoDockModuleInstance *myApplet)
252
cd_debug ("=== %s (%s)", __func__, cService);
254
gchar *str = strchr (cService, '/');
258
_remove_item (cService, -1);
263
static void on_new_application (DBusGProxy *proxy_watcher, const gchar *cIconName, gint iPosition, const gchar *cAdress, const gchar *cObjectPath, const gchar *cIconThemePath, const gchar *cLabel, const gchar *cLabelGuide,
264
#if (INDICATOR_OLD_NAMES == 0) // Natty
265
const gchar *cAccessbleDesc, // WTF is this new param ??
267
CairoDockModuleInstance *myApplet)
270
cd_debug ("=== %s (%s, %s, %s, %s, %d)", __func__, cAdress, cObjectPath, cIconName, cIconThemePath, iPosition);
271
#if (INDICATOR_OLD_NAMES == 0) // Natty
272
cd_debug (" %s", cAccessbleDesc);
274
/// position +1 for items placed after this one...
275
CDStatusNotifierItem *pItem;
277
for (it = myData.pItems; it != NULL; it = it->next)
280
if (pItem->iPosition >= iPosition)
283
cd_debug ("=== %s -> %d -> %d", pItem->cId, pItem->iPosition-1, pItem->iPosition);
287
_add_new_item (cAdress, cObjectPath, iPosition);
292
static void on_removed_application (DBusGProxy *proxy_watcher, gint iPosition, CairoDockModuleInstance *myApplet)
295
cd_debug ("=== %s (%d)", __func__, iPosition);
297
_remove_item (NULL, iPosition);
299
/// position -1 for items placed after this one...
300
CDStatusNotifierItem *pItem;
302
for (it = myData.pItems; it != NULL; it = it->next)
305
if (pItem->iPosition >= iPosition)
308
cd_debug ("=== %s -> %d -> %d", pItem->cId, pItem->iPosition+1, pItem->iPosition);
320
static void _on_get_applications_from_service (DBusGProxy *proxy, DBusGProxyCall *call_id, CairoDockModuleInstance *myApplet)
322
cd_debug ("=== %s ()", __func__);
325
//\______________________ get the applications list from the service.
326
GPtrArray *pApplications = NULL;
327
GError *erreur = NULL;
328
GType g_type_ptrarray = g_type_ptrarray = dbus_g_type_get_collection ("GPtrArray",
329
dbus_g_type_get_struct("GValueArray",
330
G_TYPE_STRING, // iconname
331
G_TYPE_INT, // position
332
G_TYPE_STRING, // dbusaddress
333
DBUS_TYPE_G_OBJECT_PATH, // dbusobject
334
G_TYPE_STRING, // iconpath
335
G_TYPE_STRING, // label
336
G_TYPE_STRING, // labelguide
338
gboolean bSuccess = dbus_g_proxy_end_call (proxy,
341
g_type_ptrarray, &pApplications,
345
cd_debug ("=== couldn't get applications in the systray (%s)", erreur->message);
346
g_error_free (erreur);
349
if (pApplications == NULL)
352
//\______________________ build each items.
353
cd_debug ("=== got %d aplications", pApplications->len);
357
CDStatusNotifierItem *pItem=NULL;
358
//cd_debug ("=== %d apps in the systray", pApplications->len);
359
for (i = 0; i < pApplications->len; i ++)
361
cd_debug ("=== %d) %p", i, pApplications->pdata[i]);
362
va = pApplications->pdata[i];
366
const gchar *cIconName = NULL;
368
const gchar *cAdress = NULL;
369
const gchar *cObjectPath = NULL;
370
const gchar *cIconThemePath = NULL;
371
const gchar *cLabel = NULL;
372
const gchar *cLabelGuide = NULL;
374
v = g_value_array_get_nth (va, 0);
375
if (v && G_VALUE_HOLDS_STRING (v))
376
cIconName = g_value_get_string (v);
378
v = g_value_array_get_nth (va, 1);
379
if (v && G_VALUE_HOLDS_INT (v))
380
iPosition = g_value_get_int (v);
382
v = g_value_array_get_nth (va, 2);
383
if (v && G_VALUE_HOLDS_STRING (v))
384
cAdress = g_value_get_string (v);
386
v = g_value_array_get_nth (va, 3);
387
if (v && G_VALUE_HOLDS_BOXED (v))
388
cObjectPath = (gchar*)g_value_get_boxed (v);
390
v = g_value_array_get_nth (va, 4);
391
if (v && G_VALUE_HOLDS_STRING (v))
392
cIconThemePath = g_value_get_string (v);
394
v = g_value_array_get_nth (va, 5);
395
if (v && G_VALUE_HOLDS_STRING (v))
396
cLabel = g_value_get_string (v);
398
v = g_value_array_get_nth (va, 6);
399
if (v && G_VALUE_HOLDS_STRING (v))
400
cLabelGuide = g_value_get_string (v);
402
cd_debug ("=== + item {%s ; %d ; %s ; %s ; %s ; %s ; %s}",
411
pItem = cd_satus_notifier_create_item (cAdress, cObjectPath);
414
if (pItem->iPosition == -1)
415
pItem->iPosition = iPosition;
416
if (pItem->cTitle == NULL && pItem->cLabel == NULL)
417
pItem->cLabel = g_strdup (cLabel && *cLabel != '\0' ? cLabel : pItem->cId);
418
myData.pItems = g_list_prepend (myData.pItems, pItem);
421
if (myConfig.bCompactMode)
423
cd_satus_notifier_reload_compact_mode ();
427
cd_satus_notifier_load_icons_from_items ();
430
g_ptr_array_free (pApplications, TRUE);
434
static void _cd_satus_notifier_get_items_from_ias (void)
436
if (! myData.bIASWatched)
438
cd_debug ("=== %s ()", __func__);
440
g_return_if_fail (myData.pProxyIndicatorApplicationService == NULL);
442
myData.pProxyIndicatorApplicationService = cairo_dock_create_new_session_proxy (
443
CD_INDICATOR_APPLICATION_ADDR,
444
CD_INDICATOR_APPLICATION_OBJ,
445
CD_INDICATOR_APPLICATION_IFACE);
447
// get the current items
448
dbus_g_proxy_begin_call (myData.pProxyIndicatorApplicationService,
450
(DBusGProxyCallNotify)_on_get_applications_from_service,
452
(GDestroyNotify) NULL,
455
// connect to the signals to keep the list of items up-to-date.
456
#if (INDICATOR_OLD_NAMES != 0) // Maverick
457
dbus_g_object_register_marshaller(_cd_cclosure_marshal_VOID__STRING_INT_STRING_STRING_STRING_STRING_STRING,
458
G_TYPE_NONE, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
460
dbus_g_object_register_marshaller(_cd_cclosure_marshal_VOID__STRING_INT_STRING_STRING_STRING_STRING_STRING_STRING,
461
G_TYPE_NONE, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING,
462
#if (INDICATOR_OLD_NAMES != 0) // Maverick
463
G_TYPE_STRING, // dbusobject
465
DBUS_TYPE_G_OBJECT_PATH, // dbusobject
467
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
469
dbus_g_proxy_add_signal(myData.pProxyIndicatorApplicationService, "ApplicationAdded",
470
G_TYPE_STRING, // iconname
471
G_TYPE_INT, // position
472
G_TYPE_STRING, // dbusaddress
473
#if (INDICATOR_OLD_NAMES != 0) // Maverick
474
G_TYPE_STRING, // dbusobject
476
DBUS_TYPE_G_OBJECT_PATH, // dbusobject
478
G_TYPE_STRING, // iconpath
479
G_TYPE_STRING, // label
480
G_TYPE_STRING, // labelguide
481
#if (INDICATOR_OLD_NAMES == 0) // Natty
482
G_TYPE_STRING, // accessibledesc
485
dbus_g_proxy_connect_signal(myData.pProxyIndicatorApplicationService, "ApplicationAdded",
486
G_CALLBACK(on_new_application), myApplet, NULL);
488
dbus_g_proxy_add_signal(myData.pProxyIndicatorApplicationService, "ApplicationRemoved",
489
G_TYPE_INT, // position
491
dbus_g_proxy_connect_signal(myData.pProxyIndicatorApplicationService, "ApplicationRemoved",
492
G_CALLBACK(on_removed_application), myApplet, NULL);
495
static void _on_get_applications_from_watcher (DBusGProxy *proxy, DBusGProxyCall *call_id, CairoDockModuleInstance *myApplet)
497
cd_debug ("=== %s ()", __func__);
500
gchar **pApplications = NULL;
501
GError *erreur = NULL;
502
gboolean bSuccess = dbus_g_proxy_end_call (proxy,
505
G_TYPE_STRV, &pApplications,
509
cd_debug ("=== couldn't get applications from the watcher (%s)", erreur->message);
510
g_error_free (erreur);
517
if (pApplications == NULL)
520
CDStatusNotifierItem *pItem;
521
for (i = 0; pApplications[i] != NULL; i ++)
523
pItem = cd_satus_notifier_create_item (pApplications[i], NULL);
526
cd_debug ("=== => + %s", pItem->cTitle);
527
myData.pItems = g_list_prepend (myData.pItems, pItem);
530
g_strfreev (pApplications);
532
if (myConfig.bCompactMode)
534
cd_satus_notifier_reload_compact_mode ();
538
cd_satus_notifier_load_icons_from_items ();
541
else // un watcher asocial comme celui d'Ubuntu, on essaye avec l'"indicator-application".
543
cd_debug ("=== this watcher is not so friendly, let's try the 'application indicator'");
544
myData.bBrokenWatcher = TRUE;
545
if (myData.bIASWatched)
546
_cd_satus_notifier_get_items_from_ias ();
552
/////////////////////////////////
553
/// connection to the Watcher ///
554
/////////////////////////////////
556
static void _on_register_host (DBusGProxy *proxy, DBusGProxyCall *call_id, CairoDockModuleInstance *myApplet)
558
cd_debug ("=== %s ()", __func__);
560
GError *erreur = NULL;
561
gboolean bSuccess = dbus_g_proxy_end_call (proxy,
567
cd_debug ("couldn't register to the Notification Watcher (%s)", erreur->message);
568
g_error_free (erreur);
573
if (bSuccess) // we are friend now, let's ask him the current items.
575
cd_debug ("=== found a friendly watcher, now ask for the items...");
576
// get the current items
577
myData.pProxyWatcherProps = cairo_dock_create_new_session_proxy (
578
CD_STATUS_NOTIFIER_WATCHER_ADDR,
579
CD_STATUS_NOTIFIER_WATCHER_OBJ,
580
DBUS_INTERFACE_PROPERTIES);
581
dbus_g_proxy_begin_call (myData.pProxyWatcherProps,
583
(DBusGProxyCallNotify)_on_get_applications_from_watcher,
585
(GDestroyNotify) NULL,
586
G_TYPE_STRING, CD_STATUS_NOTIFIER_WATCHER_IFACE,
587
G_TYPE_STRING, "RegisteredStatusNotifierItems",
590
// connect to the signals to keep the list of items up-to-date.
591
dbus_g_proxy_add_signal(myData.pProxyWatcher, "ServiceRegistered",
592
G_TYPE_STRING, G_TYPE_INVALID); // StatusNotifierItemRegistered
593
dbus_g_proxy_connect_signal(myData.pProxyWatcher, "ServiceRegistered",
594
G_CALLBACK(on_new_item), myApplet, NULL);
596
dbus_g_proxy_add_signal(myData.pProxyWatcher, "ServiceUnregistered",
597
G_TYPE_STRING, G_TYPE_INVALID); // StatusNotifierItemUnregistered
598
dbus_g_proxy_connect_signal(myData.pProxyWatcher, "ServiceUnregistered",
599
G_CALLBACK(on_removed_item), myApplet, NULL);
601
else // an asocial watcher like the Ubuntu's one, let's try with the IAS if availeble.
603
cd_debug ("=== no friendy watcher, let's try the 'application indicator'");
604
myData.bBrokenWatcher = TRUE;
605
if (myData.bIASWatched)
606
_cd_satus_notifier_get_items_from_ias ();
610
static void _on_watcher_owner_changed (gboolean bOwned, gpointer data)
612
cd_debug ("=== Watcher is on the bus (%d)", bOwned);
616
// set up a proxy to the Watcher
617
myData.pProxyWatcher = cairo_dock_create_new_session_proxy (
618
CD_STATUS_NOTIFIER_WATCHER_ADDR,
619
CD_STATUS_NOTIFIER_WATCHER_OBJ,
620
CD_STATUS_NOTIFIER_WATCHER_IFACE); // whenever it appears on the bus, we'll get it.
622
// and register to it.
623
cd_debug ("=== register to the it");
624
dbus_g_proxy_begin_call (myData.pProxyWatcher, "RegisterNotificationHost",
625
(DBusGProxyCallNotify)_on_register_host,
627
(GDestroyNotify) NULL,
628
G_TYPE_STRING, myData.cHostName,
631
else // no more watcher on the bus.
633
g_object_unref (myData.pProxyWatcher);
634
myData.pProxyWatcher = NULL;
636
g_object_unref (myData.pProxyWatcherProps);
637
myData.pProxyWatcherProps = NULL;
639
g_list_foreach (myData.pItems, (GFunc) cd_free_item, NULL);
640
g_list_free (myData.pItems);
641
myData.pItems = NULL;
643
g_hash_table_remove_all (myData.pThemePaths);
645
// empty the list of items and redraw.
646
if (! myConfig.bCompactMode)
648
CD_APPLET_DELETE_MY_ICONS_LIST;
652
// draw an 'failed' image to not have an empty icon.
653
CD_APPLET_SET_IMAGE_ON_MY_ICON (MY_APPLET_SHARE_DATA_DIR"/icon-broken.svg");
655
myData.bBrokenWatcher = FALSE;
658
static void _on_detect_watcher (gboolean bPresent, gpointer data)
660
cd_debug ("=== Watcher is present: %d", bPresent);
661
s_pDetectWatcherCall = NULL;
662
// if present, set up proxy.
665
_on_watcher_owner_changed (TRUE, NULL);
667
else if (myConfig.bCompactMode) // in compact mode, draw an 'failed' image to not have an empty icon.
669
CD_APPLET_SET_IMAGE_ON_MY_ICON (MY_APPLET_SHARE_DATA_DIR"/icon-broken.svg");
672
// watch whenever the Watcher goes up or down.
673
cairo_dock_watch_dbus_name_owner (CD_STATUS_NOTIFIER_WATCHER_ADDR,
674
(CairoDockDbusNameOwnerChangedFunc) _on_watcher_owner_changed,
678
/////////////////////////////
679
/// connection to the IAS ///
680
/////////////////////////////
682
static void _on_start_service (DBusGProxy *proxy, guint status, GError *error, gpointer user_data)
684
// if service has not started, then we'll assume we don't need it (eg.: KDE)
685
if (error != NULL) // couldn't start the service.
687
cd_debug ("=== Unable to start the indicator service (%s), assuming we don't need it", error->message);
690
if (status != DBUS_START_REPLY_SUCCESS && status != DBUS_START_REPLY_ALREADY_RUNNING) // started but wrong status.
692
cd_debug ("=== Unable to start the indicator service (got status %d), assuming we don't need it", status);
695
cd_debug ("=== Indicator Service has started");
697
static void _on_watch_service (DBusGProxy *proxy, DBusGProxyCall *call, gpointer data)
699
GError *error = NULL;
700
guint service_api_version=0, this_service_version=0;
701
dbus_g_proxy_end_call (proxy, call, &error,
702
G_TYPE_UINT, &service_api_version,
703
G_TYPE_UINT, &this_service_version,
705
cd_debug ("=== got indicator service (API: %d, service: %d, broken watcher: %d)", service_api_version, this_service_version, myData.bBrokenWatcher);
707
if (service_api_version > 0) /// shouldn't the 2 versions be equal ?...
709
myData.bIASWatched = TRUE; // now we're friend with the IAS
711
if (myData.bBrokenWatcher) // if the watcher is not our friend, let's ask the IAS the current items.
713
_cd_satus_notifier_get_items_from_ias ();
717
static void _on_ias_owner_changed (gboolean bOwned, gpointer data)
719
cd_debug ("=== Indicator Applications Service is on the bus (%d)", bOwned);
723
// set up a proxy to the Service
724
myData.pProxyIndicatorService = cairo_dock_create_new_session_proxy (
725
CD_INDICATOR_APPLICATION_ADDR,
726
CD_INDICATOR_SERVICE_OBJECT,
727
CD_INDICATOR_SERVICE_INTERFACE);
730
cd_debug ("=== watch it");
731
dbus_g_proxy_begin_call (myData.pProxyIndicatorService,
733
(DBusGProxyCallNotify)_on_watch_service,
735
(GDestroyNotify) NULL,
738
else // no more IAS on the bus.
740
g_object_unref (myData.pProxyIndicatorService);
741
myData.pProxyIndicatorService = NULL;
743
g_object_unref (myData.pProxyIndicatorApplicationService);
744
myData.pProxyIndicatorApplicationService = NULL;
746
myData.bIASWatched = FALSE;
749
static void _on_detect_ias (gboolean bPresent, gpointer data)
751
cd_debug ("=== Indicator Applications Service is present: %d", bPresent);
752
s_pDetectIASCall = NULL;
753
// if present, set up proxy, else try to start the service.
756
_on_ias_owner_changed (TRUE, NULL);
758
else // not present, maybe the service is not started => try starting it.
760
cd_debug ("=== try to start the Indicator Service...");
761
DBusGProxy *dbus_proxy = cairo_dock_get_main_proxy ();
762
org_freedesktop_DBus_start_service_by_name_async (dbus_proxy,
763
CD_INDICATOR_APPLICATION_ADDR,
768
// watch whenever the Service goes up or down.
769
cairo_dock_watch_dbus_name_owner (CD_STATUS_NOTIFIER_WATCHER_ADDR,
770
(CairoDockDbusNameOwnerChangedFunc) _on_ias_owner_changed,
119
//////////////////////////
120
/// Start/stop service ///
121
//////////////////////////
774
123
/* watch Watcher + IAS ->
775
124
Watcher ON -> make proxy -> register -> ok => Get items -> ok => load items