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