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.
23 #include "eggtrayicon.h"
25 #define SYSTEM_TRAY_REQUEST_DOCK 0
26 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
27 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
29 static GtkPlugClass
*parent_class
= NULL
;
31 static void egg_tray_icon_init (EggTrayIcon
*icon
);
32 static void egg_tray_icon_class_init (EggTrayIconClass
*klass
);
34 static void egg_tray_icon_unrealize (GtkWidget
*widget
);
36 static void egg_tray_icon_update_manager_window (EggTrayIcon
*icon
);
39 egg_tray_icon_get_type (void)
41 static GType our_type
= 0;
43 our_type
= g_type_from_name("EggTrayIcon");
47 static const GTypeInfo our_info
=
49 sizeof (EggTrayIconClass
),
51 (GBaseFinalizeFunc
) NULL
,
52 (GClassInitFunc
) egg_tray_icon_class_init
,
53 NULL
, /* class_finalize */
54 NULL
, /* class_data */
57 (GInstanceInitFunc
) egg_tray_icon_init
60 our_type
= g_type_register_static (GTK_TYPE_PLUG
, "EggTrayIcon", &our_info
, 0);
62 else if (parent_class
== NULL
) {
63 /* we're reheating the old class from a previous instance - engage ugly hack =( */
64 egg_tray_icon_class_init((EggTrayIconClass
*)g_type_class_peek(our_type
));
71 egg_tray_icon_init (EggTrayIcon
*icon
)
75 gtk_widget_add_events (GTK_WIDGET (icon
), GDK_PROPERTY_CHANGE_MASK
);
79 egg_tray_icon_class_init (EggTrayIconClass
*klass
)
81 GtkWidgetClass
*widget_class
= (GtkWidgetClass
*)klass
;
83 parent_class
= g_type_class_peek_parent (klass
);
85 widget_class
->unrealize
= egg_tray_icon_unrealize
;
88 static GdkFilterReturn
89 egg_tray_icon_manager_filter (GdkXEvent
*xevent
, GdkEvent
*event
, gpointer user_data
)
91 EggTrayIcon
*icon
= user_data
;
92 XEvent
*xev
= (XEvent
*)xevent
;
94 if (xev
->xany
.type
== ClientMessage
&&
95 xev
->xclient
.message_type
== icon
->manager_atom
&&
96 xev
->xclient
.data
.l
[1] == icon
->selection_atom
)
98 egg_tray_icon_update_manager_window (icon
);
100 else if (xev
->xany
.window
== icon
->manager_window
)
102 if (xev
->xany
.type
== DestroyNotify
)
104 egg_tray_icon_update_manager_window (icon
);
108 return GDK_FILTER_CONTINUE
;
112 egg_tray_icon_unrealize (GtkWidget
*widget
)
114 EggTrayIcon
*icon
= EGG_TRAY_ICON (widget
);
115 GdkWindow
*root_window
;
117 if (icon
->manager_window
!= None
)
121 #if HAVE_GTK_MULTIHEAD
122 gdkwin
= gdk_window_lookup_for_display (gtk_widget_get_display (widget
),
123 icon
->manager_window
);
125 gdkwin
= gdk_window_lookup (icon
->manager_window
);
128 gdk_window_remove_filter (gdkwin
, egg_tray_icon_manager_filter
, icon
);
131 #if HAVE_GTK_MULTIHEAD
132 root_window
= gdk_screen_get_root_window (gtk_widget_get_screen (widget
));
134 root_window
= gdk_window_lookup (gdk_x11_get_default_root_xwindow ());
137 gdk_window_remove_filter (root_window
, egg_tray_icon_manager_filter
, icon
);
139 if (GTK_WIDGET_CLASS (parent_class
)->unrealize
)
140 (* GTK_WIDGET_CLASS (parent_class
)->unrealize
) (widget
);
144 egg_tray_icon_send_manager_message (EggTrayIcon
*icon
,
151 XClientMessageEvent ev
;
154 ev
.type
= ClientMessage
;
156 ev
.message_type
= icon
->system_tray_opcode_atom
;
158 ev
.data
.l
[0] = gdk_x11_get_server_time (GTK_WIDGET (icon
)->window
);
159 ev
.data
.l
[1] = message
;
160 ev
.data
.l
[2] = data1
;
161 ev
.data
.l
[3] = data2
;
162 ev
.data
.l
[4] = data3
;
164 #if HAVE_GTK_MULTIHEAD
165 display
= GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon
)));
167 display
= gdk_display
;
170 gdk_error_trap_push ();
172 icon
->manager_window
, False
, NoEventMask
, (XEvent
*)&ev
);
173 XSync (display
, False
);
174 gdk_error_trap_pop ();
178 egg_tray_icon_send_dock_request (EggTrayIcon
*icon
)
180 egg_tray_icon_send_manager_message (icon
,
181 SYSTEM_TRAY_REQUEST_DOCK
,
182 icon
->manager_window
,
183 gtk_plug_get_id (GTK_PLUG (icon
)),
188 egg_tray_icon_update_manager_window (EggTrayIcon
*icon
)
192 #if HAVE_GTK_MULTIHEAD
193 xdisplay
= GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon
)));
195 xdisplay
= gdk_display
;
198 if (icon
->manager_window
!= None
)
202 #if HAVE_GTK_MULTIHEAD
203 gdkwin
= gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon
)),
204 icon
->manager_window
);
206 gdkwin
= gdk_window_lookup (icon
->manager_window
);
209 gdk_window_remove_filter (gdkwin
, egg_tray_icon_manager_filter
, icon
);
212 XGrabServer (xdisplay
);
214 icon
->manager_window
= XGetSelectionOwner (xdisplay
,
215 icon
->selection_atom
);
217 if (icon
->manager_window
!= None
)
218 XSelectInput (xdisplay
,
219 icon
->manager_window
, StructureNotifyMask
);
221 XUngrabServer (xdisplay
);
224 if (icon
->manager_window
!= None
)
228 #if HAVE_GTK_MULTIHEAD
229 gdkwin
= gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon
)),
230 icon
->manager_window
);
232 gdkwin
= gdk_window_lookup (icon
->manager_window
);
235 gdk_window_add_filter (gdkwin
, egg_tray_icon_manager_filter
, icon
);
237 /* Send a request that we'd like to dock */
238 egg_tray_icon_send_dock_request (icon
);
243 egg_tray_icon_new_for_xscreen (Screen
*xscreen
, const char *name
)
247 GdkWindow
*root_window
;
249 g_return_val_if_fail (xscreen
!= NULL
, NULL
);
251 icon
= g_object_new (EGG_TYPE_TRAY_ICON
, NULL
);
252 gtk_window_set_title (GTK_WINDOW (icon
), name
);
254 #if HAVE_GTK_MULTIHEAD
255 /* FIXME: this code does not compile, screen is undefined. Now try
256 * getting the GdkScreen from xscreen (:. Dunno how to solve this
257 * (there is prolly some easy way I cant think of right now)
259 gtk_plug_construct_for_display (GTK_PLUG (icon
),
260 gdk_screen_get_display (screen
), 0);
262 gtk_plug_construct (GTK_PLUG (icon
), 0);
265 gtk_widget_realize (GTK_WIDGET (icon
));
267 /* Now see if there's a manager window around */
268 g_snprintf (buffer
, sizeof (buffer
),
269 "_NET_SYSTEM_TRAY_S%d",
270 XScreenNumberOfScreen (xscreen
));
272 icon
->selection_atom
= XInternAtom (DisplayOfScreen (xscreen
),
275 icon
->manager_atom
= XInternAtom (DisplayOfScreen (xscreen
),
278 icon
->system_tray_opcode_atom
= XInternAtom (DisplayOfScreen (xscreen
),
279 "_NET_SYSTEM_TRAY_OPCODE", False
);
281 egg_tray_icon_update_manager_window (icon
);
283 #if HAVE_GTK_MULTIHEAD
284 root_window
= gdk_screen_get_root_window (gtk_widget_get_screen (screen
));
286 root_window
= gdk_window_lookup (gdk_x11_get_default_root_xwindow ());
289 /* Add a root window filter so that we get changes on MANAGER */
290 gdk_window_add_filter (root_window
,
291 egg_tray_icon_manager_filter
, icon
);
296 #if HAVE_GTK_MULTIHEAD
298 egg_tray_icon_new_for_screen (GdkScreen
*screen
, const char *name
)
303 g_return_val_if_fail (GDK_IS_SCREEN (screen
), NULL
);
305 return egg_tray_icon_new_for_xscreen (GDK_SCREEN_XSCREEN (screen
), name
);
310 egg_tray_icon_new (const gchar
*name
)
312 return egg_tray_icon_new_for_xscreen (DefaultScreenOfDisplay (gdk_display
), name
);
316 egg_tray_icon_send_message (EggTrayIcon
*icon
,
318 const gchar
*message
,
323 g_return_val_if_fail (EGG_IS_TRAY_ICON (icon
), 0);
324 g_return_val_if_fail (timeout
>= 0, 0);
325 g_return_val_if_fail (message
!= NULL
, 0);
327 if (icon
->manager_window
== None
)
331 len
= strlen (message
);
333 stamp
= icon
->stamp
++;
335 /* Get ready to send the message */
336 egg_tray_icon_send_manager_message (icon
, SYSTEM_TRAY_BEGIN_MESSAGE
,
337 (Window
)gtk_plug_get_id (GTK_PLUG (icon
)),
338 timeout
, len
, stamp
);
340 /* Now to send the actual message */
341 gdk_error_trap_push ();
344 XClientMessageEvent ev
;
347 #if HAVE_GTK_MULTIHEAD
348 xdisplay
= GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon
)));
350 xdisplay
= gdk_display
;
353 ev
.type
= ClientMessage
;
354 ev
.window
= (Window
)gtk_plug_get_id (GTK_PLUG (icon
));
356 ev
.message_type
= XInternAtom (xdisplay
,
357 "_NET_SYSTEM_TRAY_MESSAGE_DATA", False
);
360 memcpy (&ev
.data
, message
, 20);
366 memcpy (&ev
.data
, message
, len
);
370 XSendEvent (xdisplay
,
371 icon
->manager_window
, False
, StructureNotifyMask
, (XEvent
*)&ev
);
372 XSync (xdisplay
, False
);
374 gdk_error_trap_pop ();
380 egg_tray_icon_cancel_message (EggTrayIcon
*icon
,
383 g_return_if_fail (EGG_IS_TRAY_ICON (icon
));
384 g_return_if_fail (id
> 0);
386 egg_tray_icon_send_manager_message (icon
, SYSTEM_TRAY_CANCEL_MESSAGE
,
387 (Window
)gtk_plug_get_id (GTK_PLUG (icon
)),