]> Pileus Git - ~andy/gtk/blob - gtk/gtktrayicon-x11.c
Fix symbol lists for make check.
[~andy/gtk] / gtk / gtktrayicon-x11.c
1 /* gtktrayicon.c
2  * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 /*
21  * This is an implementation of the freedesktop.org "system tray" spec,
22  * http://www.freedesktop.org/wiki/Standards/systemtray-spec
23  */
24
25 #include <config.h>
26 #include <string.h>
27 #include <libintl.h>
28
29 #include "gtkintl.h"
30 #include "gtkprivate.h"
31 #include "gtktrayicon.h"
32
33 #include "gtkalias.h"
34
35 #include "x11/gdkx.h"
36 #include <X11/Xatom.h>
37
38 #define SYSTEM_TRAY_REQUEST_DOCK    0
39 #define SYSTEM_TRAY_BEGIN_MESSAGE   1
40 #define SYSTEM_TRAY_CANCEL_MESSAGE  2
41
42 #define SYSTEM_TRAY_ORIENTATION_HORZ 0
43 #define SYSTEM_TRAY_ORIENTATION_VERT 1
44
45 enum {
46   PROP_0,
47   PROP_ORIENTATION
48 };
49
50 struct _GtkTrayIconPrivate
51 {
52   guint stamp;
53   
54   Atom selection_atom;
55   Atom manager_atom;
56   Atom system_tray_opcode_atom;
57   Atom orientation_atom;
58   Window manager_window;
59
60   GtkOrientation orientation;
61 };
62          
63 static void gtk_tray_icon_get_property  (GObject     *object,
64                                          guint        prop_id,
65                                          GValue      *value,
66                                          GParamSpec  *pspec);
67
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,
71                                          GdkEventAny *event);
72
73 static void gtk_tray_icon_update_manager_window    (GtkTrayIcon *icon,
74                                                     gboolean     dock_if_realized);
75 static void gtk_tray_icon_manager_window_destroyed (GtkTrayIcon *icon);
76
77 G_DEFINE_TYPE (GtkTrayIcon, gtk_tray_icon, GTK_TYPE_PLUG);
78
79 static void
80 gtk_tray_icon_class_init (GtkTrayIconClass *class)
81 {
82   GObjectClass *gobject_class = (GObjectClass *)class;
83   GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
84
85   gobject_class->get_property = gtk_tray_icon_get_property;
86
87   widget_class->realize   = gtk_tray_icon_realize;
88   widget_class->unrealize = gtk_tray_icon_unrealize;
89   widget_class->delete_event = gtk_tray_icon_delete;
90
91   g_object_class_install_property (gobject_class,
92                                    PROP_ORIENTATION,
93                                    g_param_spec_enum ("orientation",
94                                                       P_("Orientation"),
95                                                       P_("The orientation of the tray"),
96                                                       GTK_TYPE_ORIENTATION,
97                                                       GTK_ORIENTATION_HORIZONTAL,
98                                                       GTK_PARAM_READABLE));
99
100   g_type_class_add_private (class, sizeof (GtkTrayIconPrivate));
101 }
102
103 static void
104 gtk_tray_icon_init (GtkTrayIcon *icon)
105 {
106   icon->priv = G_TYPE_INSTANCE_GET_PRIVATE (icon, GTK_TYPE_TRAY_ICON,
107                                             GtkTrayIconPrivate);
108   
109   icon->priv->stamp = 1;
110   icon->priv->orientation = GTK_ORIENTATION_HORIZONTAL;
111   
112   gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
113 }
114
115 static void
116 gtk_tray_icon_get_property (GObject    *object,
117                             guint       prop_id,
118                             GValue     *value,
119                             GParamSpec *pspec)
120 {
121   GtkTrayIcon *icon = GTK_TRAY_ICON (object);
122
123   switch (prop_id)
124     {
125     case PROP_ORIENTATION:
126       g_value_set_enum (value, icon->priv->orientation);
127       break;
128     default:
129       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
130       break;
131     }
132 }
133
134 static void
135 gtk_tray_icon_get_orientation_property (GtkTrayIcon *icon)
136 {
137   Display *xdisplay;
138   Atom type;
139   int format;
140   union {
141         gulong *prop;
142         guchar *prop_ch;
143   } prop = { NULL };
144   gulong nitems;
145   gulong bytes_after;
146   int error, result;
147
148   g_assert (icon->priv->manager_window != None);
149   
150   xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
151
152   gdk_error_trap_push ();
153   type = None;
154   result = XGetWindowProperty (xdisplay,
155                                icon->priv->manager_window,
156                                icon->priv->orientation_atom,
157                                0, G_MAXLONG, FALSE,
158                                XA_CARDINAL,
159                                &type, &format, &nitems,
160                                &bytes_after, &(prop.prop_ch));
161   error = gdk_error_trap_pop ();
162
163   if (error || result != Success)
164     return;
165
166   if (type == XA_CARDINAL)
167     {
168       GtkOrientation orientation;
169
170       orientation = (prop.prop [0] == SYSTEM_TRAY_ORIENTATION_HORZ) ?
171                                         GTK_ORIENTATION_HORIZONTAL :
172                                         GTK_ORIENTATION_VERTICAL;
173
174       if (icon->priv->orientation != orientation)
175         {
176           icon->priv->orientation = orientation;
177
178           g_object_notify (G_OBJECT (icon), "orientation");
179         }
180     }
181
182   if (prop.prop)
183     XFree (prop.prop);
184 }
185
186 static GdkFilterReturn
187 gtk_tray_icon_manager_filter (GdkXEvent *xevent, 
188                               GdkEvent  *event, 
189                               gpointer   user_data)
190 {
191   GtkTrayIcon *icon = user_data;
192   XEvent *xev = (XEvent *)xevent;
193
194   if (xev->xany.type == ClientMessage &&
195       xev->xclient.message_type == icon->priv->manager_atom &&
196       xev->xclient.data.l[1] == icon->priv->selection_atom)
197     {
198       gtk_tray_icon_update_manager_window (icon, TRUE);
199     }
200   else if (xev->xany.window == icon->priv->manager_window)
201     {
202       if (xev->xany.type == PropertyNotify &&
203           xev->xproperty.atom == icon->priv->orientation_atom)
204         {
205           gtk_tray_icon_get_orientation_property (icon);
206         }
207       if (xev->xany.type == DestroyNotify)
208         {
209           gtk_tray_icon_manager_window_destroyed (icon);
210         }
211     }
212   
213   return GDK_FILTER_CONTINUE;
214 }
215
216 static void
217 gtk_tray_icon_unrealize (GtkWidget *widget)
218 {
219   GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
220   GdkWindow *root_window;
221
222   if (icon->priv->manager_window != None)
223     {
224       GdkWindow *gdkwin;
225
226       gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (widget),
227                                               icon->priv->manager_window);
228       
229       gdk_window_remove_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
230     }
231
232   root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
233
234   gdk_window_remove_filter (root_window, gtk_tray_icon_manager_filter, icon);
235
236   if (GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->unrealize)
237     (* GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->unrealize) (widget);
238 }
239
240 static void
241 gtk_tray_icon_send_manager_message (GtkTrayIcon *icon,
242                                     long         message,
243                                     Window       window,
244                                     long         data1,
245                                     long         data2,
246                                     long         data3)
247 {
248   XClientMessageEvent ev;
249   Display *display;
250   
251   ev.type = ClientMessage;
252   ev.window = window;
253   ev.message_type = icon->priv->system_tray_opcode_atom;
254   ev.format = 32;
255   ev.data.l[0] = gdk_x11_get_server_time (GTK_WIDGET (icon)->window);
256   ev.data.l[1] = message;
257   ev.data.l[2] = data1;
258   ev.data.l[3] = data2;
259   ev.data.l[4] = data3;
260
261   display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
262   
263   gdk_error_trap_push ();
264   XSendEvent (display,
265               icon->priv->manager_window, False, NoEventMask, (XEvent *)&ev);
266   XSync (display, False);
267   gdk_error_trap_pop ();
268 }
269
270 static void
271 gtk_tray_icon_send_dock_request (GtkTrayIcon *icon)
272 {
273   gtk_tray_icon_send_manager_message (icon,
274                                       SYSTEM_TRAY_REQUEST_DOCK,
275                                       icon->priv->manager_window,
276                                       gtk_plug_get_id (GTK_PLUG (icon)),
277                                       0, 0);
278 }
279
280 static void
281 gtk_tray_icon_update_manager_window (GtkTrayIcon *icon,
282                                      gboolean     dock_if_realized)
283 {
284   Display *xdisplay;
285   
286   if (icon->priv->manager_window != None)
287     return;
288
289   xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
290   
291   XGrabServer (xdisplay);
292   
293   icon->priv->manager_window = XGetSelectionOwner (xdisplay,
294                                                    icon->priv->selection_atom);
295
296   if (icon->priv->manager_window != None)
297     XSelectInput (xdisplay,
298                   icon->priv->manager_window, StructureNotifyMask|PropertyChangeMask);
299
300   XUngrabServer (xdisplay);
301   XFlush (xdisplay);
302   
303   if (icon->priv->manager_window != None)
304     {
305       GdkWindow *gdkwin;
306
307       gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
308                                               icon->priv->manager_window);
309       
310       gdk_window_add_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
311
312       if (dock_if_realized && GTK_WIDGET_REALIZED (icon))
313         gtk_tray_icon_send_dock_request (icon);
314
315       gtk_tray_icon_get_orientation_property (icon);
316     }
317 }
318
319 static void
320 gtk_tray_icon_manager_window_destroyed (GtkTrayIcon *icon)
321 {
322   GdkWindow *gdkwin;
323   
324   g_return_if_fail (icon->priv->manager_window != None);
325
326   gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
327                                           icon->priv->manager_window);
328       
329   gdk_window_remove_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
330
331   icon->priv->manager_window = None;
332
333   gtk_tray_icon_update_manager_window (icon, TRUE);
334 }
335
336 static gboolean 
337 gtk_tray_icon_delete (GtkWidget   *widget,
338                       GdkEventAny *event)
339 {
340   GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
341   GdkWindow *gdkwin;
342
343   if (icon->priv->manager_window != None)
344     {  
345       gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
346                                               icon->priv->manager_window);
347       
348       gdk_window_remove_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
349       
350       icon->priv->manager_window = None;
351     }
352
353   gtk_tray_icon_update_manager_window (icon, TRUE);  
354
355   return TRUE;
356 }
357
358 static void
359 gtk_tray_icon_realize (GtkWidget *widget)
360 {
361   GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
362   GdkScreen *screen;
363   GdkDisplay *display;
364   Display *xdisplay;
365   char buffer[256];
366   GdkWindow *root_window;
367
368   if (GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->realize)
369     GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->realize (widget);
370
371   screen = gtk_widget_get_screen (widget);
372   display = gdk_screen_get_display (screen);
373   xdisplay = gdk_x11_display_get_xdisplay (display);
374
375   /* Now see if there's a manager window around */
376   g_snprintf (buffer, sizeof (buffer),
377               "_NET_SYSTEM_TRAY_S%d",
378               gdk_screen_get_number (screen));
379
380   icon->priv->selection_atom = XInternAtom (xdisplay, buffer, False);
381   
382   icon->priv->manager_atom = XInternAtom (xdisplay, "MANAGER", False);
383   
384   icon->priv->system_tray_opcode_atom = XInternAtom (xdisplay,
385                                                      "_NET_SYSTEM_TRAY_OPCODE",
386                                                      False);
387
388   icon->priv->orientation_atom = XInternAtom (xdisplay,
389                                               "_NET_SYSTEM_TRAY_ORIENTATION",
390                                               False);
391
392   gtk_tray_icon_update_manager_window (icon, FALSE);
393   gtk_tray_icon_send_dock_request (icon);
394
395   root_window = gdk_screen_get_root_window (screen);
396   
397   /* Add a root window filter so that we get changes on MANAGER */
398   gdk_window_add_filter (root_window,
399                          gtk_tray_icon_manager_filter, icon);
400 }
401
402 guint
403 _gtk_tray_icon_send_message (GtkTrayIcon *icon,
404                              gint         timeout,
405                              const gchar *message,
406                              gint         len)
407 {
408   guint stamp;
409   
410   g_return_val_if_fail (GTK_IS_TRAY_ICON (icon), 0);
411   g_return_val_if_fail (timeout >= 0, 0);
412   g_return_val_if_fail (message != NULL, 0);
413                      
414   if (icon->priv->manager_window == None)
415     return 0;
416
417   if (len < 0)
418     len = strlen (message);
419
420   stamp = icon->priv->stamp++;
421   
422   /* Get ready to send the message */
423   gtk_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE,
424                                       icon->priv->manager_window,
425                                       timeout, len, stamp);
426
427   /* Now to send the actual message */
428   gdk_error_trap_push ();
429   while (len > 0)
430     {
431       XClientMessageEvent ev;
432       Display *xdisplay;
433
434       xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
435       
436       ev.type = ClientMessage;
437       ev.window = icon->priv->manager_window;
438       ev.format = 8;
439       ev.message_type = XInternAtom (xdisplay,
440                                      "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
441       if (len > 20)
442         {
443           memcpy (&ev.data, message, 20);
444           len -= 20;
445           message += 20;
446         }
447       else
448         {
449           memcpy (&ev.data, message, len);
450           len = 0;
451         }
452
453       XSendEvent (xdisplay,
454                   icon->priv->manager_window, False, 
455                   StructureNotifyMask, (XEvent *)&ev);
456       XSync (xdisplay, False);
457     }
458
459   gdk_error_trap_pop ();
460
461   return stamp;
462 }
463
464 void
465 _gtk_tray_icon_cancel_message (GtkTrayIcon *icon,
466                                guint        id)
467 {
468   g_return_if_fail (GTK_IS_TRAY_ICON (icon));
469   g_return_if_fail (id > 0);
470   
471   gtk_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE,
472                                       icon->priv->manager_window,
473                                       id, 0, 0);
474 }
475
476 GtkTrayIcon *
477 _gtk_tray_icon_new_for_screen (GdkScreen  *screen, 
478                                const gchar *name)
479 {
480   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
481
482   return g_object_new (GTK_TYPE_TRAY_ICON, 
483                        "screen", screen, 
484                        "title", name, 
485                        NULL);
486 }
487
488 GtkTrayIcon*
489 _gtk_tray_icon_new (const gchar *name)
490 {
491   return g_object_new (GTK_TYPE_TRAY_ICON, 
492                        "title", name, 
493                        NULL);
494 }
495
496 GtkOrientation
497 _gtk_tray_icon_get_orientation (GtkTrayIcon *icon)
498 {
499   g_return_val_if_fail (GTK_IS_TRAY_ICON (icon), GTK_ORIENTATION_HORIZONTAL);
500
501   return icon->priv->orientation;
502 }
503
504
505 #define __GTK_TRAY_ICON_X11_C__
506 #include "gtkaliasdef.c"
507