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 Permission to use this file under wxWindows license given by
26 From andersca@gnu.org Tue Dec 9 13:01:56 2003
27 Subject: Re: eggtrayicon.{c,h} licensing
28 From: Anders Carlsson <andersca@gnu.org>
29 To: Vaclav Slavik <vaclav.slavik@matfyz.cz>
31 On tis, 2003-12-09 at 11:42 +0100, Vaclav Slavik wrote:
34 > I'm working on the wxWindows cross-platform GUI toolkit
35 > (http://www.wxwindows.org) which uses GTK+ and it would save me a lot
36 > of time if I could use your eggtrayicon code to implement tray icons
37 > on X11. Unfortunately I can't use it right now because it is not part
38 > of any library we could depend on (as we do depend on GTK+) and would
39 > have to be included in our sources and it is under the LGPL license.
40 > The problem is that wxWindows' license is more permissive (see
41 > http://www.opensource.org/licenses/wxwindows.php for details) and so
42 > I can't take your code and put it under wxWindows License. And I
43 > can't put code that can't be used under the terms of wxWindows
44 > License into wxWindows either. Do you think it would be possible to
45 > get permission to include eggtrayicon under wxWindows license?
51 Sure, that's fine by me.
58 #include "wx/platform.h"
62 #include <gtk/gtkversion.h>
63 #if GTK_CHECK_VERSION(2, 1, 0)
66 #include "eggtrayicon.h"
68 #include <gdkconfig.h>
69 #if defined (GDK_WINDOWING_X11)
71 #include <X11/Xatom.h>
72 #elif defined (GDK_WINDOWING_WIN32)
73 #include <gdk/gdkwin32.h>
77 #define SYSTEM_TRAY_REQUEST_DOCK 0
78 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
79 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
81 #define SYSTEM_TRAY_ORIENTATION_HORZ 0
82 #define SYSTEM_TRAY_ORIENTATION_VERT 1
89 static GtkPlugClass
*parent_class
= NULL
;
91 static void egg_tray_icon_init (EggTrayIcon
*icon
);
92 static void egg_tray_icon_class_init (EggTrayIconClass
*klass
);
94 static void egg_tray_icon_get_property (GObject
*object
,
99 static void egg_tray_icon_realize (GtkWidget
*widget
);
100 static void egg_tray_icon_unrealize (GtkWidget
*widget
);
102 static void egg_tray_icon_add (GtkContainer
*container
,
105 #ifdef GDK_WINDOWING_X11
106 static void egg_tray_icon_update_manager_window (EggTrayIcon
*icon
,
107 gboolean dock_if_realized
);
108 static void egg_tray_icon_manager_window_destroyed (EggTrayIcon
*icon
);
112 egg_tray_icon_get_type (void)
114 static GType our_type
= 0;
118 static const GTypeInfo our_info
=
120 sizeof (EggTrayIconClass
),
121 (GBaseInitFunc
) NULL
,
122 (GBaseFinalizeFunc
) NULL
,
123 (GClassInitFunc
) egg_tray_icon_class_init
,
124 NULL
, /* class_finalize */
125 NULL
, /* class_data */
126 sizeof (EggTrayIcon
),
128 (GInstanceInitFunc
) egg_tray_icon_init
131 our_type
= g_type_register_static (GTK_TYPE_PLUG
, "EggTrayIcon", &our_info
, 0);
138 egg_tray_icon_init (EggTrayIcon
*icon
)
141 icon
->orientation
= GTK_ORIENTATION_HORIZONTAL
;
143 gtk_widget_add_events (GTK_WIDGET (icon
), GDK_PROPERTY_CHANGE_MASK
);
147 egg_tray_icon_class_init (EggTrayIconClass
*klass
)
149 GObjectClass
*gobject_class
= (GObjectClass
*)klass
;
150 GtkWidgetClass
*widget_class
= (GtkWidgetClass
*)klass
;
151 GtkContainerClass
*container_class
= (GtkContainerClass
*)klass
;
153 parent_class
= g_type_class_peek_parent (klass
);
155 gobject_class
->get_property
= egg_tray_icon_get_property
;
157 widget_class
->realize
= egg_tray_icon_realize
;
158 widget_class
->unrealize
= egg_tray_icon_unrealize
;
160 container_class
->add
= egg_tray_icon_add
;
162 g_object_class_install_property (gobject_class
,
164 g_param_spec_enum ("orientation",
166 "The orientation of the tray.",
167 GTK_TYPE_ORIENTATION
,
168 GTK_ORIENTATION_HORIZONTAL
,
173 egg_tray_icon_get_property (GObject
*object
,
178 EggTrayIcon
*icon
= EGG_TRAY_ICON (object
);
182 case PROP_ORIENTATION
:
183 g_value_set_enum (value
, icon
->orientation
);
186 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
191 #ifdef GDK_WINDOWING_X11
194 egg_tray_icon_get_orientation_property (EggTrayIcon
*icon
)
207 g_assert (icon
->manager_window
!= None
);
209 xdisplay
= GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon
)));
211 gdk_error_trap_push ();
213 result
= XGetWindowProperty (xdisplay
,
214 icon
->manager_window
,
215 icon
->orientation_atom
,
218 &type
, &format
, &nitems
,
219 &bytes_after
, &(prop
.prop_ch
));
220 error
= gdk_error_trap_pop ();
222 if (error
|| result
!= Success
)
225 if (type
== XA_CARDINAL
)
227 GtkOrientation orientation
;
229 orientation
= (prop
.prop
[0] == SYSTEM_TRAY_ORIENTATION_HORZ
) ?
230 GTK_ORIENTATION_HORIZONTAL
:
231 GTK_ORIENTATION_VERTICAL
;
233 if (icon
->orientation
!= orientation
)
235 icon
->orientation
= orientation
;
237 g_object_notify (G_OBJECT (icon
), "orientation");
245 static GdkFilterReturn
246 egg_tray_icon_manager_filter (GdkXEvent
*xevent
, GdkEvent
*event
, gpointer user_data
)
248 EggTrayIcon
*icon
= user_data
;
249 XEvent
*xev
= (XEvent
*)xevent
;
251 if (xev
->xany
.type
== ClientMessage
&&
252 xev
->xclient
.message_type
== icon
->manager_atom
&&
253 xev
->xclient
.data
.l
[1] == icon
->selection_atom
)
255 egg_tray_icon_update_manager_window (icon
, TRUE
);
257 else if (xev
->xany
.window
== icon
->manager_window
)
259 if (xev
->xany
.type
== PropertyNotify
&&
260 xev
->xproperty
.atom
== icon
->orientation_atom
)
262 egg_tray_icon_get_orientation_property (icon
);
264 if (xev
->xany
.type
== DestroyNotify
)
266 egg_tray_icon_manager_window_destroyed (icon
);
269 return GDK_FILTER_CONTINUE
;
275 egg_tray_icon_unrealize (GtkWidget
*widget
)
277 #ifdef GDK_WINDOWING_X11
278 EggTrayIcon
*icon
= EGG_TRAY_ICON (widget
);
279 GdkWindow
*root_window
;
281 if (icon
->manager_window
!= None
)
285 gdkwin
= gdk_window_lookup_for_display (gtk_widget_get_display (widget
),
286 icon
->manager_window
);
288 gdk_window_remove_filter (gdkwin
, egg_tray_icon_manager_filter
, icon
);
291 root_window
= gdk_screen_get_root_window (gtk_widget_get_screen (widget
));
293 gdk_window_remove_filter (root_window
, egg_tray_icon_manager_filter
, icon
);
295 if (GTK_WIDGET_CLASS (parent_class
)->unrealize
)
296 (* GTK_WIDGET_CLASS (parent_class
)->unrealize
) (widget
);
300 #ifdef GDK_WINDOWING_X11
303 egg_tray_icon_send_manager_message (EggTrayIcon
*icon
,
310 XClientMessageEvent ev
;
313 ev
.type
= ClientMessage
;
315 ev
.message_type
= icon
->system_tray_opcode_atom
;
317 ev
.data
.l
[0] = gdk_x11_get_server_time (GTK_WIDGET (icon
)->window
);
318 ev
.data
.l
[1] = message
;
319 ev
.data
.l
[2] = data1
;
320 ev
.data
.l
[3] = data2
;
321 ev
.data
.l
[4] = data3
;
323 display
= GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon
)));
325 gdk_error_trap_push ();
327 icon
->manager_window
, False
, NoEventMask
, (XEvent
*)&ev
);
328 XSync (display
, False
);
329 gdk_error_trap_pop ();
333 egg_tray_icon_send_dock_request (EggTrayIcon
*icon
)
335 egg_tray_icon_send_manager_message (icon
,
336 SYSTEM_TRAY_REQUEST_DOCK
,
337 icon
->manager_window
,
338 gtk_plug_get_id (GTK_PLUG (icon
)),
343 egg_tray_icon_update_manager_window (EggTrayIcon
*icon
,
344 gboolean dock_if_realized
)
348 if (icon
->manager_window
!= None
)
351 xdisplay
= GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon
)));
353 XGrabServer (xdisplay
);
355 icon
->manager_window
= XGetSelectionOwner (xdisplay
,
356 icon
->selection_atom
);
358 if (icon
->manager_window
!= None
)
359 XSelectInput (xdisplay
,
360 icon
->manager_window
, StructureNotifyMask
|PropertyChangeMask
);
362 XUngrabServer (xdisplay
);
365 if (icon
->manager_window
!= None
)
369 gdkwin
= gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon
)),
370 icon
->manager_window
);
372 gdk_window_add_filter (gdkwin
, egg_tray_icon_manager_filter
, icon
);
374 if (dock_if_realized
&& GTK_WIDGET_REALIZED (icon
))
375 egg_tray_icon_send_dock_request (icon
);
377 egg_tray_icon_get_orientation_property (icon
);
382 egg_tray_icon_manager_window_destroyed (EggTrayIcon
*icon
)
386 g_return_if_fail (icon
->manager_window
!= None
);
388 gdkwin
= gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon
)),
389 icon
->manager_window
);
391 gdk_window_remove_filter (gdkwin
, egg_tray_icon_manager_filter
, icon
);
393 icon
->manager_window
= None
;
395 egg_tray_icon_update_manager_window (icon
, TRUE
);
401 transparent_expose_event (GtkWidget
*widget
, GdkEventExpose
*event
, gpointer user_data
)
403 gdk_window_clear_area (widget
->window
, event
->area
.x
, event
->area
.y
,
404 event
->area
.width
, event
->area
.height
);
409 make_transparent_again (GtkWidget
*widget
, GtkStyle
*previous_style
,
412 gdk_window_set_back_pixmap (widget
->window
, NULL
, TRUE
);
416 make_transparent (GtkWidget
*widget
, gpointer user_data
)
418 if (GTK_WIDGET_NO_WINDOW (widget
) || GTK_WIDGET_APP_PAINTABLE (widget
))
421 gtk_widget_set_app_paintable (widget
, TRUE
);
422 gtk_widget_set_double_buffered (widget
, FALSE
);
423 gdk_window_set_back_pixmap (widget
->window
, NULL
, TRUE
);
424 g_signal_connect (widget
, "expose_event",
425 G_CALLBACK (transparent_expose_event
), NULL
);
426 g_signal_connect_after (widget
, "style_set",
427 G_CALLBACK (make_transparent_again
), NULL
);
431 egg_tray_icon_realize (GtkWidget
*widget
)
433 #ifdef GDK_WINDOWING_X11
434 EggTrayIcon
*icon
= EGG_TRAY_ICON (widget
);
439 GdkWindow
*root_window
;
441 if (GTK_WIDGET_CLASS (parent_class
)->realize
)
442 GTK_WIDGET_CLASS (parent_class
)->realize (widget
);
444 make_transparent (widget
, NULL
);
446 screen
= gtk_widget_get_screen (widget
);
447 display
= gdk_screen_get_display (screen
);
448 xdisplay
= gdk_x11_display_get_xdisplay (display
);
450 /* Now see if there's a manager window around */
451 g_snprintf (buffer
, sizeof (buffer
),
452 "_NET_SYSTEM_TRAY_S%d",
453 gdk_screen_get_number (screen
));
455 icon
->selection_atom
= XInternAtom (xdisplay
, buffer
, False
);
457 icon
->manager_atom
= XInternAtom (xdisplay
, "MANAGER", False
);
459 icon
->system_tray_opcode_atom
= XInternAtom (xdisplay
,
460 "_NET_SYSTEM_TRAY_OPCODE",
463 icon
->orientation_atom
= XInternAtom (xdisplay
,
464 "_NET_SYSTEM_TRAY_ORIENTATION",
467 egg_tray_icon_update_manager_window (icon
, FALSE
);
468 egg_tray_icon_send_dock_request (icon
);
470 root_window
= gdk_screen_get_root_window (screen
);
472 /* Add a root window filter so that we get changes on MANAGER */
473 gdk_window_add_filter (root_window
,
474 egg_tray_icon_manager_filter
, icon
);
479 egg_tray_icon_add (GtkContainer
*container
, GtkWidget
*widget
)
481 g_signal_connect (widget
, "realize",
482 G_CALLBACK (make_transparent
), NULL
);
483 GTK_CONTAINER_CLASS (parent_class
)->add (container
, widget
);
487 egg_tray_icon_new_for_screen (GdkScreen
*screen
, const char *name
)
489 g_return_val_if_fail (GDK_IS_SCREEN (screen
), NULL
);
491 return g_object_new (EGG_TYPE_TRAY_ICON
, "screen", screen
, "title", name
, NULL
);
495 egg_tray_icon_new (const gchar
*name
)
497 return g_object_new (EGG_TYPE_TRAY_ICON
, "title", name
, NULL
);
501 egg_tray_icon_send_message (EggTrayIcon
*icon
,
503 const gchar
*message
,
508 g_return_val_if_fail (EGG_IS_TRAY_ICON (icon
), 0);
509 g_return_val_if_fail (timeout
>= 0, 0);
510 g_return_val_if_fail (message
!= NULL
, 0);
512 #ifdef GDK_WINDOWING_X11
513 if (icon
->manager_window
== None
)
518 len
= strlen (message
);
520 stamp
= icon
->stamp
++;
522 #ifdef GDK_WINDOWING_X11
523 /* Get ready to send the message */
524 egg_tray_icon_send_manager_message (icon
, SYSTEM_TRAY_BEGIN_MESSAGE
,
525 icon
->manager_window
,
526 timeout
, len
, stamp
);
528 /* Now to send the actual message */
529 gdk_error_trap_push ();
532 XClientMessageEvent ev
;
535 xdisplay
= GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon
)));
537 ev
.type
= ClientMessage
;
538 ev
.window
= icon
->manager_window
;
540 ev
.message_type
= XInternAtom (xdisplay
,
541 "_NET_SYSTEM_TRAY_MESSAGE_DATA", False
);
544 memcpy (&ev
.data
, message
, 20);
550 memcpy (&ev
.data
, message
, len
);
554 XSendEvent (xdisplay
,
555 icon
->manager_window
, False
, StructureNotifyMask
, (XEvent
*)&ev
);
556 XSync (xdisplay
, False
);
558 gdk_error_trap_pop ();
565 egg_tray_icon_cancel_message (EggTrayIcon
*icon
,
568 g_return_if_fail (EGG_IS_TRAY_ICON (icon
));
569 g_return_if_fail (id
> 0);
570 #ifdef GDK_WINDOWING_X11
571 egg_tray_icon_send_manager_message (icon
, SYSTEM_TRAY_CANCEL_MESSAGE
,
572 (Window
)gtk_plug_get_id (GTK_PLUG (icon
)),
578 egg_tray_icon_get_orientation (EggTrayIcon
*icon
)
580 g_return_val_if_fail (EGG_IS_TRAY_ICON (icon
), GTK_ORIENTATION_HORIZONTAL
);
582 return icon
->orientation
;
589 #endif /* GTK_CHECK_VERSION(2, 1, 0) */
590 #endif /* wxUSE_TASKBARICON */