UPS: apcupsd clean sources
[tomato.git] / release / src / router / apcupsd / src / gapcmon / eggtrayicon.c
blob6b437ea6ac3cffd87e384a89c995bab09fb5478b
2 /* eggtrayicon.c serial-0085-0 ***************************************
3 * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
5 * Modified by James Scott, Jr <skoona@users.sourceforge.net>
6 * - To enhance events and size management 4/2006
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
24 #include <string.h>
25 #include <libintl.h>
27 #include <gdkconfig.h>
29 #include <gdk/gdkx.h>
30 #include <X11/Xatom.h>
32 #if defined (GDK_WINDOWING_WIN32)
33 #include <gdk/gdkwin32.h>
34 #endif
36 #include "eggtrayicon.h"
39 #define _(x) x
40 #define N_(x) x
42 #define SYSTEM_TRAY_REQUEST_DOCK 0
43 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
44 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
46 #define SYSTEM_TRAY_ORIENTATION_HORZ 0
47 #define SYSTEM_TRAY_ORIENTATION_VERT 1
49 enum {
50 PROP_0,
51 PROP_ORIENTATION
54 static GtkPlugClass *parent_class = NULL;
56 static void egg_tray_icon_init(EggTrayIcon * icon);
57 static void egg_tray_icon_class_init(EggTrayIconClass * klass);
59 static void egg_tray_icon_get_property(GObject * object,
60 guint prop_id, GValue * value, GParamSpec * pspec);
62 static void egg_tray_icon_realize(GtkWidget * widget);
63 static void egg_tray_icon_unrealize(GtkWidget * widget);
65 static void egg_tray_icon_add (GtkContainer *container,
66 GtkWidget *widget);
68 static void egg_tray_icon_update_manager_window(EggTrayIcon * icon,
69 gboolean dock_if_realized);
70 static void egg_tray_icon_manager_window_destroyed(EggTrayIcon * icon);
72 GType egg_tray_icon_get_type(void)
74 static GType our_type = 0;
76 if (our_type == 0) {
77 static const GTypeInfo our_info = {
78 sizeof(EggTrayIconClass),
79 (GBaseInitFunc) NULL,
80 (GBaseFinalizeFunc) NULL,
81 (GClassInitFunc) egg_tray_icon_class_init,
82 NULL, /* class_finalize */
83 NULL, /* class_data */
84 sizeof(EggTrayIcon),
85 0, /* n_preallocs */
86 (GInstanceInitFunc) egg_tray_icon_init
89 our_type = g_type_register_static(GTK_TYPE_PLUG, "EggTrayIcon", &our_info, 0);
92 return our_type;
95 static void egg_tray_icon_init(EggTrayIcon * icon)
97 icon->stamp = 1;
98 icon->orientation = GTK_ORIENTATION_HORIZONTAL;
100 gtk_widget_add_events(GTK_WIDGET(icon), GDK_PROPERTY_CHANGE_MASK |
101 GDK_BUTTON_PRESS_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
104 static void egg_tray_icon_class_init(EggTrayIconClass * klass)
106 GObjectClass *gobject_class = (GObjectClass *) klass;
107 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
108 GtkContainerClass *container_class = (GtkContainerClass *)klass;
110 parent_class = g_type_class_peek_parent(klass);
112 gobject_class->get_property = egg_tray_icon_get_property;
114 widget_class->realize = egg_tray_icon_realize;
115 widget_class->unrealize = egg_tray_icon_unrealize;
117 container_class->add = egg_tray_icon_add;
119 g_object_class_install_property(gobject_class,
120 PROP_ORIENTATION,
121 g_param_spec_enum("orientation",
122 "Orientation",
123 "The orientation of the tray.",
124 GTK_TYPE_ORIENTATION, GTK_ORIENTATION_HORIZONTAL, G_PARAM_READABLE));
126 #if defined (GDK_WINDOWING_WIN32)
127 g_warning("Port eggtrayicon to Win32");
128 #endif
131 static void
132 egg_tray_icon_get_property(GObject * object,
133 guint prop_id, GValue * value, GParamSpec * pspec)
135 EggTrayIcon *icon = EGG_TRAY_ICON(object);
137 switch (prop_id) {
138 case PROP_ORIENTATION:
139 g_value_set_enum(value, icon->orientation);
140 break;
141 default:
142 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
143 break;
147 static void egg_tray_icon_get_orientation_property(EggTrayIcon * icon)
149 Display *xdisplay;
150 Atom type;
151 int format;
152 union {
153 gulong *prop;
154 guchar *prop_ch;
155 } prop = {
156 NULL};
157 gulong nitems;
158 gulong bytes_after;
159 int error, result;
161 g_assert(icon->manager_window != None);
163 xdisplay = GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(GTK_WIDGET(icon)));
165 gdk_error_trap_push();
166 type = None;
167 result = XGetWindowProperty(xdisplay,
168 icon->manager_window,
169 icon->orientation_atom,
170 0, G_MAXLONG, FALSE,
171 XA_CARDINAL, &type, &format, &nitems, &bytes_after, &(prop.prop_ch));
172 error = gdk_error_trap_pop();
174 if (error || result != Success)
175 return;
177 if (type == XA_CARDINAL) {
178 GtkOrientation orientation;
180 orientation = (prop.prop[0] == SYSTEM_TRAY_ORIENTATION_HORZ) ?
181 GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
183 if (icon->orientation != orientation) {
184 icon->orientation = orientation;
186 g_object_notify(G_OBJECT(icon), "orientation");
190 if (prop.prop)
191 XFree(prop.prop);
194 static GdkFilterReturn
195 egg_tray_icon_manager_filter(GdkXEvent * xevent, GdkEvent * event,
196 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) {
204 egg_tray_icon_update_manager_window(icon, TRUE);
205 } else if (xev->xany.window == icon->manager_window) {
206 if (xev->xany.type == PropertyNotify &&
207 xev->xproperty.atom == icon->orientation_atom) {
208 egg_tray_icon_get_orientation_property(icon);
210 if (xev->xany.type == DestroyNotify) {
211 egg_tray_icon_manager_window_destroyed(icon);
214 return GDK_FILTER_CONTINUE;
217 static void egg_tray_icon_unrealize(GtkWidget * widget)
219 EggTrayIcon *icon = EGG_TRAY_ICON(widget);
220 GdkWindow *root_window;
222 if (icon->manager_window != None) {
223 GdkWindow *gdkwin;
225 gdkwin = gdk_window_lookup_for_display(gtk_widget_get_display(widget),
226 icon->manager_window);
228 gdk_window_remove_filter(gdkwin, egg_tray_icon_manager_filter, icon);
231 root_window = gdk_screen_get_root_window(gtk_widget_get_screen(widget));
233 gdk_window_remove_filter(root_window, egg_tray_icon_manager_filter, icon);
235 if (GTK_WIDGET_CLASS(parent_class)->unrealize)
236 (*GTK_WIDGET_CLASS(parent_class)->unrealize) (widget);
239 static void
240 egg_tray_icon_send_manager_message(EggTrayIcon * icon,
241 long message, Window window, long data1, long data2, long data3)
243 XClientMessageEvent ev;
244 Display *display;
246 ev.type = ClientMessage;
247 ev.window = window;
248 ev.message_type = icon->system_tray_opcode_atom;
249 ev.format = 32;
250 ev.data.l[0] = gdk_x11_get_server_time(GTK_WIDGET(icon)->window);
251 ev.data.l[1] = message;
252 ev.data.l[2] = data1;
253 ev.data.l[3] = data2;
254 ev.data.l[4] = data3;
256 display = GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(GTK_WIDGET(icon)));
258 gdk_error_trap_push();
259 XSendEvent(display, icon->manager_window, False, NoEventMask, (XEvent *) & ev);
260 XSync(display, False);
261 gdk_error_trap_pop();
264 static void egg_tray_icon_send_dock_request(EggTrayIcon * icon)
266 egg_tray_icon_send_manager_message(icon,
267 SYSTEM_TRAY_REQUEST_DOCK,
268 icon->manager_window, gtk_plug_get_id(GTK_PLUG(icon)), 0, 0);
271 static void
272 egg_tray_icon_update_manager_window(EggTrayIcon * icon, gboolean dock_if_realized)
274 Display *xdisplay;
276 if (icon->manager_window != None)
277 return;
279 xdisplay = GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(GTK_WIDGET(icon)));
281 XGrabServer(xdisplay);
283 icon->manager_window = XGetSelectionOwner(xdisplay, icon->selection_atom);
285 if (icon->manager_window != None)
286 XSelectInput(xdisplay,
287 icon->manager_window, StructureNotifyMask | PropertyChangeMask);
289 XUngrabServer(xdisplay);
290 XFlush(xdisplay);
292 if (icon->manager_window != None) {
293 GdkWindow *gdkwin;
295 gdkwin =
296 gdk_window_lookup_for_display(gtk_widget_get_display(GTK_WIDGET(icon)),
297 icon->manager_window);
299 gdk_window_add_filter(gdkwin, egg_tray_icon_manager_filter, icon);
301 if (dock_if_realized && GTK_WIDGET_REALIZED(icon))
302 egg_tray_icon_send_dock_request(icon);
304 egg_tray_icon_get_orientation_property(icon);
308 static gboolean
309 transparent_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
311 gdk_window_clear_area (widget->window, event->area.x, event->area.y,
312 event->area.width, event->area.height);
313 return FALSE;
316 static void
317 make_transparent_again (GtkWidget *widget, GtkStyle *previous_style,
318 gpointer user_data)
320 gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
323 static void
324 make_transparent (GtkWidget *widget, gpointer user_data)
326 if (GTK_WIDGET_NO_WINDOW (widget) || GTK_WIDGET_APP_PAINTABLE (widget))
327 return;
329 gtk_widget_set_app_paintable (widget, TRUE);
330 gtk_widget_set_double_buffered (widget, FALSE);
331 gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
332 g_signal_connect (widget, "expose_event",
333 G_CALLBACK (transparent_expose_event), NULL);
334 g_signal_connect_after (widget, "style_set",
335 G_CALLBACK (make_transparent_again), NULL);
338 static void egg_tray_icon_manager_window_destroyed(EggTrayIcon * icon)
340 GdkWindow *gdkwin;
342 g_return_if_fail(icon->manager_window != None);
344 gdkwin = gdk_window_lookup_for_display(gtk_widget_get_display(GTK_WIDGET(icon)),
345 icon->manager_window);
347 gdk_window_remove_filter(gdkwin, egg_tray_icon_manager_filter, icon);
349 icon->manager_window = None;
351 egg_tray_icon_update_manager_window(icon, TRUE);
355 static void egg_tray_icon_realize(GtkWidget * widget)
357 EggTrayIcon *icon = EGG_TRAY_ICON(widget);
358 GdkScreen *screen;
359 GdkDisplay *display;
360 Display *xdisplay;
361 char buffer[256];
362 GdkWindow *root_window;
364 if (GTK_WIDGET_CLASS(parent_class)->realize)
365 GTK_WIDGET_CLASS(parent_class)->realize(widget);
367 make_transparent (widget, NULL);
369 screen = gtk_widget_get_screen(widget);
370 display = gdk_screen_get_display(screen);
371 xdisplay = gdk_x11_display_get_xdisplay(display);
373 /* Now see if there's a manager window around */
374 g_snprintf(buffer, sizeof(buffer),
375 "_NET_SYSTEM_TRAY_S%d", gdk_screen_get_number(screen));
377 icon->selection_atom = XInternAtom(xdisplay, buffer, False);
379 icon->manager_atom = XInternAtom(xdisplay, "MANAGER", False);
381 icon->system_tray_opcode_atom = XInternAtom(xdisplay,
382 "_NET_SYSTEM_TRAY_OPCODE", False);
384 icon->orientation_atom = XInternAtom(xdisplay,
385 "_NET_SYSTEM_TRAY_ORIENTATION", False);
387 egg_tray_icon_update_manager_window(icon, FALSE);
388 egg_tray_icon_send_dock_request(icon);
390 root_window = gdk_screen_get_root_window(screen);
392 /* Add a root window filter so that we get changes on MANAGER */
393 gdk_window_add_filter(root_window, egg_tray_icon_manager_filter, icon);
396 static void
397 egg_tray_icon_add (GtkContainer *container, GtkWidget *widget)
399 g_signal_connect (widget, "realize",
400 G_CALLBACK (make_transparent), NULL);
401 GTK_CONTAINER_CLASS (parent_class)->add (container, widget);
404 EggTrayIcon *egg_tray_icon_new_for_screen(GdkScreen * screen, const char *name)
406 g_return_val_if_fail(GDK_IS_SCREEN(screen), NULL);
408 return g_object_new(EGG_TYPE_TRAY_ICON, "screen", screen, "title", name, NULL);
411 EggTrayIcon *egg_tray_icon_new(const gchar * name)
413 return g_object_new(EGG_TYPE_TRAY_ICON, "title", name, NULL);
416 guint
417 egg_tray_icon_send_message(EggTrayIcon * icon,
418 gint timeout, const gchar * message, gint len)
420 guint stamp;
422 g_return_val_if_fail(EGG_IS_TRAY_ICON(icon), 0);
423 g_return_val_if_fail(timeout >= 0, 0);
424 g_return_val_if_fail(message != NULL, 0);
426 if (icon->manager_window == None)
427 return 0;
429 if (len < 0)
430 len = strlen(message);
432 stamp = icon->stamp++;
434 /* Get ready to send the message */
435 egg_tray_icon_send_manager_message(icon, SYSTEM_TRAY_BEGIN_MESSAGE,
436 (Window) gtk_plug_get_id(GTK_PLUG(icon)), timeout, len, stamp);
438 /* Now to send the actual message */
439 gdk_error_trap_push();
440 while (len > 0) {
441 XClientMessageEvent ev;
442 Display *xdisplay;
444 xdisplay = GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(GTK_WIDGET(icon)));
446 ev.type = ClientMessage;
447 ev.window = (Window) gtk_plug_get_id(GTK_PLUG(icon));
448 ev.format = 8;
449 ev.message_type = XInternAtom(xdisplay,
450 "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
451 if (len > 20) {
452 memcpy(&ev.data, message, 20);
453 len -= 20;
454 message += 20;
455 } else {
456 memcpy(&ev.data, message, len);
457 len = 0;
460 XSendEvent(xdisplay,
461 icon->manager_window, False, StructureNotifyMask, (XEvent *) & ev);
462 XSync(xdisplay, False);
464 gdk_error_trap_pop();
466 return stamp;
469 void egg_tray_icon_cancel_message(EggTrayIcon * icon, guint id)
471 g_return_if_fail(EGG_IS_TRAY_ICON(icon));
472 g_return_if_fail(id > 0);
474 egg_tray_icon_send_manager_message(icon, SYSTEM_TRAY_CANCEL_MESSAGE,
475 (Window) gtk_plug_get_id(GTK_PLUG(icon)), id, 0, 0);
478 GtkOrientation egg_tray_icon_get_orientation(EggTrayIcon * icon)
480 g_return_val_if_fail(EGG_IS_TRAY_ICON(icon), GTK_ORIENTATION_HORIZONTAL);
482 return icon->orientation;