]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/tray-monitor/eggtrayicon.c
kes Fix sscanf problems reported by Peter Buschman that caused
[bacula/bacula] / bacula / src / tray-monitor / eggtrayicon.c
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 extern "C" {
22
23 #include <string.h>
24 #include <libintl.h>
25
26 #include "eggtrayicon.h"
27
28 #include <gdk/gdkx.h>
29 #include <X11/Xatom.h>
30
31 #ifndef EGG_COMPILATION
32 #ifndef _
33 #define _(x) dgettext (GETTEXT_PACKAGE, x)
34 #define N_(x) x
35 #endif
36 #else
37 #define _(x) x
38 #define N_(x) x
39 #endif
40
41 #define SYSTEM_TRAY_REQUEST_DOCK    0
42 #define SYSTEM_TRAY_BEGIN_MESSAGE   1
43 #define SYSTEM_TRAY_CANCEL_MESSAGE  2
44
45 #define SYSTEM_TRAY_ORIENTATION_HORZ 0
46 #define SYSTEM_TRAY_ORIENTATION_VERT 1
47
48 enum {
49   PROP_0,
50   PROP_ORIENTATION
51 };
52
53 static GtkPlugClass *parent_class = NULL;
54
55 static void egg_tray_icon_init (EggTrayIcon *icon);
56 static void egg_tray_icon_class_init (EggTrayIconClass *klass);
57
58 static void egg_tray_icon_get_property (GObject    *object,
59                                         guint       prop_id,
60                                         GValue     *value,
61                                         GParamSpec *pspec);
62
63 static void egg_tray_icon_realize   (GtkWidget *widget);
64 static void egg_tray_icon_unrealize (GtkWidget *widget);
65
66 static void egg_tray_icon_update_manager_window (EggTrayIcon *icon);
67
68 GType
69 egg_tray_icon_get_type (void)
70 {
71   static GType our_type = 0;
72
73   if (our_type == 0)
74     {
75       static const GTypeInfo our_info =
76       {
77         sizeof (EggTrayIconClass),
78         (GBaseInitFunc) NULL,
79         (GBaseFinalizeFunc) NULL,
80         (GClassInitFunc) egg_tray_icon_class_init,
81         NULL, /* class_finalize */
82         NULL, /* class_data */
83         sizeof (EggTrayIcon),
84         0,    /* n_preallocs */
85         (GInstanceInitFunc) egg_tray_icon_init
86       };
87
88       our_type = g_type_register_static (GTK_TYPE_PLUG, "EggTrayIcon", &our_info, (GTypeFlags)0);
89     }
90
91   return our_type;
92 }
93
94 static void
95 egg_tray_icon_init (EggTrayIcon *icon)
96 {
97   icon->stamp = 1;
98   icon->orientation = GTK_ORIENTATION_HORIZONTAL;
99
100   gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
101 }
102
103 static void
104 egg_tray_icon_class_init (EggTrayIconClass *klass)
105 {
106   GObjectClass *gobject_class = (GObjectClass *)klass;
107   GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
108
109   parent_class = (GtkPlugClass*)g_type_class_peek_parent (klass);
110
111   gobject_class->get_property = egg_tray_icon_get_property;
112
113   widget_class->realize   = egg_tray_icon_realize;
114   widget_class->unrealize = egg_tray_icon_unrealize;
115
116   g_object_class_install_property (gobject_class,
117                                    PROP_ORIENTATION,
118                                    g_param_spec_enum ("orientation",
119                                                       "Orientation",
120                                                       "The orientation of the tray.",
121                                                       GTK_TYPE_ORIENTATION,
122                                                       GTK_ORIENTATION_HORIZONTAL,
123                                                       G_PARAM_READABLE));
124 }
125
126 static void
127 egg_tray_icon_get_property (GObject    *object,
128                             guint       prop_id,
129                             GValue     *value,
130                             GParamSpec *pspec)
131 {
132   EggTrayIcon *icon = EGG_TRAY_ICON (object);
133
134   switch (prop_id)
135     {
136     case PROP_ORIENTATION:
137       g_value_set_enum (value, icon->orientation);
138       break;
139     default:
140       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
141       break;
142     }
143 }
144
145 static void
146 egg_tray_icon_get_orientation_property (EggTrayIcon *icon)
147 {
148   Display *xdisplay;
149   Atom type;
150   int format;
151   union {
152         gulong *prop;
153         guchar *prop_ch;
154   } prop = { NULL };
155   gulong nitems;
156   gulong bytes_after;
157   int error, result;
158
159   g_assert (icon->manager_window != None);
160
161   xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
162
163   gdk_error_trap_push ();
164   type = None;
165   result = XGetWindowProperty (xdisplay,
166                                icon->manager_window,
167                                icon->orientation_atom,
168                                0, G_MAXLONG, FALSE,
169                                XA_CARDINAL,
170                                &type, &format, &nitems,
171                                &bytes_after, &(prop.prop_ch));
172   error = gdk_error_trap_pop ();
173
174   if (error || result != Success)
175     return;
176
177   if (type == XA_CARDINAL)
178     {
179       GtkOrientation orientation;
180
181       orientation = (prop.prop [0] == SYSTEM_TRAY_ORIENTATION_HORZ) ?
182                                         GTK_ORIENTATION_HORIZONTAL :
183                                         GTK_ORIENTATION_VERTICAL;
184
185       if (icon->orientation != orientation)
186         {
187           icon->orientation = orientation;
188
189           g_object_notify (G_OBJECT (icon), "orientation");
190         }
191     }
192
193   if (prop.prop)
194     XFree (prop.prop);
195 }
196
197 static GdkFilterReturn
198 egg_tray_icon_manager_filter (GdkXEvent *xevent, GdkEvent *event, gpointer user_data)
199 {
200   EggTrayIcon *icon = (EggTrayIcon*)user_data;
201   XEvent *xev = (XEvent *)xevent;
202
203   if (xev->xany.type == ClientMessage &&
204       xev->xclient.message_type == icon->manager_atom &&
205       (unsigned long)xev->xclient.data.l[1] == icon->selection_atom)
206     {
207       egg_tray_icon_update_manager_window (icon);
208     }
209   else if (xev->xany.window == icon->manager_window)
210     {
211       if (xev->xany.type == PropertyNotify &&
212           xev->xproperty.atom == icon->orientation_atom)
213         {
214           egg_tray_icon_get_orientation_property (icon);
215         }
216       if (xev->xany.type == DestroyNotify)
217         {
218           egg_tray_icon_update_manager_window (icon);
219         }
220     }
221
222   return GDK_FILTER_CONTINUE;
223 }
224
225 static void
226 egg_tray_icon_unrealize (GtkWidget *widget)
227 {
228   EggTrayIcon *icon = EGG_TRAY_ICON (widget);
229   GdkWindow *root_window;
230
231   if (icon->manager_window != None)
232     {
233       GdkWindow *gdkwin;
234
235       gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (widget),
236                                               icon->manager_window);
237
238       gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
239     }
240
241   root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
242
243   gdk_window_remove_filter (root_window, egg_tray_icon_manager_filter, icon);
244
245   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
246     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
247 }
248
249 static void
250 egg_tray_icon_send_manager_message (EggTrayIcon *icon,
251                                     long         message,
252                                     Window       window,
253                                     long         data1,
254                                     long         data2,
255                                     long         data3)
256 {
257   XClientMessageEvent ev;
258   Display *display;
259
260   ev.type = ClientMessage;
261   ev.window = window;
262   ev.message_type = icon->system_tray_opcode_atom;
263   ev.format = 32;
264   ev.data.l[0] = gdk_x11_get_server_time (GTK_WIDGET (icon)->window);
265   ev.data.l[1] = message;
266   ev.data.l[2] = data1;
267   ev.data.l[3] = data2;
268   ev.data.l[4] = data3;
269
270   display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
271
272   gdk_error_trap_push ();
273   XSendEvent (display,
274               icon->manager_window, False, NoEventMask, (XEvent *)&ev);
275   XSync (display, False);
276   gdk_error_trap_pop ();
277 }
278
279 static void
280 egg_tray_icon_send_dock_request (EggTrayIcon *icon)
281 {
282   egg_tray_icon_send_manager_message (icon,
283                                       SYSTEM_TRAY_REQUEST_DOCK,
284                                       icon->manager_window,
285                                       gtk_plug_get_id (GTK_PLUG (icon)),
286                                       0, 0);
287 }
288
289 static void
290 egg_tray_icon_update_manager_window (EggTrayIcon *icon)
291 {
292   Display *xdisplay;
293
294   xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
295
296   if (icon->manager_window != None)
297     {
298       GdkWindow *gdkwin;
299
300       gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
301                                               icon->manager_window);
302
303       gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
304     }
305
306   XGrabServer (xdisplay);
307
308   icon->manager_window = XGetSelectionOwner (xdisplay,
309                                              icon->selection_atom);
310
311   if (icon->manager_window != None)
312     XSelectInput (xdisplay,
313                   icon->manager_window, StructureNotifyMask|PropertyChangeMask);
314
315   XUngrabServer (xdisplay);
316   XFlush (xdisplay);
317
318   if (icon->manager_window != None)
319     {
320       GdkWindow *gdkwin;
321
322       gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
323                                               icon->manager_window);
324
325       gdk_window_add_filter (gdkwin, egg_tray_icon_manager_filter, icon);
326
327       /* Send a request that we'd like to dock */
328       egg_tray_icon_send_dock_request (icon);
329
330       egg_tray_icon_get_orientation_property (icon);
331     }
332 }
333
334 static void
335 egg_tray_icon_realize (GtkWidget *widget)
336 {
337   EggTrayIcon *icon = EGG_TRAY_ICON (widget);
338   GdkScreen *screen;
339   GdkDisplay *display;
340   Display *xdisplay;
341   char buffer[256];
342   GdkWindow *root_window;
343
344   if (GTK_WIDGET_CLASS (parent_class)->realize)
345     GTK_WIDGET_CLASS (parent_class)->realize (widget);
346
347   screen = gtk_widget_get_screen (widget);
348   display = gdk_screen_get_display (screen);
349   xdisplay = gdk_x11_display_get_xdisplay (display);
350
351   /* Now see if there's a manager window around */
352   g_snprintf (buffer, sizeof (buffer),
353               "_NET_SYSTEM_TRAY_S%d",
354               gdk_screen_get_number (screen));
355
356   icon->selection_atom = XInternAtom (xdisplay, buffer, False);
357
358   icon->manager_atom = XInternAtom (xdisplay, "MANAGER", False);
359
360   icon->system_tray_opcode_atom = XInternAtom (xdisplay,
361                                                    "_NET_SYSTEM_TRAY_OPCODE",
362                                                    False);
363
364   icon->orientation_atom = XInternAtom (xdisplay,
365                                         "_NET_SYSTEM_TRAY_ORIENTATION",
366                                         False);
367
368   egg_tray_icon_update_manager_window (icon);
369
370   root_window = gdk_screen_get_root_window (screen);
371
372   /* Add a root window filter so that we get changes on MANAGER */
373   gdk_window_add_filter (root_window,
374                          egg_tray_icon_manager_filter, icon);
375 }
376
377 EggTrayIcon *
378 egg_tray_icon_new_for_xscreen (Screen *xscreen, const char *name)
379 {
380   GdkDisplay *display;
381   GdkScreen *screen;
382
383   display = gdk_x11_lookup_xdisplay (DisplayOfScreen (xscreen));
384   screen = gdk_display_get_screen (display, XScreenNumberOfScreen (xscreen));
385
386   return egg_tray_icon_new_for_screen (screen, name);
387 }
388
389 EggTrayIcon *
390 egg_tray_icon_new_for_screen (GdkScreen *screen, const char *name)
391 {
392   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
393
394   return (EggTrayIcon*)g_object_new (EGG_TYPE_TRAY_ICON, "screen", screen, "title", name, NULL);
395 }
396
397 EggTrayIcon*
398 egg_tray_icon_new (const gchar *name)
399 {
400   return (EggTrayIcon*)g_object_new (EGG_TYPE_TRAY_ICON, "title", name, NULL);
401 }
402
403 guint
404 egg_tray_icon_send_message (EggTrayIcon *icon,
405                             gint         timeout,
406                             const gchar *message,
407                             gint         len)
408 {
409   guint stamp;
410
411   g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), 0);
412   g_return_val_if_fail (timeout >= 0, 0);
413   g_return_val_if_fail (message != NULL, 0);
414
415   if (icon->manager_window == None)
416     return 0;
417
418   if (len < 0)
419     len = strlen (message);
420
421   stamp = icon->stamp++;
422
423   /* Get ready to send the message */
424   egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE,
425                                       (Window)gtk_plug_get_id (GTK_PLUG (icon)),
426                                       timeout, len, stamp);
427
428   /* Now to send the actual message */
429   gdk_error_trap_push ();
430   while (len > 0)
431     {
432       XClientMessageEvent ev;
433       Display *xdisplay;
434
435       xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
436
437       ev.type = ClientMessage;
438       ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon));
439       ev.format = 8;
440       ev.message_type = XInternAtom (xdisplay,
441                                      "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
442       if (len > 20)
443         {
444           memcpy (&ev.data, message, 20);
445           len -= 20;
446           message += 20;
447         }
448       else
449         {
450           memcpy (&ev.data, message, len);
451           len = 0;
452         }
453
454       XSendEvent (xdisplay,
455                   icon->manager_window, False, StructureNotifyMask, (XEvent *)&ev);
456       XSync (xdisplay, False);
457     }
458   gdk_error_trap_pop ();
459
460   return stamp;
461 }
462
463 void
464 egg_tray_icon_cancel_message (EggTrayIcon *icon,
465                               guint        id)
466 {
467   g_return_if_fail (EGG_IS_TRAY_ICON (icon));
468   g_return_if_fail (id > 0);
469
470   egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE,
471                                       (Window)gtk_plug_get_id (GTK_PLUG (icon)),
472                                       id, 0, 0);
473 }
474
475 GtkOrientation
476 egg_tray_icon_get_orientation (EggTrayIcon *icon)
477 {
478   g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), GTK_ORIENTATION_HORIZONTAL);
479
480   return icon->orientation;
481 }
482
483 } //extern "C"