]> Pileus Git - ~andy/gtk/blob - gtk/gtktrayicon-x11.c
stylecontext: Do invalidation on first resize container
[~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, see <http://www.gnu.org/licenses/>.
16  */
17
18 /*
19  * This is an implementation of the freedesktop.org "system tray" spec,
20  * http://www.freedesktop.org/wiki/Standards/systemtray-spec
21  */
22
23 #include "config.h"
24
25 #include <math.h>
26 #include <string.h>
27
28 #include "x11/gdkx.h"
29 #include <X11/Xatom.h>
30 #include <cairo-xlib.h>
31
32 #include "gtkintl.h"
33 #include "gtkprivate.h"
34 #include "gtktrayicon.h"
35 #include "gtktestutils.h"
36 #include "gtkdebug.h"
37 #include "gtktypebuiltins.h"
38
39 #define SYSTEM_TRAY_REQUEST_DOCK    0
40 #define SYSTEM_TRAY_BEGIN_MESSAGE   1
41 #define SYSTEM_TRAY_CANCEL_MESSAGE  2
42
43 #define SYSTEM_TRAY_ORIENTATION_HORZ 0
44 #define SYSTEM_TRAY_ORIENTATION_VERT 1
45
46 enum {
47   PROP_0,
48   PROP_ORIENTATION,
49   PROP_FG_COLOR,
50   PROP_ERROR_COLOR,
51   PROP_WARNING_COLOR,
52   PROP_SUCCESS_COLOR,
53   PROP_PADDING,
54   PROP_ICON_SIZE
55 };
56
57 struct _GtkTrayIconPrivate
58 {
59   guint stamp;
60   
61   Atom selection_atom;
62   Atom manager_atom;
63   Atom system_tray_opcode_atom;
64   Atom orientation_atom;
65   Atom visual_atom;
66   Atom colors_atom;
67   Atom padding_atom;
68   Atom icon_size_atom;
69   Window manager_window;
70   GdkVisual *manager_visual;
71   gboolean manager_visual_rgba;
72
73   GtkOrientation orientation;
74   GdkRGBA fg_color;
75   GdkRGBA error_color;
76   GdkRGBA warning_color;
77   GdkRGBA success_color;
78   gint padding;
79   gint icon_size;
80 };
81
82 static void gtk_tray_icon_constructed   (GObject     *object);
83 static void gtk_tray_icon_dispose       (GObject     *object);
84
85 static void gtk_tray_icon_get_property  (GObject     *object,
86                                          guint        prop_id,
87                                          GValue      *value,
88                                          GParamSpec  *pspec);
89
90 static void     gtk_tray_icon_realize   (GtkWidget   *widget);
91 static void     gtk_tray_icon_style_updated (GtkWidget   *widget);
92 static gboolean gtk_tray_icon_delete    (GtkWidget   *widget,
93                                          GdkEventAny *event);
94 static gboolean gtk_tray_icon_draw      (GtkWidget   *widget,
95                                          cairo_t     *cr);
96
97 static void gtk_tray_icon_clear_manager_window     (GtkTrayIcon *icon);
98 static void gtk_tray_icon_update_manager_window    (GtkTrayIcon *icon);
99 static void gtk_tray_icon_manager_window_destroyed (GtkTrayIcon *icon);
100
101 static GdkFilterReturn gtk_tray_icon_manager_filter (GdkXEvent *xevent,
102                                                      GdkEvent  *event,
103                                                      gpointer   user_data);
104
105
106 G_DEFINE_TYPE (GtkTrayIcon, gtk_tray_icon, GTK_TYPE_PLUG)
107
108 static void
109 gtk_tray_icon_class_init (GtkTrayIconClass *class)
110 {
111   GObjectClass *gobject_class = (GObjectClass *)class;
112   GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
113
114   gobject_class->get_property = gtk_tray_icon_get_property;
115   gobject_class->constructed = gtk_tray_icon_constructed;
116   gobject_class->dispose = gtk_tray_icon_dispose;
117
118   widget_class->realize = gtk_tray_icon_realize;
119   widget_class->style_updated = gtk_tray_icon_style_updated;
120   widget_class->delete_event = gtk_tray_icon_delete;
121   widget_class->draw = gtk_tray_icon_draw;
122
123   g_object_class_install_property (gobject_class,
124                                    PROP_ORIENTATION,
125                                    g_param_spec_enum ("orientation",
126                                                       P_("Orientation"),
127                                                       P_("The orientation of the tray"),
128                                                       GTK_TYPE_ORIENTATION,
129                                                       GTK_ORIENTATION_HORIZONTAL,
130                                                       GTK_PARAM_READABLE));
131
132   g_object_class_install_property (gobject_class,
133                                    PROP_FG_COLOR,
134                                    g_param_spec_boxed ("fg-color",
135                                                        P_("Foreground color"),
136                                                        P_("Foreground color for symbolic icons"),
137                                                        GDK_TYPE_RGBA,
138                                                        GTK_PARAM_READABLE));
139
140   g_object_class_install_property (gobject_class,
141                                    PROP_ERROR_COLOR,
142                                    g_param_spec_boxed ("error-color",
143                                                        P_("Error color"),
144                                                        P_("Error color for symbolic icons"),
145                                                        GDK_TYPE_RGBA,
146                                                        GTK_PARAM_READABLE));
147
148   g_object_class_install_property (gobject_class,
149                                    PROP_WARNING_COLOR,
150                                    g_param_spec_boxed ("warning-color",
151                                                        P_("Warning color"),
152                                                        P_("Warning color for symbolic icons"),
153                                                        GDK_TYPE_RGBA,
154                                                        GTK_PARAM_READABLE));
155
156   g_object_class_install_property (gobject_class,
157                                    PROP_SUCCESS_COLOR,
158                                    g_param_spec_boxed ("success-color",
159                                                        P_("Success color"),
160                                                        P_("Success color for symbolic icons"),
161                                                        GDK_TYPE_RGBA,
162                                                        GTK_PARAM_READABLE));
163
164   g_object_class_install_property (gobject_class,
165                                    PROP_PADDING,
166                                    g_param_spec_int ("padding",
167                                                      P_("Padding"),
168                                                      P_("Padding that should be put around icons in the tray"),
169                                                      0,
170                                                      G_MAXINT,
171                                                      0,
172                                                      GTK_PARAM_READABLE));
173
174   g_object_class_install_property (gobject_class,
175                                    PROP_ICON_SIZE,
176                                    g_param_spec_int ("icon-size",
177                                                      P_("Icon Size"),
178                                                      P_("The pixel size that icons should be forced to, or zero"),
179                                                      0,
180                                                      G_MAXINT,
181                                                      0,
182                                                      GTK_PARAM_READABLE));
183
184   g_type_class_add_private (class, sizeof (GtkTrayIconPrivate));
185 }
186
187 static void
188 gtk_tray_icon_init (GtkTrayIcon *icon)
189 {
190   icon->priv = G_TYPE_INSTANCE_GET_PRIVATE (icon, GTK_TYPE_TRAY_ICON,
191                                             GtkTrayIconPrivate);
192
193   icon->priv->stamp = 1;
194   icon->priv->orientation = GTK_ORIENTATION_HORIZONTAL;
195   icon->priv->fg_color.red        = 0.0;
196   icon->priv->fg_color.green      = 0.0;
197   icon->priv->fg_color.blue       = 0.0;
198   icon->priv->fg_color.alpha      = 1.0;
199   icon->priv->error_color.red     = 0.7968;
200   icon->priv->error_color.green   = 0.0;
201   icon->priv->error_color.blue    = 0.0;
202   icon->priv->error_color.alpha   = 1.0;
203   icon->priv->warning_color.red   = 0.9570;
204   icon->priv->warning_color.green = 0.4726;
205   icon->priv->warning_color.blue  = 0.2421;
206   icon->priv->warning_color.alpha = 1.0;
207   icon->priv->success_color.red   = 0.3047;
208   icon->priv->success_color.green = 0.6016;
209   icon->priv->success_color.blue  = 0.0234;
210   icon->priv->success_color.alpha = 1.0;
211   icon->priv->padding = 0;
212   icon->priv->icon_size = 0;
213
214   gtk_widget_set_app_paintable (GTK_WIDGET (icon), TRUE);
215   gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
216 }
217
218 static void
219 gtk_tray_icon_constructed (GObject *object)
220 {
221   /* Do setup that depends on the screen; screen has been set at this point */
222
223   GtkTrayIcon *icon = GTK_TRAY_ICON (object);
224   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (object));
225   GdkWindow *root_window = gdk_screen_get_root_window (screen);
226   GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (object));
227   Display *xdisplay = gdk_x11_display_get_xdisplay (display);
228   char buffer[256];
229   
230   g_snprintf (buffer, sizeof (buffer),
231               "_NET_SYSTEM_TRAY_S%d",
232               gdk_screen_get_number (screen));
233
234   icon->priv->selection_atom = XInternAtom (xdisplay, buffer, False);
235   
236   icon->priv->manager_atom = XInternAtom (xdisplay, "MANAGER", False);
237   
238   icon->priv->system_tray_opcode_atom = XInternAtom (xdisplay,
239                                                      "_NET_SYSTEM_TRAY_OPCODE",
240                                                      False);
241
242   icon->priv->orientation_atom = XInternAtom (xdisplay,
243                                               "_NET_SYSTEM_TRAY_ORIENTATION",
244                                               False);
245
246   icon->priv->visual_atom = XInternAtom (xdisplay,
247                                          "_NET_SYSTEM_TRAY_VISUAL",
248                                          False);
249
250   icon->priv->colors_atom = XInternAtom (xdisplay,
251                                          "_NET_SYSTEM_TRAY_COLORS",
252                                          False);
253
254   icon->priv->padding_atom = XInternAtom (xdisplay,
255                                          "_NET_SYSTEM_TRAY_PADDING",
256                                          False);
257
258   icon->priv->icon_size_atom = XInternAtom (xdisplay,
259                                             "_NET_SYSTEM_TRAY_ICON_SIZE",
260                                             False);
261
262   /* Add a root window filter so that we get changes on MANAGER */
263   gdk_window_add_filter (root_window,
264                          gtk_tray_icon_manager_filter, icon);
265
266   gtk_tray_icon_update_manager_window (icon);
267 }
268
269 static void
270 gtk_tray_icon_clear_manager_window (GtkTrayIcon *icon)
271 {
272   GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (icon));
273
274   if (icon->priv->manager_window != None)
275     {
276       GdkWindow *gdkwin;
277
278       gdkwin = gdk_x11_window_lookup_for_display (display,
279                                                   icon->priv->manager_window);
280
281       gdk_window_remove_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
282
283       icon->priv->manager_window = None;
284       icon->priv->manager_visual = NULL;
285     }
286 }
287
288 static void
289 gtk_tray_icon_dispose (GObject *object)
290 {
291   GtkTrayIcon *icon = GTK_TRAY_ICON (object);
292   GtkWidget *widget = GTK_WIDGET (object);
293   GdkWindow *root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
294
295   gtk_tray_icon_clear_manager_window (icon);
296
297   gdk_window_remove_filter (root_window, gtk_tray_icon_manager_filter, icon);
298
299   G_OBJECT_CLASS (gtk_tray_icon_parent_class)->dispose (object);
300 }
301
302 static void
303 gtk_tray_icon_get_property (GObject    *object,
304                             guint       prop_id,
305                             GValue     *value,
306                             GParamSpec *pspec)
307 {
308   GtkTrayIcon *icon = GTK_TRAY_ICON (object);
309
310   switch (prop_id)
311     {
312     case PROP_ORIENTATION:
313       g_value_set_enum (value, icon->priv->orientation);
314       break;
315     case PROP_FG_COLOR:
316       g_value_set_boxed (value, &icon->priv->fg_color);
317       break;
318     case PROP_ERROR_COLOR:
319       g_value_set_boxed (value, &icon->priv->error_color);
320       break;
321     case PROP_WARNING_COLOR:
322       g_value_set_boxed (value, &icon->priv->warning_color);
323       break;
324     case PROP_SUCCESS_COLOR:
325       g_value_set_boxed (value, &icon->priv->success_color);
326       break;
327     case PROP_PADDING:
328       g_value_set_int (value, icon->priv->padding);
329       break;
330     case PROP_ICON_SIZE:
331       g_value_set_int (value, icon->priv->icon_size);
332       break;
333     default:
334       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
335       break;
336     }
337 }
338
339 static gboolean
340 gtk_tray_icon_draw (GtkWidget *widget, 
341                     cairo_t   *cr)
342 {
343   GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
344   GtkWidget *focus_child;
345   GdkWindow *window;
346   gint border_width;
347   gboolean retval = FALSE;
348   cairo_surface_t *target;
349
350   window = gtk_widget_get_window (widget);
351   target = cairo_get_group_target (cr);
352
353   if (icon->priv->manager_visual_rgba ||
354       cairo_surface_get_type (target) != CAIRO_SURFACE_TYPE_XLIB ||
355       cairo_xlib_surface_get_drawable (target) != GDK_WINDOW_XID (window))
356     {
357       /* Clear to transparent */
358       cairo_set_source_rgba (cr, 0, 0, 0, 0);
359       cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
360       cairo_paint (cr);
361     }
362   else
363     {
364       GdkRectangle clip;
365
366       if (gdk_cairo_get_clip_rectangle (cr, &clip))
367         {
368           /* Clear to parent-relative pixmap
369            * We need to use direct X access here because GDK doesn't know about
370            * the parent realtive pixmap. */
371           cairo_surface_flush (target);
372
373           XClearArea (GDK_WINDOW_XDISPLAY (window),
374                       GDK_WINDOW_XID (window),
375                       clip.x, clip.y,
376                       clip.width, clip.height,
377                       False);
378           cairo_surface_mark_dirty_rectangle (target, 
379                                               clip.x, clip.y,
380                                               clip.width, clip.height);
381         }
382     }
383
384   if (GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->draw)
385     retval = GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->draw (widget, cr);
386
387   focus_child = gtk_container_get_focus_child (GTK_CONTAINER (widget));
388   if (focus_child && gtk_widget_has_visible_focus (focus_child))
389     {
390       GtkStyleContext *context;
391
392       border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
393       context = gtk_widget_get_style_context (widget);
394
395       gtk_render_focus (context, cr, border_width, border_width,
396                         gtk_widget_get_allocated_width (widget) - 2 * border_width,
397                         gtk_widget_get_allocated_height (widget) - 2 * border_width);
398     }
399
400   return retval;
401 }
402
403 static void
404 gtk_tray_icon_get_orientation_property (GtkTrayIcon *icon)
405 {
406   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (icon));
407   GdkDisplay *display = gdk_screen_get_display (screen);
408   Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
409
410   Atom type;
411   int format;
412   union {
413         gulong *prop;
414         guchar *prop_ch;
415   } prop = { NULL };
416   gulong nitems;
417   gulong bytes_after;
418   int error, result;
419
420   g_assert (icon->priv->manager_window != None);
421   
422   gdk_error_trap_push ();
423   type = None;
424   result = XGetWindowProperty (xdisplay,
425                                icon->priv->manager_window,
426                                icon->priv->orientation_atom,
427                                0, G_MAXLONG, FALSE,
428                                XA_CARDINAL,
429                                &type, &format, &nitems,
430                                &bytes_after, &(prop.prop_ch));
431   error = gdk_error_trap_pop ();
432
433   if (error || result != Success)
434     return;
435
436   if (type == XA_CARDINAL && nitems == 1 && format == 32)
437     {
438       GtkOrientation orientation;
439
440       orientation = (prop.prop [0] == SYSTEM_TRAY_ORIENTATION_HORZ) ?
441                                         GTK_ORIENTATION_HORIZONTAL :
442                                         GTK_ORIENTATION_VERTICAL;
443
444       if (icon->priv->orientation != orientation)
445         {
446           icon->priv->orientation = orientation;
447
448           g_object_notify (G_OBJECT (icon), "orientation");
449         }
450     }
451
452   if (type != None)
453     XFree (prop.prop);
454 }
455
456 static void
457 gtk_tray_icon_get_visual_property (GtkTrayIcon *icon)
458 {
459   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (icon));
460   GdkDisplay *display = gdk_screen_get_display (screen);
461   Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
462
463   Atom type;
464   int format;
465   union {
466         gulong *prop;
467         guchar *prop_ch;
468   } prop = { NULL };
469   gulong nitems;
470   gulong bytes_after;
471   int error, result;
472
473   g_assert (icon->priv->manager_window != None);
474
475   gdk_error_trap_push ();
476   type = None;
477   result = XGetWindowProperty (xdisplay,
478                                icon->priv->manager_window,
479                                icon->priv->visual_atom,
480                                0, G_MAXLONG, FALSE,
481                                XA_VISUALID,
482                                &type, &format, &nitems,
483                                &bytes_after, &(prop.prop_ch));
484   error = gdk_error_trap_pop ();
485
486   if (!error && result == Success &&
487       type == XA_VISUALID && nitems == 1 && format == 32)
488     {
489       VisualID visual_id;
490       GdkVisual *visual;
491       gint red_prec, green_prec, blue_prec;
492
493       visual_id = prop.prop[0];
494       visual = gdk_x11_screen_lookup_visual (screen, visual_id);
495       gdk_visual_get_red_pixel_details (visual, NULL, NULL, &red_prec);
496       gdk_visual_get_green_pixel_details (visual, NULL, NULL, &green_prec);
497       gdk_visual_get_blue_pixel_details (visual, NULL, NULL, &blue_prec);
498       icon->priv->manager_visual = visual;
499       icon->priv->manager_visual_rgba =
500           (red_prec + blue_prec + green_prec < gdk_visual_get_depth (visual));
501     }
502   else
503     {
504       icon->priv->manager_visual = NULL;
505       icon->priv->manager_visual_rgba = FALSE;
506     }
507
508   /* For the background-relative hack we use when we aren't
509    * using a real RGBA visual, we can't be double-buffered
510    */
511   gtk_widget_set_double_buffered (GTK_WIDGET (icon), icon->priv->manager_visual_rgba);
512
513   if (type != None)
514     XFree (prop.prop);
515 }
516
517 static void
518 gtk_tray_icon_get_colors_property (GtkTrayIcon *icon)
519 {
520   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (icon));
521   GdkDisplay *display = gdk_screen_get_display (screen);
522   Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
523
524   Atom type;
525   int format;
526   union {
527         gulong *prop;
528         guchar *prop_ch;
529   } prop = { NULL };
530   gulong nitems;
531   gulong bytes_after;
532   int error, result;
533
534   g_assert (icon->priv->manager_window != None);
535
536   gdk_error_trap_push ();
537   type = None;
538   result = XGetWindowProperty (xdisplay,
539                                icon->priv->manager_window,
540                                icon->priv->colors_atom,
541                                0, G_MAXLONG, FALSE,
542                                XA_CARDINAL,
543                                &type, &format, &nitems,
544                                &bytes_after, &(prop.prop_ch));
545   error = gdk_error_trap_pop ();
546
547   if (error || result != Success)
548     return;
549
550   if (type == XA_CARDINAL && nitems == 12 && format == 32)
551     {
552       GdkRGBA color;
553
554       g_object_freeze_notify (G_OBJECT (icon));
555
556       color.red = prop.prop[0] / 65535.0;
557       color.green = prop.prop[1] / 65535.0;
558       color.blue = prop.prop[2] / 65535.0;
559
560       if (!gdk_rgba_equal (&icon->priv->fg_color, &color))
561         {
562           icon->priv->fg_color = color;
563
564           g_object_notify (G_OBJECT (icon), "fg-color");
565         }
566
567       color.red = prop.prop[3] / 65535.0;
568       color.green = prop.prop[4] / 65535.0;
569       color.blue = prop.prop[5] / 65535.0;
570
571       if (!gdk_rgba_equal (&icon->priv->error_color, &color))
572         {
573           icon->priv->error_color = color;
574
575           g_object_notify (G_OBJECT (icon), "error-color");
576         }
577
578       color.red = prop.prop[6] / 65535.0;
579       color.green = prop.prop[7] / 65535.0;
580       color.blue = prop.prop[8] / 65535.0;
581
582       if (!gdk_rgba_equal (&icon->priv->warning_color, &color))
583         {
584           icon->priv->warning_color = color;
585
586           g_object_notify (G_OBJECT (icon), "warning-color");
587         }
588
589       color.red = prop.prop[9] / 65535.0;
590       color.green = prop.prop[10] /  65535.0;
591       color.blue = prop.prop[11] / 65535.0;
592
593       if (!gdk_rgba_equal (&icon->priv->success_color, &color))
594         {
595           icon->priv->success_color = color;
596
597           g_object_notify (G_OBJECT (icon), "success-color");
598         }
599
600       g_object_thaw_notify (G_OBJECT (icon));
601     }
602
603   if (type != None)
604     XFree (prop.prop);
605 }
606
607 static void
608 gtk_tray_icon_get_padding_property (GtkTrayIcon *icon)
609 {
610   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (icon));
611   GdkDisplay *display = gdk_screen_get_display (screen);
612   Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
613
614   Atom type;
615   int format;
616   union {
617         gulong *prop;
618         guchar *prop_ch;
619   } prop = { NULL };
620   gulong nitems;
621   gulong bytes_after;
622   int error, result;
623
624   g_assert (icon->priv->manager_window != None);
625
626   gdk_error_trap_push ();
627   type = None;
628   result = XGetWindowProperty (xdisplay,
629                                icon->priv->manager_window,
630                                icon->priv->padding_atom,
631                                0, G_MAXLONG, FALSE,
632                                XA_CARDINAL,
633                                &type, &format, &nitems,
634                                &bytes_after, &(prop.prop_ch));
635   error = gdk_error_trap_pop ();
636
637   if (!error && result == Success &&
638       type == XA_CARDINAL && nitems == 1 && format == 32)
639     {
640       gint padding;
641
642       padding = prop.prop[0];
643
644       if (icon->priv->padding != padding)
645         {
646           icon->priv->padding = padding;
647
648           g_object_notify (G_OBJECT (icon), "padding");
649         }
650     }
651
652   if (type != None)
653     XFree (prop.prop);
654 }
655
656 static void
657 gtk_tray_icon_get_icon_size_property (GtkTrayIcon *icon)
658 {
659   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (icon));
660   GdkDisplay *display = gdk_screen_get_display (screen);
661   Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
662
663   Atom type;
664   int format;
665   union {
666         gulong *prop;
667         guchar *prop_ch;
668   } prop = { NULL };
669   gulong nitems;
670   gulong bytes_after;
671   int error, result;
672
673   g_assert (icon->priv->manager_window != None);
674
675   gdk_error_trap_push ();
676   type = None;
677   result = XGetWindowProperty (xdisplay,
678                                icon->priv->manager_window,
679                                icon->priv->icon_size_atom,
680                                0, G_MAXLONG, FALSE,
681                                XA_CARDINAL,
682                                &type, &format, &nitems,
683                                &bytes_after, &(prop.prop_ch));
684   error = gdk_error_trap_pop ();
685
686   if (!error && result == Success &&
687       type == XA_CARDINAL && nitems == 1 && format == 32)
688     {
689       gint icon_size;
690
691       icon_size = prop.prop[0];
692
693       if (icon->priv->icon_size != icon_size)
694         {
695           icon->priv->icon_size = icon_size;
696
697           g_object_notify (G_OBJECT (icon), "icon-size");
698         }
699     }
700
701   if (type != None)
702     XFree (prop.prop);
703 }
704
705 static GdkFilterReturn
706 gtk_tray_icon_manager_filter (GdkXEvent *xevent,
707                               GdkEvent  *event,
708                               gpointer   user_data)
709 {
710   GtkTrayIcon *icon = user_data;
711   XEvent *xev = (XEvent *)xevent;
712
713   if (xev->xany.type == ClientMessage &&
714       xev->xclient.message_type == icon->priv->manager_atom &&
715       xev->xclient.data.l[1] == icon->priv->selection_atom)
716     {
717       GTK_NOTE (PLUGSOCKET,
718                 g_print ("GtkStatusIcon %p: tray manager appeared\n", icon));
719
720       gtk_tray_icon_update_manager_window (icon);
721     }
722   else if (xev->xany.window == icon->priv->manager_window)
723     {
724       if (xev->xany.type == PropertyNotify &&
725           xev->xproperty.atom == icon->priv->orientation_atom)
726         {
727           GTK_NOTE (PLUGSOCKET,
728                     g_print ("GtkStatusIcon %p: got PropertyNotify on manager window for orientation atom\n", icon));
729
730           gtk_tray_icon_get_orientation_property (icon);
731         }
732       else if (xev->xany.type == PropertyNotify &&
733                xev->xproperty.atom == icon->priv->colors_atom)
734         {
735           GTK_NOTE (PLUGSOCKET,
736                     g_print ("GtkStatusIcon %p: got PropertyNotify on manager window for colors atom\n", icon));
737
738           gtk_tray_icon_get_colors_property (icon);
739         }
740       else if (xev->xany.type == PropertyNotify &&
741                xev->xproperty.atom == icon->priv->padding_atom)
742         {
743           gtk_tray_icon_get_padding_property (icon);
744         }
745       else if (xev->xany.type == PropertyNotify &&
746                xev->xproperty.atom == icon->priv->icon_size_atom)
747         {
748           gtk_tray_icon_get_icon_size_property (icon);
749         }
750       else if (xev->xany.type == DestroyNotify)
751         {
752           GTK_NOTE (PLUGSOCKET,
753                     g_print ("GtkStatusIcon %p: got DestroyNotify for manager window\n", icon));
754
755           gtk_tray_icon_manager_window_destroyed (icon);
756         }
757       else
758         {
759           GTK_NOTE (PLUGSOCKET,
760                     g_print ("GtkStatusIcon %p: got other message on manager window\n", icon));
761         }
762     }
763   
764   return GDK_FILTER_CONTINUE;
765 }
766
767 static void
768 gtk_tray_icon_send_manager_message (GtkTrayIcon *icon,
769                                     long         message,
770                                     Window       window,
771                                     long         data1,
772                                     long         data2,
773                                     long         data3)
774 {
775   GtkWidget *widget;
776   XClientMessageEvent ev;
777   Display *display;
778
779   widget = GTK_WIDGET (icon);
780
781   memset (&ev, 0, sizeof (ev));
782   ev.type = ClientMessage;
783   ev.window = window;
784   ev.message_type = icon->priv->system_tray_opcode_atom;
785   ev.format = 32;
786   ev.data.l[0] = gdk_x11_get_server_time (gtk_widget_get_window (widget));
787   ev.data.l[1] = message;
788   ev.data.l[2] = data1;
789   ev.data.l[3] = data2;
790   ev.data.l[4] = data3;
791
792   display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (widget));
793
794   gdk_error_trap_push ();
795   XSendEvent (display,
796               icon->priv->manager_window, False, NoEventMask, (XEvent *)&ev);
797   gdk_error_trap_pop_ignored ();
798 }
799
800 static void
801 gtk_tray_icon_send_dock_request (GtkTrayIcon *icon)
802 {
803   GTK_NOTE (PLUGSOCKET,
804             g_print ("GtkStatusIcon %p: sending dock request to manager window %lx\n",
805                      icon, (gulong) icon->priv->manager_window));
806
807   gtk_tray_icon_send_manager_message (icon,
808                                       SYSTEM_TRAY_REQUEST_DOCK,
809                                       icon->priv->manager_window,
810                                       gtk_plug_get_id (GTK_PLUG (icon)),
811                                       0, 0);
812 }
813
814 static void
815 gtk_tray_icon_update_manager_window (GtkTrayIcon *icon)
816 {
817   GtkWidget *widget = GTK_WIDGET (icon);
818   GdkScreen *screen = gtk_widget_get_screen (widget);
819   GdkDisplay *display = gdk_screen_get_display (screen);
820   Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
821
822   GTK_NOTE (PLUGSOCKET,
823             g_print ("GtkStatusIcon %p: updating tray icon manager window, current manager window: %lx\n",
824                      icon, (gulong) icon->priv->manager_window));
825
826   if (icon->priv->manager_window != None)
827     return;
828
829   GTK_NOTE (PLUGSOCKET,
830             g_print ("GtkStatusIcon %p: trying to find manager window\n", icon));
831
832   XGrabServer (xdisplay);
833   
834   icon->priv->manager_window = XGetSelectionOwner (xdisplay,
835                                                    icon->priv->selection_atom);
836
837   if (icon->priv->manager_window != None)
838     XSelectInput (xdisplay,
839                   icon->priv->manager_window, StructureNotifyMask|PropertyChangeMask);
840
841   XUngrabServer (xdisplay);
842   XFlush (xdisplay);
843   
844   if (icon->priv->manager_window != None)
845     {
846       GdkWindow *gdkwin;
847
848       GTK_NOTE (PLUGSOCKET,
849         g_print ("GtkStatusIcon %p: is being managed by window %lx\n",
850                  icon, (gulong) icon->priv->manager_window));
851
852       gdkwin = gdk_x11_window_lookup_for_display (display,
853                                                   icon->priv->manager_window);
854
855       gdk_window_add_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
856
857       gtk_tray_icon_get_orientation_property (icon);
858       gtk_tray_icon_get_visual_property (icon);
859       gtk_tray_icon_get_colors_property (icon);
860       gtk_tray_icon_get_padding_property (icon);
861       gtk_tray_icon_get_icon_size_property (icon);
862
863       if (gtk_widget_get_realized (GTK_WIDGET (icon)))
864         {
865           if ((icon->priv->manager_visual == NULL &&
866                gtk_widget_get_visual (widget) == gdk_screen_get_system_visual (screen)) ||
867               (icon->priv->manager_visual == gtk_widget_get_visual (widget)))
868             {
869               /* Already have the right visual, can just dock
870                */
871               gtk_tray_icon_send_dock_request (icon);
872             }
873           else
874             {
875               /* Need to re-realize the widget to get the right visual
876                */
877               gtk_widget_hide (widget);
878               gtk_widget_unrealize (widget);
879               gtk_widget_show (widget);
880             }
881         }
882     }
883   else
884     {
885       GTK_NOTE (PLUGSOCKET,
886                 g_print ("GtkStatusIcon %p: no tray manager found\n", icon));
887     }
888 }
889
890 static void
891 gtk_tray_icon_manager_window_destroyed (GtkTrayIcon *icon)
892 {
893   g_return_if_fail (icon->priv->manager_window != None);
894
895   GTK_NOTE (PLUGSOCKET,
896             g_print ("GtkStatusIcon %p: tray manager window destroyed\n", icon));
897
898   gtk_tray_icon_clear_manager_window (icon);
899 }
900
901 static gboolean
902 gtk_tray_icon_delete (GtkWidget   *widget,
903                       GdkEventAny *event)
904 {
905 #ifdef G_ENABLE_DEBUG
906   GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
907 #endif
908
909   GTK_NOTE (PLUGSOCKET,
910             g_print ("GtkStatusIcon %p: delete notify, tray manager window %lx\n",
911                      icon, (gulong) icon->priv->manager_window));
912
913   /* A bug in X server versions up to x.org 1.5.0 means that:
914    * XFixesChangeSaveSet(...., SaveSetRoot, SaveSetUnmap) doesn't work properly
915    * and we'll left mapped in a separate toplevel window if the tray is destroyed.
916    * For simplicity just get rid of our X window and start over.
917    */
918   gtk_widget_hide (widget);
919   gtk_widget_unrealize (widget);
920   gtk_widget_show (widget);
921
922   /* Handled it, don't destroy the tray icon */
923   return TRUE;
924 }
925
926 static void
927 gtk_tray_icon_set_visual (GtkTrayIcon *icon)
928 {
929   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (icon));
930   GdkVisual *visual = icon->priv->manager_visual;
931
932   /* To avoid uncertainty about colormaps, _NET_SYSTEM_TRAY_VISUAL is supposed
933    * to be either the screen default visual or a TrueColor visual; ignore it
934    * if it is something else
935    */
936   if (visual && gdk_visual_get_visual_type (visual) != GDK_VISUAL_TRUE_COLOR)
937     visual = NULL;
938
939   if (visual == NULL)
940     visual = gdk_screen_get_system_visual (screen);
941
942   gtk_widget_set_visual (GTK_WIDGET (icon), visual);
943 }
944
945 static void
946 gtk_tray_icon_realize (GtkWidget *widget)
947 {
948   GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
949   GdkWindow *window;
950
951   /* Set our visual before realizing */
952   gtk_tray_icon_set_visual (icon);
953
954   GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->realize (widget);
955   window = gtk_widget_get_window (widget);
956   if (icon->priv->manager_visual_rgba)
957     {
958       /* Set a transparent background */
959       GdkRGBA transparent = { 0.0, 0.0, 0.0, 0.0 };
960       gdk_window_set_background_rgba (window, &transparent);
961     }
962   else
963     {
964       /* Set a parent-relative background pixmap */
965       gdk_window_set_background_pattern (window, NULL);
966     }
967
968   GTK_NOTE (PLUGSOCKET,
969             g_print ("GtkStatusIcon %p: realized, window: %lx, socket window: %lx\n",
970                      widget,
971                      (gulong) GDK_WINDOW_XID (window),
972                      gtk_plug_get_socket_window (GTK_PLUG (icon)) ?
973                      (gulong) GDK_WINDOW_XID (gtk_plug_get_socket_window (GTK_PLUG (icon))) : 0UL));
974
975   if (icon->priv->manager_window != None)
976     gtk_tray_icon_send_dock_request (icon);
977 }
978
979 static void
980 gtk_tray_icon_style_updated (GtkWidget   *widget)
981 {
982   /* The default handler resets the background according to the style. We either
983    * use a transparent background or a parent-relative background and ignore the
984    * style background. So, just don't chain up.
985    */
986 }
987
988 guint
989 _gtk_tray_icon_send_message (GtkTrayIcon *icon,
990                              gint         timeout,
991                              const gchar *message,
992                              gint         len)
993 {
994   guint stamp;
995   Display *xdisplay;
996  
997   g_return_val_if_fail (GTK_IS_TRAY_ICON (icon), 0);
998   g_return_val_if_fail (timeout >= 0, 0);
999   g_return_val_if_fail (message != NULL, 0);
1000
1001   if (icon->priv->manager_window == None)
1002     return 0;
1003
1004   if (len < 0)
1005     len = strlen (message);
1006
1007   stamp = icon->priv->stamp++;
1008   
1009   /* Get ready to send the message */
1010   gtk_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE,
1011                                       (Window)gtk_plug_get_id (GTK_PLUG (icon)),
1012                                       timeout, len, stamp);
1013
1014   /* Now to send the actual message */
1015   xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
1016   gdk_error_trap_push ();
1017   while (len > 0)
1018     {
1019       XClientMessageEvent ev;
1020
1021       memset (&ev, 0, sizeof (ev));
1022       ev.type = ClientMessage;
1023       ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon));
1024       ev.format = 8;
1025       ev.message_type = XInternAtom (xdisplay,
1026                                      "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
1027       if (len > 20)
1028         {
1029           memcpy (&ev.data, message, 20);
1030           len -= 20;
1031           message += 20;
1032         }
1033       else
1034         {
1035           memcpy (&ev.data, message, len);
1036           len = 0;
1037         }
1038
1039       XSendEvent (xdisplay,
1040                   icon->priv->manager_window, False,
1041                   StructureNotifyMask, (XEvent *)&ev);
1042     }
1043   gdk_error_trap_pop_ignored ();
1044
1045   return stamp;
1046 }
1047
1048 void
1049 _gtk_tray_icon_cancel_message (GtkTrayIcon *icon,
1050                                guint        id)
1051 {
1052   g_return_if_fail (GTK_IS_TRAY_ICON (icon));
1053   g_return_if_fail (id > 0);
1054   
1055   gtk_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE,
1056                                       (Window)gtk_plug_get_id (GTK_PLUG (icon)),
1057                                       id, 0, 0);
1058 }
1059
1060 GtkTrayIcon *
1061 _gtk_tray_icon_new_for_screen (GdkScreen  *screen, 
1062                                const gchar *name)
1063 {
1064   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
1065
1066   return g_object_new (GTK_TYPE_TRAY_ICON, 
1067                        "screen", screen, 
1068                        "title", name, 
1069                        NULL);
1070 }
1071
1072 GtkTrayIcon*
1073 _gtk_tray_icon_new (const gchar *name)
1074 {
1075   return g_object_new (GTK_TYPE_TRAY_ICON, 
1076                        "title", name, 
1077                        NULL);
1078 }
1079
1080 GtkOrientation
1081 _gtk_tray_icon_get_orientation (GtkTrayIcon *icon)
1082 {
1083   g_return_val_if_fail (GTK_IS_TRAY_ICON (icon), GTK_ORIENTATION_HORIZONTAL);
1084
1085   return icon->priv->orientation;
1086 }
1087
1088 gint
1089 _gtk_tray_icon_get_padding (GtkTrayIcon *icon)
1090 {
1091   g_return_val_if_fail (GTK_IS_TRAY_ICON (icon), 0);
1092
1093   return icon->priv->padding;
1094 }
1095
1096 gint
1097 _gtk_tray_icon_get_icon_size (GtkTrayIcon *icon)
1098 {
1099   g_return_val_if_fail (GTK_IS_TRAY_ICON (icon), 0);
1100
1101   return icon->priv->icon_size;
1102 }