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., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
24 #include "eggtrayicon.h"
27 #include <X11/Xatom.h>
29 #ifndef EGG_COMPILATION
31 #define _(x) dgettext (GETTEXT_PACKAGE, x)
39 #define SYSTEM_TRAY_REQUEST_DOCK 0
40 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
41 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
43 #define SYSTEM_TRAY_ORIENTATION_HORZ 0
44 #define SYSTEM_TRAY_ORIENTATION_VERT 1
51 static GtkPlugClass
*parent_class
= NULL
;
53 static void egg_tray_icon_init (EggTrayIcon
*icon
);
54 static void egg_tray_icon_class_init (EggTrayIconClass
*klass
);
56 static void egg_tray_icon_get_property (GObject
*object
,
61 static void egg_tray_icon_realize (GtkWidget
*widget
);
62 static void egg_tray_icon_unrealize (GtkWidget
*widget
);
64 static void egg_tray_icon_update_manager_window (EggTrayIcon
*icon
);
67 egg_tray_icon_get_type (void)
69 static GType our_type
= 0;
73 static const GTypeInfo our_info
=
75 sizeof (EggTrayIconClass
),
77 (GBaseFinalizeFunc
) NULL
,
78 (GClassInitFunc
) egg_tray_icon_class_init
,
79 NULL
, /* class_finalize */
80 NULL
, /* class_data */
83 (GInstanceInitFunc
) egg_tray_icon_init
86 our_type
= g_type_register_static (GTK_TYPE_PLUG
, "EggTrayIcon", &our_info
, 0);
93 egg_tray_icon_init (EggTrayIcon
*icon
)
96 icon
->orientation
= GTK_ORIENTATION_HORIZONTAL
;
98 gtk_widget_add_events (GTK_WIDGET (icon
), GDK_PROPERTY_CHANGE_MASK
);
102 egg_tray_icon_class_init (EggTrayIconClass
*klass
)
104 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
105 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS (klass
);
107 parent_class
= g_type_class_peek_parent (klass
);
109 gobject_class
->get_property
= egg_tray_icon_get_property
;
111 widget_class
->realize
= egg_tray_icon_realize
;
112 widget_class
->unrealize
= egg_tray_icon_unrealize
;
114 g_object_class_install_property (gobject_class
,
116 g_param_spec_enum ("orientation",
118 _("The orientation of the tray."),
119 GTK_TYPE_ORIENTATION
,
120 GTK_ORIENTATION_HORIZONTAL
,
125 egg_tray_icon_get_property (GObject
*object
,
130 EggTrayIcon
*icon
= EGG_TRAY_ICON (object
);
134 case PROP_ORIENTATION
:
135 g_value_set_enum (value
, icon
->orientation
);
138 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
144 egg_tray_icon_get_orientation_property (EggTrayIcon
*icon
)
157 g_assert (icon
->manager_window
!= None
);
159 xdisplay
= GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon
)));
161 gdk_error_trap_push ();
163 result
= XGetWindowProperty (xdisplay
,
164 icon
->manager_window
,
165 icon
->orientation_atom
,
168 &type
, &format
, &nitems
,
169 &bytes_after
, &(prop
.prop_ch
));
170 error
= gdk_error_trap_pop ();
172 if (error
|| result
!= Success
)
175 if (type
== XA_CARDINAL
)
177 GtkOrientation orientation
;
179 orientation
= (prop
.prop
[0] == SYSTEM_TRAY_ORIENTATION_HORZ
) ?
180 GTK_ORIENTATION_HORIZONTAL
:
181 GTK_ORIENTATION_VERTICAL
;
183 if (icon
->orientation
!= orientation
)
185 icon
->orientation
= orientation
;
187 g_object_notify (G_OBJECT (icon
), "orientation");
195 static GdkFilterReturn
196 egg_tray_icon_manager_filter (GdkXEvent
*xevent
, GdkEvent
*event
, gpointer user_data
)
198 EggTrayIcon
*icon
= user_data
;
199 XEvent
*xev
= (XEvent
*)xevent
;
201 if (xev
->xany
.type
== ClientMessage
&&
202 xev
->xclient
.message_type
== icon
->manager_atom
&&
203 xev
->xclient
.data
.l
[1] == icon
->selection_atom
)
205 egg_tray_icon_update_manager_window (icon
);
207 else if (xev
->xany
.window
== icon
->manager_window
)
209 if (xev
->xany
.type
== PropertyNotify
&&
210 xev
->xproperty
.atom
== icon
->orientation_atom
)
212 egg_tray_icon_get_orientation_property (icon
);
214 if (xev
->xany
.type
== DestroyNotify
)
216 egg_tray_icon_update_manager_window (icon
);
220 return GDK_FILTER_CONTINUE
;
224 egg_tray_icon_unrealize (GtkWidget
*widget
)
226 EggTrayIcon
*icon
= EGG_TRAY_ICON (widget
);
227 GdkWindow
*root_window
;
229 if (icon
->manager_window
!= None
)
233 gdkwin
= gdk_window_lookup_for_display (gtk_widget_get_display (widget
),
234 icon
->manager_window
);
236 gdk_window_remove_filter (gdkwin
, egg_tray_icon_manager_filter
, icon
);
239 root_window
= gdk_screen_get_root_window (gtk_widget_get_screen (widget
));
241 gdk_window_remove_filter (root_window
, egg_tray_icon_manager_filter
, icon
);
243 if (GTK_WIDGET_CLASS (parent_class
)->unrealize
)
244 (* GTK_WIDGET_CLASS (parent_class
)->unrealize
) (widget
);
248 egg_tray_icon_send_manager_message (EggTrayIcon
*icon
,
255 XClientMessageEvent ev
;
258 ev
.type
= ClientMessage
;
260 ev
.message_type
= icon
->system_tray_opcode_atom
;
262 ev
.data
.l
[0] = gdk_x11_get_server_time (GTK_WIDGET (icon
)->window
);
263 ev
.data
.l
[1] = message
;
264 ev
.data
.l
[2] = data1
;
265 ev
.data
.l
[3] = data2
;
266 ev
.data
.l
[4] = data3
;
268 display
= GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon
)));
270 gdk_error_trap_push ();
272 icon
->manager_window
, False
, NoEventMask
, (XEvent
*)&ev
);
273 XSync (display
, False
);
274 gdk_error_trap_pop ();
278 egg_tray_icon_send_dock_request (EggTrayIcon
*icon
)
280 egg_tray_icon_send_manager_message (icon
,
281 SYSTEM_TRAY_REQUEST_DOCK
,
282 icon
->manager_window
,
283 gtk_plug_get_id (GTK_PLUG (icon
)),
288 egg_tray_icon_update_manager_window (EggTrayIcon
*icon
)
292 xdisplay
= GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon
)));
294 if (icon
->manager_window
!= None
)
298 gdkwin
= gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon
)),
299 icon
->manager_window
);
301 gdk_window_remove_filter (gdkwin
, egg_tray_icon_manager_filter
, icon
);
304 XGrabServer (xdisplay
);
306 icon
->manager_window
= XGetSelectionOwner (xdisplay
,
307 icon
->selection_atom
);
309 if (icon
->manager_window
!= None
)
310 XSelectInput (xdisplay
,
311 icon
->manager_window
, StructureNotifyMask
|PropertyChangeMask
);
313 XUngrabServer (xdisplay
);
316 if (icon
->manager_window
!= None
)
320 gdkwin
= gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon
)),
321 icon
->manager_window
);
323 gdk_window_add_filter (gdkwin
, egg_tray_icon_manager_filter
, icon
);
325 /* Send a request that we'd like to dock */
326 egg_tray_icon_send_dock_request (icon
);
328 egg_tray_icon_get_orientation_property (icon
);
333 egg_tray_icon_realize (GtkWidget
*widget
)
335 EggTrayIcon
*icon
= EGG_TRAY_ICON (widget
);
340 GdkWindow
*root_window
;
342 if (GTK_WIDGET_CLASS (parent_class
)->realize
)
343 GTK_WIDGET_CLASS (parent_class
)->realize (widget
);
345 screen
= gtk_widget_get_screen (widget
);
346 display
= gdk_screen_get_display (screen
);
347 xdisplay
= gdk_x11_display_get_xdisplay (display
);
349 /* Now see if there's a manager window around */
350 g_snprintf (buffer
, sizeof (buffer
),
351 "_NET_SYSTEM_TRAY_S%d",
352 gdk_screen_get_number (screen
));
354 icon
->selection_atom
= XInternAtom (xdisplay
, buffer
, False
);
356 icon
->manager_atom
= XInternAtom (xdisplay
, "MANAGER", False
);
358 icon
->system_tray_opcode_atom
= XInternAtom (xdisplay
,
359 "_NET_SYSTEM_TRAY_OPCODE",
362 icon
->orientation_atom
= XInternAtom (xdisplay
,
363 "_NET_SYSTEM_TRAY_ORIENTATION",
366 egg_tray_icon_update_manager_window (icon
);
368 root_window
= gdk_screen_get_root_window (screen
);
370 /* Add a root window filter so that we get changes on MANAGER */
371 gdk_window_add_filter (root_window
,
372 egg_tray_icon_manager_filter
, icon
);
377 egg_tray_icon_new_for_xscreen (Screen
*xscreen
, const char *name
)
382 display
= gdk_x11_lookup_xdisplay (DisplayOfScreen (xscreen
));
383 screen
= gdk_display_get_screen (display
, XScreenNumberOfScreen (xscreen
));
385 return egg_tray_icon_new_for_screen (screen
, name
);
390 egg_tray_icon_new_for_screen (GdkScreen
*screen
, const char *name
)
392 g_return_val_if_fail (GDK_IS_SCREEN (screen
), NULL
);
394 return g_object_new (EGG_TYPE_TRAY_ICON
, "screen", screen
, "title", name
, NULL
);
398 egg_tray_icon_new (const gchar
*name
)
400 return g_object_new (EGG_TYPE_TRAY_ICON
, "title", name
, NULL
);
404 egg_tray_icon_send_message (EggTrayIcon
*icon
,
406 const gchar
*message
,
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);
415 if (icon
->manager_window
== None
)
419 len
= strlen (message
);
421 stamp
= icon
->stamp
++;
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
);
428 /* Now to send the actual message */
429 gdk_error_trap_push ();
432 XClientMessageEvent ev
;
435 xdisplay
= GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon
)));
437 ev
.type
= ClientMessage
;
438 ev
.window
= (Window
)gtk_plug_get_id (GTK_PLUG (icon
));
440 ev
.message_type
= XInternAtom (xdisplay
,
441 "_NET_SYSTEM_TRAY_MESSAGE_DATA", False
);
444 memcpy (&ev
.data
, message
, 20);
450 memcpy (&ev
.data
, message
, len
);
454 XSendEvent (xdisplay
,
455 icon
->manager_window
, False
, StructureNotifyMask
, (XEvent
*)&ev
);
456 XSync (xdisplay
, False
);
458 gdk_error_trap_pop ();
464 egg_tray_icon_cancel_message (EggTrayIcon
*icon
,
467 g_return_if_fail (EGG_IS_TRAY_ICON (icon
));
468 g_return_if_fail (id
> 0);
470 egg_tray_icon_send_manager_message (icon
, SYSTEM_TRAY_CANCEL_MESSAGE
,
471 (Window
)gtk_plug_get_id (GTK_PLUG (icon
)),
476 egg_tray_icon_get_orientation (EggTrayIcon
*icon
)
478 g_return_val_if_fail (EGG_IS_TRAY_ICON (icon
), GTK_ORIENTATION_HORIZONTAL
);
480 return icon
->orientation
;