]> Pileus Git - ~andy/gtk/blob - gtk/gtktrayicon-x11.c
4cb1aa79563e3db806c3eb889ef414aa4037ae6b
[~andy/gtk] / gtk / gtktrayicon-x11.c
1 /* gtktrayicon.c
2  * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * This is an implementation of the freedesktop.org "system tray" spec,
22  * http://www.freedesktop.org/wiki/Standards/systemtray-spec
23  */
24
25 #include "config.h"
26 #include <string.h>
27
28 #include "gtkintl.h"
29 #include "gtkprivate.h"
30 #include "gtktrayicon.h"
31
32 #include "gtkalias.h"
33
34 #include "x11/gdkx.h"
35 #include <X11/Xatom.h>
36
37 #define SYSTEM_TRAY_REQUEST_DOCK    0
38 #define SYSTEM_TRAY_BEGIN_MESSAGE   1
39 #define SYSTEM_TRAY_CANCEL_MESSAGE  2
40
41 #define SYSTEM_TRAY_ORIENTATION_HORZ 0
42 #define SYSTEM_TRAY_ORIENTATION_VERT 1
43
44 enum {
45   PROP_0,
46   PROP_ORIENTATION,
47   PROP_FG_COLOR,
48   PROP_ERROR_COLOR,
49   PROP_WARNING_COLOR,
50   PROP_SUCCESS_COLOR
51 };
52
53 struct _GtkTrayIconPrivate
54 {
55   guint stamp;
56   
57   Atom selection_atom;
58   Atom manager_atom;
59   Atom system_tray_opcode_atom;
60   Atom orientation_atom;
61   Atom visual_atom;
62   Atom colors_atom;
63   Window manager_window;
64   GdkVisual *manager_visual;
65   gboolean manager_visual_rgba;
66
67   GtkOrientation orientation;
68   GdkColor fg_color;
69   GdkColor error_color;
70   GdkColor warning_color;
71   GdkColor success_color;
72 };
73
74 static void gtk_tray_icon_constructed   (GObject     *object);
75 static void gtk_tray_icon_dispose       (GObject     *object);
76
77 static void gtk_tray_icon_get_property  (GObject     *object,
78                                          guint        prop_id,
79                                          GValue      *value,
80                                          GParamSpec  *pspec);
81
82 static void     gtk_tray_icon_realize   (GtkWidget   *widget);
83 static void     gtk_tray_icon_style_set (GtkWidget   *widget,
84                                          GtkStyle    *previous_style);
85 static gboolean gtk_tray_icon_delete    (GtkWidget   *widget,
86                                          GdkEventAny *event);
87 static gboolean gtk_tray_icon_expose    (GtkWidget      *widget,
88                                          GdkEventExpose *event);
89
90 static void gtk_tray_icon_clear_manager_window     (GtkTrayIcon *icon);
91 static void gtk_tray_icon_update_manager_window    (GtkTrayIcon *icon);
92 static void gtk_tray_icon_manager_window_destroyed (GtkTrayIcon *icon);
93
94 static GdkFilterReturn gtk_tray_icon_manager_filter (GdkXEvent *xevent,
95                                                      GdkEvent  *event,
96                                                      gpointer   user_data);
97
98
99 G_DEFINE_TYPE (GtkTrayIcon, gtk_tray_icon, GTK_TYPE_PLUG)
100
101 static void
102 gtk_tray_icon_class_init (GtkTrayIconClass *class)
103 {
104   GObjectClass *gobject_class = (GObjectClass *)class;
105   GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
106
107   gobject_class->get_property = gtk_tray_icon_get_property;
108   gobject_class->constructed = gtk_tray_icon_constructed;
109   gobject_class->dispose = gtk_tray_icon_dispose;
110
111   widget_class->realize = gtk_tray_icon_realize;
112   widget_class->style_set = gtk_tray_icon_style_set;
113   widget_class->delete_event = gtk_tray_icon_delete;
114   widget_class->expose_event = gtk_tray_icon_expose;
115
116   g_object_class_install_property (gobject_class,
117                                    PROP_ORIENTATION,
118                                    g_param_spec_enum ("orientation",
119                                                       P_("Orientation"),
120                                                       P_("The orientation of the tray"),
121                                                       GTK_TYPE_ORIENTATION,
122                                                       GTK_ORIENTATION_HORIZONTAL,
123                                                       GTK_PARAM_READABLE));
124
125   g_object_class_install_property (gobject_class,
126                                    PROP_FG_COLOR,
127                                    g_param_spec_boxed ("fg-color",
128                                                        P_("Foreground color"),
129                                                        P_("Foreground color for symbolic icons"),
130                                                        GDK_TYPE_COLOR,
131                                                        GTK_PARAM_READABLE));
132
133   g_object_class_install_property (gobject_class,
134                                    PROP_ERROR_COLOR,
135                                    g_param_spec_boxed ("error-color",
136                                                        P_("Error color"),
137                                                        P_("Error color for symbolic icons"),
138                                                        GDK_TYPE_COLOR,
139                                                        GTK_PARAM_READABLE));
140
141   g_object_class_install_property (gobject_class,
142                                    PROP_WARNING_COLOR,
143                                    g_param_spec_boxed ("warning-color",
144                                                        P_("Warning color"),
145                                                        P_("Warning color for symbolic icons"),
146                                                        GDK_TYPE_COLOR,
147                                                        GTK_PARAM_READABLE));
148
149   g_object_class_install_property (gobject_class,
150                                    PROP_SUCCESS_COLOR,
151                                    g_param_spec_boxed ("success-color",
152                                                        P_("Success color"),
153                                                        P_("Success color for symbolic icons"),
154                                                        GDK_TYPE_COLOR,
155                                                        GTK_PARAM_READABLE));
156
157   g_type_class_add_private (class, sizeof (GtkTrayIconPrivate));
158 }
159
160 static void
161 gtk_tray_icon_init (GtkTrayIcon *icon)
162 {
163   icon->priv = G_TYPE_INSTANCE_GET_PRIVATE (icon, GTK_TYPE_TRAY_ICON,
164                                             GtkTrayIconPrivate);
165
166   icon->priv->stamp = 1;
167   icon->priv->orientation = GTK_ORIENTATION_HORIZONTAL;
168   icon->priv->fg_color.red        = 0x0000;
169   icon->priv->fg_color.green      = 0x0000;
170   icon->priv->fg_color.blue       = 0x0000;
171   icon->priv->error_color.red     = 0xcc00;
172   icon->priv->error_color.green   = 0x0000;
173   icon->priv->error_color.blue    = 0x0000;
174   icon->priv->warning_color.red   = 0xf500;
175   icon->priv->warning_color.green = 0x7900;
176   icon->priv->warning_color.blue  = 0x3e00;
177   icon->priv->success_color.red   = 0x4e00;
178   icon->priv->success_color.green = 0x9a00;
179   icon->priv->success_color.blue  = 0x0600;
180
181   gtk_widget_set_app_paintable (GTK_WIDGET (icon), TRUE);
182   gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
183 }
184
185 static void
186 gtk_tray_icon_constructed (GObject *object)
187 {
188   /* Do setup that depends on the screen; screen has been set at this point */
189
190   GtkTrayIcon *icon = GTK_TRAY_ICON (object);
191   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (object));
192   GdkWindow *root_window = gdk_screen_get_root_window (screen);
193   GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (object));
194   Display *xdisplay = gdk_x11_display_get_xdisplay (display);
195   char buffer[256];
196   
197   g_snprintf (buffer, sizeof (buffer),
198               "_NET_SYSTEM_TRAY_S%d",
199               gdk_screen_get_number (screen));
200
201   icon->priv->selection_atom = XInternAtom (xdisplay, buffer, False);
202   
203   icon->priv->manager_atom = XInternAtom (xdisplay, "MANAGER", False);
204   
205   icon->priv->system_tray_opcode_atom = XInternAtom (xdisplay,
206                                                      "_NET_SYSTEM_TRAY_OPCODE",
207                                                      False);
208
209   icon->priv->orientation_atom = XInternAtom (xdisplay,
210                                               "_NET_SYSTEM_TRAY_ORIENTATION",
211                                               False);
212
213   icon->priv->visual_atom = XInternAtom (xdisplay,
214                                          "_NET_SYSTEM_TRAY_VISUAL",
215                                          False);
216
217   icon->priv->colors_atom = XInternAtom (xdisplay,
218                                          "_NET_SYSTEM_TRAY_COLORS",
219                                          False);
220
221   /* Add a root window filter so that we get changes on MANAGER */
222   gdk_window_add_filter (root_window,
223                          gtk_tray_icon_manager_filter, icon);
224
225   gtk_tray_icon_update_manager_window (icon);
226 }
227
228 static void
229 gtk_tray_icon_clear_manager_window (GtkTrayIcon *icon)
230 {
231   GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (icon));
232
233   if (icon->priv->manager_window != None)
234     {
235       GdkWindow *gdkwin;
236
237       gdkwin = gdk_window_lookup_for_display (display,
238                                               icon->priv->manager_window);
239
240       gdk_window_remove_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
241
242       icon->priv->manager_window = None;
243       icon->priv->manager_visual = NULL;
244     }
245 }
246
247 static void
248 gtk_tray_icon_dispose (GObject *object)
249 {
250   GtkTrayIcon *icon = GTK_TRAY_ICON (object);
251   GtkWidget *widget = GTK_WIDGET (object);
252   GdkWindow *root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
253
254   gtk_tray_icon_clear_manager_window (icon);
255
256   gdk_window_remove_filter (root_window, gtk_tray_icon_manager_filter, icon);
257 }
258
259 static void
260 gtk_tray_icon_get_property (GObject    *object,
261                             guint       prop_id,
262                             GValue     *value,
263                             GParamSpec *pspec)
264 {
265   GtkTrayIcon *icon = GTK_TRAY_ICON (object);
266
267   switch (prop_id)
268     {
269     case PROP_ORIENTATION:
270       g_value_set_enum (value, icon->priv->orientation);
271       break;
272     case PROP_FG_COLOR:
273       g_value_set_boxed (value, &icon->priv->fg_color);
274       break;
275     case PROP_ERROR_COLOR:
276       g_value_set_boxed (value, &icon->priv->error_color);
277       break;
278     case PROP_WARNING_COLOR:
279       g_value_set_boxed (value, &icon->priv->warning_color);
280       break;
281     case PROP_SUCCESS_COLOR:
282       g_value_set_boxed (value, &icon->priv->success_color);
283       break;
284     default:
285       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
286       break;
287     }
288 }
289
290 static gboolean
291 gtk_tray_icon_expose (GtkWidget      *widget, 
292                       GdkEventExpose *event)
293 {
294   GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
295   GtkWidget *focus_child;
296   gint border_width, x, y, width, height;
297   gboolean retval = FALSE;
298
299   if (icon->priv->manager_visual_rgba)
300     {
301       /* Clear to transparent */
302       cairo_t *cr = gdk_cairo_create (widget->window);
303       cairo_set_source_rgba (cr, 0, 0, 0, 0);
304       cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
305       gdk_cairo_region (cr, event->region);
306       cairo_fill (cr);
307       cairo_destroy (cr);
308     }
309   else
310     {
311       /* Clear to parent-relative pixmap */
312       gdk_window_clear_area (widget->window, event->area.x, event->area.y,
313                              event->area.width, event->area.height);
314     }
315
316   if (GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->expose_event)
317     retval = GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->expose_event (widget, event);
318
319   focus_child = GTK_CONTAINER (widget)->focus_child;
320   if (focus_child && gtk_widget_has_focus (focus_child))
321     {
322       border_width = GTK_CONTAINER (widget)->border_width;
323
324       x = widget->allocation.x + border_width;
325       y = widget->allocation.y + border_width;
326
327       width  = widget->allocation.width  - 2 * border_width;
328       height = widget->allocation.height - 2 * border_width;
329
330       gtk_paint_focus (widget->style, widget->window,
331                        gtk_widget_get_state (widget),
332                        &event->area, widget, "tray_icon",
333                        x, y, width, height);
334     }
335
336   return retval;
337 }
338
339 static void
340 gtk_tray_icon_get_orientation_property (GtkTrayIcon *icon)
341 {
342   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (icon));
343   GdkDisplay *display = gdk_screen_get_display (screen);
344   Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
345
346   Atom type;
347   int format;
348   union {
349         gulong *prop;
350         guchar *prop_ch;
351   } prop = { NULL };
352   gulong nitems;
353   gulong bytes_after;
354   int error, result;
355
356   g_assert (icon->priv->manager_window != None);
357   
358   gdk_error_trap_push ();
359   type = None;
360   result = XGetWindowProperty (xdisplay,
361                                icon->priv->manager_window,
362                                icon->priv->orientation_atom,
363                                0, G_MAXLONG, FALSE,
364                                XA_CARDINAL,
365                                &type, &format, &nitems,
366                                &bytes_after, &(prop.prop_ch));
367   error = gdk_error_trap_pop ();
368
369   if (error || result != Success)
370     return;
371
372   if (type == XA_CARDINAL && nitems == 1 && format == 32)
373     {
374       GtkOrientation orientation;
375
376       orientation = (prop.prop [0] == SYSTEM_TRAY_ORIENTATION_HORZ) ?
377                                         GTK_ORIENTATION_HORIZONTAL :
378                                         GTK_ORIENTATION_VERTICAL;
379
380       if (icon->priv->orientation != orientation)
381         {
382           icon->priv->orientation = orientation;
383
384           g_object_notify (G_OBJECT (icon), "orientation");
385         }
386     }
387
388   if (type != None)
389     XFree (prop.prop);
390 }
391
392 static void
393 gtk_tray_icon_get_visual_property (GtkTrayIcon *icon)
394 {
395   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (icon));
396   GdkDisplay *display = gdk_screen_get_display (screen);
397   Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
398
399   Atom type;
400   int format;
401   union {
402         gulong *prop;
403         guchar *prop_ch;
404   } prop = { NULL };
405   gulong nitems;
406   gulong bytes_after;
407   int error, result;
408   GdkVisual *visual;
409
410   g_assert (icon->priv->manager_window != None);
411
412   gdk_error_trap_push ();
413   type = None;
414   result = XGetWindowProperty (xdisplay,
415                                icon->priv->manager_window,
416                                icon->priv->visual_atom,
417                                0, G_MAXLONG, FALSE,
418                                XA_VISUALID,
419                                &type, &format, &nitems,
420                                &bytes_after, &(prop.prop_ch));
421   error = gdk_error_trap_pop ();
422
423   visual = NULL;
424
425   if (!error && result == Success &&
426       type == XA_VISUALID && nitems == 1 && format == 32)
427     {
428       VisualID visual_id = prop.prop[0];
429       visual = gdk_x11_screen_lookup_visual (screen, visual_id);
430     }
431
432   icon->priv->manager_visual = visual;
433   icon->priv->manager_visual_rgba = visual != NULL &&
434     (visual->red_prec + visual->blue_prec + visual->green_prec < visual->depth);
435
436   /* For the background-relative hack we use when we aren't using a real RGBA
437    * visual, we can't be double-buffered */
438   gtk_widget_set_double_buffered (GTK_WIDGET (icon), icon->priv->manager_visual_rgba);
439
440   if (type != None)
441     XFree (prop.prop);
442 }
443
444 static void
445 gtk_tray_icon_get_colors_property (GtkTrayIcon *icon)
446 {
447   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (icon));
448   GdkDisplay *display = gdk_screen_get_display (screen);
449   Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
450
451   Atom type;
452   int format;
453   union {
454         gulong *prop;
455         guchar *prop_ch;
456   } prop = { NULL };
457   gulong nitems;
458   gulong bytes_after;
459   int error, result;
460
461   g_assert (icon->priv->manager_window != None);
462
463   gdk_error_trap_push ();
464   type = None;
465   result = XGetWindowProperty (xdisplay,
466                                icon->priv->manager_window,
467                                icon->priv->colors_atom,
468                                0, G_MAXLONG, FALSE,
469                                XA_CARDINAL,
470                                &type, &format, &nitems,
471                                &bytes_after, &(prop.prop_ch));
472   error = gdk_error_trap_pop ();
473
474   if (error || result != Success)
475     return;
476
477   if (type == XA_CARDINAL && nitems == 12 && format == 32)
478     {
479       GdkColor color;
480
481       g_object_freeze_notify (G_OBJECT (icon));
482
483       color.red = prop.prop[0];
484       color.green = prop.prop[1];
485       color.blue = prop.prop[2];
486
487       if (!gdk_color_equal (&icon->priv->fg_color, &color))
488         {
489           icon->priv->fg_color = color;
490
491           g_object_notify (G_OBJECT (icon), "fg-color");
492         }
493
494       color.red = prop.prop[3];
495       color.green = prop.prop[4];
496       color.blue = prop.prop[5];
497
498       if (!gdk_color_equal (&icon->priv->error_color, &color))
499         {
500           icon->priv->error_color = color;
501
502           g_object_notify (G_OBJECT (icon), "error-color");
503         }
504
505       g_object_thaw_notify (G_OBJECT (icon));
506
507       color.red = prop.prop[6];
508       color.green = prop.prop[7];
509       color.blue = prop.prop[8];
510
511       if (!gdk_color_equal (&icon->priv->warning_color, &color))
512         {
513           icon->priv->warning_color = color;
514
515           g_object_notify (G_OBJECT (icon), "warning-color");
516         }
517
518       g_object_thaw_notify (G_OBJECT (icon));
519
520       color.red = prop.prop[9];
521       color.green = prop.prop[10];
522       color.blue = prop.prop[11];
523
524       if (!gdk_color_equal (&icon->priv->success_color, &color))
525         {
526           icon->priv->success_color = color;
527
528           g_object_notify (G_OBJECT (icon), "success-color");
529         }
530
531       g_object_thaw_notify (G_OBJECT (icon));
532     }
533
534   if (type != None)
535     XFree (prop.prop);
536 }
537
538 static GdkFilterReturn
539 gtk_tray_icon_manager_filter (GdkXEvent *xevent,
540                               GdkEvent  *event,
541                               gpointer   user_data)
542 {
543   GtkTrayIcon *icon = user_data;
544   XEvent *xev = (XEvent *)xevent;
545
546   if (xev->xany.type == ClientMessage &&
547       xev->xclient.message_type == icon->priv->manager_atom &&
548       xev->xclient.data.l[1] == icon->priv->selection_atom)
549     {
550       GTK_NOTE (PLUGSOCKET,
551                 g_print ("GtkStatusIcon %p: tray manager appeared\n", icon));
552
553       gtk_tray_icon_update_manager_window (icon);
554     }
555   else if (xev->xany.window == icon->priv->manager_window)
556     {
557       if (xev->xany.type == PropertyNotify &&
558           xev->xproperty.atom == icon->priv->orientation_atom)
559         {
560           GTK_NOTE (PLUGSOCKET,
561                     g_print ("GtkStatusIcon %p: got PropertyNotify on manager window for orientation atom\n", icon));
562
563           gtk_tray_icon_get_orientation_property (icon);
564         }
565       else if (xev->xany.type == PropertyNotify &&
566                xev->xproperty.atom == icon->priv->colors_atom)
567         {
568           GTK_NOTE (PLUGSOCKET,
569                     g_print ("GtkStatusIcon %p: got PropertyNotify on manager window for colors atom\n", icon));
570
571           gtk_tray_icon_get_colors_property (icon);
572         }
573       else if (xev->xany.type == DestroyNotify)
574         {
575           GTK_NOTE (PLUGSOCKET,
576                     g_print ("GtkStatusIcon %p: got DestroyNotify for manager window\n", icon));
577
578           gtk_tray_icon_manager_window_destroyed (icon);
579         }
580       else
581         GTK_NOTE (PLUGSOCKET,
582                   g_print ("GtkStatusIcon %p: got other message on manager window\n", icon));
583     }
584   
585   return GDK_FILTER_CONTINUE;
586 }
587
588 static void
589 gtk_tray_icon_send_manager_message (GtkTrayIcon *icon,
590                                     long         message,
591                                     Window       window,
592                                     long         data1,
593                                     long         data2,
594                                     long         data3)
595 {
596   XClientMessageEvent ev;
597   Display *display;
598   
599   memset (&ev, 0, sizeof (ev));
600   ev.type = ClientMessage;
601   ev.window = window;
602   ev.message_type = icon->priv->system_tray_opcode_atom;
603   ev.format = 32;
604   ev.data.l[0] = gdk_x11_get_server_time (GTK_WIDGET (icon)->window);
605   ev.data.l[1] = message;
606   ev.data.l[2] = data1;
607   ev.data.l[3] = data2;
608   ev.data.l[4] = data3;
609
610   display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
611   
612   gdk_error_trap_push ();
613   XSendEvent (display,
614               icon->priv->manager_window, False, NoEventMask, (XEvent *)&ev);
615   gdk_display_sync (gtk_widget_get_display (GTK_WIDGET (icon)));
616   gdk_error_trap_pop ();
617 }
618
619 static void
620 gtk_tray_icon_send_dock_request (GtkTrayIcon *icon)
621 {
622   GTK_NOTE (PLUGSOCKET,
623             g_print ("GtkStatusIcon %p: sending dock request to manager window %lx\n",
624                      icon, (gulong) icon->priv->manager_window));
625
626   gtk_tray_icon_send_manager_message (icon,
627                                       SYSTEM_TRAY_REQUEST_DOCK,
628                                       icon->priv->manager_window,
629                                       gtk_plug_get_id (GTK_PLUG (icon)),
630                                       0, 0);
631 }
632
633 static void
634 gtk_tray_icon_update_manager_window (GtkTrayIcon *icon)
635 {
636   GtkWidget *widget = GTK_WIDGET (icon);
637   GdkScreen *screen = gtk_widget_get_screen (widget);
638   GdkDisplay *display = gdk_screen_get_display (screen);
639   Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
640
641   GTK_NOTE (PLUGSOCKET,
642             g_print ("GtkStatusIcon %p: updating tray icon manager window, current manager window: %lx\n",
643                      icon, (gulong) icon->priv->manager_window));
644
645   if (icon->priv->manager_window != None)
646     return;
647
648   GTK_NOTE (PLUGSOCKET,
649             g_print ("GtkStatusIcon %p: trying to find manager window\n", icon));
650
651   XGrabServer (xdisplay);
652   
653   icon->priv->manager_window = XGetSelectionOwner (xdisplay,
654                                                    icon->priv->selection_atom);
655
656   if (icon->priv->manager_window != None)
657     XSelectInput (xdisplay,
658                   icon->priv->manager_window, StructureNotifyMask|PropertyChangeMask);
659
660   XUngrabServer (xdisplay);
661   XFlush (xdisplay);
662   
663   if (icon->priv->manager_window != None)
664     {
665       GdkWindow *gdkwin;
666
667       GTK_NOTE (PLUGSOCKET,
668                 g_print ("GtkStatusIcon %p: is being managed by window %lx\n",
669                                 icon, (gulong) icon->priv->manager_window));
670
671       gdkwin = gdk_window_lookup_for_display (display,
672                                               icon->priv->manager_window);
673       
674       gdk_window_add_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
675
676       gtk_tray_icon_get_orientation_property (icon);
677       gtk_tray_icon_get_visual_property (icon);
678       gtk_tray_icon_get_colors_property (icon);
679
680       if (gtk_widget_get_realized (GTK_WIDGET (icon)))
681         {
682           if ((icon->priv->manager_visual == NULL &&
683                gtk_widget_get_visual (widget) == gdk_screen_get_system_visual (screen)) ||
684               (icon->priv->manager_visual == gtk_widget_get_visual (widget)))
685             {
686               /* Already have the right visual, can just dock
687                */
688               gtk_tray_icon_send_dock_request (icon);
689             }
690           else
691             {
692               /* Need to re-realize the widget to get the right visual
693                */
694               gtk_widget_hide (widget);
695               gtk_widget_unrealize (widget);
696               gtk_widget_show (widget);
697             }
698         }
699     }
700   else
701     GTK_NOTE (PLUGSOCKET,
702               g_print ("GtkStatusIcon %p: no tray manager found\n", icon));
703 }
704
705 static void
706 gtk_tray_icon_manager_window_destroyed (GtkTrayIcon *icon)
707 {
708   g_return_if_fail (icon->priv->manager_window != None);
709
710   GTK_NOTE (PLUGSOCKET,
711             g_print ("GtkStatusIcon %p: tray manager window destroyed\n", icon));
712
713   gtk_tray_icon_clear_manager_window (icon);
714 }
715
716 static gboolean
717 gtk_tray_icon_delete (GtkWidget   *widget,
718                       GdkEventAny *event)
719 {
720 #ifdef G_ENABLE_DEBUG
721   GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
722 #endif
723
724   GTK_NOTE (PLUGSOCKET,
725             g_print ("GtkStatusIcon %p: delete notify, tray manager window %lx\n",
726                      icon, (gulong) icon->priv->manager_window));
727
728   /* A bug in X server versions up to x.org 1.5.0 means that:
729    * XFixesChangeSaveSet(...., SaveSetRoot, SaveSetUnmap) doesn't work properly
730    * and we'll left mapped in a separate toplevel window if the tray is destroyed.
731    * For simplicity just get rid of our X window and start over.
732    */
733   gtk_widget_hide (widget);
734   gtk_widget_unrealize (widget);
735   gtk_widget_show (widget);
736
737   /* Handled it, don't destroy the tray icon */
738   return TRUE;
739 }
740
741 static void
742 gtk_tray_icon_set_colormap (GtkTrayIcon *icon)
743 {
744   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (icon));
745   GdkColormap *colormap;
746   GdkVisual *visual = icon->priv->manager_visual;
747   gboolean new_colormap = FALSE;
748
749   /* To avoid uncertainty about colormaps, _NET_SYSTEM_TRAY_VISUAL is supposed
750    * to be either the screen default visual or a TrueColor visual; ignore it
751    * if it is something else
752    */
753   if (visual && visual->type != GDK_VISUAL_TRUE_COLOR)
754     visual = NULL;
755
756   if (visual == NULL || visual == gdk_screen_get_system_visual (screen))
757     colormap = gdk_screen_get_system_colormap (screen);
758   else if (visual == gdk_screen_get_rgb_visual (screen))
759     colormap = gdk_screen_get_rgb_colormap (screen);
760   else if (visual == gdk_screen_get_rgba_visual (screen))
761     colormap = gdk_screen_get_rgba_colormap (screen);
762   else
763     {
764       colormap = gdk_colormap_new (visual, FALSE);
765       new_colormap = TRUE;
766     }
767
768   gtk_widget_set_colormap (GTK_WIDGET (icon), colormap);
769
770   if (new_colormap)
771     g_object_unref (colormap);
772 }
773
774 static void
775 gtk_tray_icon_realize (GtkWidget *widget)
776 {
777   GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
778
779   /* Set our colormap before realizing */
780   gtk_tray_icon_set_colormap (icon);
781
782   GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->realize (widget);
783   if (icon->priv->manager_visual_rgba)
784     {
785       /* Set a transparent background */
786       GdkColor transparent = { 0, 0, 0, 0 }; /* Only pixel=0 matters */
787       gdk_window_set_background (widget->window, &transparent);
788     }
789   else
790     {
791       /* Set a parent-relative background pixmap */
792       gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
793     }
794
795   GTK_NOTE (PLUGSOCKET,
796             g_print ("GtkStatusIcon %p: realized, window: %lx, socket window: %lx\n",
797                      widget,
798                      (gulong) GDK_WINDOW_XWINDOW (widget->window),
799                      GTK_PLUG (icon)->socket_window ?
800                              (gulong) GDK_WINDOW_XWINDOW (GTK_PLUG (icon)->socket_window) : 0UL));
801
802   if (icon->priv->manager_window != None)
803     gtk_tray_icon_send_dock_request (icon);
804 }
805
806 static void
807 gtk_tray_icon_style_set (GtkWidget   *widget,
808                          GtkStyle    *previous_style)
809 {
810   /* The default handler resets the background according to the style. We either
811    * use a transparent background or a parent-relative background and ignore the
812    * style background. So, just don't chain up.
813    */
814 }
815
816 guint
817 _gtk_tray_icon_send_message (GtkTrayIcon *icon,
818                              gint         timeout,
819                              const gchar *message,
820                              gint         len)
821 {
822   guint stamp;
823   Display *xdisplay;
824  
825   g_return_val_if_fail (GTK_IS_TRAY_ICON (icon), 0);
826   g_return_val_if_fail (timeout >= 0, 0);
827   g_return_val_if_fail (message != NULL, 0);
828
829   if (icon->priv->manager_window == None)
830     return 0;
831
832   if (len < 0)
833     len = strlen (message);
834
835   stamp = icon->priv->stamp++;
836   
837   /* Get ready to send the message */
838   gtk_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE,
839                                       (Window)gtk_plug_get_id (GTK_PLUG (icon)),
840                                       timeout, len, stamp);
841
842   /* Now to send the actual message */
843   xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
844   gdk_error_trap_push ();
845   while (len > 0)
846     {
847       XClientMessageEvent ev;
848
849       memset (&ev, 0, sizeof (ev));
850       ev.type = ClientMessage;
851       ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon));
852       ev.format = 8;
853       ev.message_type = XInternAtom (xdisplay,
854                                      "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
855       if (len > 20)
856         {
857           memcpy (&ev.data, message, 20);
858           len -= 20;
859           message += 20;
860         }
861       else
862         {
863           memcpy (&ev.data, message, len);
864           len = 0;
865         }
866
867       XSendEvent (xdisplay,
868                   icon->priv->manager_window, False,
869                   StructureNotifyMask, (XEvent *)&ev);
870     }
871   gdk_display_sync (gtk_widget_get_display (GTK_WIDGET (icon)));
872   gdk_error_trap_pop ();
873
874   return stamp;
875 }
876
877 void
878 _gtk_tray_icon_cancel_message (GtkTrayIcon *icon,
879                                guint        id)
880 {
881   g_return_if_fail (GTK_IS_TRAY_ICON (icon));
882   g_return_if_fail (id > 0);
883   
884   gtk_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE,
885                                       (Window)gtk_plug_get_id (GTK_PLUG (icon)),
886                                       id, 0, 0);
887 }
888
889 GtkTrayIcon *
890 _gtk_tray_icon_new_for_screen (GdkScreen  *screen, 
891                                const gchar *name)
892 {
893   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
894
895   return g_object_new (GTK_TYPE_TRAY_ICON, 
896                        "screen", screen, 
897                        "title", name, 
898                        NULL);
899 }
900
901 GtkTrayIcon*
902 _gtk_tray_icon_new (const gchar *name)
903 {
904   return g_object_new (GTK_TYPE_TRAY_ICON, 
905                        "title", name, 
906                        NULL);
907 }
908
909 GtkOrientation
910 _gtk_tray_icon_get_orientation (GtkTrayIcon *icon)
911 {
912   g_return_val_if_fail (GTK_IS_TRAY_ICON (icon), GTK_ORIENTATION_HORIZONTAL);
913
914   return icon->priv->orientation;
915 }
916
917
918 #define __GTK_TRAY_ICON_X11_C__
919 #include "gtkaliasdef.c"
920