1 /* na-tray-manager.c
2 * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
3 * Copyright (C) 2003-2006 Vincent Untz
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 *
20 * Used to be: eggtraymanager.c
21 */
22
23 #include <config.h>
24
25 #ifndef HAVE_X11
26 #error file should only be built when HAVE_X11 is enabled
27 #endif
28
29 #include <string.h>
30 #include <libintl.h>
31
32 #include "na-tray-manager.h"
33
34 #include <gtk/gtk.h>
35 #include <glib/gi18n.h>
36 #include <gdk/gdkx.h>
37 #include <X11/Xatom.h>
38
39 #include "na-marshal.h"
40
41 /* Signals */
42 enum
43 {
44 TRAY_ICON_ADDED,
45 TRAY_ICON_REMOVED,
46 MESSAGE_SENT,
47 MESSAGE_CANCELLED,
48 LOST_SELECTION,
49 LAST_SIGNAL
50 };
51
52 enum {
53 PROP_0,
54 PROP_ORIENTATION
55 };
56
57 typedef struct
58 {
59 long id, len;
60 long remaining_len;
61
62 long timeout;
63 char *str;
64 #ifdef GDK_WINDOWING_X11
65 Window window;
66 #endif
67 } PendingMessage;
68
69 static guint manager_signals[LAST_SIGNAL] = { 0 };
70
71 #define SYSTEM_TRAY_REQUEST_DOCK 0
72 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
73 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
74
75 #define SYSTEM_TRAY_ORIENTATION_HORZ 0
76 #define SYSTEM_TRAY_ORIENTATION_VERT 1
77
78 #ifdef GDK_WINDOWING_X11
79 static gboolean na_tray_manager_check_running_screen_x11 (GdkScreen *screen);
80 #endif
81
82 static void na_tray_manager_finalize (GObject *object);
83 static void na_tray_manager_set_property (GObject *object,
84 guint prop_id,
85 const GValue *value,
86 GParamSpec *pspec);
87 static void na_tray_manager_get_property (GObject *object,
88 guint prop_id,
89 GValue *value,
90 GParamSpec *pspec);
91
92 static void na_tray_manager_unmanage (NaTrayManager *manager);
93
G_DEFINE_TYPE(NaTrayManager,na_tray_manager,G_TYPE_OBJECT)94 G_DEFINE_TYPE (NaTrayManager, na_tray_manager, G_TYPE_OBJECT)
95
96 static void
97 na_tray_manager_init (NaTrayManager *manager)
98 {
99 manager->invisible = NULL;
100 manager->socket_table = g_hash_table_new (NULL, NULL);
101
102 manager->padding = 0;
103 manager->icon_size = 0;
104
105 manager->fg.red = 0.0;
106 manager->fg.green = 0.0;
107 manager->fg.blue = 0.0;
108 manager->fg.alpha = 1.0;
109
110 manager->error.red = 1.0;
111 manager->error.green = 0.0;
112 manager->error.blue = 0.0;
113 manager->error.alpha = 1.0;
114
115 manager->warning.red = 1.0;
116 manager->warning.green = 1.0;
117 manager->warning.blue = 0.0;
118 manager->warning.alpha = 1.0;
119
120 manager->success.red = 0.0;
121 manager->success.green = 1.0;
122 manager->success.blue = 0.0;
123 manager->success.alpha = 1.0;
124 }
125
126 static void
na_tray_manager_class_init(NaTrayManagerClass * klass)127 na_tray_manager_class_init (NaTrayManagerClass *klass)
128 {
129 GObjectClass *gobject_class;
130
131 gobject_class = (GObjectClass *)klass;
132
133 gobject_class->finalize = na_tray_manager_finalize;
134 gobject_class->set_property = na_tray_manager_set_property;
135 gobject_class->get_property = na_tray_manager_get_property;
136
137 g_object_class_install_property (gobject_class,
138 PROP_ORIENTATION,
139 g_param_spec_enum ("orientation",
140 "orientation",
141 "orientation",
142 GTK_TYPE_ORIENTATION,
143 GTK_ORIENTATION_HORIZONTAL,
144 G_PARAM_READWRITE |
145 G_PARAM_CONSTRUCT |
146 G_PARAM_STATIC_NAME |
147 G_PARAM_STATIC_NICK |
148 G_PARAM_STATIC_BLURB));
149
150 manager_signals[TRAY_ICON_ADDED] =
151 g_signal_new ("tray_icon_added",
152 G_OBJECT_CLASS_TYPE (klass),
153 G_SIGNAL_RUN_LAST,
154 G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_added),
155 NULL, NULL,
156 g_cclosure_marshal_VOID__OBJECT,
157 G_TYPE_NONE, 1,
158 GTK_TYPE_SOCKET);
159
160 manager_signals[TRAY_ICON_REMOVED] =
161 g_signal_new ("tray_icon_removed",
162 G_OBJECT_CLASS_TYPE (klass),
163 G_SIGNAL_RUN_LAST,
164 G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_removed),
165 NULL, NULL,
166 g_cclosure_marshal_VOID__OBJECT,
167 G_TYPE_NONE, 1,
168 GTK_TYPE_SOCKET);
169 manager_signals[MESSAGE_SENT] =
170 g_signal_new ("message_sent",
171 G_OBJECT_CLASS_TYPE (klass),
172 G_SIGNAL_RUN_LAST,
173 G_STRUCT_OFFSET (NaTrayManagerClass, message_sent),
174 NULL, NULL,
175 _na_marshal_VOID__OBJECT_STRING_LONG_LONG,
176 G_TYPE_NONE, 4,
177 GTK_TYPE_SOCKET,
178 G_TYPE_STRING,
179 G_TYPE_LONG,
180 G_TYPE_LONG);
181 manager_signals[MESSAGE_CANCELLED] =
182 g_signal_new ("message_cancelled",
183 G_OBJECT_CLASS_TYPE (klass),
184 G_SIGNAL_RUN_LAST,
185 G_STRUCT_OFFSET (NaTrayManagerClass, message_cancelled),
186 NULL, NULL,
187 _na_marshal_VOID__OBJECT_LONG,
188 G_TYPE_NONE, 2,
189 GTK_TYPE_SOCKET,
190 G_TYPE_LONG);
191 manager_signals[LOST_SELECTION] =
192 g_signal_new ("lost_selection",
193 G_OBJECT_CLASS_TYPE (klass),
194 G_SIGNAL_RUN_LAST,
195 G_STRUCT_OFFSET (NaTrayManagerClass, lost_selection),
196 NULL, NULL,
197 g_cclosure_marshal_VOID__VOID,
198 G_TYPE_NONE, 0);
199 }
200
201 static void
na_tray_manager_finalize(GObject * object)202 na_tray_manager_finalize (GObject *object)
203 {
204 NaTrayManager *manager;
205
206 manager = NA_TRAY_MANAGER (object);
207
208 na_tray_manager_unmanage (manager);
209
210 g_list_free (manager->messages);
211 g_hash_table_destroy (manager->socket_table);
212
213 G_OBJECT_CLASS (na_tray_manager_parent_class)->finalize (object);
214 }
215
216 static void
na_tray_manager_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)217 na_tray_manager_set_property (GObject *object,
218 guint prop_id,
219 const GValue *value,
220 GParamSpec *pspec)
221 {
222 NaTrayManager *manager = NA_TRAY_MANAGER (object);
223
224 switch (prop_id)
225 {
226 case PROP_ORIENTATION:
227 na_tray_manager_set_orientation (manager, g_value_get_enum (value));
228 break;
229 default:
230 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
231 break;
232 }
233 }
234
235 static void
na_tray_manager_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)236 na_tray_manager_get_property (GObject *object,
237 guint prop_id,
238 GValue *value,
239 GParamSpec *pspec)
240 {
241 NaTrayManager *manager = NA_TRAY_MANAGER (object);
242
243 switch (prop_id)
244 {
245 case PROP_ORIENTATION:
246 g_value_set_enum (value, manager->orientation);
247 break;
248 default:
249 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
250 break;
251 }
252 }
253
254 NaTrayManager *
na_tray_manager_new(void)255 na_tray_manager_new (void)
256 {
257 NaTrayManager *manager;
258
259 manager = g_object_new (NA_TYPE_TRAY_MANAGER, NULL);
260
261 return manager;
262 }
263
264 #ifdef GDK_WINDOWING_X11
265
266 static gboolean
na_tray_manager_plug_removed(GtkSocket * socket,NaTrayManager * manager)267 na_tray_manager_plug_removed (GtkSocket *socket,
268 NaTrayManager *manager)
269 {
270 NaTrayChild *child = NA_TRAY_CHILD (socket);
271
272 g_hash_table_remove (manager->socket_table,
273 GINT_TO_POINTER (child->icon_window));
274 g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, child);
275
276 /* This destroys the socket. */
277 return FALSE;
278 }
279
280 static void
na_tray_manager_handle_dock_request(NaTrayManager * manager,XClientMessageEvent * xevent)281 na_tray_manager_handle_dock_request (NaTrayManager *manager,
282 XClientMessageEvent *xevent)
283 {
284 Window icon_window = xevent->data.l[2];
285 GtkWidget *child;
286
287 if (g_hash_table_lookup (manager->socket_table,
288 GINT_TO_POINTER (icon_window)))
289 {
290 /* We already got this notification earlier, ignore this one */
291 return;
292 }
293
294 child = na_tray_child_new (manager->screen, icon_window);
295 if (child == NULL) /* already gone or other error */
296 return;
297
298 g_signal_emit (manager, manager_signals[TRAY_ICON_ADDED], 0,
299 child);
300
301 /* If the child wasn't attached, then destroy it */
302
303 if (!GTK_IS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (child))))
304 {
305 gtk_widget_destroy (child);
306 return;
307 }
308
309 g_signal_connect (child, "plug_removed",
310 G_CALLBACK (na_tray_manager_plug_removed), manager);
311
312 gtk_socket_add_id (GTK_SOCKET (child), icon_window);
313
314 if (!gtk_socket_get_plug_window (GTK_SOCKET (child)))
315 {
316 /* Embedding failed, we won't get a plug-removed signal */
317 /* This signal destroys the socket */
318 g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, child);
319 return;
320 }
321
322 g_hash_table_insert (manager->socket_table,
323 GINT_TO_POINTER (icon_window), child);
324 gtk_widget_show (child);
325 }
326
327 static void
pending_message_free(PendingMessage * message)328 pending_message_free (PendingMessage *message)
329 {
330 g_free (message->str);
331 g_free (message);
332 }
333
334 static void
na_tray_manager_handle_message_data(NaTrayManager * manager,XClientMessageEvent * xevent)335 na_tray_manager_handle_message_data (NaTrayManager *manager,
336 XClientMessageEvent *xevent)
337 {
338 GList *p;
339 int len;
340
341 /* Try to see if we can find the pending message in the list */
342 for (p = manager->messages; p; p = p->next)
343 {
344 PendingMessage *msg = p->data;
345
346 if (xevent->window == msg->window)
347 {
348 /* Append the message */
349 len = MIN (msg->remaining_len, 20);
350
351 memcpy ((msg->str + msg->len - msg->remaining_len),
352 &xevent->data, len);
353 msg->remaining_len -= len;
354
355 if (msg->remaining_len == 0)
356 {
357 GtkSocket *socket;
358
359 socket = g_hash_table_lookup (manager->socket_table,
360 GINT_TO_POINTER (msg->window));
361
362 if (socket)
363 g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0,
364 socket, msg->str, msg->id, msg->timeout);
365
366 pending_message_free (msg);
367 manager->messages = g_list_remove_link (manager->messages, p);
368 g_list_free_1 (p);
369 }
370
371 break;
372 }
373 }
374 }
375
376 static void
na_tray_manager_handle_begin_message(NaTrayManager * manager,XClientMessageEvent * xevent)377 na_tray_manager_handle_begin_message (NaTrayManager *manager,
378 XClientMessageEvent *xevent)
379 {
380 GtkSocket *socket;
381 GList *p;
382 PendingMessage *msg;
383 long timeout;
384 long len;
385 long id;
386
387 socket = g_hash_table_lookup (manager->socket_table,
388 GINT_TO_POINTER (xevent->window));
389 /* we don't know about this tray icon, so ignore the message */
390 if (!socket)
391 return;
392
393 timeout = xevent->data.l[2];
394 len = xevent->data.l[3];
395 id = xevent->data.l[4];
396
397 /* Check if the same message is already in the queue and remove it if so */
398 for (p = manager->messages; p; p = p->next)
399 {
400 PendingMessage *pmsg = p->data;
401
402 if (xevent->window == pmsg->window &&
403 id == pmsg->id)
404 {
405 /* Hmm, we found it, now remove it */
406 pending_message_free (pmsg);
407 manager->messages = g_list_remove_link (manager->messages, p);
408 g_list_free_1 (p);
409 break;
410 }
411 }
412
413 if (len == 0)
414 {
415 g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0,
416 socket, "", id, timeout);
417 }
418 else
419 {
420 /* Now add the new message to the queue */
421 msg = g_new0 (PendingMessage, 1);
422 msg->window = xevent->window;
423 msg->timeout = timeout;
424 msg->len = len;
425 msg->id = id;
426 msg->remaining_len = msg->len;
427 msg->str = g_malloc (msg->len + 1);
428 msg->str[msg->len] = '\0';
429 manager->messages = g_list_prepend (manager->messages, msg);
430 }
431 }
432
433 static void
na_tray_manager_handle_cancel_message(NaTrayManager * manager,XClientMessageEvent * xevent)434 na_tray_manager_handle_cancel_message (NaTrayManager *manager,
435 XClientMessageEvent *xevent)
436 {
437 GList *p;
438 GtkSocket *socket;
439 long id;
440
441 id = xevent->data.l[2];
442
443 /* Check if the message is in the queue and remove it if so */
444 for (p = manager->messages; p; p = p->next)
445 {
446 PendingMessage *msg = p->data;
447
448 if (xevent->window == msg->window &&
449 id == msg->id)
450 {
451 pending_message_free (msg);
452 manager->messages = g_list_remove_link (manager->messages, p);
453 g_list_free_1 (p);
454 break;
455 }
456 }
457
458 socket = g_hash_table_lookup (manager->socket_table,
459 GINT_TO_POINTER (xevent->window));
460
461 if (socket)
462 {
463 g_signal_emit (manager, manager_signals[MESSAGE_CANCELLED], 0,
464 socket, xevent->data.l[2]);
465 }
466 }
467
468 static GdkFilterReturn
na_tray_manager_window_filter(GdkXEvent * xev,GdkEvent * event,gpointer data)469 na_tray_manager_window_filter (GdkXEvent *xev,
470 GdkEvent *event,
471 gpointer data)
472 {
473 XEvent *xevent = (GdkXEvent *)xev;
474 NaTrayManager *manager = data;
475
476 if (xevent->type == ClientMessage)
477 {
478 /* We handle this client message here. See comment in
479 * na_tray_manager_handle_client_message_opcode() for details */
480 if (xevent->xclient.message_type == manager->opcode_atom &&
481 xevent->xclient.data.l[1] == SYSTEM_TRAY_REQUEST_DOCK)
482 {
483 na_tray_manager_handle_dock_request (manager,
484 (XClientMessageEvent *) xevent);
485 return GDK_FILTER_REMOVE;
486 }
487 /* _NET_SYSTEM_TRAY_OPCODE: SYSTEM_TRAY_BEGIN_MESSAGE */
488 else if (xevent->xclient.message_type == manager->opcode_atom &&
489 xevent->xclient.data.l[1] == SYSTEM_TRAY_BEGIN_MESSAGE)
490 {
491 na_tray_manager_handle_begin_message (manager,
492 (XClientMessageEvent *) event);
493 return GDK_FILTER_REMOVE;
494 }
495 /* _NET_SYSTEM_TRAY_OPCODE: SYSTEM_TRAY_CANCEL_MESSAGE */
496 else if (xevent->xclient.message_type == manager->opcode_atom &&
497 xevent->xclient.data.l[1] == SYSTEM_TRAY_CANCEL_MESSAGE)
498 {
499 na_tray_manager_handle_cancel_message (manager,
500 (XClientMessageEvent *) event);
501 return GDK_FILTER_REMOVE;
502 }
503 /* _NET_SYSTEM_TRAY_MESSAGE_DATA */
504 else if (xevent->xclient.message_type == manager->message_data_atom)
505 {
506 na_tray_manager_handle_message_data (manager,
507 (XClientMessageEvent *) event);
508 return GDK_FILTER_REMOVE;
509 }
510 }
511 else if (xevent->type == SelectionClear)
512 {
513 g_signal_emit (manager, manager_signals[LOST_SELECTION], 0);
514 na_tray_manager_unmanage (manager);
515 }
516
517 return GDK_FILTER_CONTINUE;
518 }
519
520 #if 0
521 /* FIXME investigate why this doesn't work */
522 static gboolean
523 na_tray_manager_selection_clear_event (GtkWidget *widget,
524 GdkEventSelection *event,
525 NaTrayManager *manager)
526 {
527 g_signal_emit (manager, manager_signals[LOST_SELECTION], 0);
528 na_tray_manager_unmanage (manager);
529
530 return FALSE;
531 }
532 #endif
533 #endif
534
535 static void
na_tray_manager_unmanage(NaTrayManager * manager)536 na_tray_manager_unmanage (NaTrayManager *manager)
537 {
538 #ifdef GDK_WINDOWING_X11
539 GdkDisplay *display;
540 guint32 timestamp;
541 GtkWidget *invisible;
542 GdkWindow *window;
543
544 if (manager->invisible == NULL)
545 return;
546
547 invisible = manager->invisible;
548 window = gtk_widget_get_window (invisible);
549
550 g_assert (GTK_IS_INVISIBLE (invisible));
551 g_assert (gtk_widget_get_realized (invisible));
552 g_assert (GDK_IS_WINDOW (window));
553
554 display = gtk_widget_get_display (invisible);
555
556 if (gdk_selection_owner_get_for_display (display, manager->selection_atom) ==
557 window)
558 {
559 timestamp = gdk_x11_get_server_time (window);
560 gdk_selection_owner_set_for_display (display,
561 NULL,
562 manager->selection_atom,
563 timestamp,
564 TRUE);
565 }
566
567 gdk_window_remove_filter (window,
568 na_tray_manager_window_filter, manager);
569
570 manager->invisible = NULL; /* prior to destroy for reentrancy paranoia */
571 gtk_widget_destroy (invisible);
572 g_object_unref (G_OBJECT (invisible));
573 #endif
574 }
575
576 static void
na_tray_manager_set_orientation_property(NaTrayManager * manager)577 na_tray_manager_set_orientation_property (NaTrayManager *manager)
578 {
579 #ifdef GDK_WINDOWING_X11
580 GdkWindow *window;
581 GdkDisplay *display;
582 Atom orientation_atom;
583 gulong data[1];
584
585 g_return_if_fail (manager->invisible != NULL);
586 window = gtk_widget_get_window (manager->invisible);
587 g_return_if_fail (window != NULL);
588
589 display = gtk_widget_get_display (manager->invisible);
590 orientation_atom = gdk_x11_get_xatom_by_name_for_display (display,
591 "_NET_SYSTEM_TRAY_ORIENTATION");
592
593 data[0] = manager->orientation == GTK_ORIENTATION_HORIZONTAL ?
594 SYSTEM_TRAY_ORIENTATION_HORZ :
595 SYSTEM_TRAY_ORIENTATION_VERT;
596
597 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
598 GDK_WINDOW_XID (window),
599 orientation_atom,
600 XA_CARDINAL, 32,
601 PropModeReplace,
602 (guchar *) &data, 1);
603 #endif
604 }
605
606 static void
na_tray_manager_set_visual_property(NaTrayManager * manager)607 na_tray_manager_set_visual_property (NaTrayManager *manager)
608 {
609 #ifdef GDK_WINDOWING_X11
610 GdkWindow *window;
611 GdkDisplay *display;
612 Visual *xvisual;
613 Atom visual_atom;
614 gulong data[1];
615
616 g_return_if_fail (manager->invisible != NULL);
617 window = gtk_widget_get_window (manager->invisible);
618 g_return_if_fail (window != NULL);
619
620 /* The visual property is a hint to the tray icons as to what visual they
621 * should use for their windows. If the X server has RGBA colormaps, then
622 * we tell the tray icons to use a RGBA colormap and we'll composite the
623 * icon onto its parents with real transparency. Otherwise, we just tell
624 * the icon to use our colormap, and we'll do some hacks with parent
625 * relative backgrounds to simulate transparency.
626 */
627
628 display = gtk_widget_get_display (manager->invisible);
629 visual_atom = gdk_x11_get_xatom_by_name_for_display (display,
630 "_NET_SYSTEM_TRAY_VISUAL");
631
632 if (gdk_screen_get_rgba_visual (manager->screen) != NULL &&
633 gdk_display_supports_composite (display))
634 {
635 xvisual = GDK_VISUAL_XVISUAL (gdk_screen_get_rgba_visual (manager->screen));
636 }
637 else
638 {
639 /* We actually want the visual of the tray where the icons will
640 * be embedded. In almost all cases, this will be the same as the visual
641 * of the screen.
642 */
643 xvisual = GDK_VISUAL_XVISUAL (gdk_screen_get_system_visual (manager->screen));
644 }
645
646 data[0] = XVisualIDFromVisual (xvisual);
647
648 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
649 GDK_WINDOW_XID (window),
650 visual_atom,
651 XA_VISUALID, 32,
652 PropModeReplace,
653 (guchar *) &data, 1);
654 #endif
655 }
656
657 static void
na_tray_manager_set_padding_property(NaTrayManager * manager)658 na_tray_manager_set_padding_property (NaTrayManager *manager)
659 {
660 #ifdef GDK_WINDOWING_X11
661 GdkWindow *window;
662 GdkDisplay *display;
663 Atom atom;
664 gulong data[1];
665
666 g_return_if_fail (manager->invisible != NULL);
667 window = gtk_widget_get_window (manager->invisible);
668 g_return_if_fail (window != NULL);
669
670 display = gtk_widget_get_display (manager->invisible);
671 atom = gdk_x11_get_xatom_by_name_for_display (display,
672 "_NET_SYSTEM_TRAY_PADDING");
673
674 data[0] = manager->padding;
675
676 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
677 GDK_WINDOW_XID (window),
678 atom,
679 XA_CARDINAL, 32,
680 PropModeReplace,
681 (guchar *) &data, 1);
682 #endif
683 }
684
685 static void
na_tray_manager_set_icon_size_property(NaTrayManager * manager)686 na_tray_manager_set_icon_size_property (NaTrayManager *manager)
687 {
688 #ifdef GDK_WINDOWING_X11
689 GdkWindow *window;
690 GdkDisplay *display;
691 Atom atom;
692 gulong data[1];
693
694 g_return_if_fail (manager->invisible != NULL);
695 window = gtk_widget_get_window (manager->invisible);
696 g_return_if_fail (window != NULL);
697
698 display = gtk_widget_get_display (manager->invisible);
699 atom = gdk_x11_get_xatom_by_name_for_display (display,
700 "_NET_SYSTEM_TRAY_ICON_SIZE");
701
702 data[0] = manager->icon_size;
703
704 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
705 GDK_WINDOW_XID (window),
706 atom,
707 XA_CARDINAL, 32,
708 PropModeReplace,
709 (guchar *) &data, 1);
710 #endif
711 }
712
713 static void
na_tray_manager_set_colors_property(NaTrayManager * manager)714 na_tray_manager_set_colors_property (NaTrayManager *manager)
715 {
716 #ifdef GDK_WINDOWING_X11
717 GdkWindow *window;
718 GdkDisplay *display;
719 Atom atom;
720 gulong data[12];
721
722 g_return_if_fail (manager->invisible != NULL);
723 window = gtk_widget_get_window (manager->invisible);
724 g_return_if_fail (window != NULL);
725
726 display = gtk_widget_get_display (manager->invisible);
727 atom = gdk_x11_get_xatom_by_name_for_display (display,
728 "_NET_SYSTEM_TRAY_COLORS");
729
730 data[0] = manager->fg.red * 65535;
731 data[1] = manager->fg.green * 65535;
732 data[2] = manager->fg.blue * 65535;
733 data[3] = manager->error.red * 65535;
734 data[4] = manager->error.green * 65535;
735 data[5] = manager->error.blue * 65535;
736 data[6] = manager->warning.red * 65535;
737 data[7] = manager->warning.green * 65535;
738 data[8] = manager->warning.blue * 65535;
739 data[9] = manager->success.red * 65535;
740 data[10] = manager->success.green * 65535;
741 data[11] = manager->success.blue * 65535;
742
743 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
744 GDK_WINDOW_XID (window),
745 atom,
746 XA_CARDINAL, 32,
747 PropModeReplace,
748 (guchar *) &data, 12);
749 #endif
750 }
751
752 #ifdef GDK_WINDOWING_X11
753
754 static gboolean
na_tray_manager_manage_screen_x11(NaTrayManager * manager,GdkScreen * screen)755 na_tray_manager_manage_screen_x11 (NaTrayManager *manager,
756 GdkScreen *screen)
757 {
758 GdkDisplay *display;
759 Screen *xscreen;
760 GtkWidget *invisible;
761 GdkWindow *window;
762 char *selection_atom_name;
763 guint32 timestamp;
764
765 g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), FALSE);
766 g_return_val_if_fail (manager->screen == NULL, FALSE);
767
768 /* If there's already a manager running on the screen
769 * we can't create another one.
770 */
771 #if 0
772 if (na_tray_manager_check_running_screen_x11 (screen))
773 return FALSE;
774 #endif
775
776 manager->screen = screen;
777
778 display = gdk_screen_get_display (screen);
779 xscreen = GDK_SCREEN_XSCREEN (screen);
780
781 invisible = gtk_invisible_new_for_screen (screen);
782 gtk_widget_realize (invisible);
783
784 gtk_widget_add_events (invisible,
785 GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK);
786
787 selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d",
788 gdk_x11_screen_get_screen_number (screen));
789 manager->selection_atom = gdk_atom_intern (selection_atom_name, FALSE);
790 g_free (selection_atom_name);
791
792 manager->invisible = invisible;
793 g_object_ref (G_OBJECT (manager->invisible));
794
795 na_tray_manager_set_orientation_property (manager);
796 na_tray_manager_set_visual_property (manager);
797 na_tray_manager_set_padding_property (manager);
798 na_tray_manager_set_icon_size_property (manager);
799 na_tray_manager_set_colors_property (manager);
800
801 window = gtk_widget_get_window (invisible);
802
803 timestamp = gdk_x11_get_server_time (window);
804
805 /* Check if we could set the selection owner successfully */
806 if (gdk_selection_owner_set_for_display (display,
807 window,
808 manager->selection_atom,
809 timestamp,
810 TRUE))
811 {
812 XClientMessageEvent xev;
813 GdkAtom opcode_atom;
814 GdkAtom message_data_atom;
815
816 xev.type = ClientMessage;
817 xev.window = RootWindowOfScreen (xscreen);
818 xev.message_type = gdk_x11_get_xatom_by_name_for_display (display,
819 "MANAGER");
820
821 xev.format = 32;
822 xev.data.l[0] = timestamp;
823 xev.data.l[1] = gdk_x11_atom_to_xatom_for_display (display,
824 manager->selection_atom);
825 xev.data.l[2] = GDK_WINDOW_XID (window);
826 xev.data.l[3] = 0; /* manager specific data */
827 xev.data.l[4] = 0; /* manager specific data */
828
829 XSendEvent (GDK_DISPLAY_XDISPLAY (display),
830 RootWindowOfScreen (xscreen),
831 False, StructureNotifyMask, (XEvent *)&xev);
832
833 opcode_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_OPCODE", FALSE);
834 manager->opcode_atom = gdk_x11_atom_to_xatom_for_display (display,
835 opcode_atom);
836
837 message_data_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_MESSAGE_DATA",
838 FALSE);
839
840 manager->message_data_atom = gdk_x11_atom_to_xatom_for_display (display,
841 message_data_atom);
842
843 /* Add a window filter */
844 #if 0
845 /* This is for when we lose the selection of _NET_SYSTEM_TRAY_Sx */
846 g_signal_connect (invisible, "selection-clear-event",
847 G_CALLBACK (na_tray_manager_selection_clear_event),
848 manager);
849 #endif
850 /* This is for SYSTEM_TRAY_REQUEST_DOCK and SelectionClear */
851 gdk_window_add_filter (window,
852 na_tray_manager_window_filter, manager);
853 return TRUE;
854 }
855 else
856 {
857 gtk_widget_destroy (invisible);
858 g_object_unref (invisible);
859 manager->invisible = NULL;
860
861 manager->screen = NULL;
862
863 return FALSE;
864 }
865 }
866
867 #endif
868
869 gboolean
na_tray_manager_manage_screen(NaTrayManager * manager,GdkScreen * screen)870 na_tray_manager_manage_screen (NaTrayManager *manager,
871 GdkScreen *screen)
872 {
873 g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
874 g_return_val_if_fail (manager->screen == NULL, FALSE);
875
876 #ifdef GDK_WINDOWING_X11
877 return na_tray_manager_manage_screen_x11 (manager, screen);
878 #else
879 return FALSE;
880 #endif
881 }
882
883 #ifdef GDK_WINDOWING_X11
884
885 static gboolean
na_tray_manager_check_running_screen_x11(GdkScreen * screen)886 na_tray_manager_check_running_screen_x11 (GdkScreen *screen)
887 {
888 GdkDisplay *display;
889 Atom selection_atom;
890 char *selection_atom_name;
891
892 display = gdk_screen_get_display (screen);
893 selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d",
894 gdk_x11_screen_get_screen_number (screen));
895 selection_atom = gdk_x11_get_xatom_by_name_for_display (display,
896 selection_atom_name);
897 g_free (selection_atom_name);
898
899 if (XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
900 selection_atom) != None)
901 return TRUE;
902 else
903 return FALSE;
904 }
905
906 #endif
907
908 gboolean
na_tray_manager_check_running(GdkScreen * screen)909 na_tray_manager_check_running (GdkScreen *screen)
910 {
911 g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
912
913 #ifdef GDK_WINDOWING_X11
914 return na_tray_manager_check_running_screen_x11 (screen);
915 #else
916 return FALSE;
917 #endif
918 }
919
920 void
na_tray_manager_set_orientation(NaTrayManager * manager,GtkOrientation orientation)921 na_tray_manager_set_orientation (NaTrayManager *manager,
922 GtkOrientation orientation)
923 {
924 g_return_if_fail (NA_IS_TRAY_MANAGER (manager));
925
926 if (manager->orientation != orientation)
927 {
928 manager->orientation = orientation;
929
930 na_tray_manager_set_orientation_property (manager);
931
932 g_object_notify (G_OBJECT (manager), "orientation");
933 }
934 }
935
936 void
na_tray_manager_set_padding(NaTrayManager * manager,gint padding)937 na_tray_manager_set_padding (NaTrayManager *manager,
938 gint padding)
939 {
940 g_return_if_fail (NA_IS_TRAY_MANAGER (manager));
941
942 if (manager->padding != padding)
943 {
944 manager->padding = padding;
945
946 na_tray_manager_set_padding_property (manager);
947 }
948 }
949
950 void
na_tray_manager_set_icon_size(NaTrayManager * manager,gint icon_size)951 na_tray_manager_set_icon_size (NaTrayManager *manager,
952 gint icon_size)
953 {
954 g_return_if_fail (NA_IS_TRAY_MANAGER (manager));
955
956 if (manager->icon_size != icon_size)
957 {
958 manager->icon_size = icon_size;
959
960 na_tray_manager_set_icon_size_property (manager);
961 }
962 }
963
964 void
na_tray_manager_set_colors(NaTrayManager * manager,GdkRGBA * fg,GdkRGBA * error,GdkRGBA * warning,GdkRGBA * success)965 na_tray_manager_set_colors (NaTrayManager *manager,
966 GdkRGBA *fg,
967 GdkRGBA *error,
968 GdkRGBA *warning,
969 GdkRGBA *success)
970 {
971 g_return_if_fail (NA_IS_TRAY_MANAGER (manager));
972
973 if (!gdk_rgba_equal (&manager->fg, fg) ||
974 !gdk_rgba_equal (&manager->error, error) ||
975 !gdk_rgba_equal (&manager->warning, warning) ||
976 !gdk_rgba_equal (&manager->success, success))
977 {
978 manager->fg = *fg;
979 manager->error = *error;
980 manager->warning = *warning;
981 manager->success = *success;
982
983 na_tray_manager_set_colors_property (manager);
984 }
985 }
986
987 GtkOrientation
na_tray_manager_get_orientation(NaTrayManager * manager)988 na_tray_manager_get_orientation (NaTrayManager *manager)
989 {
990 g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), GTK_ORIENTATION_HORIZONTAL);
991
992 return manager->orientation;
993 }
994