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., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 *
20 * Used to be: eggtraymanager.c
21 */
22
23 #include <string.h>
24 #include <libintl.h>
25
26 #include "na-tray-manager.h"
27
28 #if defined (GDK_WINDOWING_X11)
29 #include <gdk/gdkx.h>
30 #include <X11/Xatom.h>
31 #elif defined (GDK_WINDOWING_WIN32)
32 #include <gdk/gdkwin32.h>
33 #endif
34 #include <gtk/gtk.h>
35
36 #include "na-marshal.h"
37
38 /* Signals */
39 enum
40 {
41 TRAY_ICON_ADDED,
42 TRAY_ICON_REMOVED,
43 MESSAGE_SENT,
44 MESSAGE_CANCELLED,
45 LOST_SELECTION,
46 LAST_SIGNAL
47 };
48
49 enum {
50 PROP_0,
51 PROP_ORIENTATION
52 };
53
54 typedef struct
55 {
56 long id, len;
57 long remaining_len;
58
59 long timeout;
60 char *str;
61 #ifdef GDK_WINDOWING_X11
62 Window window;
63 #endif
64 } PendingMessage;
65
66 static guint manager_signals[LAST_SIGNAL];
67
68 #define SYSTEM_TRAY_REQUEST_DOCK 0
69 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
70 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
71
72 #define SYSTEM_TRAY_ORIENTATION_HORZ 0
73 #define SYSTEM_TRAY_ORIENTATION_VERT 1
74
75 #ifdef GDK_WINDOWING_X11
76 static gboolean na_tray_manager_check_running_screen_x11 (GdkScreen *screen);
77 #endif
78
79 static void na_tray_manager_finalize (GObject *object);
80 static void na_tray_manager_set_property (GObject *object,
81 guint prop_id,
82 const GValue *value,
83 GParamSpec *pspec);
84 static void na_tray_manager_get_property (GObject *object,
85 guint prop_id,
86 GValue *value,
87 GParamSpec *pspec);
88
89 static void na_tray_manager_unmanage (NaTrayManager *manager);
90
G_DEFINE_TYPE(NaTrayManager,na_tray_manager,G_TYPE_OBJECT)91 G_DEFINE_TYPE (NaTrayManager, na_tray_manager, G_TYPE_OBJECT)
92
93 static void
94 na_tray_manager_init (NaTrayManager *manager)
95 {
96 manager->invisible = NULL;
97 manager->socket_table = g_hash_table_new (NULL, NULL);
98
99 manager->padding = 0;
100 manager->icon_size = 0;
101
102 manager->fg.red = 0;
103 manager->fg.green = 0;
104 manager->fg.blue = 0;
105
106 manager->error.red = 0xffff;
107 manager->error.green = 0;
108 manager->error.blue = 0;
109
110 manager->warning.red = 0xffff;
111 manager->warning.green = 0xffff;
112 manager->warning.blue = 0;
113
114 manager->success.red = 0;
115 manager->success.green = 0xffff;
116 manager->success.blue = 0;
117 }
118
119 static void
na_tray_manager_class_init(NaTrayManagerClass * klass)120 na_tray_manager_class_init (NaTrayManagerClass *klass)
121 {
122 GObjectClass *gobject_class;
123
124 gobject_class = (GObjectClass *)klass;
125
126 gobject_class->finalize = na_tray_manager_finalize;
127 gobject_class->set_property = na_tray_manager_set_property;
128 gobject_class->get_property = na_tray_manager_get_property;
129
130 g_object_class_install_property (gobject_class,
131 PROP_ORIENTATION,
132 g_param_spec_enum ("orientation",
133 "orientation",
134 "orientation",
135 GTK_TYPE_ORIENTATION,
136 GTK_ORIENTATION_HORIZONTAL,
137 G_PARAM_READWRITE |
138 G_PARAM_CONSTRUCT |
139 G_PARAM_STATIC_NAME |
140 G_PARAM_STATIC_NICK |
141 G_PARAM_STATIC_BLURB));
142
143 manager_signals[TRAY_ICON_ADDED] =
144 g_signal_new ("tray_icon_added",
145 G_OBJECT_CLASS_TYPE (klass),
146 G_SIGNAL_RUN_LAST,
147 G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_added),
148 NULL, NULL,
149 g_cclosure_marshal_VOID__OBJECT,
150 G_TYPE_NONE, 1,
151 GTK_TYPE_SOCKET);
152
153 manager_signals[TRAY_ICON_REMOVED] =
154 g_signal_new ("tray_icon_removed",
155 G_OBJECT_CLASS_TYPE (klass),
156 G_SIGNAL_RUN_LAST,
157 G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_removed),
158 NULL, NULL,
159 g_cclosure_marshal_VOID__OBJECT,
160 G_TYPE_NONE, 1,
161 GTK_TYPE_SOCKET);
162 manager_signals[MESSAGE_SENT] =
163 g_signal_new ("message_sent",
164 G_OBJECT_CLASS_TYPE (klass),
165 G_SIGNAL_RUN_LAST,
166 G_STRUCT_OFFSET (NaTrayManagerClass, message_sent),
167 NULL, NULL,
168 _na_marshal_VOID__OBJECT_STRING_LONG_LONG,
169 G_TYPE_NONE, 4,
170 GTK_TYPE_SOCKET,
171 G_TYPE_STRING,
172 G_TYPE_LONG,
173 G_TYPE_LONG);
174 manager_signals[MESSAGE_CANCELLED] =
175 g_signal_new ("message_cancelled",
176 G_OBJECT_CLASS_TYPE (klass),
177 G_SIGNAL_RUN_LAST,
178 G_STRUCT_OFFSET (NaTrayManagerClass, message_cancelled),
179 NULL, NULL,
180 _na_marshal_VOID__OBJECT_LONG,
181 G_TYPE_NONE, 2,
182 GTK_TYPE_SOCKET,
183 G_TYPE_LONG);
184 manager_signals[LOST_SELECTION] =
185 g_signal_new ("lost_selection",
186 G_OBJECT_CLASS_TYPE (klass),
187 G_SIGNAL_RUN_LAST,
188 G_STRUCT_OFFSET (NaTrayManagerClass, lost_selection),
189 NULL, NULL,
190 g_cclosure_marshal_VOID__VOID,
191 G_TYPE_NONE, 0);
192
193 #if defined (GDK_WINDOWING_X11)
194 /* Nothing */
195 #elif defined (GDK_WINDOWING_WIN32)
196 g_warning ("Port NaTrayManager to Win32");
197 #else
198 g_warning ("Port NaTrayManager to this GTK+ backend");
199 #endif
200 }
201
202 static void
na_tray_manager_finalize(GObject * object)203 na_tray_manager_finalize (GObject *object)
204 {
205 NaTrayManager *manager;
206
207 manager = NA_TRAY_MANAGER (object);
208
209 na_tray_manager_unmanage (manager);
210
211 g_list_free (manager->messages);
212 g_hash_table_destroy (manager->socket_table);
213
214 G_OBJECT_CLASS (na_tray_manager_parent_class)->finalize (object);
215 }
216
217 static void
na_tray_manager_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)218 na_tray_manager_set_property (GObject *object,
219 guint prop_id,
220 const GValue *value,
221 GParamSpec *pspec)
222 {
223 NaTrayManager *manager = NA_TRAY_MANAGER (object);
224
225 switch (prop_id)
226 {
227 case PROP_ORIENTATION:
228 na_tray_manager_set_orientation (manager, g_value_get_enum (value));
229 break;
230 default:
231 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
232 break;
233 }
234 }
235
236 static void
na_tray_manager_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)237 na_tray_manager_get_property (GObject *object,
238 guint prop_id,
239 GValue *value,
240 GParamSpec *pspec)
241 {
242 NaTrayManager *manager = NA_TRAY_MANAGER (object);
243
244 switch (prop_id)
245 {
246 case PROP_ORIENTATION:
247 g_value_set_enum (value, manager->orientation);
248 break;
249 default:
250 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
251 break;
252 }
253 }
254
255 NaTrayManager *
na_tray_manager_new(void)256 na_tray_manager_new (void)
257 {
258 NaTrayManager *manager;
259
260 manager = g_object_new (NA_TYPE_TRAY_MANAGER, NULL);
261
262 return manager;
263 }
264
265 #ifdef GDK_WINDOWING_X11
266
267 static gboolean
na_tray_manager_plug_removed(GtkSocket * socket,NaTrayManager * manager)268 na_tray_manager_plug_removed (GtkSocket *socket,
269 NaTrayManager *manager)
270 {
271 NaTrayChild *child = NA_TRAY_CHILD (socket);
272
273 g_hash_table_remove (manager->socket_table,
274 GINT_TO_POINTER (child->icon_window));
275 g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, child);
276
277 /* This destroys the socket. */
278 return FALSE;
279 }
280
281 static void
na_tray_manager_handle_dock_request(NaTrayManager * manager,XClientMessageEvent * xevent)282 na_tray_manager_handle_dock_request (NaTrayManager *manager,
283 XClientMessageEvent *xevent)
284 {
285 Window icon_window = xevent->data.l[2];
286 GtkWidget *child;
287
288 if (g_hash_table_lookup (manager->socket_table,
289 GINT_TO_POINTER (icon_window)))
290 {
291 /* We already got this notification earlier, ignore this one */
292 return;
293 }
294
295 child = na_tray_child_new (manager->screen, icon_window);
296 if (child == NULL) /* already gone or other error */
297 return;
298
299 g_signal_emit (manager, manager_signals[TRAY_ICON_ADDED], 0,
300 child);
301
302 /* If the child wasn't attached, then destroy it */
303
304 if (!GTK_IS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (child))))
305 {
306 gtk_widget_destroy (child);
307 return;
308 }
309
310 g_signal_connect (child, "plug_removed",
311 G_CALLBACK (na_tray_manager_plug_removed), manager);
312
313 gtk_socket_add_id (GTK_SOCKET (child), icon_window);
314
315 if (!gtk_socket_get_plug_window (GTK_SOCKET (child)))
316 {
317 /* Embedding failed, we won't get a plug-removed signal */
318 /* This signal destroys the socket */
319 g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, child);
320 return;
321 }
322
323 g_hash_table_insert (manager->socket_table,
324 GINT_TO_POINTER (icon_window), child);
325 gtk_widget_show (child);
326 }
327
328 static void
pending_message_free(PendingMessage * message)329 pending_message_free (PendingMessage *message)
330 {
331 g_free (message->str);
332 g_free (message);
333 }
334
335 static void
na_tray_manager_handle_message_data(NaTrayManager * manager,XClientMessageEvent * xevent)336 na_tray_manager_handle_message_data (NaTrayManager *manager,
337 XClientMessageEvent *xevent)
338 {
339 GList *p;
340 int len;
341
342 /* Try to see if we can find the pending message in the list */
343 for (p = manager->messages; p; p = p->next)
344 {
345 PendingMessage *msg = p->data;
346
347 if (xevent->window == msg->window)
348 {
349 /* Append the message */
350 len = MIN (msg->remaining_len, 20);
351
352 memcpy ((msg->str + msg->len - msg->remaining_len),
353 &xevent->data, len);
354 msg->remaining_len -= len;
355
356 if (msg->remaining_len == 0)
357 {
358 GtkSocket *socket;
359
360 socket = g_hash_table_lookup (manager->socket_table,
361 GINT_TO_POINTER (msg->window));
362
363 if (socket)
364 g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0,
365 socket, msg->str, msg->id, msg->timeout);
366
367 pending_message_free (msg);
368 manager->messages = g_list_remove_link (manager->messages, p);
369 g_list_free_1 (p);
370 }
371
372 break;
373 }
374 }
375 }
376
377 static void
na_tray_manager_handle_begin_message(NaTrayManager * manager,XClientMessageEvent * xevent)378 na_tray_manager_handle_begin_message (NaTrayManager *manager,
379 XClientMessageEvent *xevent)
380 {
381 GtkSocket *socket;
382 GList *p;
383 PendingMessage *msg;
384 long timeout;
385 long len;
386 long id;
387
388 socket = g_hash_table_lookup (manager->socket_table,
389 GINT_TO_POINTER (xevent->window));
390 /* we don't know about this tray icon, so ignore the message */
391 if (!socket)
392 return;
393
394 timeout = xevent->data.l[2];
395 len = xevent->data.l[3];
396 id = xevent->data.l[4];
397
398 /* Check if the same message is already in the queue and remove it if so */
399 for (p = manager->messages; p; p = p->next)
400 {
401 PendingMessage *pmsg = p->data;
402
403 if (xevent->window == pmsg->window &&
404 id == pmsg->id)
405 {
406 /* Hmm, we found it, now remove it */
407 pending_message_free (pmsg);
408 manager->messages = g_list_remove_link (manager->messages, p);
409 g_list_free_1 (p);
410 break;
411 }
412 }
413
414 if (len == 0)
415 {
416 g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0,
417 socket, "", id, timeout);
418 }
419 else
420 {
421 /* Now add the new message to the queue */
422 msg = g_new0 (PendingMessage, 1);
423 msg->window = xevent->window;
424 msg->timeout = timeout;
425 msg->len = len;
426 msg->id = id;
427 msg->remaining_len = msg->len;
428 msg->str = g_malloc (msg->len + 1);
429 msg->str[msg->len] = '\0';
430 manager->messages = g_list_prepend (manager->messages, msg);
431 }
432 }
433
434 static void
na_tray_manager_handle_cancel_message(NaTrayManager * manager,XClientMessageEvent * xevent)435 na_tray_manager_handle_cancel_message (NaTrayManager *manager,
436 XClientMessageEvent *xevent)
437 {
438 GList *p;
439 GtkSocket *socket;
440 long id;
441
442 id = xevent->data.l[2];
443
444 /* Check if the message is in the queue and remove it if so */
445 for (p = manager->messages; p; p = p->next)
446 {
447 PendingMessage *msg = p->data;
448
449 if (xevent->window == msg->window &&
450 id == msg->id)
451 {
452 pending_message_free (msg);
453 manager->messages = g_list_remove_link (manager->messages, p);
454 g_list_free_1 (p);
455 break;
456 }
457 }
458
459 socket = g_hash_table_lookup (manager->socket_table,
460 GINT_TO_POINTER (xevent->window));
461
462 if (socket)
463 {
464 g_signal_emit (manager, manager_signals[MESSAGE_CANCELLED], 0,
465 socket, xevent->data.l[2]);
466 }
467 }
468
469 static GdkFilterReturn
na_tray_manager_window_filter(GdkXEvent * xev,GdkEvent * event,gpointer data)470 na_tray_manager_window_filter (GdkXEvent *xev,
471 GdkEvent *event,
472 gpointer data)
473 {
474 XEvent *xevent = (GdkXEvent *)xev;
475 NaTrayManager *manager = data;
476
477 if (xevent->type == ClientMessage)
478 {
479 /* _NET_SYSTEM_TRAY_OPCODE: SYSTEM_TRAY_REQUEST_DOCK */
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 xvisual = GDK_VISUAL_XVISUAL (gdk_screen_get_rgba_visual (manager->screen));
635 else
636 {
637 /* We actually want the visual of the tray where the icons will
638 * be embedded. In almost all cases, this will be the same as the visual
639 * of the screen.
640 */
641 xvisual = GDK_VISUAL_XVISUAL (gdk_screen_get_system_visual (manager->screen));
642 }
643
644 data[0] = XVisualIDFromVisual (xvisual);
645
646 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
647 GDK_WINDOW_XID (window),
648 visual_atom,
649 XA_VISUALID, 32,
650 PropModeReplace,
651 (guchar *) &data, 1);
652 #endif
653 }
654
655 static void
na_tray_manager_set_padding_property(NaTrayManager * manager)656 na_tray_manager_set_padding_property (NaTrayManager *manager)
657 {
658 #ifdef GDK_WINDOWING_X11
659 GdkWindow *window;
660 GdkDisplay *display;
661 Atom atom;
662 gulong data[1];
663
664 g_return_if_fail (manager->invisible != NULL);
665 window = gtk_widget_get_window (manager->invisible);
666 g_return_if_fail (window != NULL);
667
668 display = gtk_widget_get_display (manager->invisible);
669 atom = gdk_x11_get_xatom_by_name_for_display (display,
670 "_NET_SYSTEM_TRAY_PADDING");
671
672 data[0] = manager->padding;
673
674 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
675 GDK_WINDOW_XID (window),
676 atom,
677 XA_CARDINAL, 32,
678 PropModeReplace,
679 (guchar *) &data, 1);
680 #endif
681 }
682
683 static void
na_tray_manager_set_icon_size_property(NaTrayManager * manager)684 na_tray_manager_set_icon_size_property (NaTrayManager *manager)
685 {
686 #ifdef GDK_WINDOWING_X11
687 GdkWindow *window;
688 GdkDisplay *display;
689 Atom atom;
690 gulong data[1];
691
692 g_return_if_fail (manager->invisible != NULL);
693 window = gtk_widget_get_window (manager->invisible);
694 g_return_if_fail (window != NULL);
695
696 display = gtk_widget_get_display (manager->invisible);
697 atom = gdk_x11_get_xatom_by_name_for_display (display,
698 "_NET_SYSTEM_TRAY_ICON_SIZE");
699
700 data[0] = manager->icon_size;
701
702 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
703 GDK_WINDOW_XID (window),
704 atom,
705 XA_CARDINAL, 32,
706 PropModeReplace,
707 (guchar *) &data, 1);
708 #endif
709 }
710
711 static void
na_tray_manager_set_colors_property(NaTrayManager * manager)712 na_tray_manager_set_colors_property (NaTrayManager *manager)
713 {
714 #ifdef GDK_WINDOWING_X11
715 GdkWindow *window;
716 GdkDisplay *display;
717 Atom atom;
718 gulong data[12];
719
720 g_return_if_fail (manager->invisible != NULL);
721 window = gtk_widget_get_window (manager->invisible);
722 g_return_if_fail (window != NULL);
723
724 display = gtk_widget_get_display (manager->invisible);
725 atom = gdk_x11_get_xatom_by_name_for_display (display,
726 "_NET_SYSTEM_TRAY_COLORS");
727
728 data[0] = manager->fg.red;
729 data[1] = manager->fg.green;
730 data[2] = manager->fg.blue;
731 data[3] = manager->error.red;
732 data[4] = manager->error.green;
733 data[5] = manager->error.blue;
734 data[6] = manager->warning.red;
735 data[7] = manager->warning.green;
736 data[8] = manager->warning.blue;
737 data[9] = manager->success.red;
738 data[10] = manager->success.green;
739 data[11] = manager->success.blue;
740
741 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
742 GDK_WINDOW_XID (window),
743 atom,
744 XA_CARDINAL, 32,
745 PropModeReplace,
746 (guchar *) &data, 12);
747 #endif
748 }
749
750 #ifdef GDK_WINDOWING_X11
751
752 static gboolean
na_tray_manager_manage_screen_x11(NaTrayManager * manager,GdkScreen * screen)753 na_tray_manager_manage_screen_x11 (NaTrayManager *manager,
754 GdkScreen *screen)
755 {
756 GdkDisplay *display;
757 Screen *xscreen;
758 GtkWidget *invisible;
759 GdkWindow *window;
760 char *selection_atom_name;
761 guint32 timestamp;
762
763 g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), FALSE);
764 g_return_val_if_fail (manager->screen == NULL, FALSE);
765
766 /* If there's already a manager running on the screen
767 * we can't create another one.
768 */
769 #if 0
770 if (na_tray_manager_check_running_screen_x11 (screen))
771 return FALSE;
772 #endif
773
774 manager->screen = screen;
775
776 display = gdk_screen_get_display (screen);
777 xscreen = GDK_SCREEN_XSCREEN (screen);
778
779 invisible = gtk_invisible_new_for_screen (screen);
780 gtk_widget_realize (invisible);
781
782 gtk_widget_add_events (invisible,
783 GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK);
784
785 selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d",
786 gdk_screen_get_number (screen));
787 manager->selection_atom = gdk_atom_intern (selection_atom_name, FALSE);
788 g_free (selection_atom_name);
789
790 manager->invisible = invisible;
791 g_object_ref (G_OBJECT (manager->invisible));
792
793 na_tray_manager_set_orientation_property (manager);
794 na_tray_manager_set_visual_property (manager);
795 na_tray_manager_set_padding_property (manager);
796 na_tray_manager_set_icon_size_property (manager);
797 na_tray_manager_set_colors_property (manager);
798
799 window = gtk_widget_get_window (invisible);
800
801 timestamp = gdk_x11_get_server_time (window);
802
803 /* Check if we could set the selection owner successfully */
804 if (gdk_selection_owner_set_for_display (display,
805 window,
806 manager->selection_atom,
807 timestamp,
808 TRUE))
809 {
810 XClientMessageEvent xev;
811 GdkAtom opcode_atom;
812 GdkAtom message_data_atom;
813
814 xev.type = ClientMessage;
815 xev.window = RootWindowOfScreen (xscreen);
816 xev.message_type = gdk_x11_get_xatom_by_name_for_display (display,
817 "MANAGER");
818
819 xev.format = 32;
820 xev.data.l[0] = timestamp;
821 xev.data.l[1] = gdk_x11_atom_to_xatom_for_display (display,
822 manager->selection_atom);
823 xev.data.l[2] = GDK_WINDOW_XID (window);
824 xev.data.l[3] = 0; /* manager specific data */
825 xev.data.l[4] = 0; /* manager specific data */
826
827 XSendEvent (GDK_DISPLAY_XDISPLAY (display),
828 RootWindowOfScreen (xscreen),
829 False, StructureNotifyMask, (XEvent *)&xev);
830
831 opcode_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_OPCODE", FALSE);
832 manager->opcode_atom = gdk_x11_atom_to_xatom_for_display (display,
833 opcode_atom);
834
835 message_data_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_MESSAGE_DATA",
836 FALSE);
837 manager->message_data_atom = gdk_x11_atom_to_xatom_for_display (display,
838 message_data_atom);
839
840 /* Add a window filter */
841 #if 0
842 /* This is for when we lose the selection of _NET_SYSTEM_TRAY_Sx */
843 g_signal_connect (invisible, "selection-clear-event",
844 G_CALLBACK (na_tray_manager_selection_clear_event),
845 manager);
846 #endif
847 gdk_window_add_filter (window,
848 na_tray_manager_window_filter, manager);
849 return TRUE;
850 }
851 else
852 {
853 gtk_widget_destroy (invisible);
854 g_object_unref (invisible);
855 manager->invisible = NULL;
856
857 manager->screen = NULL;
858
859 return FALSE;
860 }
861 }
862
863 #endif
864
865 gboolean
na_tray_manager_manage_screen(NaTrayManager * manager,GdkScreen * screen)866 na_tray_manager_manage_screen (NaTrayManager *manager,
867 GdkScreen *screen)
868 {
869 g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
870 g_return_val_if_fail (manager->screen == NULL, FALSE);
871
872 #ifdef GDK_WINDOWING_X11
873 return na_tray_manager_manage_screen_x11 (manager, screen);
874 #else
875 return FALSE;
876 #endif
877 }
878
879 #ifdef GDK_WINDOWING_X11
880
881 static gboolean
na_tray_manager_check_running_screen_x11(GdkScreen * screen)882 na_tray_manager_check_running_screen_x11 (GdkScreen *screen)
883 {
884 GdkDisplay *display;
885 Atom selection_atom;
886 char *selection_atom_name;
887
888 display = gdk_screen_get_display (screen);
889 selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d",
890 gdk_screen_get_number (screen));
891 selection_atom = gdk_x11_get_xatom_by_name_for_display (display,
892 selection_atom_name);
893 g_free (selection_atom_name);
894
895 if (XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
896 selection_atom) != None)
897 return TRUE;
898 else
899 return FALSE;
900 }
901
902 #endif
903
904 gboolean
na_tray_manager_check_running(GdkScreen * screen)905 na_tray_manager_check_running (GdkScreen *screen)
906 {
907 g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
908
909 #ifdef GDK_WINDOWING_X11
910 return na_tray_manager_check_running_screen_x11 (screen);
911 #else
912 return FALSE;
913 #endif
914 }
915
916 void
na_tray_manager_set_orientation(NaTrayManager * manager,GtkOrientation orientation)917 na_tray_manager_set_orientation (NaTrayManager *manager,
918 GtkOrientation orientation)
919 {
920 g_return_if_fail (NA_IS_TRAY_MANAGER (manager));
921
922 if (manager->orientation != orientation)
923 {
924 manager->orientation = orientation;
925
926 na_tray_manager_set_orientation_property (manager);
927
928 g_object_notify (G_OBJECT (manager), "orientation");
929 }
930 }
931
932 void
na_tray_manager_set_padding(NaTrayManager * manager,gint padding)933 na_tray_manager_set_padding (NaTrayManager *manager,
934 gint padding)
935 {
936 g_return_if_fail (NA_IS_TRAY_MANAGER (manager));
937
938 if (manager->padding != padding)
939 {
940 manager->padding = padding;
941
942 na_tray_manager_set_padding_property (manager);
943 }
944 }
945
946 void
na_tray_manager_set_icon_size(NaTrayManager * manager,gint icon_size)947 na_tray_manager_set_icon_size (NaTrayManager *manager,
948 gint icon_size)
949 {
950 g_return_if_fail (NA_IS_TRAY_MANAGER (manager));
951
952 if (manager->icon_size != icon_size)
953 {
954 manager->icon_size = icon_size;
955
956 na_tray_manager_set_icon_size_property (manager);
957 }
958 }
959
960 void
na_tray_manager_set_colors(NaTrayManager * manager,GdkColor * fg,GdkColor * error,GdkColor * warning,GdkColor * success)961 na_tray_manager_set_colors (NaTrayManager *manager,
962 GdkColor *fg,
963 GdkColor *error,
964 GdkColor *warning,
965 GdkColor *success)
966 {
967 g_return_if_fail (NA_IS_TRAY_MANAGER (manager));
968
969 if (!gdk_color_equal (&manager->fg, fg) ||
970 !gdk_color_equal (&manager->error, error) ||
971 !gdk_color_equal (&manager->warning, warning) ||
972 !gdk_color_equal (&manager->success, success))
973 {
974 manager->fg = *fg;
975 manager->error = *error;
976 manager->warning = *warning;
977 manager->success = *success;
978
979 na_tray_manager_set_colors_property (manager);
980 }
981 }
982
983 GtkOrientation
na_tray_manager_get_orientation(NaTrayManager * manager)984 na_tray_manager_get_orientation (NaTrayManager *manager)
985 {
986 g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), GTK_ORIENTATION_HORIZONTAL);
987
988 return manager->orientation;
989 }
990