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