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
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.
27 #include <gdkconfig.h>
30 #include <X11/Xatom.h>
32 #if defined (GDK_WINDOWING_WIN32)
33 #include <gdk/gdkwin32.h>
36 #include "eggtrayicon.h"
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
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
,
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;
77 static const GTypeInfo our_info
= {
78 sizeof(EggTrayIconClass
),
80 (GBaseFinalizeFunc
) NULL
,
81 (GClassInitFunc
) egg_tray_icon_class_init
,
82 NULL
, /* class_finalize */
83 NULL
, /* class_data */
86 (GInstanceInitFunc
) egg_tray_icon_init
89 our_type
= g_type_register_static(GTK_TYPE_PLUG
, "EggTrayIcon", &our_info
, 0);
95 static void egg_tray_icon_init(EggTrayIcon
* icon
)
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
,
121 g_param_spec_enum("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");
132 egg_tray_icon_get_property(GObject
* object
,
133 guint prop_id
, GValue
* value
, GParamSpec
* pspec
)
135 EggTrayIcon
*icon
= EGG_TRAY_ICON(object
);
138 case PROP_ORIENTATION
:
139 g_value_set_enum(value
, icon
->orientation
);
142 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
147 static void egg_tray_icon_get_orientation_property(EggTrayIcon
* icon
)
161 g_assert(icon
->manager_window
!= None
);
163 xdisplay
= GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(GTK_WIDGET(icon
)));
165 gdk_error_trap_push();
167 result
= XGetWindowProperty(xdisplay
,
168 icon
->manager_window
,
169 icon
->orientation_atom
,
171 XA_CARDINAL
, &type
, &format
, &nitems
, &bytes_after
, &(prop
.prop_ch
));
172 error
= gdk_error_trap_pop();
174 if (error
|| result
!= Success
)
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");
194 static GdkFilterReturn
195 egg_tray_icon_manager_filter(GdkXEvent
* xevent
, GdkEvent
* event
,
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
) {
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
);
240 egg_tray_icon_send_manager_message(EggTrayIcon
* icon
,
241 long message
, Window window
, long data1
, long data2
, long data3
)
243 XClientMessageEvent ev
;
246 ev
.type
= ClientMessage
;
248 ev
.message_type
= icon
->system_tray_opcode_atom
;
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);
272 egg_tray_icon_update_manager_window(EggTrayIcon
* icon
, gboolean dock_if_realized
)
276 if (icon
->manager_window
!= None
)
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
);
292 if (icon
->manager_window
!= None
) {
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
);
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
);
317 make_transparent_again (GtkWidget
*widget
, GtkStyle
*previous_style
,
320 gdk_window_set_back_pixmap (widget
->window
, NULL
, TRUE
);
324 make_transparent (GtkWidget
*widget
, gpointer user_data
)
326 if (GTK_WIDGET_NO_WINDOW (widget
) || GTK_WIDGET_APP_PAINTABLE (widget
))
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
)
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
);
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
);
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
);
417 egg_tray_icon_send_message(EggTrayIcon
* icon
,
418 gint timeout
, const gchar
* message
, gint len
)
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
)
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();
441 XClientMessageEvent ev
;
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
));
449 ev
.message_type
= XInternAtom(xdisplay
,
450 "_NET_SYSTEM_TRAY_MESSAGE_DATA", False
);
452 memcpy(&ev
.data
, message
, 20);
456 memcpy(&ev
.data
, message
, len
);
461 icon
->manager_window
, False
, StructureNotifyMask
, (XEvent
*) & ev
);
462 XSync(xdisplay
, False
);
464 gdk_error_trap_pop();
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
;