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