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-impl.h"
26 #include <gdkconfig.h>
27 #if defined (GDK_WINDOWING_X11)
29 #include <X11/Xatom.h>
30 #elif defined (GDK_WINDOWING_WIN32)
31 #include <gdk/gdkwin32.h>
34 #define EGG_COMPILATION
35 #ifndef EGG_COMPILATION
37 #define _(x) dgettext (GETTEXT_PACKAGE, x)
45 #define SYSTEM_TRAY_REQUEST_DOCK 0
46 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
47 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
49 #define SYSTEM_TRAY_ORIENTATION_HORZ 0
50 #define SYSTEM_TRAY_ORIENTATION_VERT 1
57 static GtkPlugClass
*parent_class
= NULL
;
59 static void egg_tray_icon_init (EggTrayIcon
*icon
);
60 static void egg_tray_icon_class_init (EggTrayIconClass
*klass
);
62 static void egg_tray_icon_get_property (GObject
*object
,
67 static void egg_tray_icon_realize (GtkWidget
*widget
);
68 static void egg_tray_icon_unrealize (GtkWidget
*widget
);
70 static void egg_tray_icon_add (GtkContainer
*container
,
73 #ifdef GDK_WINDOWING_X11
74 static void egg_tray_icon_update_manager_window (EggTrayIcon
*icon
,
75 gboolean dock_if_realized
);
76 static void egg_tray_icon_manager_window_destroyed (EggTrayIcon
*icon
);
80 egg_tray_icon_get_type (void)
82 static GType our_type
= 0;
86 static const GTypeInfo our_info
=
88 sizeof (EggTrayIconClass
),
90 (GBaseFinalizeFunc
) NULL
,
91 (GClassInitFunc
) egg_tray_icon_class_init
,
92 NULL
, /* class_finalize */
93 NULL
, /* class_data */
96 (GInstanceInitFunc
) egg_tray_icon_init
,
100 our_type
= g_type_register_static (GTK_TYPE_PLUG
, "EggTrayIcon", &our_info
, 0);
107 egg_tray_icon_init (EggTrayIcon
*icon
)
110 icon
->orientation
= GTK_ORIENTATION_HORIZONTAL
;
112 gtk_widget_add_events (GTK_WIDGET (icon
), GDK_PROPERTY_CHANGE_MASK
);
116 egg_tray_icon_class_init (EggTrayIconClass
*klass
)
118 GObjectClass
*gobject_class
= (GObjectClass
*)klass
;
119 GtkWidgetClass
*widget_class
= (GtkWidgetClass
*)klass
;
120 GtkContainerClass
*container_class
= (GtkContainerClass
*)klass
;
122 parent_class
= g_type_class_peek_parent (klass
);
124 gobject_class
->get_property
= egg_tray_icon_get_property
;
126 widget_class
->realize
= egg_tray_icon_realize
;
127 widget_class
->unrealize
= egg_tray_icon_unrealize
;
129 container_class
->add
= egg_tray_icon_add
;
131 g_object_class_install_property (gobject_class
,
133 g_param_spec_enum ("orientation",
135 _("The orientation of the tray."),
136 GTK_TYPE_ORIENTATION
,
137 GTK_ORIENTATION_HORIZONTAL
,
140 #if defined (GDK_WINDOWING_X11)
142 #elif defined (GDK_WINDOWING_WIN32)
143 g_warning ("Port eggtrayicon to Win32");
145 g_warning ("Port eggtrayicon to this GTK+ backend");
150 egg_tray_icon_get_property (GObject
*object
,
155 EggTrayIcon
*icon
= EGG_TRAY_ICON (object
);
159 case PROP_ORIENTATION
:
160 g_value_set_enum (value
, icon
->orientation
);
163 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
168 #ifdef GDK_WINDOWING_X11
171 egg_tray_icon_get_orientation_property (EggTrayIcon
*icon
)
184 g_assert (icon
->manager_window
!= None
);
186 xdisplay
= GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon
)));
188 gdk_error_trap_push ();
190 result
= XGetWindowProperty (xdisplay
,
191 icon
->manager_window
,
192 icon
->orientation_atom
,
195 &type
, &format
, &nitems
,
196 &bytes_after
, &(prop
.prop_ch
));
197 error
= gdk_error_trap_pop ();
199 if (error
|| result
!= Success
)
202 if (type
== XA_CARDINAL
)
204 GtkOrientation orientation
;
206 orientation
= (prop
.prop
[0] == SYSTEM_TRAY_ORIENTATION_HORZ
) ?
207 GTK_ORIENTATION_HORIZONTAL
:
208 GTK_ORIENTATION_VERTICAL
;
210 if (icon
->orientation
!= orientation
)
212 icon
->orientation
= orientation
;
214 g_object_notify (G_OBJECT (icon
), "orientation");
222 static GdkFilterReturn
223 egg_tray_icon_manager_filter (GdkXEvent
*xevent
, GdkEvent
*event
, gpointer user_data
)
225 EggTrayIcon
*icon
= user_data
;
226 XEvent
*xev
= (XEvent
*)xevent
;
228 if (xev
->xany
.type
== ClientMessage
&&
229 xev
->xclient
.message_type
== icon
->manager_atom
&&
230 xev
->xclient
.data
.l
[1] == icon
->selection_atom
)
232 egg_tray_icon_update_manager_window (icon
, TRUE
);
234 else if (xev
->xany
.window
== icon
->manager_window
)
236 if (xev
->xany
.type
== PropertyNotify
&&
237 xev
->xproperty
.atom
== icon
->orientation_atom
)
239 egg_tray_icon_get_orientation_property (icon
);
241 if (xev
->xany
.type
== DestroyNotify
)
243 egg_tray_icon_manager_window_destroyed (icon
);
246 return GDK_FILTER_CONTINUE
;
252 egg_tray_icon_unrealize (GtkWidget
*widget
)
254 #ifdef GDK_WINDOWING_X11
255 EggTrayIcon
*icon
= EGG_TRAY_ICON (widget
);
256 GdkWindow
*root_window
;
258 if (icon
->manager_window
!= None
)
262 gdkwin
= gdk_window_lookup_for_display (gtk_widget_get_display (widget
),
263 icon
->manager_window
);
265 gdk_window_remove_filter (gdkwin
, egg_tray_icon_manager_filter
, icon
);
268 root_window
= gdk_screen_get_root_window (gtk_widget_get_screen (widget
));
270 gdk_window_remove_filter (root_window
, egg_tray_icon_manager_filter
, icon
);
272 if (GTK_WIDGET_CLASS (parent_class
)->unrealize
)
273 (* GTK_WIDGET_CLASS (parent_class
)->unrealize
) (widget
);
277 #ifdef GDK_WINDOWING_X11
280 egg_tray_icon_send_manager_message (EggTrayIcon
*icon
,
287 XClientMessageEvent ev
;
290 ev
.type
= ClientMessage
;
292 ev
.message_type
= icon
->system_tray_opcode_atom
;
294 ev
.data
.l
[0] = gdk_x11_get_server_time (GTK_WIDGET (icon
)->window
);
295 ev
.data
.l
[1] = message
;
296 ev
.data
.l
[2] = data1
;
297 ev
.data
.l
[3] = data2
;
298 ev
.data
.l
[4] = data3
;
300 display
= GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon
)));
302 gdk_error_trap_push ();
304 icon
->manager_window
, False
, NoEventMask
, (XEvent
*)&ev
);
305 XSync (display
, False
);
306 gdk_error_trap_pop ();
310 egg_tray_icon_send_dock_request (EggTrayIcon
*icon
)
312 egg_tray_icon_send_manager_message (icon
,
313 SYSTEM_TRAY_REQUEST_DOCK
,
314 icon
->manager_window
,
315 gtk_plug_get_id (GTK_PLUG (icon
)),
320 egg_tray_icon_update_manager_window (EggTrayIcon
*icon
,
321 gboolean dock_if_realized
)
325 if (icon
->manager_window
!= None
)
328 xdisplay
= GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon
)));
330 XGrabServer (xdisplay
);
332 icon
->manager_window
= XGetSelectionOwner (xdisplay
,
333 icon
->selection_atom
);
335 if (icon
->manager_window
!= None
)
336 XSelectInput (xdisplay
,
337 icon
->manager_window
, StructureNotifyMask
|PropertyChangeMask
);
339 XUngrabServer (xdisplay
);
342 if (icon
->manager_window
!= None
)
346 gdkwin
= gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon
)),
347 icon
->manager_window
);
349 gdk_window_add_filter (gdkwin
, egg_tray_icon_manager_filter
, icon
);
351 if (dock_if_realized
&& GTK_WIDGET_REALIZED (icon
))
352 egg_tray_icon_send_dock_request (icon
);
354 egg_tray_icon_get_orientation_property (icon
);
359 egg_tray_icon_manager_window_destroyed (EggTrayIcon
*icon
)
363 g_return_if_fail (icon
->manager_window
!= None
);
365 gdkwin
= gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon
)),
366 icon
->manager_window
);
368 gdk_window_remove_filter (gdkwin
, egg_tray_icon_manager_filter
, icon
);
370 icon
->manager_window
= None
;
372 egg_tray_icon_update_manager_window (icon
, TRUE
);
378 transparent_expose_event (GtkWidget
*widget
, GdkEventExpose
*event
, gpointer user_data
)
380 gdk_window_clear_area (widget
->window
, event
->area
.x
, event
->area
.y
,
381 event
->area
.width
, event
->area
.height
);
386 make_transparent_again (GtkWidget
*widget
, GtkStyle
*previous_style
,
389 gdk_window_set_back_pixmap (widget
->window
, NULL
, TRUE
);
393 make_transparent (GtkWidget
*widget
, gpointer user_data
)
395 if (GTK_WIDGET_NO_WINDOW (widget
) || GTK_WIDGET_APP_PAINTABLE (widget
))
398 gtk_widget_set_app_paintable (widget
, TRUE
);
399 gtk_widget_set_double_buffered (widget
, FALSE
);
400 gdk_window_set_back_pixmap (widget
->window
, NULL
, TRUE
);
401 g_signal_connect (widget
, "expose_event",
402 G_CALLBACK (transparent_expose_event
), NULL
);
403 g_signal_connect_after (widget
, "style_set",
404 G_CALLBACK (make_transparent_again
), NULL
);
408 egg_tray_icon_realize (GtkWidget
*widget
)
410 #ifdef GDK_WINDOWING_X11
411 EggTrayIcon
*icon
= EGG_TRAY_ICON (widget
);
416 GdkWindow
*root_window
;
418 if (GTK_WIDGET_CLASS (parent_class
)->realize
)
419 GTK_WIDGET_CLASS (parent_class
)->realize (widget
);
421 make_transparent (widget
, NULL
);
423 screen
= gtk_widget_get_screen (widget
);
424 display
= gdk_screen_get_display (screen
);
425 xdisplay
= gdk_x11_display_get_xdisplay (display
);
427 /* Now see if there's a manager window around */
428 g_snprintf (buffer
, sizeof (buffer
),
429 "_NET_SYSTEM_TRAY_S%d",
430 gdk_screen_get_number (screen
));
432 icon
->selection_atom
= XInternAtom (xdisplay
, buffer
, False
);
434 icon
->manager_atom
= XInternAtom (xdisplay
, "MANAGER", False
);
436 icon
->system_tray_opcode_atom
= XInternAtom (xdisplay
,
437 "_NET_SYSTEM_TRAY_OPCODE",
440 icon
->orientation_atom
= XInternAtom (xdisplay
,
441 "_NET_SYSTEM_TRAY_ORIENTATION",
444 egg_tray_icon_update_manager_window (icon
, FALSE
);
445 egg_tray_icon_send_dock_request (icon
);
447 root_window
= gdk_screen_get_root_window (screen
);
449 /* Add a root window filter so that we get changes on MANAGER */
450 gdk_window_add_filter (root_window
,
451 egg_tray_icon_manager_filter
, icon
);
456 egg_tray_icon_add (GtkContainer
*container
, GtkWidget
*widget
)
458 g_signal_connect (widget
, "realize",
459 G_CALLBACK (make_transparent
), NULL
);
460 GTK_CONTAINER_CLASS (parent_class
)->add (container
, widget
);
464 egg_tray_icon_new_for_screen (GdkScreen
*screen
, const char *name
)
466 g_return_val_if_fail (GDK_IS_SCREEN (screen
), NULL
);
468 return g_object_new (EGG_TYPE_TRAY_ICON
, "screen", screen
, "title", name
, NULL
);
472 egg_tray_icon_new (const gchar
*name
)
474 return g_object_new (EGG_TYPE_TRAY_ICON
, "title", name
, NULL
);
478 egg_tray_icon_send_message (EggTrayIcon
*icon
,
480 const gchar
*message
,
485 g_return_val_if_fail (EGG_IS_TRAY_ICON (icon
), 0);
486 g_return_val_if_fail (timeout
>= 0, 0);
487 g_return_val_if_fail (message
!= NULL
, 0);
489 #ifdef GDK_WINDOWING_X11
490 if (icon
->manager_window
== None
)
495 len
= strlen (message
);
497 stamp
= icon
->stamp
++;
499 #ifdef GDK_WINDOWING_X11
500 /* Get ready to send the message */
501 egg_tray_icon_send_manager_message (icon
, SYSTEM_TRAY_BEGIN_MESSAGE
,
502 (Window
)gtk_plug_get_id (GTK_PLUG (icon
)),
503 timeout
, len
, stamp
);
505 /* Now to send the actual message */
506 gdk_error_trap_push ();
509 XClientMessageEvent ev
;
512 xdisplay
= GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon
)));
514 ev
.type
= ClientMessage
;
515 ev
.window
= (Window
)gtk_plug_get_id (GTK_PLUG (icon
));
517 ev
.message_type
= XInternAtom (xdisplay
,
518 "_NET_SYSTEM_TRAY_MESSAGE_DATA", False
);
521 memcpy (&ev
.data
, message
, 20);
527 memcpy (&ev
.data
, message
, len
);
531 XSendEvent (xdisplay
,
532 icon
->manager_window
, False
, StructureNotifyMask
, (XEvent
*)&ev
);
533 XSync (xdisplay
, False
);
535 gdk_error_trap_pop ();
542 egg_tray_icon_cancel_message (EggTrayIcon
*icon
,
545 g_return_if_fail (EGG_IS_TRAY_ICON (icon
));
546 g_return_if_fail (id
> 0);
547 #ifdef GDK_WINDOWING_X11
548 egg_tray_icon_send_manager_message (icon
, SYSTEM_TRAY_CANCEL_MESSAGE
,
549 (Window
)gtk_plug_get_id (GTK_PLUG (icon
)),
555 egg_tray_icon_get_orientation (EggTrayIcon
*icon
)
557 g_return_val_if_fail (EGG_IS_TRAY_ICON (icon
), GTK_ORIENTATION_HORIZONTAL
);
559 return icon
->orientation
;