2 * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
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.
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.
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.
21 * This is an implementation of the freedesktop.org "system tray" spec,
22 * http://www.freedesktop.org/wiki/Standards/systemtray-spec
30 #include "gtkprivate.h"
31 #include "gtktrayicon.h"
36 #include <X11/Xatom.h>
38 #define SYSTEM_TRAY_REQUEST_DOCK 0
39 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
40 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
42 #define SYSTEM_TRAY_ORIENTATION_HORZ 0
43 #define SYSTEM_TRAY_ORIENTATION_VERT 1
50 struct _GtkTrayIconPrivate
56 Atom system_tray_opcode_atom;
57 Atom orientation_atom;
58 Window manager_window;
60 GtkOrientation orientation;
63 static void gtk_tray_icon_get_property (GObject *object,
68 static void gtk_tray_icon_realize (GtkWidget *widget);
69 static void gtk_tray_icon_unrealize (GtkWidget *widget);
70 static gboolean gtk_tray_icon_delete (GtkWidget *widget,
72 static gboolean gtk_tray_icon_expose (GtkWidget *widget,
73 GdkEventExpose *event);
75 static void gtk_tray_icon_update_manager_window (GtkTrayIcon *icon,
76 gboolean dock_if_realized);
77 static void gtk_tray_icon_manager_window_destroyed (GtkTrayIcon *icon);
79 G_DEFINE_TYPE (GtkTrayIcon, gtk_tray_icon, GTK_TYPE_PLUG)
82 gtk_tray_icon_class_init (GtkTrayIconClass *class)
84 GObjectClass *gobject_class = (GObjectClass *)class;
85 GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
87 gobject_class->get_property = gtk_tray_icon_get_property;
89 widget_class->realize = gtk_tray_icon_realize;
90 widget_class->unrealize = gtk_tray_icon_unrealize;
91 widget_class->delete_event = gtk_tray_icon_delete;
92 widget_class->expose_event = gtk_tray_icon_expose;
94 g_object_class_install_property (gobject_class,
96 g_param_spec_enum ("orientation",
98 P_("The orientation of the tray"),
100 GTK_ORIENTATION_HORIZONTAL,
101 GTK_PARAM_READABLE));
103 g_type_class_add_private (class, sizeof (GtkTrayIconPrivate));
107 gtk_tray_icon_init (GtkTrayIcon *icon)
109 icon->priv = G_TYPE_INSTANCE_GET_PRIVATE (icon, GTK_TYPE_TRAY_ICON,
112 icon->priv->stamp = 1;
113 icon->priv->orientation = GTK_ORIENTATION_HORIZONTAL;
115 gtk_widget_set_app_paintable (GTK_WIDGET (icon), TRUE);
116 gtk_widget_set_double_buffered (GTK_WIDGET (icon), FALSE);
117 gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
121 gtk_tray_icon_get_property (GObject *object,
126 GtkTrayIcon *icon = GTK_TRAY_ICON (object);
130 case PROP_ORIENTATION:
131 g_value_set_enum (value, icon->priv->orientation);
134 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
140 gtk_tray_icon_expose (GtkWidget *widget,
141 GdkEventExpose *event)
143 gdk_window_clear_area (widget->window, event->area.x, event->area.y,
144 event->area.width, event->area.height);
146 if (GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->expose_event)
147 return GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->expose_event (widget, event);
153 gtk_tray_icon_get_orientation_property (GtkTrayIcon *icon)
166 g_assert (icon->priv->manager_window != None);
168 xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
170 gdk_error_trap_push ();
172 result = XGetWindowProperty (xdisplay,
173 icon->priv->manager_window,
174 icon->priv->orientation_atom,
177 &type, &format, &nitems,
178 &bytes_after, &(prop.prop_ch));
179 error = gdk_error_trap_pop ();
181 if (error || result != Success)
184 if (type == XA_CARDINAL)
186 GtkOrientation orientation;
188 orientation = (prop.prop [0] == SYSTEM_TRAY_ORIENTATION_HORZ) ?
189 GTK_ORIENTATION_HORIZONTAL :
190 GTK_ORIENTATION_VERTICAL;
192 if (icon->priv->orientation != orientation)
194 icon->priv->orientation = orientation;
196 g_object_notify (G_OBJECT (icon), "orientation");
204 static GdkFilterReturn
205 gtk_tray_icon_manager_filter (GdkXEvent *xevent,
209 GtkTrayIcon *icon = user_data;
210 XEvent *xev = (XEvent *)xevent;
212 if (xev->xany.type == ClientMessage &&
213 xev->xclient.message_type == icon->priv->manager_atom &&
214 xev->xclient.data.l[1] == icon->priv->selection_atom)
216 gtk_tray_icon_update_manager_window (icon, TRUE);
218 else if (xev->xany.window == icon->priv->manager_window)
220 if (xev->xany.type == PropertyNotify &&
221 xev->xproperty.atom == icon->priv->orientation_atom)
223 gtk_tray_icon_get_orientation_property (icon);
225 if (xev->xany.type == DestroyNotify)
227 gtk_tray_icon_manager_window_destroyed (icon);
231 return GDK_FILTER_CONTINUE;
235 gtk_tray_icon_unrealize (GtkWidget *widget)
237 GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
238 GdkWindow *root_window;
240 if (icon->priv->manager_window != None)
244 gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (widget),
245 icon->priv->manager_window);
247 gdk_window_remove_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
250 root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
252 gdk_window_remove_filter (root_window, gtk_tray_icon_manager_filter, icon);
254 if (GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->unrealize)
255 (* GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->unrealize) (widget);
259 gtk_tray_icon_send_manager_message (GtkTrayIcon *icon,
266 XClientMessageEvent ev;
269 memset (&ev, 0, sizeof (ev));
270 ev.type = ClientMessage;
272 ev.message_type = icon->priv->system_tray_opcode_atom;
274 ev.data.l[0] = gdk_x11_get_server_time (GTK_WIDGET (icon)->window);
275 ev.data.l[1] = message;
276 ev.data.l[2] = data1;
277 ev.data.l[3] = data2;
278 ev.data.l[4] = data3;
280 display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
282 gdk_error_trap_push ();
284 icon->priv->manager_window, False, NoEventMask, (XEvent *)&ev);
285 XSync (display, False);
286 gdk_error_trap_pop ();
290 gtk_tray_icon_send_dock_request (GtkTrayIcon *icon)
292 gtk_tray_icon_send_manager_message (icon,
293 SYSTEM_TRAY_REQUEST_DOCK,
294 icon->priv->manager_window,
295 gtk_plug_get_id (GTK_PLUG (icon)),
300 gtk_tray_icon_update_manager_window (GtkTrayIcon *icon,
301 gboolean dock_if_realized)
305 if (icon->priv->manager_window != None)
308 xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
310 XGrabServer (xdisplay);
312 icon->priv->manager_window = XGetSelectionOwner (xdisplay,
313 icon->priv->selection_atom);
315 if (icon->priv->manager_window != None)
316 XSelectInput (xdisplay,
317 icon->priv->manager_window, StructureNotifyMask|PropertyChangeMask);
319 XUngrabServer (xdisplay);
322 if (icon->priv->manager_window != None)
326 gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
327 icon->priv->manager_window);
329 gdk_window_add_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
331 if (dock_if_realized && GTK_WIDGET_REALIZED (icon))
332 gtk_tray_icon_send_dock_request (icon);
334 gtk_tray_icon_get_orientation_property (icon);
339 gtk_tray_icon_manager_window_destroyed (GtkTrayIcon *icon)
343 g_return_if_fail (icon->priv->manager_window != None);
345 gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
346 icon->priv->manager_window);
348 gdk_window_remove_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
350 icon->priv->manager_window = None;
352 gtk_tray_icon_update_manager_window (icon, TRUE);
356 gtk_tray_icon_delete (GtkWidget *widget,
359 GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
362 if (icon->priv->manager_window != None)
364 gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
365 icon->priv->manager_window);
367 gdk_window_remove_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
369 icon->priv->manager_window = None;
372 gtk_tray_icon_update_manager_window (icon, TRUE);
378 gtk_tray_icon_realize (GtkWidget *widget)
380 GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
385 GdkWindow *root_window;
387 if (GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->realize)
388 GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->realize (widget);
390 screen = gtk_widget_get_screen (widget);
391 display = gdk_screen_get_display (screen);
392 xdisplay = gdk_x11_display_get_xdisplay (display);
394 /* Now see if there's a manager window around */
395 g_snprintf (buffer, sizeof (buffer),
396 "_NET_SYSTEM_TRAY_S%d",
397 gdk_screen_get_number (screen));
399 icon->priv->selection_atom = XInternAtom (xdisplay, buffer, False);
401 icon->priv->manager_atom = XInternAtom (xdisplay, "MANAGER", False);
403 icon->priv->system_tray_opcode_atom = XInternAtom (xdisplay,
404 "_NET_SYSTEM_TRAY_OPCODE",
407 icon->priv->orientation_atom = XInternAtom (xdisplay,
408 "_NET_SYSTEM_TRAY_ORIENTATION",
411 gtk_tray_icon_update_manager_window (icon, FALSE);
412 gtk_tray_icon_send_dock_request (icon);
414 root_window = gdk_screen_get_root_window (screen);
416 /* Add a root window filter so that we get changes on MANAGER */
417 gdk_window_add_filter (root_window,
418 gtk_tray_icon_manager_filter, icon);
422 _gtk_tray_icon_send_message (GtkTrayIcon *icon,
424 const gchar *message,
429 g_return_val_if_fail (GTK_IS_TRAY_ICON (icon), 0);
430 g_return_val_if_fail (timeout >= 0, 0);
431 g_return_val_if_fail (message != NULL, 0);
433 if (icon->priv->manager_window == None)
437 len = strlen (message);
439 stamp = icon->priv->stamp++;
441 /* Get ready to send the message */
442 gtk_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE,
443 (Window)gtk_plug_get_id (GTK_PLUG (icon)),
444 timeout, len, stamp);
446 /* Now to send the actual message */
447 gdk_error_trap_push ();
450 XClientMessageEvent ev;
453 xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
455 memset (&ev, 0, sizeof (ev));
456 ev.type = ClientMessage;
457 ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon));
459 ev.message_type = XInternAtom (xdisplay,
460 "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
463 memcpy (&ev.data, message, 20);
469 memcpy (&ev.data, message, len);
473 XSendEvent (xdisplay,
474 icon->priv->manager_window, False,
475 StructureNotifyMask, (XEvent *)&ev);
476 XSync (xdisplay, False);
479 gdk_error_trap_pop ();
485 _gtk_tray_icon_cancel_message (GtkTrayIcon *icon,
488 g_return_if_fail (GTK_IS_TRAY_ICON (icon));
489 g_return_if_fail (id > 0);
491 gtk_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE,
492 (Window)gtk_plug_get_id (GTK_PLUG (icon)),
497 _gtk_tray_icon_new_for_screen (GdkScreen *screen,
500 g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
502 return g_object_new (GTK_TYPE_TRAY_ICON,
509 _gtk_tray_icon_new (const gchar *name)
511 return g_object_new (GTK_TYPE_TRAY_ICON,
517 _gtk_tray_icon_get_orientation (GtkTrayIcon *icon)
519 g_return_val_if_fail (GTK_IS_TRAY_ICON (icon), GTK_ORIENTATION_HORIZONTAL);
521 return icon->priv->orientation;
525 #define __GTK_TRAY_ICON_X11_C__
526 #include "gtkaliasdef.c"