00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <string.h>
00022 #include <gdk/gdkx.h>
00023 #include "eggtrayicon.h"
00024
00025 #define SYSTEM_TRAY_REQUEST_DOCK 0
00026 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
00027 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
00028
00029 static GtkPlugClass *parent_class = NULL;
00030
00031 static void egg_tray_icon_init (EggTrayIcon *icon);
00032 static void egg_tray_icon_class_init (EggTrayIconClass *klass);
00033
00034 static void egg_tray_icon_update_manager_window (EggTrayIcon *icon);
00035
00036 GType
00037 egg_tray_icon_get_type (void)
00038 {
00039 static GType our_type = 0;
00040
00041 if (our_type == 0)
00042 {
00043 static const GTypeInfo our_info =
00044 {
00045 sizeof (EggTrayIconClass),
00046 (GBaseInitFunc) NULL,
00047 (GBaseFinalizeFunc) NULL,
00048 (GClassInitFunc) egg_tray_icon_class_init,
00049 NULL,
00050 NULL,
00051 sizeof (EggTrayIcon),
00052 0,
00053 (GInstanceInitFunc) egg_tray_icon_init
00054 };
00055
00056 our_type = g_type_register_static (GTK_TYPE_PLUG, "EggTrayIcon", &our_info, 0);
00057 }
00058
00059 return our_type;
00060 }
00061
00062 static void
00063 egg_tray_icon_init (EggTrayIcon *icon)
00064 {
00065 icon->stamp = 1;
00066
00067 gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
00068 }
00069
00070 static void
00071 egg_tray_icon_class_init (EggTrayIconClass *klass)
00072 {
00073 parent_class = g_type_class_peek_parent (klass);
00074 }
00075
00076 static GdkFilterReturn
00077 egg_tray_icon_manager_filter (GdkXEvent *xevent, GdkEvent *event, gpointer user_data)
00078 {
00079 EggTrayIcon *icon = user_data;
00080 XEvent *xev = (XEvent *)xevent;
00081
00082 if (xev->xany.type == ClientMessage &&
00083 xev->xclient.message_type == icon->manager_atom &&
00084 xev->xclient.data.l[1] == icon->selection_atom)
00085 {
00086 egg_tray_icon_update_manager_window (icon);
00087 }
00088 else if (xev->xany.window == icon->manager_window)
00089 {
00090 if (xev->xany.type == DestroyNotify)
00091 {
00092 egg_tray_icon_update_manager_window (icon);
00093 }
00094 }
00095
00096 return GDK_FILTER_CONTINUE;
00097 }
00098
00099 static void
00100 egg_tray_icon_send_manager_message (EggTrayIcon *icon,
00101 long message,
00102 Window window,
00103 long data1,
00104 long data2,
00105 long data3)
00106 {
00107 XClientMessageEvent ev;
00108 Display *display;
00109
00110 ev.type = ClientMessage;
00111 ev.window = window;
00112 ev.message_type = icon->system_tray_opcode_atom;
00113 ev.format = 32;
00114 ev.data.l[0] = gdk_x11_get_server_time (GTK_WIDGET (icon)->window);
00115 ev.data.l[1] = message;
00116 ev.data.l[2] = data1;
00117 ev.data.l[3] = data2;
00118 ev.data.l[4] = data3;
00119
00120 #if HAVE_GTK_MULTIHEAD
00121 display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
00122 #else
00123 display = gdk_display;
00124 #endif
00125
00126 gdk_error_trap_push ();
00127 XSendEvent (display,
00128 icon->manager_window, False, NoEventMask, (XEvent *)&ev);
00129 XSync (display, False);
00130 gdk_error_trap_pop ();
00131 }
00132
00133 static void
00134 egg_tray_icon_send_dock_request (EggTrayIcon *icon)
00135 {
00136 egg_tray_icon_send_manager_message (icon,
00137 SYSTEM_TRAY_REQUEST_DOCK,
00138 icon->manager_window,
00139 gtk_plug_get_id (GTK_PLUG (icon)),
00140 0, 0);
00141 }
00142
00143 static void
00144 egg_tray_icon_update_manager_window (EggTrayIcon *icon)
00145 {
00146 Display *xdisplay;
00147
00148 #if HAVE_GTK_MULTIHEAD
00149 xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
00150 #else
00151 xdisplay = gdk_display;
00152 #endif
00153
00154 if (icon->manager_window != None)
00155 {
00156 GdkWindow *gdkwin;
00157
00158 #if HAVE_GTK_MULTIHEAD
00159 gdkwin = gdk_window_lookup_for_display (display,
00160 icon->manager_window);
00161 #else
00162 gdkwin = gdk_window_lookup (icon->manager_window);
00163 #endif
00164
00165 gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
00166 }
00167
00168 XGrabServer (xdisplay);
00169
00170 icon->manager_window = XGetSelectionOwner (xdisplay,
00171 icon->selection_atom);
00172
00173 if (icon->manager_window != None)
00174 XSelectInput (xdisplay,
00175 icon->manager_window, StructureNotifyMask);
00176
00177 XUngrabServer (xdisplay);
00178 XFlush (xdisplay);
00179
00180 if (icon->manager_window != None)
00181 {
00182 GdkWindow *gdkwin;
00183
00184 #if HAVE_GTK_MULTIHEAD
00185 gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
00186 icon->manager_window);
00187 #else
00188 gdkwin = gdk_window_lookup (icon->manager_window);
00189 #endif
00190
00191 gdk_window_add_filter (gdkwin, egg_tray_icon_manager_filter, icon);
00192
00193
00194 egg_tray_icon_send_dock_request (icon);
00195 }
00196 }
00197
00198 EggTrayIcon *
00199 egg_tray_icon_new_for_xscreen (Screen *xscreen, const char *name)
00200 {
00201 EggTrayIcon *icon;
00202 char buffer[256];
00203 GdkWindow *root_window;
00204
00205 g_return_val_if_fail (xscreen != NULL, NULL);
00206
00207 icon = g_object_new (EGG_TYPE_TRAY_ICON, NULL);
00208 gtk_window_set_title (GTK_WINDOW (icon), name);
00209
00210 #if HAVE_GTK_MULTIHEAD
00211 gtk_plug_construct_for_display (GTK_PLUG (icon),
00212 gdk_screen_get_display (screen), 0);
00213 #else
00214 gtk_plug_construct (GTK_PLUG (icon), 0);
00215 #endif
00216
00217 gtk_widget_realize (GTK_WIDGET (icon));
00218
00219
00220 g_snprintf (buffer, sizeof (buffer),
00221 "_NET_SYSTEM_TRAY_S%d",
00222 XScreenNumberOfScreen (xscreen));
00223
00224 icon->selection_atom = XInternAtom (DisplayOfScreen (xscreen),
00225 buffer, False);
00226
00227 icon->manager_atom = XInternAtom (DisplayOfScreen (xscreen),
00228 "MANAGER", False);
00229
00230 icon->system_tray_opcode_atom = XInternAtom (DisplayOfScreen (xscreen),
00231 "_NET_SYSTEM_TRAY_OPCODE", False);
00232
00233 egg_tray_icon_update_manager_window (icon);
00234
00235 #if HAVE_GTK_MULTIHEAD
00236 root_window = gdk_screen_get_root_window (screen);
00237 #else
00238 root_window = gdk_window_lookup (gdk_x11_get_default_root_xwindow ());
00239 #endif
00240
00241
00242 gdk_window_add_filter (root_window,
00243 egg_tray_icon_manager_filter, icon);
00244
00245 return icon;
00246 }
00247
00248 #if HAVE_GTK_MULTIHEAD
00249 EggTrayIcon *
00250 egg_tray_icon_new_for_screen (GdkScreen *screen, const char *name)
00251 {
00252 EggTrayIcon *icon;
00253 char buffer[256];
00254
00255 g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
00256
00257 return egg_tray_icon_new_for_xscreen (GDK_SCREEN_XSCREEN (screen), name);
00258 }
00259 #endif
00260
00261 EggTrayIcon*
00262 egg_tray_icon_new (const gchar *name)
00263 {
00264 return egg_tray_icon_new_for_xscreen (DefaultScreenOfDisplay (gdk_display), name);
00265 }
00266
00267 guint
00268 egg_tray_icon_send_message (EggTrayIcon *icon,
00269 gint timeout,
00270 const gchar *message,
00271 gint len)
00272 {
00273 guint stamp;
00274
00275 g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), 0);
00276 g_return_val_if_fail (timeout >= 0, 0);
00277 g_return_val_if_fail (message != NULL, 0);
00278
00279 if (icon->manager_window == None)
00280 return 0;
00281
00282 if (len < 0)
00283 len = strlen (message);
00284
00285 stamp = icon->stamp++;
00286
00287
00288 egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE,
00289 (Window)gtk_plug_get_id (GTK_PLUG (icon)),
00290 timeout, len, stamp);
00291
00292
00293 gdk_error_trap_push ();
00294 while (len > 0)
00295 {
00296 XClientMessageEvent ev;
00297 Display *xdisplay;
00298
00299 #if HAVE_GTK_MULTIHEAD
00300 xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
00301 #else
00302 xdisplay = gdk_display;
00303 #endif
00304
00305 ev.type = ClientMessage;
00306 ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon));
00307 ev.format = 8;
00308 ev.message_type = XInternAtom (xdisplay,
00309 "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
00310 if (len > 20)
00311 {
00312 memcpy (&ev.data, message, 20);
00313 len -= 20;
00314 message += 20;
00315 }
00316 else
00317 {
00318 memcpy (&ev.data, message, len);
00319 len = 0;
00320 }
00321
00322 XSendEvent (xdisplay,
00323 icon->manager_window, False, StructureNotifyMask, (XEvent *)&ev);
00324 XSync (xdisplay, False);
00325 }
00326 gdk_error_trap_pop ();
00327
00328 return stamp;
00329 }
00330
00331 void
00332 egg_tray_icon_cancel_message (EggTrayIcon *icon,
00333 guint id)
00334 {
00335 g_return_if_fail (EGG_IS_TRAY_ICON (icon));
00336 g_return_if_fail (id > 0);
00337
00338 egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE,
00339 (Window)gtk_plug_get_id (GTK_PLUG (icon)),
00340 id, 0, 0);
00341 }