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