1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
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.
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.
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.
21 Bacula® - The Network Backup Solution
23 Copyright (C) 2004-2006 Free Software Foundation Europe e.V.
25 The main author of Bacula is Kern Sibbald, with contributions from
26 many others, a complete list can be found in the file AUTHORS.
27 This program is Free Software; you can redistribute it and/or
28 modify it under the terms of version two of the GNU General Public
29 License as published by the Free Software Foundation plus additions
30 that are listed in the file LICENSE.
32 This program is distributed in the hope that it will be useful, but
33 WITHOUT ANY WARRANTY; without even the implied warranty of
34 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
35 General Public License for more details.
37 You should have received a copy of the GNU General Public License
38 along with this program; if not, write to the Free Software
39 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
42 Bacula® is a registered trademark of John Walker.
43 The licensor of Bacula is the Free Software Foundation Europe
44 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
45 Switzerland, email:ftf@fsfeurope.org.
53 #include "eggtrayicon.h"
56 #include <X11/Xatom.h>
58 #ifndef EGG_COMPILATION
60 #define _(x) dgettext (GETTEXT_PACKAGE, x)
68 #define SYSTEM_TRAY_REQUEST_DOCK 0
69 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
70 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
72 #define SYSTEM_TRAY_ORIENTATION_HORZ 0
73 #define SYSTEM_TRAY_ORIENTATION_VERT 1
80 static GtkPlugClass *parent_class = NULL;
82 static void egg_tray_icon_init (EggTrayIcon *icon);
83 static void egg_tray_icon_class_init (EggTrayIconClass *klass);
85 static void egg_tray_icon_get_property (GObject *object,
90 static void egg_tray_icon_realize (GtkWidget *widget);
91 static void egg_tray_icon_unrealize (GtkWidget *widget);
93 static void egg_tray_icon_update_manager_window (EggTrayIcon *icon);
96 egg_tray_icon_get_type (void)
98 static GType our_type = 0;
102 static const GTypeInfo our_info =
104 sizeof (EggTrayIconClass),
105 (GBaseInitFunc) NULL,
106 (GBaseFinalizeFunc) NULL,
107 (GClassInitFunc) egg_tray_icon_class_init,
108 NULL, /* class_finalize */
109 NULL, /* class_data */
110 sizeof (EggTrayIcon),
112 (GInstanceInitFunc) egg_tray_icon_init
115 our_type = g_type_register_static (GTK_TYPE_PLUG, "EggTrayIcon", &our_info, (GTypeFlags)0);
122 egg_tray_icon_init (EggTrayIcon *icon)
125 icon->orientation = GTK_ORIENTATION_HORIZONTAL;
127 gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
131 egg_tray_icon_class_init (EggTrayIconClass *klass)
133 GObjectClass *gobject_class = (GObjectClass *)klass;
134 GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
136 parent_class = (GtkPlugClass*)g_type_class_peek_parent (klass);
138 gobject_class->get_property = egg_tray_icon_get_property;
140 widget_class->realize = egg_tray_icon_realize;
141 widget_class->unrealize = egg_tray_icon_unrealize;
143 g_object_class_install_property (gobject_class,
145 g_param_spec_enum ("orientation",
147 "The orientation of the tray.",
148 GTK_TYPE_ORIENTATION,
149 GTK_ORIENTATION_HORIZONTAL,
154 egg_tray_icon_get_property (GObject *object,
159 EggTrayIcon *icon = EGG_TRAY_ICON (object);
163 case PROP_ORIENTATION:
164 g_value_set_enum (value, icon->orientation);
167 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
173 egg_tray_icon_get_orientation_property (EggTrayIcon *icon)
186 g_assert (icon->manager_window != None);
188 xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
190 gdk_error_trap_push ();
192 result = XGetWindowProperty (xdisplay,
193 icon->manager_window,
194 icon->orientation_atom,
197 &type, &format, &nitems,
198 &bytes_after, &(prop.prop_ch));
199 error = gdk_error_trap_pop ();
201 if (error || result != Success)
204 if (type == XA_CARDINAL)
206 GtkOrientation orientation;
208 orientation = (prop.prop [0] == SYSTEM_TRAY_ORIENTATION_HORZ) ?
209 GTK_ORIENTATION_HORIZONTAL :
210 GTK_ORIENTATION_VERTICAL;
212 if (icon->orientation != orientation)
214 icon->orientation = orientation;
216 g_object_notify (G_OBJECT (icon), "orientation");
224 static GdkFilterReturn
225 egg_tray_icon_manager_filter (GdkXEvent *xevent, GdkEvent *event, gpointer user_data)
227 EggTrayIcon *icon = (EggTrayIcon*)user_data;
228 XEvent *xev = (XEvent *)xevent;
230 if (xev->xany.type == ClientMessage &&
231 xev->xclient.message_type == icon->manager_atom &&
232 (unsigned long)xev->xclient.data.l[1] == icon->selection_atom)
234 egg_tray_icon_update_manager_window (icon);
236 else if (xev->xany.window == icon->manager_window)
238 if (xev->xany.type == PropertyNotify &&
239 xev->xproperty.atom == icon->orientation_atom)
241 egg_tray_icon_get_orientation_property (icon);
243 if (xev->xany.type == DestroyNotify)
245 egg_tray_icon_update_manager_window (icon);
249 return GDK_FILTER_CONTINUE;
253 egg_tray_icon_unrealize (GtkWidget *widget)
255 EggTrayIcon *icon = EGG_TRAY_ICON (widget);
256 GdkWindow *root_window;
258 if (icon->manager_window != None)
262 gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (widget),
263 icon->manager_window);
265 gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
268 root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
270 gdk_window_remove_filter (root_window, egg_tray_icon_manager_filter, icon);
272 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
273 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
277 egg_tray_icon_send_manager_message (EggTrayIcon *icon,
284 XClientMessageEvent ev;
287 ev.type = ClientMessage;
289 ev.message_type = icon->system_tray_opcode_atom;
291 ev.data.l[0] = gdk_x11_get_server_time (GTK_WIDGET (icon)->window);
292 ev.data.l[1] = message;
293 ev.data.l[2] = data1;
294 ev.data.l[3] = data2;
295 ev.data.l[4] = data3;
297 display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
299 gdk_error_trap_push ();
301 icon->manager_window, False, NoEventMask, (XEvent *)&ev);
302 XSync (display, False);
303 gdk_error_trap_pop ();
307 egg_tray_icon_send_dock_request (EggTrayIcon *icon)
309 egg_tray_icon_send_manager_message (icon,
310 SYSTEM_TRAY_REQUEST_DOCK,
311 icon->manager_window,
312 gtk_plug_get_id (GTK_PLUG (icon)),
317 egg_tray_icon_update_manager_window (EggTrayIcon *icon)
321 xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
323 if (icon->manager_window != None)
327 gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
328 icon->manager_window);
330 gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
333 XGrabServer (xdisplay);
335 icon->manager_window = XGetSelectionOwner (xdisplay,
336 icon->selection_atom);
338 if (icon->manager_window != None)
339 XSelectInput (xdisplay,
340 icon->manager_window, StructureNotifyMask|PropertyChangeMask);
342 XUngrabServer (xdisplay);
345 if (icon->manager_window != None)
349 gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
350 icon->manager_window);
352 gdk_window_add_filter (gdkwin, egg_tray_icon_manager_filter, icon);
354 /* Send a request that we'd like to dock */
355 egg_tray_icon_send_dock_request (icon);
357 egg_tray_icon_get_orientation_property (icon);
362 egg_tray_icon_realize (GtkWidget *widget)
364 EggTrayIcon *icon = EGG_TRAY_ICON (widget);
369 GdkWindow *root_window;
371 if (GTK_WIDGET_CLASS (parent_class)->realize)
372 GTK_WIDGET_CLASS (parent_class)->realize (widget);
374 screen = gtk_widget_get_screen (widget);
375 display = gdk_screen_get_display (screen);
376 xdisplay = gdk_x11_display_get_xdisplay (display);
378 /* Now see if there's a manager window around */
379 g_snprintf (buffer, sizeof (buffer),
380 "_NET_SYSTEM_TRAY_S%d",
381 gdk_screen_get_number (screen));
383 icon->selection_atom = XInternAtom (xdisplay, buffer, False);
385 icon->manager_atom = XInternAtom (xdisplay, "MANAGER", False);
387 icon->system_tray_opcode_atom = XInternAtom (xdisplay,
388 "_NET_SYSTEM_TRAY_OPCODE",
391 icon->orientation_atom = XInternAtom (xdisplay,
392 "_NET_SYSTEM_TRAY_ORIENTATION",
395 egg_tray_icon_update_manager_window (icon);
397 root_window = gdk_screen_get_root_window (screen);
399 /* Add a root window filter so that we get changes on MANAGER */
400 gdk_window_add_filter (root_window,
401 egg_tray_icon_manager_filter, icon);
405 egg_tray_icon_new_for_xscreen (Screen *xscreen, const char *name)
410 display = gdk_x11_lookup_xdisplay (DisplayOfScreen (xscreen));
411 screen = gdk_display_get_screen (display, XScreenNumberOfScreen (xscreen));
413 return egg_tray_icon_new_for_screen (screen, name);
417 egg_tray_icon_new_for_screen (GdkScreen *screen, const char *name)
419 g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
421 return (EggTrayIcon*)g_object_new (EGG_TYPE_TRAY_ICON, "screen", screen, "title", name, NULL);
425 egg_tray_icon_new (const gchar *name)
427 return (EggTrayIcon*)g_object_new (EGG_TYPE_TRAY_ICON, "title", name, NULL);
431 egg_tray_icon_send_message (EggTrayIcon *icon,
433 const gchar *message,
438 g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), 0);
439 g_return_val_if_fail (timeout >= 0, 0);
440 g_return_val_if_fail (message != NULL, 0);
442 if (icon->manager_window == None)
446 len = strlen (message);
448 stamp = icon->stamp++;
450 /* Get ready to send the message */
451 egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE,
452 (Window)gtk_plug_get_id (GTK_PLUG (icon)),
453 timeout, len, stamp);
455 /* Now to send the actual message */
456 gdk_error_trap_push ();
459 XClientMessageEvent ev;
462 xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
464 ev.type = ClientMessage;
465 ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon));
467 ev.message_type = XInternAtom (xdisplay,
468 "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
471 memcpy (&ev.data, message, 20);
477 memcpy (&ev.data, message, len);
481 XSendEvent (xdisplay,
482 icon->manager_window, False, StructureNotifyMask, (XEvent *)&ev);
483 XSync (xdisplay, False);
485 gdk_error_trap_pop ();
491 egg_tray_icon_cancel_message (EggTrayIcon *icon,
494 g_return_if_fail (EGG_IS_TRAY_ICON (icon));
495 g_return_if_fail (id > 0);
497 egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE,
498 (Window)gtk_plug_get_id (GTK_PLUG (icon)),
503 egg_tray_icon_get_orientation (EggTrayIcon *icon)
505 g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), GTK_ORIENTATION_HORIZONTAL);
507 return icon->orientation;