1 /*
2 * statusnotifier - Copyright (C) 2014 Olivier Brunel
3 *
4 * statusnotifier.c
5 * Copyright (C) 2014 Olivier Brunel <jjk@jjacky.com>
6 *
7 * This file is part of statusnotifier.
8 *
9 * statusnotifier is free software: you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the Free
11 * Software Foundation, either version 3 of the License, or (at your option) any
12 * later version.
13 *
14 * statusnotifier is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE.
17 * See the GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * statusnotifier. If not, see http://www.gnu.org/licenses/
21 */
22
23 //#include "config.h"
24
25 #include <unistd.h>
26 #include <gdk/gdk.h>
27 #include "statusnotifier.h"
28 #include "enums.h"
29 #include "interfaces.h"
30 #include "closures.h"
31
32 /**
33 * SECTION:statusnotifier
34 * @Short_description: A StatusNotifierItem as per KDE's specifications
35 *
36 * Starting with Plasma Next, KDE doesn't support the XEmbed systray in favor of
37 * their own Status Notifier Specification.
38 *
39 * A #StatusNotifier is a #GObject that can be used to represent a
40 * StatusNotifierItem, handling all the DBus implementation and leaving you
41 * simply dealing with regular properties and signals.
42 *
43 * You can simply create a new #StatusNotifier using one of the helper function,
44 * e.g. status_notifier_new_from_icon_name(), or simply creating an object as
45 * usual - you then just need to make sure to specify #StatusNotifier:id :
46 * <programlisting>
47 * sn = (StatusNotifier *) g_object_new (TYPE_STATUS_NOTIFIER,
48 * "id", "app-id",
49 * "status", STATUS_NOTIFIER_STATUS_NEEDS_ATTENTION,
50 * "main-icon-name", "app-icon",
51 * "attention-icon-pixbuf", pixbuf,
52 * "tooltip-title", "My tooltip",
53 * "tooltip-body", "This is an item about <b>app</b>",
54 * NULL);
55 * </programlisting>
56 *
57 * You can also set properties (other than id) after creation. Once ready, call
58 * status_notifier_register() to register the item on the session bus and to the
59 * StatusNotifierWatcher.
60 *
61 * If an error occurs, signal #StatusNotifier::registration-failed will be
62 * emitted. On success, #StatusNotifier:state will be
63 * %STATUS_NOTIFIER_STATE_REGISTERED. See status_notifier_register() for more.
64 *
65 * Once registered, you can change properties as needed, and the proper DBus
66 * signal will be emitted to let visualizations (hosts) know, and connect to the
67 * signals (such as #StatusNotifier::context-menu) which will be emitted when
68 * the corresponding DBus method was called.
69 *
70 * For reference, the KDE specifications can be found at
71 * http://www.notmart.org/misc/statusnotifieritem/index.html
72 */
73
74 enum
75 {
76 PROP_0,
77
78 PROP_ID,
79 PROP_TITLE,
80 PROP_CATEGORY,
81 PROP_STATUS,
82 PROP_MAIN_ICON_NAME,
83 PROP_MAIN_ICON_PIXBUF,
84 PROP_OVERLAY_ICON_NAME,
85 PROP_OVERLAY_ICON_PIXBUF,
86 PROP_ATTENTION_ICON_NAME,
87 PROP_ATTENTION_ICON_PIXBUF,
88 PROP_ATTENTION_MOVIE_NAME,
89 PROP_TOOLTIP_ICON_NAME,
90 PROP_TOOLTIP_ICON_PIXBUF,
91 PROP_TOOLTIP_TITLE,
92 PROP_TOOLTIP_BODY,
93 PROP_WINDOW_ID,
94
95 PROP_STATE,
96
97 NB_PROPS
98 };
99
100 static guint prop_name_from_icon[_NB_STATUS_NOTIFIER_ICONS] = {
101 PROP_MAIN_ICON_NAME,
102 PROP_ATTENTION_ICON_NAME,
103 PROP_OVERLAY_ICON_NAME,
104 PROP_TOOLTIP_ICON_NAME
105 };
106 static guint prop_pixbuf_from_icon[_NB_STATUS_NOTIFIER_ICONS] = {
107 PROP_MAIN_ICON_PIXBUF,
108 PROP_ATTENTION_ICON_PIXBUF,
109 PROP_OVERLAY_ICON_PIXBUF,
110 PROP_TOOLTIP_ICON_PIXBUF
111 };
112
113 enum
114 {
115 SIGNAL_REGISTRATION_FAILED,
116 SIGNAL_CONTEXT_MENU,
117 SIGNAL_ACTIVATE,
118 SIGNAL_SECONDARY_ACTIVATE,
119 SIGNAL_SCROLL,
120 NB_SIGNALS
121 };
122
123 struct _StatusNotifierPrivate
124 {
125 gchar *id;
126 StatusNotifierCategory category;
127 gchar *title;
128 StatusNotifierStatus status;
129 struct {
130 gboolean has_pixbuf;
131 union {
132 gchar *icon_name;
133 GdkPixbuf *pixbuf;
134 };
135 } icon[_NB_STATUS_NOTIFIER_ICONS];
136 gchar *attention_movie_name;
137 gchar *tooltip_title;
138 gchar *tooltip_body;
139 guint32 window_id;
140
141 guint tooltip_freeze;
142
143 StatusNotifierState state;
144 guint dbus_watch_id;
145 guint dbus_sid;
146 guint dbus_owner_id;
147 guint dbus_reg_id;
148 GDBusProxy *dbus_proxy;
149 GDBusConnection *dbus_conn;
150 GError *dbus_err;
151 };
152
153 static guint uniq_id = 0;
154
155 static GParamSpec *status_notifier_props[NB_PROPS] = { NULL, };
156 static guint status_notifier_signals[NB_SIGNALS] = { 0, };
157
158 #define notify(sn,prop) \
159 g_object_notify_by_pspec ((GObject *) sn, status_notifier_props[prop])
160
161 static void status_notifier_set_property (GObject *object,
162 guint prop_id,
163 const GValue *value,
164 GParamSpec *pspec);
165 static void status_notifier_get_property (GObject *object,
166 guint prop_id,
167 GValue *value,
168 GParamSpec *pspec);
169 static void status_notifier_finalize (GObject *object);
170
G_DEFINE_TYPE(StatusNotifier,status_notifier,G_TYPE_OBJECT)171 G_DEFINE_TYPE (StatusNotifier, status_notifier, G_TYPE_OBJECT)
172
173 static void
174 status_notifier_class_init (StatusNotifierClass *klass)
175 {
176 GObjectClass *o_class;
177
178 o_class = G_OBJECT_CLASS (klass);
179 o_class->set_property = status_notifier_set_property;
180 o_class->get_property = status_notifier_get_property;
181 o_class->finalize = status_notifier_finalize;
182
183 /**
184 * StatusNotifier:id:
185 *
186 * It's a name that should be unique for this application and consistent
187 * between sessions, such as the application name itself.
188 */
189 status_notifier_props[PROP_ID] =
190 g_param_spec_string ("id", "id", "Unique application identifier",
191 NULL,
192 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
193
194 /**
195 * StatusNotifier:title:
196 *
197 * It's a name that describes the application, it can be more descriptive
198 * than #StatusNotifier:id.
199 */
200 status_notifier_props[PROP_TITLE] =
201 g_param_spec_string ("title", "title", "Descriptive name for the item",
202 NULL,
203 G_PARAM_READWRITE);
204
205 /**
206 * StatusNotifier:category:
207 *
208 * Describes the category of this item.
209 */
210 status_notifier_props[PROP_CATEGORY] =
211 g_param_spec_enum ("category", "category", "Category of the item",
212 TYPE_STATUS_NOTIFIER_CATEGORY,
213 STATUS_NOTIFIER_CATEGORY_APPLICATION_STATUS,
214 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
215
216 /**
217 * StatusNotifier:status:
218 *
219 * Describes the status of this item or of the associated application.
220 */
221 status_notifier_props[PROP_STATUS] =
222 g_param_spec_enum ("status", "status", "Status of the item",
223 TYPE_STATUS_NOTIFIER_STATUS,
224 STATUS_NOTIFIER_STATUS_PASSIVE,
225 G_PARAM_READWRITE);
226
227 /**
228 * StatusNotifier:main-icon-name:
229 *
230 * The item can carry an icon that can be used by the visualization to
231 * identify the item.
232 *
233 * An icon can either be identified by its Freedesktop-compliant icon name,
234 * set by this property, or by the icon data itself, set by the property
235 * #StatusNotifier:main-icon-pixbuf.
236 *
237 * It is currently not possible to set both, as setting one will unset the
238 * other.
239 */
240 status_notifier_props[PROP_MAIN_ICON_NAME] =
241 g_param_spec_string ("main-icon-name", "main-icon-name",
242 "Icon name for the main icon",
243 NULL,
244 G_PARAM_READWRITE);
245
246 /**
247 * StatusNotifier:main-icon-pixbuf:
248 *
249 * The item can carry an icon that can be used by the visualization to
250 * identify the item.
251 *
252 * An icon can either be identified by its Freedesktop-compliant icon name,
253 * set by property #StatusNotifier:main-icon-name, or by the icon data
254 * itself, set by this property.
255 *
256 * It is currently not possible to set both, as setting one will unset the
257 * other.
258 */
259 status_notifier_props[PROP_MAIN_ICON_PIXBUF] =
260 g_param_spec_object ("main-icon-pixbuf", "main-icon-pixbuf",
261 "Pixbuf for the main icon",
262 GDK_TYPE_PIXBUF,
263 G_PARAM_READWRITE);
264
265 /**
266 * StatusNotifier:overlay-icon-name:
267 *
268 * This can be used by the visualization to indicate extra state
269 * information, for instance as an overlay for the main icon.
270 *
271 * An icon can either be identified by its Freedesktop-compliant icon name,
272 * set by this property, or by the icon data itself, set by property
273 * #StatusNotifier:overlay-icon-pixbuf.
274 *
275 * It is currently not possible to set both, as setting one will unset the
276 * other.
277 */
278 status_notifier_props[PROP_OVERLAY_ICON_NAME] =
279 g_param_spec_string ("overlay-icon-name", "overlay-icon-name",
280 "Icon name for the overlay icon",
281 NULL,
282 G_PARAM_READWRITE);
283
284 /**
285 * StatusNotifier:overlay-icon-pixbuf:
286 *
287 * This can be used by the visualization to indicate extra state
288 * information, for instance as an overlay for the main icon.
289 *
290 * An icon can either be identified by its Freedesktop-compliant icon name,
291 * set by property #StatusNotifier:overlay-icon-name, or by the icon data
292 * itself, set by this property.
293 *
294 * It is currently not possible to set both, as setting one will unset the
295 * other.
296 */
297 status_notifier_props[PROP_OVERLAY_ICON_PIXBUF] =
298 g_param_spec_object ("overlay-icon-pixbuf", "overlay-icon-pixbuf",
299 "Pixbuf for the overlay icon",
300 GDK_TYPE_PIXBUF,
301 G_PARAM_READWRITE);
302
303 /**
304 * StatusNotifier:attention-icon-name:
305 *
306 * This can be used by the visualization to indicate that the item is in
307 * %STATUS_NOTIFIER_STATUS_NEEDS_ATTENTION status.
308 *
309 * An icon can either be identified by its Freedesktop-compliant icon name,
310 * set by this property, or by the icon data itself, set by property
311 * #StatusNotifier:attention-icon-pixbuf.
312 *
313 * It is currently not possible to set both, as setting one will unset the
314 * other.
315 */
316 status_notifier_props[PROP_ATTENTION_ICON_NAME] =
317 g_param_spec_string ("attention-icon-name", "attention-icon-name",
318 "Icon name for the attention icon",
319 NULL,
320 G_PARAM_READWRITE);
321
322 /**
323 * StatusNotifier:attention-icon-pixbuf:
324 *
325 * This can be used by the visualization to indicate that the item is in
326 * %STATUS_NOTIFIER_STATUS_NEEDS_ATTENTION status.
327 *
328 * An icon can either be identified by its Freedesktop-compliant icon name,
329 * set by property #StatusNotifier:attention-icon-name, or by the icon data
330 * itself, set by this property.
331 *
332 * It is currently not possible to set both, as setting one will unset the
333 * other.
334 */
335 status_notifier_props[PROP_ATTENTION_ICON_PIXBUF] =
336 g_param_spec_object ("attention-icon-pixbuf", "attention-icon-pixbuf",
337 "Pixbuf for the attention icon",
338 GDK_TYPE_PIXBUF,
339 G_PARAM_READWRITE);
340
341 /**
342 * StatusNotifier:attention-movie-name:
343 *
344 * In addition to the icon, the item can also specify an animation
345 * associated to the #STATUS_NOTIFIER_STATUS_NEEDS_ATTENTION status.
346 *
347 * This should be either a Freedesktop-compliant icon name or a full path.
348 * The visualization can chose between the movie or icon (or using neither
349 * of those) at its discretion.
350 */
351 status_notifier_props[PROP_ATTENTION_MOVIE_NAME] =
352 g_param_spec_string ("attention-movie-name", "attention-movie-name",
353 "Animation name/full path when the item is in needs-attention status",
354 NULL,
355 G_PARAM_READWRITE);
356
357 /**
358 * StatusNotifier:tooltip-icon-name:
359 *
360 * A tooltip can be defined on the item; It can be used by the visualization
361 * to show as a tooltip (or by any other mean it considers appropriate).
362 *
363 * The tooltip is composed of a title, a body, and an icon. Note that
364 * changing any of these will trigger a DBus signal NewToolTip (for hosts to
365 * refresh DBus property ToolTip), see status_notifier_freeze_tooltip() for
366 * changing more than one and only emitting one DBus signal at the end.
367 *
368 * The icon can either be identified by its Freedesktop-compliant icon name,
369 * set by this property, or by the icon data itself, set by property
370 * #StatusNotifier:tooltip-icon-pixbuf.
371 *
372 * It is currently not possible to set both, as setting one will unset the
373 * other.
374 */
375 status_notifier_props[PROP_TOOLTIP_ICON_NAME] =
376 g_param_spec_string ("tooltip-icon-name", "tooltip-icon-name",
377 "Icon name for the tooltip icon",
378 NULL,
379 G_PARAM_READWRITE);
380
381 /**
382 * StatusNotifier:tooltip-icon-pixbuf:
383 *
384 * A tooltip can be defined on the item; It can be used by the visualization
385 * to show as a tooltip (or by any other mean it considers appropriate).
386 *
387 * The tooltip is composed of a title, a body, and an icon. Note that
388 * changing any of these will trigger a DBus signal NewToolTip (for hosts to
389 * refresh DBus property ToolTip), see status_notifier_freeze_tooltip() for
390 * changing more than one and only emitting one DBus signal at the end.
391 *
392 * The icon can either be identified by its Freedesktop-compliant icon name,
393 * set by property #StatusNotifier:tooltip-icon-name, or by the icon data
394 * itself, set by this property.
395 *
396 * It is currently not possible to set both, as setting one will unset the
397 * other.
398 */
399 status_notifier_props[PROP_TOOLTIP_ICON_PIXBUF] =
400 g_param_spec_object ("tooltip-icon-pixbuf", "tooltip-icon-pixbuf",
401 "Pixbuf for the tooltip icon",
402 GDK_TYPE_PIXBUF,
403 G_PARAM_READWRITE);
404
405 /**
406 * StatusNotifier:tooltip-title:
407 *
408 * A tooltip can be defined on the item; It can be used by the visualization
409 * to show as a tooltip (or by any other mean it considers appropriate).
410 *
411 * The tooltip is composed of a title, a body, and an icon. Note that
412 * changing any of these will trigger a DBus signal NewToolTip (for hosts to
413 * refresh DBus property ToolTip), see status_notifier_freeze_tooltip() for
414 * changing more than one and only emitting one DBus signal at the end.
415 */
416 status_notifier_props[PROP_TOOLTIP_TITLE] =
417 g_param_spec_string ("tooltip-title", "tooltip-title",
418 "Title of the tooltip",
419 NULL,
420 G_PARAM_READWRITE);
421
422 /**
423 * StatusNotifier:tooltip-body:
424 *
425 * A tooltip can be defined on the item; It can be used by the visualization
426 * to show as a tooltip (or by any other mean it considers appropriate).
427 *
428 * The tooltip is composed of a title, a body, and an icon. Note that
429 * changing any of these will trigger a DBus signal NewToolTip (for hosts to
430 * refresh DBus property ToolTip), see status_notifier_freeze_tooltip() for
431 * changing more than one and only emitting one DBus signal at the end.
432 *
433 * This body can contain some markup, which consists of a small subset of
434 * XHTML. See http://www.notmart.org/misc/statusnotifieritem/markup.html for
435 * more.
436 */
437 status_notifier_props[PROP_TOOLTIP_BODY] =
438 g_param_spec_string ("tooltip-body", "tooltip-body",
439 "Body of the tooltip",
440 NULL,
441 G_PARAM_READWRITE);
442
443 /**
444 * StatusNotifier:window-id:
445 *
446 * It's the windowing-system dependent identifier for a window, the
447 * application can chose one of its windows to be available trough this
448 * property or just set 0 if it's not interested.
449 */
450 status_notifier_props[PROP_WINDOW_ID] =
451 g_param_spec_uint ("window-id", "window-id", "Window ID",
452 0, G_MAXUINT32,
453 0,
454 G_PARAM_READWRITE);
455
456 /**
457 * StatusNotifier:state:
458 *
459 * The state of the item, regarding its DBus registration on the
460 * StatusNotifierWatcher. After you've created the item, you need to call
461 * status_notifier_register() to have it registered via DBus on the watcher.
462 *
463 * See status_notifier_register() for more.
464 */
465 status_notifier_props[PROP_STATE] =
466 g_param_spec_enum ("state", "state",
467 "DBus registration state of the item",
468 TYPE_STATUS_NOTIFIER_STATE,
469 STATUS_NOTIFIER_STATE_NOT_REGISTERED,
470 G_PARAM_READABLE);
471
472 g_object_class_install_properties (o_class, NB_PROPS, status_notifier_props);
473
474 /**
475 * StatusNotifier::registration-failed:
476 * @sn: The #StatusNotifier
477 * @error: A #GError with the reason of failure
478 *
479 * This signal is emited after a call to status_notifier_register() when
480 * registering the item eventually failed (e.g. if there wasn't (yet) any
481 * StatusNotifierHost registered.)
482 *
483 * When this happens, you should fallback to using the systray. You should
484 * also check #StatusNotifier:state as it might still be
485 * %STATUS_NOTIFIER_STATE_REGISTERING if the registration remains eventually
486 * possible (e.g. waiting for a StatusNotifierHost to register)
487 *
488 * See status_notifier_register() for more.
489 */
490 status_notifier_signals[SIGNAL_REGISTRATION_FAILED] = g_signal_new (
491 "registration-failed",
492 TYPE_STATUS_NOTIFIER,
493 G_SIGNAL_RUN_LAST,
494 G_STRUCT_OFFSET (StatusNotifierClass, registration_failed),
495 NULL,
496 NULL,
497 g_cclosure_marshal_VOID__BOXED,
498 G_TYPE_NONE,
499 1,
500 G_TYPE_ERROR);
501
502 /**
503 * StatusNotifier::context-menu:
504 * @sn: The #StatusNotifier
505 * @x: screen coordinates X
506 * @y: screen coordinates Y
507 *
508 * Emitted when the ContextMenu method was called on the item. Item should
509 * then show a context menu, this is typically a consequence of user input,
510 * such as mouse right click over the graphical representation of the item.
511 *
512 * @x and @y are to be considered an hint to the item about where to show
513 * the context menu.
514 */
515 status_notifier_signals[SIGNAL_CONTEXT_MENU] = g_signal_new (
516 "context-menu",
517 TYPE_STATUS_NOTIFIER,
518 G_SIGNAL_RUN_LAST,
519 G_STRUCT_OFFSET (StatusNotifierClass, context_menu),
520 g_signal_accumulator_true_handled,
521 NULL,
522 g_cclosure_user_marshal_BOOLEAN__INT_INT,
523 G_TYPE_BOOLEAN,
524 2,
525 G_TYPE_INT,
526 G_TYPE_INT);
527
528 /**
529 * StatusNotifier::activate:
530 * @sn: The #StatusNotifier
531 * @x: screen coordinates X
532 * @y: screen coordinates Y
533 *
534 * Emitted when the Activate method was called on the item. Activation of
535 * the item was requested, this is typically a consequence of user input,
536 * such as mouse left click over the graphical representation of the item.
537 *
538 * @x and @y are to be considered an hint to the item about where to show
539 * the context menu.
540 */
541 status_notifier_signals[SIGNAL_ACTIVATE] = g_signal_new (
542 "activate",
543 TYPE_STATUS_NOTIFIER,
544 G_SIGNAL_RUN_LAST,
545 G_STRUCT_OFFSET (StatusNotifierClass, activate),
546 g_signal_accumulator_true_handled,
547 NULL,
548 g_cclosure_user_marshal_BOOLEAN__INT_INT,
549 G_TYPE_BOOLEAN,
550 2,
551 G_TYPE_INT,
552 G_TYPE_INT);
553
554 /**
555 * StatusNotifier::secondary-activate:
556 * @sn: The #StatusNotifier
557 * @x: screen coordinates X
558 * @y: screen coordinates Y
559 *
560 * Emitted when the SecondaryActivate method was called on the item.
561 * Secondary and less important form of activation (compared to
562 * #StatusNotifier::activate) of the item was requested. This is typically a
563 * consequence of user input, such as mouse middle click over the graphical
564 * representation of the item.
565 *
566 * @x and @y are to be considered an hint to the item about where to show
567 * the context menu.
568 */
569 status_notifier_signals[SIGNAL_SECONDARY_ACTIVATE] = g_signal_new (
570 "secondary-activate",
571 TYPE_STATUS_NOTIFIER,
572 G_SIGNAL_RUN_LAST,
573 G_STRUCT_OFFSET (StatusNotifierClass, secondary_activate),
574 g_signal_accumulator_true_handled,
575 NULL,
576 g_cclosure_user_marshal_BOOLEAN__INT_INT,
577 G_TYPE_BOOLEAN,
578 2,
579 G_TYPE_INT,
580 G_TYPE_INT);
581
582 /**
583 * StatusNotifier::scroll:
584 * @sn: The #StatusNotifier
585 * @delta: the amount of scroll
586 * @orientation: orientation of the scroll request
587 *
588 * Emitted when the Scroll method was called on the item. The user asked for
589 * a scroll action. This is caused from input such as mouse wheel over the
590 * graphical representation of the item.
591 */
592 status_notifier_signals[SIGNAL_SCROLL] = g_signal_new (
593 "scroll",
594 TYPE_STATUS_NOTIFIER,
595 G_SIGNAL_RUN_LAST,
596 G_STRUCT_OFFSET (StatusNotifierClass, scroll),
597 g_signal_accumulator_true_handled,
598 NULL,
599 g_cclosure_user_marshal_BOOLEAN__INT_INT,
600 G_TYPE_BOOLEAN,
601 2,
602 G_TYPE_INT,
603 TYPE_STATUS_NOTIFIER_SCROLL_ORIENTATION);
604
605 g_type_class_add_private (klass, sizeof (StatusNotifierPrivate));
606 }
607
608 static void
status_notifier_init(StatusNotifier * sn)609 status_notifier_init (StatusNotifier *sn)
610 {
611 sn->priv = G_TYPE_INSTANCE_GET_PRIVATE (sn,
612 TYPE_STATUS_NOTIFIER, StatusNotifierPrivate);
613 }
614
615 static void
status_notifier_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)616 status_notifier_set_property (GObject *object,
617 guint prop_id,
618 const GValue *value,
619 GParamSpec *pspec)
620 {
621 StatusNotifier *sn = (StatusNotifier *) object;
622 StatusNotifierPrivate *priv = sn->priv;
623
624 switch (prop_id)
625 {
626 case PROP_ID: /* G_PARAM_CONSTRUCT_ONLY */
627 priv->id = g_value_dup_string (value);
628 break;
629 case PROP_TITLE:
630 status_notifier_set_title (sn, g_value_get_string (value));
631 break;
632 case PROP_CATEGORY: /* G_PARAM_CONSTRUCT_ONLY */
633 priv->category = g_value_get_enum (value);
634 break;
635 case PROP_STATUS:
636 status_notifier_set_status (sn, g_value_get_enum (value));
637 break;
638 case PROP_MAIN_ICON_NAME:
639 status_notifier_set_from_icon_name (sn, STATUS_NOTIFIER_ICON,
640 g_value_get_string (value));
641 break;
642 case PROP_MAIN_ICON_PIXBUF:
643 status_notifier_set_from_pixbuf (sn, STATUS_NOTIFIER_ICON,
644 g_value_get_object (value));
645 break;
646 case PROP_OVERLAY_ICON_NAME:
647 status_notifier_set_from_icon_name (sn, STATUS_NOTIFIER_OVERLAY_ICON,
648 g_value_get_string (value));
649 break;
650 case PROP_OVERLAY_ICON_PIXBUF:
651 status_notifier_set_from_pixbuf (sn, STATUS_NOTIFIER_OVERLAY_ICON,
652 g_value_get_object (value));
653 break;
654 case PROP_ATTENTION_ICON_NAME:
655 status_notifier_set_from_icon_name (sn, STATUS_NOTIFIER_ATTENTION_ICON,
656 g_value_get_string (value));
657 break;
658 case PROP_ATTENTION_ICON_PIXBUF:
659 status_notifier_set_from_pixbuf (sn, STATUS_NOTIFIER_ATTENTION_ICON,
660 g_value_get_object (value));
661 break;
662 case PROP_ATTENTION_MOVIE_NAME:
663 status_notifier_set_attention_movie_name (sn, g_value_get_string (value));
664 break;
665 case PROP_TOOLTIP_ICON_NAME:
666 status_notifier_set_from_icon_name (sn, STATUS_NOTIFIER_TOOLTIP_ICON,
667 g_value_get_string (value));
668 break;
669 case PROP_TOOLTIP_ICON_PIXBUF:
670 status_notifier_set_from_pixbuf (sn, STATUS_NOTIFIER_TOOLTIP_ICON,
671 g_value_get_object (value));
672 break;
673 case PROP_TOOLTIP_TITLE:
674 status_notifier_set_tooltip_title (sn, g_value_get_string (value));
675 break;
676 case PROP_TOOLTIP_BODY:
677 status_notifier_set_tooltip_body (sn, g_value_get_string (value));
678 break;
679 case PROP_WINDOW_ID:
680 status_notifier_set_window_id (sn, g_value_get_uint (value));
681 default:
682 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
683 break;
684 }
685 }
686
687 static void
status_notifier_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)688 status_notifier_get_property (GObject *object,
689 guint prop_id,
690 GValue *value,
691 GParamSpec *pspec)
692 {
693 StatusNotifier *sn = (StatusNotifier *) object;
694 StatusNotifierPrivate *priv = sn->priv;
695
696 switch (prop_id)
697 {
698 case PROP_ID:
699 g_value_set_string (value, priv->id);
700 break;
701 case PROP_TITLE:
702 g_value_set_string (value, priv->title);
703 break;
704 case PROP_CATEGORY:
705 g_value_set_enum (value, priv->category);
706 break;
707 case PROP_STATUS:
708 g_value_set_enum (value, priv->status);
709 break;
710 case PROP_MAIN_ICON_NAME:
711 g_value_take_string (value, status_notifier_get_icon_name (sn,
712 STATUS_NOTIFIER_ICON));
713 break;
714 case PROP_MAIN_ICON_PIXBUF:
715 g_value_take_object (value, status_notifier_get_pixbuf (sn,
716 STATUS_NOTIFIER_ICON));
717 break;
718 case PROP_OVERLAY_ICON_NAME:
719 g_value_take_string (value, status_notifier_get_icon_name (sn,
720 STATUS_NOTIFIER_OVERLAY_ICON));
721 break;
722 case PROP_OVERLAY_ICON_PIXBUF:
723 g_value_take_object (value, status_notifier_get_pixbuf (sn,
724 STATUS_NOTIFIER_OVERLAY_ICON));
725 break;
726 case PROP_ATTENTION_ICON_NAME:
727 g_value_take_string (value, status_notifier_get_icon_name (sn,
728 STATUS_NOTIFIER_ATTENTION_ICON));
729 break;
730 case PROP_ATTENTION_ICON_PIXBUF:
731 g_value_take_object (value, status_notifier_get_pixbuf (sn,
732 STATUS_NOTIFIER_ATTENTION_ICON));
733 break;
734 case PROP_ATTENTION_MOVIE_NAME:
735 g_value_set_string (value, priv->attention_movie_name);
736 break;
737 case PROP_TOOLTIP_ICON_NAME:
738 g_value_take_string (value, status_notifier_get_icon_name (sn,
739 STATUS_NOTIFIER_TOOLTIP_ICON));
740 break;
741 case PROP_TOOLTIP_ICON_PIXBUF:
742 g_value_take_object (value, status_notifier_get_pixbuf (sn,
743 STATUS_NOTIFIER_TOOLTIP_ICON));
744 break;
745 case PROP_TOOLTIP_TITLE:
746 g_value_set_string (value, priv->tooltip_title);
747 break;
748 case PROP_TOOLTIP_BODY:
749 g_value_set_string (value, priv->tooltip_body);
750 break;
751 case PROP_WINDOW_ID:
752 g_value_set_uint (value, priv->window_id);
753 case PROP_STATE:
754 g_value_set_enum (value, priv->state);
755 break;
756 default:
757 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
758 break;
759 }
760 }
761
762 static void
free_icon(StatusNotifier * sn,StatusNotifierIcon icon)763 free_icon (StatusNotifier *sn, StatusNotifierIcon icon)
764 {
765 StatusNotifierPrivate *priv = sn->priv;
766
767 if (priv->icon[icon].has_pixbuf)
768 g_object_unref (priv->icon[icon].pixbuf);
769 else
770 g_free (priv->icon[icon].icon_name);
771 priv->icon[icon].has_pixbuf = FALSE;
772 priv->icon[icon].icon_name = NULL;
773 }
774
775 static void
dbus_free(StatusNotifier * sn)776 dbus_free (StatusNotifier *sn)
777 {
778 StatusNotifierPrivate *priv = sn->priv;
779
780 if (priv->dbus_watch_id > 0)
781 {
782 g_bus_unwatch_name (priv->dbus_watch_id);
783 priv->dbus_watch_id = 0;
784 }
785 if (priv->dbus_sid > 0)
786 {
787 g_signal_handler_disconnect (priv->dbus_proxy, priv->dbus_sid);
788 priv->dbus_sid = 0;
789 }
790 if (G_LIKELY (priv->dbus_owner_id > 0))
791 {
792 g_bus_unown_name (priv->dbus_owner_id);
793 priv->dbus_owner_id = 0;
794 }
795 if (priv->dbus_proxy)
796 {
797 g_object_unref (priv->dbus_proxy);
798 priv->dbus_proxy = NULL;
799 }
800 if (priv->dbus_reg_id > 0)
801 {
802 g_dbus_connection_unregister_object (priv->dbus_conn, priv->dbus_reg_id);
803 priv->dbus_reg_id = 0;
804 }
805 if (priv->dbus_conn)
806 {
807 g_object_unref (priv->dbus_conn);
808 priv->dbus_conn = NULL;
809 }
810 }
811
812 static void
status_notifier_finalize(GObject * object)813 status_notifier_finalize (GObject *object)
814 {
815 StatusNotifier *sn = (StatusNotifier *) object;
816 StatusNotifierPrivate *priv = sn->priv;
817 guint i;
818
819 g_free (priv->id);
820 g_free (priv->title);
821 for (i = 0; i < _NB_STATUS_NOTIFIER_ICONS; ++i)
822 free_icon (sn, i);
823 g_free (priv->attention_movie_name);
824 g_free (priv->tooltip_title);
825 g_free (priv->tooltip_body);
826
827 dbus_free (sn);
828
829 G_OBJECT_CLASS (status_notifier_parent_class)->finalize (object);
830 }
831
832 static void
dbus_notify(StatusNotifier * sn,guint prop)833 dbus_notify (StatusNotifier *sn, guint prop)
834 {
835 StatusNotifierPrivate *priv = sn->priv;
836 const gchar *signal;
837
838 if (priv->state != STATUS_NOTIFIER_STATE_REGISTERED)
839 return;
840
841 switch (prop)
842 {
843 case PROP_STATUS:
844 {
845 const gchar *s_status[] = {
846 "Passive",
847 "Active",
848 "NeedsAttention"
849 };
850 signal = "NewStatus";
851 g_dbus_connection_emit_signal (priv->dbus_conn,
852 NULL,
853 ITEM_OBJECT,
854 ITEM_INTERFACE,
855 signal,
856 g_variant_new ("(s)", s_status[priv->status]),
857 NULL);
858 return;
859 }
860 case PROP_TITLE:
861 signal = "NewTitle";
862 break;
863 case PROP_MAIN_ICON_NAME:
864 case PROP_MAIN_ICON_PIXBUF:
865 signal = "NewIcon";
866 break;
867 case PROP_ATTENTION_ICON_NAME:
868 case PROP_ATTENTION_ICON_PIXBUF:
869 signal = "NewAttentionIcon";
870 break;
871 case PROP_OVERLAY_ICON_NAME:
872 case PROP_OVERLAY_ICON_PIXBUF:
873 signal = "NewOverlayIcon";
874 break;
875 case PROP_TOOLTIP_TITLE:
876 case PROP_TOOLTIP_BODY:
877 case PROP_TOOLTIP_ICON_NAME:
878 case PROP_TOOLTIP_ICON_PIXBUF:
879 signal = "NewToolTip";
880 break;
881 default:
882 g_return_if_reached ();
883 }
884
885 g_dbus_connection_emit_signal (priv->dbus_conn,
886 NULL,
887 ITEM_OBJECT,
888 ITEM_INTERFACE,
889 signal,
890 NULL,
891 NULL);
892 }
893
894 /**
895 * status_notifier_new_from_pixbuf:
896 * @id: The application id
897 * @category: The category for the item
898 * @pixbuf: The icon to use as main icon
899 *
900 * Creates a new item
901 *
902 * Returns: (transfer full): A new #StatusNotifier
903 */
904 StatusNotifier *
status_notifier_new_from_pixbuf(const gchar * id,StatusNotifierCategory category,GdkPixbuf * pixbuf)905 status_notifier_new_from_pixbuf (const gchar *id,
906 StatusNotifierCategory category,
907 GdkPixbuf *pixbuf)
908 {
909 return (StatusNotifier *) g_object_new (TYPE_STATUS_NOTIFIER,
910 "id", id,
911 "category", category,
912 "main-icon-pixbuf", pixbuf,
913 NULL);
914 }
915
916 /**
917 * status_notifier_new_from_icon_name:
918 * @id: The application id
919 * @category: The category for the item
920 * @icon_name: The name of the icon to use as main icon
921 *
922 * Creates a new item
923 *
924 * Returns: (transfer full): A new #StatusNotifier
925 */
926 StatusNotifier *
status_notifier_new_from_icon_name(const gchar * id,StatusNotifierCategory category,const gchar * icon_name)927 status_notifier_new_from_icon_name (const gchar *id,
928 StatusNotifierCategory category,
929 const gchar *icon_name)
930 {
931 return (StatusNotifier *) g_object_new (TYPE_STATUS_NOTIFIER,
932 "id", id,
933 "category", category,
934 "main-icon-name", icon_name,
935 NULL);
936 }
937
938 /**
939 * status_notifier_get_id:
940 * @sn: A #StatusNotifier
941 *
942 * Returns the application id of @sn
943 *
944 * Returns: The application id of @sn. The string is owned by @sn, you should
945 * not free it
946 */
947 const gchar *
status_notifier_get_id(StatusNotifier * sn)948 status_notifier_get_id (StatusNotifier *sn)
949 {
950 g_return_val_if_fail (IS_STATUS_NOTIFIER (sn), NULL);
951 return sn->priv->id;
952 }
953
954 /**
955 * status_notifier_get_category:
956 * @sn: A #StatusNotifier
957 *
958 * Returns the category of @sn
959 *
960 * Returns: The category of @sn
961 */
962 StatusNotifierCategory
status_notifier_get_category(StatusNotifier * sn)963 status_notifier_get_category (StatusNotifier *sn)
964 {
965 g_return_val_if_fail (IS_STATUS_NOTIFIER (sn), -1);
966 return sn->priv->category;
967 }
968
969 /**
970 * status_notifier_set_from_pixbuf:
971 * @sn: A #StatusNotifier
972 * @icon: Which icon to set
973 * @pixbuf: A #GdkPixbuf to use for @icon
974 *
975 * Sets the icon @icon to @pixbuf.
976 *
977 * An icon can either be identified by its Freedesktop-compliant icon name,
978 * or by the icon data itself (via #GdkPixbuf).
979 *
980 * It is currently not possible to set both, as setting one will unset the
981 * other.
982 */
983 void
status_notifier_set_from_pixbuf(StatusNotifier * sn,StatusNotifierIcon icon,GdkPixbuf * pixbuf)984 status_notifier_set_from_pixbuf (StatusNotifier *sn,
985 StatusNotifierIcon icon,
986 GdkPixbuf *pixbuf)
987 {
988 StatusNotifierPrivate *priv;
989
990 g_return_if_fail (IS_STATUS_NOTIFIER (sn));
991 priv = sn->priv;
992
993 free_icon (sn, icon);
994 priv->icon[icon].has_pixbuf = TRUE;
995 priv->icon[icon].pixbuf = g_object_ref (pixbuf);
996
997 notify (sn, prop_name_from_icon[icon]);
998 if (icon != STATUS_NOTIFIER_TOOLTIP_ICON || priv->tooltip_freeze == 0)
999 dbus_notify (sn, prop_name_from_icon[icon]);
1000 }
1001
1002 /**
1003 * status_notifier_set_from_icon_name:
1004 * @sn: A #StatusNotifier
1005 * @icon: Which icon to set
1006 * @icon_name: Name of an icon to use for @icon
1007 *
1008 * Sets the icon @icon to be @icon_name.
1009 *
1010 * An icon can either be identified by its Freedesktop-compliant icon name,
1011 * or by the icon data itself (via #GdkPixbuf).
1012 *
1013 * It is currently not possible to set both, as setting one will unset the
1014 * other.
1015 */
1016 void
status_notifier_set_from_icon_name(StatusNotifier * sn,StatusNotifierIcon icon,const gchar * icon_name)1017 status_notifier_set_from_icon_name (StatusNotifier *sn,
1018 StatusNotifierIcon icon,
1019 const gchar *icon_name)
1020 {
1021 StatusNotifierPrivate *priv;
1022
1023 g_return_if_fail (IS_STATUS_NOTIFIER (sn));
1024 priv = sn->priv;
1025
1026 free_icon (sn, icon);
1027 priv->icon[icon].icon_name = g_strdup (icon_name);
1028
1029 notify (sn, prop_pixbuf_from_icon[icon]);
1030 if (icon != STATUS_NOTIFIER_TOOLTIP_ICON || priv->tooltip_freeze == 0)
1031 dbus_notify (sn, prop_name_from_icon[icon]);
1032 }
1033
1034 /**
1035 * status_notifier_has_pixbuf:
1036 * @sn: A #StatusNotifier
1037 * @icon: Which icon
1038 *
1039 * Returns whether icon @icon currently has a #GdkPixbuf set or not. If so, the
1040 * icon data will be sent via DBus, else the icon name (if any) will be used.
1041 *
1042 * Returns: %TRUE is a #GdkPixbuf is set for @icon, else %FALSE
1043 */
1044 gboolean
status_notifier_has_pixbuf(StatusNotifier * sn,StatusNotifierIcon icon)1045 status_notifier_has_pixbuf (StatusNotifier *sn,
1046 StatusNotifierIcon icon)
1047 {
1048 g_return_val_if_fail (IS_STATUS_NOTIFIER (sn), FALSE);
1049 return sn->priv->icon[icon].has_pixbuf;
1050 }
1051
1052 /**
1053 * status_notifier_get_pixbuf:
1054 * @sn: A #StatusNotifier
1055 * @icon: The icon to get
1056 *
1057 * Returns the #GdkPixbuf set for @icon, if there's one. Not that it will return
1058 * %NULL if an icon name is set.
1059 *
1060 * Returns: (transfer full): The #GdkPixbuf set for @icon, or %NULL
1061 */
1062 GdkPixbuf *
status_notifier_get_pixbuf(StatusNotifier * sn,StatusNotifierIcon icon)1063 status_notifier_get_pixbuf (StatusNotifier *sn,
1064 StatusNotifierIcon icon)
1065 {
1066 StatusNotifierPrivate *priv;
1067
1068 g_return_val_if_fail (IS_STATUS_NOTIFIER (sn), NULL);
1069 priv = sn->priv;
1070
1071 if (!priv->icon[icon].has_pixbuf)
1072 return NULL;
1073
1074 return g_object_ref (priv->icon[icon].pixbuf);
1075 }
1076
1077 /**
1078 * status_notifier_get_icon_name:
1079 * @sn: A #StatusNotifier
1080 * @icon: The icon to get
1081 *
1082 * Returns the icon name set for @icon, if there's one. Not that it will return
1083 * %NULL if a #GdkPixbuf is set.
1084 *
1085 * Returns: (transfer full): A newly allocated string of the icon name set for
1086 * @icon, free using g_free()
1087 */
1088 gchar *
status_notifier_get_icon_name(StatusNotifier * sn,StatusNotifierIcon icon)1089 status_notifier_get_icon_name (StatusNotifier *sn,
1090 StatusNotifierIcon icon)
1091 {
1092 StatusNotifierPrivate *priv;
1093
1094 g_return_val_if_fail (IS_STATUS_NOTIFIER (sn), NULL);
1095 priv = sn->priv;
1096
1097 if (priv->icon[icon].has_pixbuf)
1098 return NULL;
1099
1100 return g_strdup (priv->icon[icon].icon_name);
1101 }
1102
1103 /**
1104 * status_notifier_set_attention_movie_name:
1105 * @sn: A #StatusNotifier
1106 * @movie_name: The name of the movie
1107 *
1108 * In addition to the icon, the item can also specify an animation associated to
1109 * the #STATUS_NOTIFIER_STATUS_NEEDS_ATTENTION status.
1110 *
1111 * This should be either a Freedesktop-compliant icon name or a full path. The
1112 * visualization can chose between the movie or icon (or using neither of those)
1113 * at its discretion.
1114 */
1115 void
status_notifier_set_attention_movie_name(StatusNotifier * sn,const gchar * movie_name)1116 status_notifier_set_attention_movie_name (StatusNotifier *sn,
1117 const gchar *movie_name)
1118 {
1119 StatusNotifierPrivate *priv;
1120
1121 g_return_if_fail (IS_STATUS_NOTIFIER (sn));
1122 priv = sn->priv;
1123
1124 g_free (priv->attention_movie_name);
1125 priv->attention_movie_name = g_strdup (movie_name);
1126
1127 notify (sn, PROP_ATTENTION_MOVIE_NAME);
1128 }
1129
1130 /**
1131 * status_notifier_get_attention_movie_name:
1132 * @sn: A #StatusNotifier
1133 *
1134 * Returns the movie name set for animation associated with the
1135 * #STATUS_NOTIFIER_STATUS_NEEDS_ATTENTION status
1136 *
1137 * Returns: A newly allocated string with the movie name, free using g_free()
1138 * when done
1139 */
1140 gchar *
status_notifier_get_attention_movie_name(StatusNotifier * sn)1141 status_notifier_get_attention_movie_name (StatusNotifier *sn)
1142 {
1143 g_return_val_if_fail (IS_STATUS_NOTIFIER (sn), NULL);
1144 return g_strdup (sn->priv->attention_movie_name);
1145 }
1146
1147 /**
1148 * status_notifier_set_title:
1149 * @sn: A #StatusNotifier
1150 * @title: The title
1151 *
1152 * Sets the title of the item (might be used by visualization e.g. in menu of
1153 * hidden items when #STATUS_NOTIFIER_STATUS_PASSIVE)
1154 */
1155 void
status_notifier_set_title(StatusNotifier * sn,const gchar * title)1156 status_notifier_set_title (StatusNotifier *sn,
1157 const gchar *title)
1158 {
1159 StatusNotifierPrivate *priv;
1160
1161 g_return_if_fail (IS_STATUS_NOTIFIER (sn));
1162 priv = sn->priv;
1163
1164 g_free (priv->title);
1165 priv->title = g_strdup (title);
1166
1167 notify (sn, PROP_TITLE);
1168 dbus_notify (sn, PROP_TITLE);
1169 }
1170
1171 /**
1172 * status_notifier_get_title:
1173 * @sn: A #StatusNotifier
1174 *
1175 * Returns the title of the item
1176 *
1177 * Returns: A newly allocated string, free with g_free() when done
1178 */
1179 gchar *
status_notifier_get_title(StatusNotifier * sn)1180 status_notifier_get_title (StatusNotifier *sn)
1181 {
1182 g_return_val_if_fail (IS_STATUS_NOTIFIER (sn), NULL);
1183 return g_strdup (sn->priv->title);
1184 }
1185
1186 /**
1187 * status_notifier_set_status:
1188 * @sn: A #StatusNotifier
1189 * @status: The new status
1190 *
1191 * Sets the item status to @status, describing the status of this item or of the
1192 * associated application.
1193 */
1194 void
status_notifier_set_status(StatusNotifier * sn,StatusNotifierStatus status)1195 status_notifier_set_status (StatusNotifier *sn,
1196 StatusNotifierStatus status)
1197 {
1198 StatusNotifierPrivate *priv;
1199
1200 g_return_if_fail (IS_STATUS_NOTIFIER (sn));
1201 priv = sn->priv;
1202
1203 priv->status = status;
1204
1205 notify (sn, PROP_STATUS);
1206 dbus_notify (sn, PROP_STATUS);
1207 }
1208
1209 /**
1210 * status_notifier_get_status:
1211 * @sn: A #StatusNotifier
1212 *
1213 * Returns the status of @sn
1214 *
1215 * Returns: Current status of @sn
1216 */
1217 StatusNotifierStatus
status_notifier_get_status(StatusNotifier * sn)1218 status_notifier_get_status (StatusNotifier *sn)
1219 {
1220 g_return_val_if_fail (IS_STATUS_NOTIFIER (sn), -1);
1221 return sn->priv->status;
1222 }
1223
1224 /**
1225 * status_notifier_set_window_id:
1226 * @sn: A #StatusNotifier
1227 * @window_id: The window ID
1228 *
1229 * Sets the window ID for @sn
1230 *
1231 * It's the windowing-system dependent identifier for a window, the application
1232 * can chose one of its windows to be available trough this property or just set
1233 * 0 if it's not interested.
1234 */
1235 void
status_notifier_set_window_id(StatusNotifier * sn,guint32 window_id)1236 status_notifier_set_window_id (StatusNotifier *sn,
1237 guint32 window_id)
1238 {
1239 StatusNotifierPrivate *priv;
1240
1241 g_return_if_fail (IS_STATUS_NOTIFIER (sn));
1242 priv = sn->priv;
1243
1244 priv->window_id = window_id;
1245
1246 notify (sn, PROP_WINDOW_ID);
1247 }
1248
1249 /**
1250 * status_notifier_get_window_id:
1251 * @sn: A #StatusNotifier
1252 *
1253 * Returns the windowing-system dependent idnetifier for a window associated
1254 * with @sn
1255 *
1256 * Returns: The window ID associated with @sn
1257 */
1258 guint32
status_notifier_get_window_id(StatusNotifier * sn)1259 status_notifier_get_window_id (StatusNotifier *sn)
1260 {
1261 g_return_val_if_fail (IS_STATUS_NOTIFIER (sn), 0);
1262 return sn->priv->window_id;
1263 }
1264
1265 /**
1266 * status_notifier_freeze_tooltip:
1267 * @sn:A #StatusNotifier
1268 *
1269 * Increases the freeze count for tooltip on @sn. If the freeze count is
1270 * non-zero, the emission of a DBus signal for StatusNotifierHost to refresh the
1271 * ToolTip property will be blocked until the freeze count drops back to zero
1272 * (via status_notifier_thaw_tooltip())
1273 *
1274 * This is to allow to set the different properties forming the tooltip (title,
1275 * body and icon) without triggering a refresh afetr each change (as there is a
1276 * single property ToolTip on the DBus item, with all data).
1277 *
1278 * Every call to status_notifier_freeze_tooltip() should later be followed by a
1279 * call to status_notifier_thaw_tooltip()
1280 */
1281 void
status_notifier_freeze_tooltip(StatusNotifier * sn)1282 status_notifier_freeze_tooltip (StatusNotifier *sn)
1283 {
1284 g_return_if_fail (IS_STATUS_NOTIFIER (sn));
1285 ++sn->priv->tooltip_freeze;
1286 }
1287
1288 /**
1289 * status_notifier_thaw_tooltip:
1290 * @sn: A #StatusNotifier
1291 *
1292 * Reverts the effect of a previous call to status_notifier_freeze_tooltip(). If
1293 * the freeze count drops back to zero, a signal NewToolTip will be emitted on
1294 * the DBus object for @sn, for StatusNotifierHost to refresh its ToolTip
1295 * property.
1296 *
1297 * It is an error to call this function when the freeze count is zero.
1298 */
1299 void
status_notifier_thaw_tooltip(StatusNotifier * sn)1300 status_notifier_thaw_tooltip (StatusNotifier *sn)
1301 {
1302 StatusNotifierPrivate *priv;
1303
1304 g_return_if_fail (IS_STATUS_NOTIFIER (sn));
1305 priv = sn->priv;
1306 g_return_if_fail (priv->tooltip_freeze > 0);
1307
1308 if (--priv->tooltip_freeze == 0)
1309 dbus_notify (sn, PROP_TOOLTIP_TITLE);
1310 }
1311
1312 /**
1313 * status_notifier_set_tooltip:
1314 * @sn: A #StatusNotifier
1315 * @icon_name: The icon name to be used for #STATUS_NOTIFIER_TOOLTIP_ICON
1316 * @title: The title of the tooltip
1317 * @body: The body of the tooltip
1318 *
1319 * This is an helper function that allows to set icon name, title and body of
1320 * the tooltip and then emit one DBus signal NewToolTip.
1321 *
1322 * It is equivalent to the following code, and similar code can be used e.g. to
1323 * set the icon from a #GdkPixbuf instead:
1324 * <programlisting>
1325 * status_notifier_freeze_tooltip (sn);
1326 * status_notifier_set_from_icon_name (sn, STATUS_NOTIFIER_TOOLTIP_ICON, icon_name);
1327 * status_notifier_set_tooltip_title (sn, title);
1328 * status_notifier_set_tooltip_body (sn, body);
1329 * status_notifier_thaw_tooltip (sn);
1330 * </programlisting>
1331 */
1332 void
status_notifier_set_tooltip(StatusNotifier * sn,const gchar * icon_name,const gchar * title,const gchar * body)1333 status_notifier_set_tooltip (StatusNotifier *sn,
1334 const gchar *icon_name,
1335 const gchar *title,
1336 const gchar *body)
1337 {
1338 StatusNotifierPrivate *priv;
1339
1340 g_return_if_fail (IS_STATUS_NOTIFIER (sn));
1341 priv = sn->priv;
1342
1343 ++priv->tooltip_freeze;
1344 status_notifier_set_from_icon_name (sn, STATUS_NOTIFIER_TOOLTIP_ICON, icon_name);
1345 status_notifier_set_tooltip_title (sn, title);
1346 status_notifier_set_tooltip_body (sn, body);
1347 status_notifier_thaw_tooltip (sn);
1348 }
1349
1350 /**
1351 * status_notifier_set_tooltip_title:
1352 * @sn: A #StatusNotifier
1353 * @title: The tooltip title
1354 *
1355 * Sets the title of the tooltip
1356 *
1357 * The tooltip is composed of a title, a body, and an icon. Note that changing
1358 * any of these will trigger a DBus signal NewToolTip (for hosts to refresh DBus
1359 * property ToolTip), see status_notifier_freeze_tooltip() for changing more
1360 * than one and only emitting one DBus signal at the end.
1361 */
1362 void
status_notifier_set_tooltip_title(StatusNotifier * sn,const gchar * title)1363 status_notifier_set_tooltip_title (StatusNotifier *sn,
1364 const gchar *title)
1365 {
1366 StatusNotifierPrivate *priv;
1367
1368 g_return_if_fail (IS_STATUS_NOTIFIER (sn));
1369 priv = sn->priv;
1370
1371 g_free (priv->tooltip_title);
1372 priv->tooltip_title = g_strdup (title);
1373
1374 notify (sn, PROP_TOOLTIP_TITLE);
1375 if (priv->tooltip_freeze == 0)
1376 dbus_notify (sn, PROP_TOOLTIP_TITLE);
1377 }
1378
1379 /**
1380 * status_notifier_get_tooltip_title:
1381 * @sn: A #StatusNotifier
1382 *
1383 * Returns the tooltip title
1384 *
1385 * Returns: A newly allocated string of the tooltip title, use g_free() when
1386 * done
1387 */
1388 gchar *
status_notifier_get_tooltip_title(StatusNotifier * sn)1389 status_notifier_get_tooltip_title (StatusNotifier *sn)
1390 {
1391 g_return_val_if_fail (IS_STATUS_NOTIFIER (sn), NULL);
1392 return g_strdup (sn->priv->tooltip_title);
1393 }
1394
1395 /**
1396 * status_notifier_set_tooltip_body:
1397 * @sn: A #StatusNotifier
1398 * @body: The tooltip body
1399 *
1400 * Sets the body of the tooltip
1401 *
1402 * This body can contain some markup, which consists of a small subset of XHTML.
1403 * See http://www.notmart.org/misc/statusnotifieritem/markup.html for more.
1404 *
1405 * The tooltip is composed of a title, a body, and an icon. Note that changing
1406 * any of these will trigger a DBus signal NewToolTip (for hosts to refresh DBus
1407 * property ToolTip), see status_notifier_freeze_tooltip() for changing more
1408 * than one and only emitting one DBus signal at the end.
1409 */
1410 void
status_notifier_set_tooltip_body(StatusNotifier * sn,const gchar * body)1411 status_notifier_set_tooltip_body (StatusNotifier *sn,
1412 const gchar *body)
1413 {
1414 StatusNotifierPrivate *priv;
1415
1416 g_return_if_fail (IS_STATUS_NOTIFIER (sn));
1417 priv = sn->priv;
1418
1419 g_free (priv->tooltip_body);
1420 priv->tooltip_body = g_strdup (body);
1421
1422 notify (sn, PROP_TOOLTIP_BODY);
1423 if (priv->tooltip_freeze == 0)
1424 dbus_notify (sn, PROP_TOOLTIP_BODY);
1425 }
1426
1427 /**
1428 * status_notifier_get_tooltip_body:
1429 * @sn: A #StatusNotifier
1430 *
1431 * Returns the tooltip body
1432 *
1433 * Returns: A newly allocated string of the tooltip body, use g_free() when done
1434 */
1435 gchar *
status_notifier_get_tooltip_body(StatusNotifier * sn)1436 status_notifier_get_tooltip_body (StatusNotifier *sn)
1437 {
1438 g_return_val_if_fail (IS_STATUS_NOTIFIER (sn), NULL);
1439 return g_strdup (sn->priv->tooltip_body);
1440 }
1441
1442 static void
method_call(GDBusConnection * conn,const gchar * sender,const gchar * object,const gchar * interface,const gchar * method,GVariant * params,GDBusMethodInvocation * invocation,gpointer data)1443 method_call (GDBusConnection *conn,
1444 const gchar *sender,
1445 const gchar *object,
1446 const gchar *interface,
1447 const gchar *method,
1448 GVariant *params,
1449 GDBusMethodInvocation *invocation,
1450 gpointer data)
1451 {
1452 (void)conn;
1453 (void)sender;
1454 (void)object;
1455 (void)interface;
1456 StatusNotifier *sn = (StatusNotifier *) data;
1457 guint signal;
1458 gint x, y;
1459 gboolean ret;
1460
1461 if (!g_strcmp0 (method, "ContextMenu"))
1462 signal = SIGNAL_CONTEXT_MENU;
1463 else if (!g_strcmp0 (method, "Activate"))
1464 signal = SIGNAL_ACTIVATE;
1465 else if (!g_strcmp0 (method, "SecondaryActivate"))
1466 signal = SIGNAL_SECONDARY_ACTIVATE;
1467 else if (!g_strcmp0 (method, "Scroll"))
1468 {
1469 gint delta, orientation;
1470 gchar *s_orientation;
1471
1472 g_variant_get (params, "(is)", &delta, &s_orientation);
1473 if (!g_strcmp0 (s_orientation, "vertical"))
1474 orientation = STATUS_NOTIFIER_SCROLL_ORIENTATION_VERTICAL;
1475 else
1476 orientation = STATUS_NOTIFIER_SCROLL_ORIENTATION_HORIZONTAL;
1477 g_free (s_orientation);
1478
1479 g_signal_emit (sn, status_notifier_signals[SIGNAL_SCROLL], 0,
1480 delta, orientation, &ret);
1481 g_dbus_method_invocation_return_value (invocation, NULL);
1482 return;
1483 }
1484 else
1485 /* should never happen */
1486 g_return_if_reached ();
1487
1488 g_variant_get (params, "(ii)", &x, &y);
1489 g_signal_emit (sn, status_notifier_signals[signal], 0, x, y, &ret);
1490 g_dbus_method_invocation_return_value (invocation, NULL);
1491 }
1492
1493 static GVariant *
get_icon_pixmap(StatusNotifier * sn,StatusNotifierIcon icon)1494 get_icon_pixmap (StatusNotifier *sn, StatusNotifierIcon icon)
1495 {
1496 StatusNotifierPrivate *priv = sn->priv;
1497 GVariantBuilder *builder;
1498 cairo_surface_t *surface;
1499 cairo_t *cr;
1500 gint width, height, stride;
1501 guint *data;
1502
1503 if (G_UNLIKELY (!priv->icon[icon].has_pixbuf))
1504 return NULL;
1505
1506 width = gdk_pixbuf_get_width (priv->icon[icon].pixbuf);
1507 height = gdk_pixbuf_get_height (priv->icon[icon].pixbuf);
1508
1509 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
1510 cr = cairo_create (surface);
1511 gdk_cairo_set_source_pixbuf (cr, priv->icon[icon].pixbuf, 0, 0);
1512 cairo_paint (cr);
1513 cairo_destroy (cr);
1514
1515 stride = cairo_image_surface_get_stride (surface);
1516 cairo_surface_flush (surface);
1517 data = (guint *) cairo_image_surface_get_data (surface);
1518 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
1519 guint i, max;
1520
1521 max = (stride * height) / sizeof (guint);
1522 for (i = 0; i < max; ++i)
1523 data[i] = GUINT_TO_BE (data[i]);
1524 #endif
1525
1526 builder = g_variant_builder_new (G_VARIANT_TYPE ("a(iiay)"));
1527 g_variant_builder_open (builder, G_VARIANT_TYPE ("(iiay)"));
1528 g_variant_builder_add (builder, "i", width);
1529 g_variant_builder_add (builder, "i", height);
1530 g_variant_builder_add_value (builder,
1531 g_variant_new_from_data (G_VARIANT_TYPE ("ay"),
1532 data,
1533 stride * height,
1534 TRUE,
1535 (GDestroyNotify) cairo_surface_destroy,
1536 surface));
1537 g_variant_builder_close (builder);
1538 GVariant *pixmap = g_variant_new ("a(iiay)", builder);
1539
1540 g_variant_builder_unref(builder); // Allow the builder to be deallocated
1541
1542 return pixmap;
1543 }
1544
1545 static GVariant *
get_prop(GDBusConnection * conn,const gchar * sender,const gchar * object,const gchar * interface,const gchar * property,GError ** error,gpointer data)1546 get_prop (GDBusConnection *conn,
1547 const gchar *sender,
1548 const gchar *object,
1549 const gchar *interface,
1550 const gchar *property,
1551 GError **error,
1552 gpointer data)
1553 {
1554 (void)conn;
1555 (void)sender;
1556 (void)object;
1557 (void)interface;
1558 (void)error;
1559 StatusNotifier *sn = (StatusNotifier *) data;
1560 StatusNotifierPrivate *priv = sn->priv;
1561
1562 if (!g_strcmp0 (property, "Id"))
1563 return g_variant_new ("s", priv->id);
1564 else if (!g_strcmp0 (property, "Category"))
1565 {
1566 const gchar *s_category[] = {
1567 "ApplicationStatus",
1568 "Communications",
1569 "SystemServices",
1570 "Hardware"
1571 };
1572 return g_variant_new ("s", s_category[priv->category]);
1573 }
1574 else if (!g_strcmp0 (property, "Title"))
1575 return g_variant_new ("s", (priv->title) ? priv->title : "");
1576 else if (!g_strcmp0 (property, "Status"))
1577 {
1578 const gchar *s_status[] = {
1579 "Passive",
1580 "Active",
1581 "NeedsAttention"
1582 };
1583 return g_variant_new ("s", s_status[priv->status]);
1584 }
1585 else if (!g_strcmp0 (property, "WindowId"))
1586 return g_variant_new ("i", priv->window_id);
1587 else if (!g_strcmp0 (property, "IconName"))
1588 return g_variant_new ("s", (!priv->icon[STATUS_NOTIFIER_ICON].has_pixbuf)
1589 ? ((priv->icon[STATUS_NOTIFIER_ICON].icon_name)
1590 ? priv->icon[STATUS_NOTIFIER_ICON].icon_name : "") : "");
1591 else if (!g_strcmp0 (property, "IconPixmap"))
1592 return get_icon_pixmap (sn, STATUS_NOTIFIER_ICON);
1593 else if (!g_strcmp0 (property, "OverlayIconName"))
1594 return g_variant_new ("s", (!priv->icon[STATUS_NOTIFIER_OVERLAY_ICON].has_pixbuf)
1595 ? ((priv->icon[STATUS_NOTIFIER_OVERLAY_ICON].icon_name)
1596 ? priv->icon[STATUS_NOTIFIER_OVERLAY_ICON].icon_name : "") : "");
1597 else if (!g_strcmp0 (property, "OverlayIconPixmap"))
1598 return get_icon_pixmap (sn, STATUS_NOTIFIER_OVERLAY_ICON);
1599 else if (!g_strcmp0 (property, "AttentionIconName"))
1600 return g_variant_new ("s", (!priv->icon[STATUS_NOTIFIER_ATTENTION_ICON].has_pixbuf)
1601 ? ((priv->icon[STATUS_NOTIFIER_ATTENTION_ICON].icon_name)
1602 ? priv->icon[STATUS_NOTIFIER_ATTENTION_ICON].icon_name : "") : "");
1603 else if (!g_strcmp0 (property, "AttentionIconPixmap"))
1604 return get_icon_pixmap (sn, STATUS_NOTIFIER_ATTENTION_ICON);
1605 else if (!g_strcmp0 (property, "AttentionMovieName"))
1606 return g_variant_new ("s", (priv->attention_movie_name)
1607 ? priv->attention_movie_name : "");
1608 else if (!g_strcmp0 (property, "ToolTip"))
1609 {
1610 GVariant *variant;
1611 GVariant *pixmap;
1612
1613 if (!priv->icon[STATUS_NOTIFIER_TOOLTIP_ICON].has_pixbuf)
1614 {
1615 variant = g_variant_new ("(sa(iiay)ss)",
1616 (priv->icon[STATUS_NOTIFIER_TOOLTIP_ICON].icon_name)
1617 ? priv->icon[STATUS_NOTIFIER_TOOLTIP_ICON].icon_name : "",
1618 NULL,
1619 (priv->tooltip_title) ? priv->tooltip_title : "",
1620 (priv->tooltip_body) ? priv->tooltip_body : "");
1621 return variant;
1622 }
1623
1624 pixmap = get_icon_pixmap (sn, STATUS_NOTIFIER_TOOLTIP_ICON);
1625 variant = g_variant_new ("(sa(iiay)ss)",
1626 "",
1627 pixmap,
1628 (priv->tooltip_title) ? priv->tooltip_title : "",
1629 (priv->tooltip_body) ? priv->tooltip_body : "");
1630 g_variant_unref (pixmap);
1631
1632 return variant;
1633 }
1634
1635 g_return_val_if_reached (NULL);
1636 }
1637
1638 static void
dbus_failed(StatusNotifier * sn,GError * error,gboolean fatal)1639 dbus_failed (StatusNotifier *sn, GError *error, gboolean fatal)
1640 {
1641 StatusNotifierPrivate *priv = sn->priv;
1642
1643 dbus_free (sn);
1644 if (fatal)
1645 {
1646 priv->state = STATUS_NOTIFIER_STATE_FAILED;
1647 notify (sn, PROP_STATE);
1648 }
1649 g_signal_emit (sn, status_notifier_signals[SIGNAL_REGISTRATION_FAILED], 0,
1650 error);
1651 g_error_free (error);
1652 }
1653
1654 static void
bus_acquired(GDBusConnection * conn,const gchar * name,gpointer data)1655 bus_acquired (GDBusConnection *conn, const gchar *name, gpointer data)
1656 {
1657 (void)name;
1658 GError *err = NULL;
1659 StatusNotifier *sn = (StatusNotifier *) data;
1660 StatusNotifierPrivate *priv = sn->priv;
1661 GDBusInterfaceVTable interface_vtable = {
1662 .method_call = method_call,
1663 .get_property = get_prop,
1664 .set_property = NULL
1665 };
1666 GDBusNodeInfo *info;
1667
1668 info = g_dbus_node_info_new_for_xml (item_xml, NULL);
1669 priv->dbus_reg_id = g_dbus_connection_register_object (conn,
1670 ITEM_OBJECT,
1671 info->interfaces[0],
1672 &interface_vtable,
1673 sn, NULL,
1674 &err);
1675 g_dbus_node_info_unref (info);
1676 if (priv->dbus_reg_id == 0)
1677 {
1678 dbus_failed (sn, err, TRUE);
1679 return;
1680 }
1681
1682 priv->dbus_conn = g_object_ref (conn);
1683 }
1684
1685 static void
register_item_cb(GObject * sce,GAsyncResult * result,gpointer data)1686 register_item_cb (GObject *sce, GAsyncResult *result, gpointer data)
1687 {
1688 GError *err = NULL;
1689 StatusNotifier *sn = (StatusNotifier *) data;
1690 StatusNotifierPrivate *priv = sn->priv;
1691 GVariant *variant;
1692
1693 variant = g_dbus_proxy_call_finish ((GDBusProxy *) sce, result, &err);
1694 if (!variant)
1695 {
1696 dbus_failed (sn, err, TRUE);
1697 return;
1698 }
1699 g_variant_unref (variant);
1700
1701 priv->state = STATUS_NOTIFIER_STATE_REGISTERED;
1702 notify (sn, PROP_STATE);
1703 }
1704
1705 static void
name_acquired(GDBusConnection * conn,const gchar * name,gpointer data)1706 name_acquired (GDBusConnection *conn, const gchar *name, gpointer data)
1707 {
1708 (void)conn;
1709 StatusNotifier *sn = (StatusNotifier *) data;
1710 StatusNotifierPrivate *priv = sn->priv;
1711
1712 g_dbus_proxy_call (priv->dbus_proxy,
1713 "RegisterStatusNotifierItem",
1714 g_variant_new ("(s)", name),
1715 G_DBUS_CALL_FLAGS_NONE,
1716 -1,
1717 NULL,
1718 register_item_cb,
1719 sn);
1720 g_object_unref (priv->dbus_proxy);
1721 priv->dbus_proxy = NULL;
1722 }
1723
1724 static void
name_lost(GDBusConnection * conn,const gchar * name,gpointer data)1725 name_lost (GDBusConnection *conn, const gchar *name, gpointer data)
1726 {
1727 (void)name;
1728 GError *err = NULL;
1729 StatusNotifier *sn = (StatusNotifier *) data;
1730
1731 if (!conn)
1732 g_set_error (&err, STATUS_NOTIFIER_ERROR,
1733 STATUS_NOTIFIER_ERROR_NO_CONNECTION,
1734 "Failed to establish DBus connection");
1735 else
1736 g_set_error (&err, STATUS_NOTIFIER_ERROR,
1737 STATUS_NOTIFIER_ERROR_NO_NAME,
1738 "Failed to acquire name for item");
1739 dbus_failed (sn, err, TRUE);
1740 }
1741
1742 static void
dbus_reg_item(StatusNotifier * sn)1743 dbus_reg_item (StatusNotifier *sn)
1744 {
1745 StatusNotifierPrivate *priv = sn->priv;
1746 gchar buf[64], *b = buf;
1747
1748 if (G_UNLIKELY (g_snprintf (buf, 64, "org.kde.StatusNotifierItem-%u-%u",
1749 getpid (), ++uniq_id) >= 64))
1750 b = g_strdup_printf ("org.kde.StatusNotifierItem-%u-%u",
1751 getpid (), uniq_id);
1752 priv->dbus_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
1753 b,
1754 G_BUS_NAME_OWNER_FLAGS_NONE,
1755 bus_acquired,
1756 name_acquired,
1757 name_lost,
1758 sn, NULL);
1759 if (G_UNLIKELY (b != buf))
1760 g_free (b);
1761 }
1762
1763 static void
watcher_signal(GDBusProxy * proxy,const gchar * sender,const gchar * signal,GVariant * params,StatusNotifier * sn)1764 watcher_signal (GDBusProxy *proxy,
1765 const gchar *sender,
1766 const gchar *signal,
1767 GVariant *params,
1768 StatusNotifier *sn)
1769 {
1770 (void)proxy;
1771 (void)sender;
1772 (void)params;
1773 StatusNotifierPrivate *priv = sn->priv;
1774
1775 if (!g_strcmp0 (signal, "StatusNotifierHostRegistered"))
1776 {
1777 g_signal_handler_disconnect (priv->dbus_proxy, priv->dbus_sid);
1778 priv->dbus_sid = 0;
1779
1780 dbus_reg_item (sn);
1781 }
1782 }
1783
1784 static void
proxy_cb(GObject * sce,GAsyncResult * result,gpointer data)1785 proxy_cb (GObject *sce, GAsyncResult *result, gpointer data)
1786 {
1787 (void)sce;
1788 GError *err = NULL;
1789 StatusNotifier *sn = (StatusNotifier *) data;
1790 StatusNotifierPrivate *priv = sn->priv;
1791 GVariant *variant;
1792
1793 priv->dbus_proxy = g_dbus_proxy_new_for_bus_finish (result, &err);
1794 if (!priv->dbus_proxy)
1795 {
1796 dbus_failed (sn, err, TRUE);
1797 return;
1798 }
1799
1800 variant = g_dbus_proxy_get_cached_property (priv->dbus_proxy,
1801 "IsStatusNotifierHostRegistered");
1802 if (!variant || !g_variant_get_boolean (variant))
1803 {
1804 GDBusProxy *proxy;
1805
1806 g_set_error (&err, STATUS_NOTIFIER_ERROR,
1807 STATUS_NOTIFIER_ERROR_NO_HOST,
1808 "No Host registered on the Watcher");
1809 if (variant)
1810 g_variant_unref (variant);
1811
1812 /* keep the proxy, we'll wait for the signal when a host registers */
1813 proxy = priv->dbus_proxy;
1814 /* (so dbus_free() from dbus_failed() doesn't unref) */
1815 priv->dbus_proxy = NULL;
1816 dbus_failed (sn, err, FALSE);
1817 priv->dbus_proxy = proxy;
1818
1819 priv->dbus_sid = g_signal_connect (priv->dbus_proxy, "g-signal",
1820 (GCallback) watcher_signal, sn);
1821 return;
1822 }
1823 g_variant_unref (variant);
1824
1825 dbus_reg_item (sn);
1826 }
1827
1828 static void
watcher_appeared(GDBusConnection * conn,const gchar * name,const gchar * owner,gpointer data)1829 watcher_appeared (GDBusConnection *conn,
1830 const gchar *name,
1831 const gchar *owner,
1832 gpointer data)
1833 {
1834 (void)conn;
1835 (void)name;
1836 (void)owner;
1837 StatusNotifier *sn = data;
1838 StatusNotifierPrivate *priv = sn->priv;
1839 GDBusNodeInfo *info;
1840
1841 g_bus_unwatch_name (priv->dbus_watch_id);
1842 priv->dbus_watch_id = 0;
1843
1844 info = g_dbus_node_info_new_for_xml (watcher_xml, NULL);
1845 g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
1846 G_DBUS_PROXY_FLAGS_NONE,
1847 info->interfaces[0],
1848 WATCHER_NAME,
1849 WATCHER_OBJECT,
1850 WATCHER_INTERFACE,
1851 NULL,
1852 proxy_cb,
1853 sn);
1854 g_dbus_node_info_unref (info);
1855 }
1856
1857 static void
watcher_vanished(GDBusConnection * conn,const gchar * name,gpointer data)1858 watcher_vanished (GDBusConnection *conn,
1859 const gchar *name,
1860 gpointer data)
1861 {
1862 (void)conn;
1863 (void)name;
1864 GError *err = NULL;
1865 StatusNotifier *sn = data;
1866 StatusNotifierPrivate *priv = sn->priv;
1867 guint id;
1868
1869 /* keep the watch active, so if a watcher shows up we'll resume the
1870 * registering automatically */
1871 id = priv->dbus_watch_id;
1872 /* (so dbus_free() from dbus_failed() doesn't unwatch) */
1873 priv->dbus_watch_id = 0;
1874
1875 g_set_error (&err, STATUS_NOTIFIER_ERROR,
1876 STATUS_NOTIFIER_ERROR_NO_WATCHER,
1877 "No Watcher found");
1878 dbus_failed (sn, err, FALSE);
1879
1880 priv->dbus_watch_id = id;
1881 }
1882
1883 /**
1884 * status_notifier_register:
1885 * @sn: A #StatusNotifier
1886 *
1887 * Registers @sn to the StatusNotifierWatcher over DBus.
1888 *
1889 * Once you have created your #StatusNotifier you need to register it, so any
1890 * host/visualization can use it and update their GUI as needed.
1891 *
1892 * This function will connect to the StatusNotifierWatcher and make sure at
1893 * least one StatusNotifierHost is registered. Then, it will register a new
1894 * StatusNotifierItem on the session bus and register it with the watcher.
1895 *
1896 * When done, property #StatusNotifier:state will change to
1897 * %STATUS_NOTIFIER_STATE_REGISTERED. If something fails, signal
1898 * #StatusNotifier::registration-failed will be emitted, at which point you
1899 * should fallback to using the systray.
1900 *
1901 * However there are two possible types of failures: fatal and non-fatal ones.
1902 * Fatal error means that #StatusNotifier:state will be
1903 * %STATUS_NOTIFIER_STATE_FAILED and you can unref @sn.
1904 *
1905 * Non-fatal error means it will still be %STATUS_NOTIFIER_STATE_REGISTERING as
1906 * the registration process could still eventually succeed. For example, if
1907 * there was no host registered on the watcher, as soon as signal
1908 * StatusNotifierHostRegistered is emitted on the watcher, the registration
1909 * process for @sn will complete and #StatusNotifier:state set to
1910 * %STATUS_NOTIFIER_STATE_REGISTERED, at which point you should stop using the
1911 * systray.
1912 *
1913 * This also means it is possible to have multiple signals
1914 * #StatusNotifier::registration-failed emitted on the same #StatusNotifier.
1915 *
1916 * Note that you can call status_notifier_register() after a fatal error
1917 * occured, to try again. You can also unref @sn while it is
1918 * %STATUS_NOTIFIER_STATE_REGISTERING safely.
1919 */
1920 void
status_notifier_register(StatusNotifier * sn)1921 status_notifier_register (StatusNotifier *sn)
1922
1923 {
1924 StatusNotifierPrivate *priv;
1925
1926 g_return_if_fail (IS_STATUS_NOTIFIER (sn));
1927 priv = sn->priv;
1928
1929 if (priv->state == STATUS_NOTIFIER_STATE_REGISTERING
1930 || priv->state == STATUS_NOTIFIER_STATE_REGISTERED)
1931 return;
1932 priv->state = STATUS_NOTIFIER_STATE_REGISTERING;
1933
1934 priv->dbus_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
1935 WATCHER_NAME,
1936 G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
1937 watcher_appeared,
1938 watcher_vanished,
1939 sn, NULL);
1940 }
1941
1942 /**
1943 * status_notifier_get_state:
1944 * @sn: A #StatusNotifier
1945 *
1946 * Returns the DBus registration state of @sn. See status_notifier_register()
1947 * for more.
1948 *
1949 * Returns: The DBus registration state of @sn
1950 */
1951 StatusNotifierState
status_notifier_get_state(StatusNotifier * sn)1952 status_notifier_get_state (StatusNotifier *sn)
1953 {
1954 g_return_val_if_fail (IS_STATUS_NOTIFIER (sn), FALSE);
1955 return sn->priv->state;
1956 }
1957