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 };
48 
49 struct _GtkTrayIconPrivate
50 {
51   guint stamp;
52 
53   Atom selection_atom;
54   Atom manager_atom;
55   Atom system_tray_opcode_atom;
56   Atom orientation_atom;
57   Atom visual_atom;
58   Window manager_window;
59   GdkVisual *manager_visual;
60   gboolean manager_visual_rgba;
61 
62   GtkOrientation orientation;
63 };
64 
65 static void gtk_tray_icon_constructed   (GObject     *object);
66 static void gtk_tray_icon_dispose       (GObject     *object);
67 
68 static void gtk_tray_icon_get_property  (GObject     *object,
69 				 	 guint        prop_id,
70 					 GValue      *value,
71 					 GParamSpec  *pspec);
72 
73 static void     gtk_tray_icon_realize   (GtkWidget   *widget);
74 static void     gtk_tray_icon_style_set (GtkWidget   *widget,
75 					 GtkStyle    *previous_style);
76 static gboolean gtk_tray_icon_delete    (GtkWidget   *widget,
77 					 GdkEventAny *event);
78 static gboolean gtk_tray_icon_expose    (GtkWidget      *widget,
79 					 GdkEventExpose *event);
80 
81 static void gtk_tray_icon_clear_manager_window     (GtkTrayIcon *icon);
82 static void gtk_tray_icon_update_manager_window    (GtkTrayIcon *icon);
83 static void gtk_tray_icon_manager_window_destroyed (GtkTrayIcon *icon);
84 
85 static GdkFilterReturn gtk_tray_icon_manager_filter (GdkXEvent *xevent,
86 						     GdkEvent  *event,
87 						     gpointer   user_data);
88 
89 
G_DEFINE_TYPE(GtkTrayIcon,gtk_tray_icon,GTK_TYPE_PLUG)90 G_DEFINE_TYPE (GtkTrayIcon, gtk_tray_icon, GTK_TYPE_PLUG)
91 
92 static void
93 gtk_tray_icon_class_init (GtkTrayIconClass *class)
94 {
95   GObjectClass *gobject_class = (GObjectClass *)class;
96   GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
97 
98   gobject_class->get_property = gtk_tray_icon_get_property;
99   gobject_class->constructed = gtk_tray_icon_constructed;
100   gobject_class->dispose = gtk_tray_icon_dispose;
101 
102   widget_class->realize = gtk_tray_icon_realize;
103   widget_class->style_set = gtk_tray_icon_style_set;
104   widget_class->delete_event = gtk_tray_icon_delete;
105   widget_class->expose_event = gtk_tray_icon_expose;
106 
107   g_object_class_install_property (gobject_class,
108 				   PROP_ORIENTATION,
109 				   g_param_spec_enum ("orientation",
110 						      P_("Orientation"),
111 						      P_("The orientation of the tray"),
112 						      GTK_TYPE_ORIENTATION,
113 						      GTK_ORIENTATION_HORIZONTAL,
114 						      GTK_PARAM_READABLE));
115 
116   g_type_class_add_private (class, sizeof (GtkTrayIconPrivate));
117 }
118 
119 static void
gtk_tray_icon_init(GtkTrayIcon * icon)120 gtk_tray_icon_init (GtkTrayIcon *icon)
121 {
122   icon->priv = G_TYPE_INSTANCE_GET_PRIVATE (icon, GTK_TYPE_TRAY_ICON,
123 					    GtkTrayIconPrivate);
124 
125   icon->priv->stamp = 1;
126   icon->priv->orientation = GTK_ORIENTATION_HORIZONTAL;
127 
128   gtk_widget_set_app_paintable (GTK_WIDGET (icon), TRUE);
129   gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
130 }
131 
132 static void
gtk_tray_icon_constructed(GObject * object)133 gtk_tray_icon_constructed (GObject *object)
134 {
135   /* Do setup that depends on the screen; screen has been set at this point */
136 
137   GtkTrayIcon *icon = GTK_TRAY_ICON (object);
138   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (object));
139   GdkWindow *root_window = gdk_screen_get_root_window (screen);
140   GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (object));
141   Display *xdisplay = gdk_x11_display_get_xdisplay (display);
142   char buffer[256];
143 
144   g_snprintf (buffer, sizeof (buffer),
145 	      "_NET_SYSTEM_TRAY_S%d",
146 	      gdk_screen_get_number (screen));
147 
148   icon->priv->selection_atom = XInternAtom (xdisplay, buffer, False);
149 
150   icon->priv->manager_atom = XInternAtom (xdisplay, "MANAGER", False);
151 
152   icon->priv->system_tray_opcode_atom = XInternAtom (xdisplay,
153 						     "_NET_SYSTEM_TRAY_OPCODE",
154 						     False);
155 
156   icon->priv->orientation_atom = XInternAtom (xdisplay,
157 					      "_NET_SYSTEM_TRAY_ORIENTATION",
158 					      False);
159 
160   icon->priv->visual_atom = XInternAtom (xdisplay,
161 					 "_NET_SYSTEM_TRAY_VISUAL",
162 					 False);
163 
164   /* Add a root window filter so that we get changes on MANAGER */
165   gdk_window_add_filter (root_window,
166 			 gtk_tray_icon_manager_filter, icon);
167 
168   gtk_tray_icon_update_manager_window (icon);
169 }
170 
171 static void
gtk_tray_icon_clear_manager_window(GtkTrayIcon * icon)172 gtk_tray_icon_clear_manager_window (GtkTrayIcon *icon)
173 {
174   GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (icon));
175 
176   if (icon->priv->manager_window != None)
177     {
178       GdkWindow *gdkwin;
179 
180       gdkwin = gdk_window_lookup_for_display (display,
181                                               icon->priv->manager_window);
182 
183       gdk_window_remove_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
184 
185       icon->priv->manager_window = None;
186       icon->priv->manager_visual = NULL;
187     }
188 }
189 
190 static void
gtk_tray_icon_dispose(GObject * object)191 gtk_tray_icon_dispose (GObject *object)
192 {
193   GtkTrayIcon *icon = GTK_TRAY_ICON (object);
194   GtkWidget *widget = GTK_WIDGET (object);
195   GdkWindow *root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
196 
197   gtk_tray_icon_clear_manager_window (icon);
198 
199   gdk_window_remove_filter (root_window, gtk_tray_icon_manager_filter, icon);
200 
201   G_OBJECT_CLASS (gtk_tray_icon_parent_class)->dispose (object);
202 }
203 
204 static void
gtk_tray_icon_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)205 gtk_tray_icon_get_property (GObject    *object,
206 			    guint       prop_id,
207 			    GValue     *value,
208 			    GParamSpec *pspec)
209 {
210   GtkTrayIcon *icon = GTK_TRAY_ICON (object);
211 
212   switch (prop_id)
213     {
214     case PROP_ORIENTATION:
215       g_value_set_enum (value, icon->priv->orientation);
216       break;
217     default:
218       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
219       break;
220     }
221 }
222 
223 static gboolean
gtk_tray_icon_expose(GtkWidget * widget,GdkEventExpose * event)224 gtk_tray_icon_expose (GtkWidget      *widget,
225 		      GdkEventExpose *event)
226 {
227   GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
228   GtkWidget *focus_child;
229   gint border_width, x, y, width, height;
230   gboolean retval = FALSE;
231 
232   if (icon->priv->manager_visual_rgba)
233     {
234       /* Clear to transparent */
235       cairo_t *cr = gdk_cairo_create (widget->window);
236       cairo_set_source_rgba (cr, 0, 0, 0, 0);
237       cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
238       gdk_cairo_region (cr, event->region);
239       cairo_fill (cr);
240       cairo_destroy (cr);
241     }
242   else
243     {
244       /* Clear to parent-relative pixmap */
245       gdk_window_clear_area (widget->window, event->area.x, event->area.y,
246 			     event->area.width, event->area.height);
247     }
248 
249   if (GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->expose_event)
250     retval = GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->expose_event (widget, event);
251 
252   focus_child = GTK_CONTAINER (widget)->focus_child;
253   if (focus_child && gtk_widget_has_focus (focus_child))
254     {
255       border_width = GTK_CONTAINER (widget)->border_width;
256 
257       x = widget->allocation.x + border_width;
258       y = widget->allocation.y + border_width;
259 
260       width  = widget->allocation.width  - 2 * border_width;
261       height = widget->allocation.height - 2 * border_width;
262 
263       gtk_paint_focus (widget->style, widget->window,
264                        gtk_widget_get_state (widget),
265                        &event->area, widget, "tray_icon",
266                        x, y, width, height);
267     }
268 
269   return retval;
270 }
271 
272 static void
gtk_tray_icon_get_orientation_property(GtkTrayIcon * icon)273 gtk_tray_icon_get_orientation_property (GtkTrayIcon *icon)
274 {
275   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (icon));
276   GdkDisplay *display = gdk_screen_get_display (screen);
277   Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
278 
279   Atom type;
280   int format;
281   union {
282 	gulong *prop;
283 	guchar *prop_ch;
284   } prop = { NULL };
285   gulong nitems;
286   gulong bytes_after;
287   int error, result;
288 
289   g_assert (icon->priv->manager_window != None);
290 
291   gdk_error_trap_push ();
292   type = None;
293   result = XGetWindowProperty (xdisplay,
294 			       icon->priv->manager_window,
295 			       icon->priv->orientation_atom,
296 			       0, G_MAXLONG, FALSE,
297 			       XA_CARDINAL,
298 			       &type, &format, &nitems,
299 			       &bytes_after, &(prop.prop_ch));
300   error = gdk_error_trap_pop ();
301 
302   if (error || result != Success)
303     return;
304 
305   if (type == XA_CARDINAL && nitems == 1 && format == 32)
306     {
307       GtkOrientation orientation;
308 
309       orientation = (prop.prop [0] == SYSTEM_TRAY_ORIENTATION_HORZ) ?
310 					GTK_ORIENTATION_HORIZONTAL :
311 					GTK_ORIENTATION_VERTICAL;
312 
313       if (icon->priv->orientation != orientation)
314 	{
315 	  icon->priv->orientation = orientation;
316 
317 	  g_object_notify (G_OBJECT (icon), "orientation");
318 	}
319     }
320 
321   if (type != None)
322     XFree (prop.prop);
323 }
324 
325 static void
gtk_tray_icon_get_visual_property(GtkTrayIcon * icon)326 gtk_tray_icon_get_visual_property (GtkTrayIcon *icon)
327 {
328   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (icon));
329   GdkDisplay *display = gdk_screen_get_display (screen);
330   Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
331 
332   Atom type;
333   int format;
334   union {
335 	gulong *prop;
336 	guchar *prop_ch;
337   } prop = { NULL };
338   gulong nitems;
339   gulong bytes_after;
340   int error, result;
341 
342   g_assert (icon->priv->manager_window != None);
343 
344   gdk_error_trap_push ();
345   type = None;
346   result = XGetWindowProperty (xdisplay,
347 			       icon->priv->manager_window,
348 			       icon->priv->visual_atom,
349 			       0, G_MAXLONG, FALSE,
350 			       XA_VISUALID,
351 			       &type, &format, &nitems,
352 			       &bytes_after, &(prop.prop_ch));
353   error = gdk_error_trap_pop ();
354 
355   if (!error && result == Success &&
356       type == XA_VISUALID && nitems == 1 && format == 32)
357     {
358       VisualID visual_id;
359       GdkVisual *visual;
360 
361       visual_id = prop.prop[0];
362       visual = gdk_x11_screen_lookup_visual (screen, visual_id);
363 
364       icon->priv->manager_visual = visual;
365       icon->priv->manager_visual_rgba = visual != NULL &&
366         (visual->red_prec + visual->blue_prec + visual->green_prec < visual->depth);
367     }
368   else
369     {
370       icon->priv->manager_visual = NULL;
371       icon->priv->manager_visual_rgba = FALSE;
372     }
373 
374 
375   /* For the background-relative hack we use when we aren't using a real RGBA
376    * visual, we can't be double-buffered */
377   gtk_widget_set_double_buffered (GTK_WIDGET (icon), icon->priv->manager_visual_rgba);
378 
379   if (type != None)
380     XFree (prop.prop);
381 }
382 
383 static GdkFilterReturn
gtk_tray_icon_manager_filter(GdkXEvent * xevent,GdkEvent * event,gpointer user_data)384 gtk_tray_icon_manager_filter (GdkXEvent *xevent,
385 			      GdkEvent  *event,
386 			      gpointer   user_data)
387 {
388   GtkTrayIcon *icon = user_data;
389   XEvent *xev = (XEvent *)xevent;
390 
391   if (xev->xany.type == ClientMessage &&
392       xev->xclient.message_type == icon->priv->manager_atom &&
393       xev->xclient.data.l[1] == icon->priv->selection_atom)
394     {
395       GTK_NOTE (PLUGSOCKET,
396 		g_print ("GtkStatusIcon %p: tray manager appeared\n", icon));
397 
398       gtk_tray_icon_update_manager_window (icon);
399     }
400   else if (xev->xany.window == icon->priv->manager_window)
401     {
402       if (xev->xany.type == PropertyNotify &&
403 	  xev->xproperty.atom == icon->priv->orientation_atom)
404 	{
405           GTK_NOTE (PLUGSOCKET,
406 		    g_print ("GtkStatusIcon %p: got PropertyNotify on manager window for orientation atom\n", icon));
407 
408 	  gtk_tray_icon_get_orientation_property (icon);
409 	}
410       else if (xev->xany.type == DestroyNotify)
411 	{
412           GTK_NOTE (PLUGSOCKET,
413 		    g_print ("GtkStatusIcon %p: got DestroyNotify for manager window\n", icon));
414 
415 	  gtk_tray_icon_manager_window_destroyed (icon);
416 	}
417       else
418         GTK_NOTE (PLUGSOCKET,
419 		  g_print ("GtkStatusIcon %p: got other message on manager window\n", icon));
420     }
421 
422   return GDK_FILTER_CONTINUE;
423 }
424 
425 static void
gtk_tray_icon_send_manager_message(GtkTrayIcon * icon,long message,Window window,long data1,long data2,long data3)426 gtk_tray_icon_send_manager_message (GtkTrayIcon *icon,
427 				    long         message,
428 				    Window       window,
429 				    long         data1,
430 				    long         data2,
431 				    long         data3)
432 {
433   XClientMessageEvent ev;
434   Display *display;
435 
436   memset (&ev, 0, sizeof (ev));
437   ev.type = ClientMessage;
438   ev.window = window;
439   ev.message_type = icon->priv->system_tray_opcode_atom;
440   ev.format = 32;
441   ev.data.l[0] = gdk_x11_get_server_time (GTK_WIDGET (icon)->window);
442   ev.data.l[1] = message;
443   ev.data.l[2] = data1;
444   ev.data.l[3] = data2;
445   ev.data.l[4] = data3;
446 
447   display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
448 
449   gdk_error_trap_push ();
450   XSendEvent (display,
451 	      icon->priv->manager_window, False, NoEventMask, (XEvent *)&ev);
452   gdk_display_sync (gtk_widget_get_display (GTK_WIDGET (icon)));
453   gdk_error_trap_pop ();
454 }
455 
456 static void
gtk_tray_icon_send_dock_request(GtkTrayIcon * icon)457 gtk_tray_icon_send_dock_request (GtkTrayIcon *icon)
458 {
459   GTK_NOTE (PLUGSOCKET,
460 	    g_print ("GtkStatusIcon %p: sending dock request to manager window %lx\n",
461 	    	     icon, (gulong) icon->priv->manager_window));
462 
463   gtk_tray_icon_send_manager_message (icon,
464 				      SYSTEM_TRAY_REQUEST_DOCK,
465 				      icon->priv->manager_window,
466 				      gtk_plug_get_id (GTK_PLUG (icon)),
467 				      0, 0);
468 }
469 
470 static void
gtk_tray_icon_update_manager_window(GtkTrayIcon * icon)471 gtk_tray_icon_update_manager_window (GtkTrayIcon *icon)
472 {
473   GtkWidget *widget = GTK_WIDGET (icon);
474   GdkScreen *screen = gtk_widget_get_screen (widget);
475   GdkDisplay *display = gdk_screen_get_display (screen);
476   Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
477 
478   GTK_NOTE (PLUGSOCKET,
479 	    g_print ("GtkStatusIcon %p: updating tray icon manager window, current manager window: %lx\n",
480 		     icon, (gulong) icon->priv->manager_window));
481 
482   if (icon->priv->manager_window != None)
483     return;
484 
485   GTK_NOTE (PLUGSOCKET,
486 	    g_print ("GtkStatusIcon %p: trying to find manager window\n", icon));
487 
488   XGrabServer (xdisplay);
489 
490   icon->priv->manager_window = XGetSelectionOwner (xdisplay,
491 						   icon->priv->selection_atom);
492 
493   if (icon->priv->manager_window != None)
494     XSelectInput (xdisplay,
495 		  icon->priv->manager_window, StructureNotifyMask|PropertyChangeMask);
496 
497   XUngrabServer (xdisplay);
498   XFlush (xdisplay);
499 
500   if (icon->priv->manager_window != None)
501     {
502       GdkWindow *gdkwin;
503 
504       GTK_NOTE (PLUGSOCKET,
505 		g_print ("GtkStatusIcon %p: is being managed by window %lx\n",
506 				icon, (gulong) icon->priv->manager_window));
507 
508       gdkwin = gdk_window_lookup_for_display (display,
509 					      icon->priv->manager_window);
510 
511       gdk_window_add_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
512 
513       gtk_tray_icon_get_orientation_property (icon);
514       gtk_tray_icon_get_visual_property (icon);
515 
516       if (gtk_widget_get_realized (GTK_WIDGET (icon)))
517 	{
518 	  if ((icon->priv->manager_visual == NULL &&
519 	       gtk_widget_get_visual (widget) == gdk_screen_get_system_visual (screen)) ||
520 	      (icon->priv->manager_visual == gtk_widget_get_visual (widget)))
521 	    {
522 	      /* Already have the right visual, can just dock
523 	       */
524 	      gtk_tray_icon_send_dock_request (icon);
525 	    }
526 	  else
527 	    {
528 	      /* Need to re-realize the widget to get the right visual
529 	       */
530 	      gtk_widget_hide (widget);
531 	      gtk_widget_unrealize (widget);
532 	      gtk_widget_show (widget);
533 	    }
534 	}
535     }
536   else
537     GTK_NOTE (PLUGSOCKET,
538 	      g_print ("GtkStatusIcon %p: no tray manager found\n", icon));
539 }
540 
541 static void
gtk_tray_icon_manager_window_destroyed(GtkTrayIcon * icon)542 gtk_tray_icon_manager_window_destroyed (GtkTrayIcon *icon)
543 {
544   g_return_if_fail (icon->priv->manager_window != None);
545 
546   GTK_NOTE (PLUGSOCKET,
547 	    g_print ("GtkStatusIcon %p: tray manager window destroyed\n", icon));
548 
549   gtk_tray_icon_clear_manager_window (icon);
550 }
551 
552 static gboolean
gtk_tray_icon_delete(GtkWidget * widget,GdkEventAny * event)553 gtk_tray_icon_delete (GtkWidget   *widget,
554 		      GdkEventAny *event)
555 {
556 #ifdef G_ENABLE_DEBUG
557   GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
558 #endif
559 
560   GTK_NOTE (PLUGSOCKET,
561 	    g_print ("GtkStatusIcon %p: delete notify, tray manager window %lx\n",
562 		     icon, (gulong) icon->priv->manager_window));
563 
564   /* A bug in X server versions up to x.org 1.5.0 means that:
565    * XFixesChangeSaveSet(...., SaveSetRoot, SaveSetUnmap) doesn't work properly
566    * and we'll left mapped in a separate toplevel window if the tray is destroyed.
567    * For simplicity just get rid of our X window and start over.
568    */
569   gtk_widget_hide (widget);
570   gtk_widget_unrealize (widget);
571   gtk_widget_show (widget);
572 
573   /* Handled it, don't destroy the tray icon */
574   return TRUE;
575 }
576 
577 static void
gtk_tray_icon_set_colormap(GtkTrayIcon * icon)578 gtk_tray_icon_set_colormap (GtkTrayIcon *icon)
579 {
580   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (icon));
581   GdkColormap *colormap;
582   GdkVisual *visual = icon->priv->manager_visual;
583   gboolean new_colormap = FALSE;
584 
585   /* To avoid uncertainty about colormaps, _NET_SYSTEM_TRAY_VISUAL is supposed
586    * to be either the screen default visual or a TrueColor visual; ignore it
587    * if it is something else
588    */
589   if (visual && visual->type != GDK_VISUAL_TRUE_COLOR)
590     visual = NULL;
591 
592   if (visual == NULL || visual == gdk_screen_get_system_visual (screen))
593     colormap = gdk_screen_get_system_colormap (screen);
594   else if (visual == gdk_screen_get_rgb_visual (screen))
595     colormap = gdk_screen_get_rgb_colormap (screen);
596   else if (visual == gdk_screen_get_rgba_visual (screen))
597     colormap = gdk_screen_get_rgba_colormap (screen);
598   else
599     {
600       colormap = gdk_colormap_new (visual, FALSE);
601       new_colormap = TRUE;
602     }
603 
604   gtk_widget_set_colormap (GTK_WIDGET (icon), colormap);
605 
606   if (new_colormap)
607     g_object_unref (colormap);
608 }
609 
610 static void
gtk_tray_icon_realize(GtkWidget * widget)611 gtk_tray_icon_realize (GtkWidget *widget)
612 {
613   GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
614 
615   /* Set our colormap before realizing */
616   gtk_tray_icon_set_colormap (icon);
617 
618   GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->realize (widget);
619   if (icon->priv->manager_visual_rgba)
620     {
621       /* Set a transparent background */
622       GdkColor transparent = { 0, 0, 0, 0 }; /* Only pixel=0 matters */
623       gdk_window_set_background (widget->window, &transparent);
624     }
625   else
626     {
627       /* Set a parent-relative background pixmap */
628       gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
629     }
630 
631   GTK_NOTE (PLUGSOCKET,
632 	    g_print ("GtkStatusIcon %p: realized, window: %lx, socket window: %lx\n",
633 		     widget,
634 		     (gulong) GDK_WINDOW_XWINDOW (widget->window),
635 		     GTK_PLUG (icon)->socket_window ?
636 			     (gulong) GDK_WINDOW_XWINDOW (GTK_PLUG (icon)->socket_window) : 0UL));
637 
638   if (icon->priv->manager_window != None)
639     gtk_tray_icon_send_dock_request (icon);
640 }
641 
642 static void
gtk_tray_icon_style_set(GtkWidget * widget,GtkStyle * previous_style)643 gtk_tray_icon_style_set (GtkWidget   *widget,
644 			 GtkStyle    *previous_style)
645 {
646   /* The default handler resets the background according to the style. We either
647    * use a transparent background or a parent-relative background and ignore the
648    * style background. So, just don't chain up.
649    */
650 }
651 
652 guint
_gtk_tray_icon_send_message(GtkTrayIcon * icon,gint timeout,const gchar * message,gint len)653 _gtk_tray_icon_send_message (GtkTrayIcon *icon,
654 			     gint         timeout,
655 			     const gchar *message,
656 			     gint         len)
657 {
658   guint stamp;
659   Display *xdisplay;
660 
661   g_return_val_if_fail (GTK_IS_TRAY_ICON (icon), 0);
662   g_return_val_if_fail (timeout >= 0, 0);
663   g_return_val_if_fail (message != NULL, 0);
664 
665   if (icon->priv->manager_window == None)
666     return 0;
667 
668   if (len < 0)
669     len = strlen (message);
670 
671   stamp = icon->priv->stamp++;
672 
673   /* Get ready to send the message */
674   gtk_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE,
675 				      (Window)gtk_plug_get_id (GTK_PLUG (icon)),
676 				      timeout, len, stamp);
677 
678   /* Now to send the actual message */
679   xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
680   gdk_error_trap_push ();
681   while (len > 0)
682     {
683       XClientMessageEvent ev;
684 
685       memset (&ev, 0, sizeof (ev));
686       ev.type = ClientMessage;
687       ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon));
688       ev.format = 8;
689       ev.message_type = XInternAtom (xdisplay,
690 				     "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
691       if (len > 20)
692 	{
693 	  memcpy (&ev.data, message, 20);
694 	  len -= 20;
695 	  message += 20;
696 	}
697       else
698 	{
699 	  memcpy (&ev.data, message, len);
700 	  len = 0;
701 	}
702 
703       XSendEvent (xdisplay,
704 		  icon->priv->manager_window, False,
705 		  StructureNotifyMask, (XEvent *)&ev);
706     }
707   gdk_display_sync (gtk_widget_get_display (GTK_WIDGET (icon)));
708   gdk_error_trap_pop ();
709 
710   return stamp;
711 }
712 
713 void
_gtk_tray_icon_cancel_message(GtkTrayIcon * icon,guint id)714 _gtk_tray_icon_cancel_message (GtkTrayIcon *icon,
715 			       guint        id)
716 {
717   g_return_if_fail (GTK_IS_TRAY_ICON (icon));
718   g_return_if_fail (id > 0);
719 
720   gtk_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE,
721 				      (Window)gtk_plug_get_id (GTK_PLUG (icon)),
722 				      id, 0, 0);
723 }
724 
725 GtkTrayIcon *
_gtk_tray_icon_new_for_screen(GdkScreen * screen,const gchar * name)726 _gtk_tray_icon_new_for_screen (GdkScreen  *screen,
727 			       const gchar *name)
728 {
729   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
730 
731   return g_object_new (GTK_TYPE_TRAY_ICON,
732 		       "screen", screen,
733 		       "title", name,
734 		       NULL);
735 }
736 
737 GtkTrayIcon*
_gtk_tray_icon_new(const gchar * name)738 _gtk_tray_icon_new (const gchar *name)
739 {
740   return g_object_new (GTK_TYPE_TRAY_ICON,
741 		       "title", name,
742 		       NULL);
743 }
744 
745 GtkOrientation
_gtk_tray_icon_get_orientation(GtkTrayIcon * icon)746 _gtk_tray_icon_get_orientation (GtkTrayIcon *icon)
747 {
748   g_return_val_if_fail (GTK_IS_TRAY_ICON (icon), GTK_ORIENTATION_HORIZONTAL);
749 
750   return icon->priv->orientation;
751 }
752 
753 
754 #define __GTK_TRAY_ICON_X11_C__
755 #include "gtkaliasdef.c"
756 
757