Drawing Into Foreign Windows

George Peter Staplin: Someone asked me how to draw into a foreign window today, and I've wondered how to do it for a while, so I came up with the working solution below. It works well if you just need to display some canvas data in another window. I'm not quite sure how to make buttons or canvas objects displayed in the foreign window respond to keyboard and mouse events. Also for most needs wish -use xid, and/or toplevel -use xid will work well if you want to use another X window as a parent for the Tk window.-

A demo application for Unix-like operating systems that draws on the root/background window can be found in this package: http://www.xmission.com/~georgeps/dancingroot/DancingRoot-0.9.1.tgz

 #include <stdio.h>
 #include <stdlib.h>
 #include <tcl.h>
 #include <tk.h>
 /*Include this canvas file so that we can convert 
  *the canvas struct pointer to a TkCanvas pointer
  */
 #include "tkCanvas.h"

 int drawOnRootCmd (ClientData clientData,
        Tcl_Interp *interp, 
        int objc, 
        Tcl_Obj *CONST objv[])
 { 
        Tk_Window wmain = Tk_MainWindow (interp);
        Tk_Window canvas;
        Window oldCanvas;
        Window root;
        Display *display; 
        Tcl_CmdInfo canvasCmd;
        TkCanvas *canvasPtr;
        int screen;
        int displayWidth;
        int displayHeight;

        if (objc != 2) {
                Tcl_WrongNumArgs(interp, objc, objv, "pathToCanvas");
                return TCL_ERROR;
        }

        display = Tk_Display (wmain);
        screen = DefaultScreen (display);
        
        displayWidth = DisplayWidth (display, screen);
        displayHeight = DisplayHeight (display, screen);

        root = RootWindow (display, screen);
        canvas = Tk_NameToWindow (interp, Tcl_GetString (objv[1]), wmain);

        if (canvas == NULL) {
                Tcl_SetResult (interp, "invalid window path", TCL_STATIC);
                return TCL_ERROR;
        }
        Tk_MakeWindowExist (canvas);

        if (Tk_IsMapped (canvas) == 0) {
                Tk_MapWindow (canvas);
        }
        
        Tcl_GetCommandInfo (interp, Tcl_GetString (objv[1]), &canvasCmd);
        canvasPtr = (TkCanvas *) canvasCmd.clientData;

        oldCanvas = (((Tk_FakeWin *) (canvas))->window);
        (((Tk_FakeWin *) (canvas))->window) = root;

        Tk_CanvasEventuallyRedraw ((Tk_Canvas) canvasPtr, 0, 0, displayWidth, displayHeight);
        while (Tcl_DoOneEvent (TCL_DONT_WAIT));

        (((Tk_FakeWin *) (canvas))->window) = oldCanvas;

        return TCL_OK;
 }

 int Dancingroot_Init (Tcl_Interp *interp) {

 /*Maybe if I add a configure script later on I'll deal with 
  *Tcl stubs.
  */
 #ifdef USE_TCL_STUBS
        Tcl_InitStubs (interp, "8.0", 0);
        Tk_InitStubs (interp, "8.0", 0);
 #endif

        Tcl_CreateObjCommand (interp, 
                "drawOnRoot", 
                drawOnRootCmd, 
                (ClientData) NULL,
                (Tcl_CmdDeleteProc *) NULL
        );

        Tcl_PkgProvide (interp, "DancingRoot", "0.9.1");

        return TCL_OK;
 }