progress.*: cosmetix
[k8lowj.git] / src / eggtrayicon.c
blob52d4c46ddcc1e1ae81bc29a4279b45e91d4cd5ad
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>
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.
20 #include <string.h>
22 #include <gdk/gdkx.h>
24 #include "eggtrayicon.h"
27 enum {
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");
47 if (our_type == 0) {
48 static const GTypeInfo our_info = {
49 sizeof(EggTrayIconClass),
50 (GBaseInitFunc) NULL,
51 (GBaseFinalizeFunc) NULL,
52 (GClassInitFunc) egg_tray_icon_class_init,
53 NULL, /* class_finalize */
54 NULL, /* class_data */
55 sizeof(EggTrayIcon),
56 0, /* n_preallocs */
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));
64 return our_type;
68 static void egg_tray_icon_init (EggTrayIcon *icon) {
69 icon->stamp = 1;
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) {
99 GdkWindow *gdkwin;
100 #if HAVE_GTK_MULTIHEAD
101 gdkwin = gdk_window_lookup_for_display(gtk_widget_get_display(widget), icon->manager_window);
102 #else
103 gdkwin = gdk_window_lookup(icon->manager_window);
104 #endif
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));
109 #else
110 root_window = gdk_window_lookup(gdk_x11_get_default_root_xwindow());
111 #endif
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;
119 Display *display;
120 ev.type = ClientMessage;
121 ev.window = window;
122 ev.message_type = icon->system_tray_opcode_atom;
123 ev.format = 32;
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)));
131 #else
132 display = gdk_display;
133 #endif
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) {
147 Display *xdisplay;
148 #if HAVE_GTK_MULTIHEAD
149 xdisplay = GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(GTK_WIDGET(icon)));
150 #else
151 xdisplay = gdk_display;
152 #endif
153 if (icon->manager_window != None) {
154 GdkWindow *gdkwin;
155 #if HAVE_GTK_MULTIHEAD
156 gdkwin = gdk_window_lookup_for_display(gtk_widget_get_display(GTK_WIDGET(icon)), icon->manager_window);
157 #else
158 gdkwin = gdk_window_lookup(icon->manager_window);
159 #endif
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);
166 XFlush(xdisplay);
167 if (icon->manager_window != None) {
168 GdkWindow *gdkwin;
169 #if HAVE_GTK_MULTIHEAD
170 gdkwin = gdk_window_lookup_for_display(gtk_widget_get_display(GTK_WIDGET(icon)), icon->manager_window);
171 #else
172 gdkwin = gdk_window_lookup(icon->manager_window);
173 #endif
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) {
182 EggTrayIcon *icon;
183 char buffer[256];
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);
194 #else
195 gtk_plug_construct(GTK_PLUG(icon), 0);
196 #endif
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));
206 #else
207 root_window = gdk_window_lookup(gdk_x11_get_default_root_xwindow());
208 #endif
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);
211 return icon;
215 #if HAVE_GTK_MULTIHEAD
216 EggTrayIcon *egg_tray_icon_new_for_screen (GdkScreen *screen, const char *name) {
217 EggTrayIcon *icon;
218 char buffer[256];
219 g_return_val_if_fail(GDK_IS_SCREEN(screen), NULL);
220 return egg_tray_icon_new_for_xscreen(GDK_SCREEN_XSCREEN(screen), name);
222 #endif
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) {
231 guint stamp;
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();
242 while (len > 0) {
243 XClientMessageEvent ev;
244 Display *xdisplay;
245 #if HAVE_GTK_MULTIHEAD
246 xdisplay = GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(GTK_WIDGET(icon)));
247 #else
248 xdisplay = gdk_display;
249 #endif
250 ev.type = ClientMessage;
251 ev.window = (Window) gtk_plug_get_id(GTK_PLUG(icon));
252 ev.format = 8;
253 ev.message_type = XInternAtom(xdisplay, "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
254 if (len > 20) {
255 memcpy(&ev.data, message, 20);
256 len -= 20;
257 message += 20;
258 } else {
259 memcpy(&ev.data, message, len);
260 len = 0;
262 XSendEvent(xdisplay, icon->manager_window, False, StructureNotifyMask, (XEvent *)&ev);
263 XSync(xdisplay, False);
265 gdk_error_trap_pop();
267 return stamp;
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);