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