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.
24 #include "eggtrayicon.h"
28 SYSTEM_TRAY_REQUEST_DOCK
,
29 SYSTEM_TRAY_BEGIN_MESSAGE
,
30 SYSTEM_TRAY_CANCEL_MESSAGE
34 static GtkPlugClass
*parent_class
= NULL
;
36 static void egg_tray_icon_init (EggTrayIcon
*icon
);
37 static void egg_tray_icon_class_init (EggTrayIconClass
*klass
);
39 static void egg_tray_icon_unrealize (GtkWidget
*widget
);
41 static void egg_tray_icon_update_manager_window (EggTrayIcon
*icon
);
44 GType
egg_tray_icon_get_type (void) {
45 static GType our_type
= 0;
46 our_type
= g_type_from_name("EggTrayIcon");
48 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
59 our_type
= g_type_register_static(GTK_TYPE_PLUG
, "EggTrayIcon", &our_info
, 0);
60 } else if (parent_class
== NULL
) {
61 /* we're reheating the old class from a previous instance - engage ugly hack =( */
62 egg_tray_icon_class_init((EggTrayIconClass
*) g_type_class_peek(our_type
));
68 static void egg_tray_icon_init (EggTrayIcon
*icon
) {
70 gtk_widget_add_events(GTK_WIDGET(icon
), GDK_PROPERTY_CHANGE_MASK
);
74 static void egg_tray_icon_class_init (EggTrayIconClass
*klass
) {
75 GtkWidgetClass
*widget_class
= (GtkWidgetClass
*)klass
;
76 parent_class
= g_type_class_peek_parent(klass
);
77 widget_class
->unrealize
= egg_tray_icon_unrealize
;
81 static GdkFilterReturn
egg_tray_icon_manager_filter (GdkXEvent
*xevent
, GdkEvent
*event
, gpointer user_data
) {
82 EggTrayIcon
*icon
= user_data
;
83 XEvent
*xev
= (XEvent
*)xevent
;
84 if (xev
->xany
.type
== ClientMessage
&& xev
->xclient
.message_type
== icon
->manager_atom
&& xev
->xclient
.data
.l
[1] == icon
->selection_atom
) {
85 egg_tray_icon_update_manager_window(icon
);
86 } else if (xev
->xany
.window
== icon
->manager_window
) {
87 if (xev
->xany
.type
== DestroyNotify
) {
88 egg_tray_icon_update_manager_window(icon
);
91 return GDK_FILTER_CONTINUE
;
95 static void egg_tray_icon_unrealize (GtkWidget
*widget
) {
96 EggTrayIcon
*icon
= EGG_TRAY_ICON(widget
);
97 GdkWindow
*root_window
;
98 if (icon
->manager_window
!= None
) {
100 #if HAVE_GTK_MULTIHEAD
101 gdkwin
= gdk_window_lookup_for_display(gtk_widget_get_display(widget
), icon
->manager_window
);
103 gdkwin
= gdk_window_lookup(icon
->manager_window
);
105 gdk_window_remove_filter(gdkwin
, egg_tray_icon_manager_filter
, icon
);
107 #if HAVE_GTK_MULTIHEAD
108 root_window
= gdk_screen_get_root_window(gtk_widget_get_screen(widget
));
110 root_window
= gdk_window_lookup(gdk_x11_get_default_root_xwindow());
112 gdk_window_remove_filter(root_window
, egg_tray_icon_manager_filter
, icon
);
113 if (GTK_WIDGET_CLASS(parent_class
)->unrealize
) (*GTK_WIDGET_CLASS(parent_class
)->unrealize
)(widget
);
117 static void egg_tray_icon_send_manager_message (EggTrayIcon
*icon
, long message
, Window window
, long data1
, long data2
, long data3
) {
118 XClientMessageEvent ev
;
120 ev
.type
= ClientMessage
;
122 ev
.message_type
= icon
->system_tray_opcode_atom
;
124 ev
.data
.l
[0] = gdk_x11_get_server_time(GTK_WIDGET(icon
)->window
);
125 ev
.data
.l
[1] = message
;
126 ev
.data
.l
[2] = data1
;
127 ev
.data
.l
[3] = data2
;
128 ev
.data
.l
[4] = data3
;
129 #if HAVE_GTK_MULTIHEAD
130 display
= GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(GTK_WIDGET(icon
)));
132 display
= gdk_display
;
134 gdk_error_trap_push();
135 XSendEvent(display
, icon
->manager_window
, False
, NoEventMask
, (XEvent
*)&ev
);
136 XSync(display
, False
);
137 gdk_error_trap_pop();
141 static void egg_tray_icon_send_dock_request (EggTrayIcon
*icon
) {
142 egg_tray_icon_send_manager_message(icon
, SYSTEM_TRAY_REQUEST_DOCK
, icon
->manager_window
, gtk_plug_get_id(GTK_PLUG(icon
)), 0, 0);
146 static void egg_tray_icon_update_manager_window (EggTrayIcon
*icon
) {
148 #if HAVE_GTK_MULTIHEAD
149 xdisplay
= GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(GTK_WIDGET(icon
)));
151 xdisplay
= gdk_display
;
153 if (icon
->manager_window
!= None
) {
155 #if HAVE_GTK_MULTIHEAD
156 gdkwin
= gdk_window_lookup_for_display(gtk_widget_get_display(GTK_WIDGET(icon
)), icon
->manager_window
);
158 gdkwin
= gdk_window_lookup(icon
->manager_window
);
160 gdk_window_remove_filter(gdkwin
, egg_tray_icon_manager_filter
, icon
);
162 XGrabServer(xdisplay
);
163 icon
->manager_window
= XGetSelectionOwner(xdisplay
, icon
->selection_atom
);
164 if (icon
->manager_window
!= None
) XSelectInput(xdisplay
, icon
->manager_window
, StructureNotifyMask
);
165 XUngrabServer(xdisplay
);
167 if (icon
->manager_window
!= None
) {
169 #if HAVE_GTK_MULTIHEAD
170 gdkwin
= gdk_window_lookup_for_display(gtk_widget_get_display(GTK_WIDGET(icon
)), icon
->manager_window
);
172 gdkwin
= gdk_window_lookup(icon
->manager_window
);
174 gdk_window_add_filter(gdkwin
, egg_tray_icon_manager_filter
, icon
);
175 /* Send a request that we'd like to dock */
176 egg_tray_icon_send_dock_request(icon
);
181 EggTrayIcon
*egg_tray_icon_new_for_xscreen (Screen
*xscreen
, const char *name
) {
184 GdkWindow
*root_window
;
185 g_return_val_if_fail(xscreen
!= NULL
, NULL
);
186 icon
= g_object_new(EGG_TYPE_TRAY_ICON
, NULL
);
187 gtk_window_set_title(GTK_WINDOW(icon
), name
);
188 #if HAVE_GTK_MULTIHEAD
189 /* FIXME: this code does not compile, screen is undefined. Now try
190 * getting the GdkScreen from xscreen (:. Dunno how to solve this
191 * (there is prolly some easy way I cant think of right now)
193 gtk_plug_construct_for_display(GTK_PLUG(icon
), gdk_screen_get_display(screen
), 0);
195 gtk_plug_construct(GTK_PLUG(icon
), 0);
197 gtk_widget_realize(GTK_WIDGET(icon
));
198 /* Now see if there's a manager window around */
199 g_snprintf(buffer
, sizeof(buffer
), "_NET_SYSTEM_TRAY_S%d", XScreenNumberOfScreen(xscreen
));
200 icon
->selection_atom
= XInternAtom(DisplayOfScreen(xscreen
), buffer
, False
);
201 icon
->manager_atom
= XInternAtom(DisplayOfScreen(xscreen
), "MANAGER", False
);
202 icon
->system_tray_opcode_atom
= XInternAtom(DisplayOfScreen(xscreen
), "_NET_SYSTEM_TRAY_OPCODE", False
);
203 egg_tray_icon_update_manager_window(icon
);
204 #if HAVE_GTK_MULTIHEAD
205 root_window
= gdk_screen_get_root_window(gtk_widget_get_screen(screen
));
207 root_window
= gdk_window_lookup(gdk_x11_get_default_root_xwindow());
209 /* Add a root window filter so that we get changes on MANAGER */
210 gdk_window_add_filter(root_window
, egg_tray_icon_manager_filter
, icon
);
215 #if HAVE_GTK_MULTIHEAD
216 EggTrayIcon
*egg_tray_icon_new_for_screen (GdkScreen
*screen
, const char *name
) {
219 g_return_val_if_fail(GDK_IS_SCREEN(screen
), NULL
);
220 return egg_tray_icon_new_for_xscreen(GDK_SCREEN_XSCREEN(screen
), name
);
225 EggTrayIcon
*egg_tray_icon_new (const gchar
*name
) {
226 return egg_tray_icon_new_for_xscreen(DefaultScreenOfDisplay(gdk_display
), name
);
230 guint
egg_tray_icon_send_message (EggTrayIcon
*icon
, gint timeout
, const gchar
*message
, gint len
) {
232 g_return_val_if_fail(EGG_IS_TRAY_ICON(icon
), 0);
233 g_return_val_if_fail(timeout
>= 0, 0);
234 g_return_val_if_fail(message
!= NULL
, 0);
235 if (icon
->manager_window
== None
) return 0;
236 if (len
< 0) len
= strlen(message
);
237 stamp
= icon
->stamp
++;
238 /* Get ready to send the message */
239 egg_tray_icon_send_manager_message(icon
, SYSTEM_TRAY_BEGIN_MESSAGE
, (Window
) gtk_plug_get_id(GTK_PLUG(icon
)), timeout
, len
, stamp
);
240 /* Now to send the actual message */
241 gdk_error_trap_push();
243 XClientMessageEvent ev
;
245 #if HAVE_GTK_MULTIHEAD
246 xdisplay
= GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(GTK_WIDGET(icon
)));
248 xdisplay
= gdk_display
;
250 ev
.type
= ClientMessage
;
251 ev
.window
= (Window
) gtk_plug_get_id(GTK_PLUG(icon
));
253 ev
.message_type
= XInternAtom(xdisplay
, "_NET_SYSTEM_TRAY_MESSAGE_DATA", False
);
255 memcpy(&ev
.data
, message
, 20);
259 memcpy(&ev
.data
, message
, len
);
262 XSendEvent(xdisplay
, icon
->manager_window
, False
, StructureNotifyMask
, (XEvent
*)&ev
);
263 XSync(xdisplay
, False
);
265 gdk_error_trap_pop();
271 void egg_tray_icon_cancel_message (EggTrayIcon
*icon
, guint id
) {
272 g_return_if_fail(EGG_IS_TRAY_ICON(icon
));
273 g_return_if_fail(id
> 0);
274 egg_tray_icon_send_manager_message(icon
, SYSTEM_TRAY_CANCEL_MESSAGE
, (Window
) gtk_plug_get_id(GTK_PLUG(icon
)), id
, 0, 0);