TkTray is an extension for managing system tray icons with Tk on X Window System.
There are some other extensions for this purpose, like freedock mentioned at system tray. I decided to write my own implementation for a number of reasons:
Tktray is released under BSDL. It uses Tk image manipulation routines to handle images, thus no external dependencies are introduced.
Tktray will handle the case when the system tray manager is not running yet when an icon is created. When the tray manager starts later, Tktray will notice it and request docking for all existing icons.
Version history:
Googie - 3 Nov 2009 - Just tried it (v1.2) under Slackware64 13.0 with Xorg 1.6.3 and KDE 4.3.1 with composition effects enabled. It crashes while trying to:
tktray::icon .tray -image $img
The crash message is:
X Error of failed request: BadValue (integer parameter out of range for operation) Major opcode of failed request: 53 (X_CreatePixmap) Value in failed request: 0x0 Serial number of failed request: 415 Current serial number in output stream: 416
WK I couldn't contact the author directly, and it seems that older GCCs throw a bunch of errors, here's a patch for 1.1 version:
diff -r -u tktray-1.1-orig/tktray.c tktray-1.1/tktray.c --- tktray-1.1-orig/tktray.c 2006-01-06 18:43:47.000000000 +0100 +++ tktray-1.1/tktray.c 2007-02-03 14:51:56.000000000 +0100 @@ -72,7 +72,14 @@ static void TKU_ImageChanged(ClientData cd,int x, int y, int w, int h, int imgw, int imgh) { + register int cx,cy; + XImage *xim; + char *ida; + XGCValues gcv; TKU_ImageRep *ir = (TKU_ImageRep*) cd; + GC gc; + Tk_PhotoImageBlock pib; + ir->resized = ( (ir->w != imgw) || (ir->h != imgh) ||(ir->mask == None) ); if (ir->resized) { @@ -91,11 +98,9 @@ x = 0; y = 0; w = imgw; h = imgh; } /* Allright: create XImage, put it onto mask */ - char *ida = Tcl_Alloc(w*h); - XImage *xim = XCreateImage(Tk_Display(ir->tkwin), + ida = Tcl_Alloc(w*h); + xim = XCreateImage(Tk_Display(ir->tkwin), Tk_Visual(ir->tkwin),1,XYBitmap,0,ida,w,h,8,0); - register int cx,cy; - Tk_PhotoImageBlock pib; Tk_PhotoGetImage(ir->photo,&pib); for(cy=0;cy<h;++cy) for(cx=0;cx<w;++cx) { @@ -105,10 +110,9 @@ XPutPixel(xim, cx, cy, alpha<128? 0: 1); } - XGCValues gcv; gcv.foreground = 1; gcv.background = 0; - GC gc = XCreateGC(Tk_Display(ir->tkwin), + gc = XCreateGC(Tk_Display(ir->tkwin), ir->mask,GCForeground|GCBackground,&gcv); XSync(Tk_Display(ir->tkwin),False); //FIXME: DEBUG: XPutImage(Tk_Display(ir->tkwin),ir->mask,gc,xim,0,0,x,y,w,h); @@ -125,6 +129,8 @@ TKU_BindImageRep( Tcl_Interp * interp, Tk_Window tkwin, CONST char *image_name, TKU_ImageRep *ir) { Tk_PhotoHandle photo; + int w,h; + if (image_name == NULL) return TCL_ERROR; photo = Tk_FindPhoto(interp, image_name); @@ -136,7 +142,6 @@ ir->photo = photo; ir->tkimg = Tk_GetImage(interp, tkwin, image_name, TKU_ImageChanged, (ClientData)ir); - int w,h; Tk_PhotoGetSize(photo,&w,&h); TKU_ImageChanged((ClientData)ir,0,0,w,h,w,h); return TCL_OK; @@ -229,6 +234,7 @@ Window child_return; int wcmd; int i; + unsigned long timeout = 0; enum {XWC_CONFIGURE=0, XWC_BALLOON, XWC_BBOX}; const char *st_wcmd[]={"configure","balloon","bbox",NULL}; @@ -250,7 +256,6 @@ Tcl_WrongNumArgs(interp, 2, objv, "message ?timeout?"); return TCL_ERROR; } - unsigned long timeout = 0; if (objc==4) if (Tcl_GetLongFromObj(interp,objv[3],&timeout)!=TCL_OK) return TCL_ERROR; @@ -304,8 +309,8 @@ Window tray = XGetSelectionOwner(dpy,icon->docksel); if (tray != None) { - Tk_MakeWindowExist(tkwin); XEvent ev; + Tk_MakeWindowExist(tkwin); memset(&ev, 0, sizeof(ev)); ev.xclient.type = ClientMessage; ev.xclient.window = tray; @@ -370,8 +375,9 @@ Tk_Window tkwin = icon -> tkwin; Window w = icon -> wrapper; Display *dpy = Tk_Display(tkwin); + Atom XEMBED_INFO; Tk_MakeWindowExist(tkwin); - Atom XEMBED_INFO = Tk_InternAtom(tkwin,"_XEMBED_INFO"); + XEMBED_INFO = Tk_InternAtom(tkwin,"_XEMBED_INFO"); if (mask & ICON_CONF_IMAGE) { TKU_BindImageRep(icon->interp,tkwin, icon->imageString,&icon->img); @@ -398,8 +404,9 @@ DockIcon *icon = (DockIcon*) cd; icon->flags |=ICON_FLAG_RESHAPE_ON_REDRAW; if (icon->img.resized) { + XSizeHints *xszh; Tk_MakeWindowExist(icon->tkwin); - XSizeHints *xszh = XAllocSizeHints(); + xszh = XAllocSizeHints(); xszh->flags = PMinSize; xszh->min_width = imgw+2; xszh->min_height = imgh+2; @@ -453,10 +460,12 @@ static void DisplayIcon(ClientData cd) { + int winw; + int winh; DockIcon *icon = (DockIcon*)cd; if (icon->flags & ICON_FLAG_DELETED) return ; - int winw = Tk_Width(icon->tkwin), - winh = Tk_Height(icon->tkwin); + winw = Tk_Width(icon->tkwin), + winh = Tk_Height(icon->tkwin); icon->flags&=(~ICON_FLAG_REDRAW_PENDING); if (winw==0||winh==0||(!icon->visible)|| icon->img.tkimg==NULL) @@ -512,15 +521,18 @@ { Tk_Window tkwin = icon -> tkwin; Display* dpy = Tk_Display(tkwin); + int length; + XEvent ev; + if (icon->tray == None) return 0; - int length = strlen(utf8msg); - XEvent ev; + length = strlen(utf8msg); + memset(&ev, 0, sizeof(ev)); ev.type = ClientMessage; ev.xclient.window = icon->wrapper; ev.xclient.message_type = - Tk_InternAtom(tkwin,"_NET_SYSTEM_TRAY_OPCODE"); + Tk_InternAtom(tkwin,"_NET_SYSTEM_TRAY_OPCODE"); ev.xclient.format = 32; ev.xclient.data.l[0]=time(NULL); ev.xclient.data.l[1]=SYSTEM_TRAY_BEGIN_MESSAGE; @@ -530,6 +542,7 @@ XSendEvent(dpy, icon->tray, False, NoEventMask, &ev); XSync(dpy, False); /* Sending message elements */ + ev.xclient.message_type = Tk_InternAtom(tkwin,"_NET_SYSTEM_TRAY_MESSAGE_DATA"); ev.xclient.format = 8; @@ -553,14 +566,16 @@ int objc, Tcl_Obj * CONST objv[]) { DockIcon *icon; + Tk_Window tkwin; if (objc < 2||(objc%2)) { Tcl_WrongNumArgs(interp, 1, objv, "pathName ?option value ...?"); return TCL_ERROR; } - Tk_Window tkwin = - Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), + tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), Tcl_GetString(objv[1]),""); + TkpWmSetState((TkWindow*)tkwin, WithdrawnState); + if (tkwin == NULL) return TCL_ERROR; TKU_CompleteToplevel(tkwin);