Code:
// gcc -Wall -g tray.c -o tray `pkg-config gtk+-2.0 --cflags --libs`
#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
typedef struct tray_struct * trayicon;
void create_tray_and_dock (trayicon tray);
#define SYSTEM_TRAY_REQUEST_DOCK 0
struct tray_struct {
Window manager_window;
GdkWindow *manager_window_gdk;
gint screen_height;
GdkWindow *root_gdk;
GdkWindow *plug_gdk;
Window plug_xlib;
GdkPixbuf *icon_pixbuf;
Atom system_tray_opcode_atom;
Atom manager_atom;
};
Window get_manager_window (void)
{
static gboolean first_call=TRUE;
static Atom selection_atom;
Window manager_window=None;
if (first_call) {
gchar *tmp=g_strdup_printf ("_NET_SYSTEM_TRAY_S%i",
XScreenNumberOfScreen(XDefaultScreenOfDisplay(GDK_DISPLAY())));
selection_atom = XInternAtom(GDK_DISPLAY(), tmp, False);
g_free (tmp);
first_call=FALSE;
}
gdk_error_trap_push();
XGrabServer (GDK_DISPLAY());
manager_window = XGetSelectionOwner (GDK_DISPLAY(), selection_atom);
XUngrabServer (GDK_DISPLAY());
XFlush (GDK_DISPLAY());
if (gdk_error_trap_pop())
return None;
printf ("get manager window: %d\n", manager_window);
return manager_window;
}
void dock_window(trayicon tray)
{
XClientMessageEvent ev;
ev.type = ClientMessage;
ev.window =tray->manager_window;
ev.message_type =tray->system_tray_opcode_atom;
ev.format = 32;
ev.data.l[0] = CurrentTime;
ev.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK;
ev.data.l[2] = tray->plug_xlib;
ev.data.l[3] =0;
ev.data.l[4] = 0;
gdk_error_trap_push ();
XSendEvent (GDK_DISPLAY(), tray->manager_window, False,
NoEventMask,
(XEvent *)&ev);
XSync (GDK_DISPLAY(), False);
gdk_error_trap_pop ();
}
void reparent_notify (trayicon tray)
{
XSetWindowBackgroundPixmap(GDK_DISPLAY(), tray->plug_xlib,
ParentRelative);
XClearWindow (GDK_DISPLAY(), tray->plug_xlib);
XFlush (GDK_DISPLAY());
}
void expose (trayicon tray)
{
GdkGC *gc =gdk_gc_new (tray->plug_gdk);
XClearWindow (GDK_DISPLAY(), tray->plug_xlib);
XFlush (GDK_DISPLAY());
gdk_draw_pixbuf (tray->plug_gdk, gc, tray->icon_pixbuf,
0, 0, 0, 0, 24, 24, GDK_RGB_DITHER_NONE, 0, 0);
}
static GdkFilterReturn filter (GdkXEvent *xevent,
GdkEvent *event, gpointer user_data)
{
XEvent *xev = (XEvent *)xevent;
trayicon tray = (trayicon) user_data;
if (xev->xany.type == ReparentNotify) {
printf ("reparen notify\n");
reparent_notify(tray);
}
if (xev->xany.type == Expose) {
printf ("expose notify\n");
expose(tray);
}
return GDK_FILTER_CONTINUE;
}
void create_tray_and_dock (trayicon tray)
{
if (tray->manager_window==None)
tray->manager_window=get_manager_window();
if (tray->manager_window==None)
return;
tray->manager_window_gdk=NULL;
tray->manager_window_gdk=gdk_window_foreign_new(tray->manager_window);
tray->plug_xlib= XCreateSimpleWindow(GDK_DISPLAY(),
GDK_ROOT_WINDOW(), 0,
0, 24, 24, 0, 0, 0);
XSelectInput(GDK_DISPLAY(), tray->plug_xlib, StructureNotifyMask |
ExposureMask);
tray->plug_gdk=gdk_window_foreign_new (tray->plug_xlib);
gdk_window_add_filter (tray->plug_gdk, filter, (gpointer) tray);
dock_window (tray);
}
void trayicon_show (trayicon tray)
{
g_assert (tray != NULL);
tray->screen_height=gdk_screen_get_height (gdk_screen_get_default());
tray->root_gdk =gdk_screen_get_root_window
(gdk_screen_get_default());
tray->manager_window = get_manager_window ();
tray->manager_window_gdk =NULL;
create_tray_and_dock(tray);
}
trayicon trayicon_new(GdkPixbuf *default_icon)
{
g_assert (default_icon != NULL);
trayicon tray;
printf ("trayicon_new\n");
tray =(trayicon)g_malloc(sizeof(struct tray_struct));
tray->manager_atom = XInternAtom (GDK_DISPLAY(), "MANAGER", False);
tray->system_tray_opcode_atom = XInternAtom (GDK_DISPLAY(),
"_NET_SYSTEM_TRAY_OPCODE", False);
tray->icon_pixbuf=g_object_ref (default_icon);
return tray;
}
int main (int argc, char *argv[])
{
gtk_init (&argc, &argv);
trayicon tray;
GdkPixbuf *pixbuf=gdk_pixbuf_new_from_file ("/home/bob/Download/gaim.png",
NULL);
tray=trayicon_new(pixbuf);
g_object_unref (pixbuf);
create_tray_and_dock(tray);
//dock_window(tray);
trayicon_show (tray);
expose(tray);
gtk_main ();
return 0;
}