By [George Peter Staplin] The original is here: http://www.xmission.com/~georgeps/Xlib_TclTk.html This tutorial demonstrates how to use Xlib with Tcl/Tk. In the example provided, Xlib is used to draw two boxes in a Tk window. A working C program is provided at the end of this tutorial, that should compile in OpenBSD, Linux, and other Unix like systems. Most Tcl/Tk C programming examples provide information about writing a complete widget. This tutorial is designed in mind for using Tcl/Tk as a C library. It is assumed that the reader knows the basics of C, Xlib, Tk, and compiling programs. ---- '''Getting Started''' The first step is to create a main for your application. Place the main function after the app_init and other commands that you will create. int main(int argc, char *argv[]) { /*Tk_Main automatically calls Tcl_CreateInterp()*/ Tk_Main(argc, argv, app_init); } The beginning of the app_init procedure looks like this: int app_init (Tcl_Interp *interp) { if (Tcl_Init (interp) != TCL_OK) { fprintf (stderr, "%s", Tcl_GetStringResult(interp)); exit (0); } if (Tk_Init (interp) != TCL_OK) { fprintf (stderr, "%s", Tcl_GetStringResult(interp)); exit (0); } /*Create new commands and enter an event loop*/ } If an error occurs when loading Tcl or Tk, an error message will be reported to the console and the program will exit. Below is a short snippet taken from tcl.h that explains the return definitions, such as TCL_OK, which is used above. ---- '''Tcl Return Definitions''' * TCL_OK Command completed normally; the interpreter's result contains the command's result. * TCL_ERROR The command couldn't be completed successfully; the interpreter's result describes what went wrong. * TCL_RETURN The command requests that the current procedure return; the interpreter's result contains the procedure's return value. * TCL_BREAK The command requests that the innermost loop be exited; the interpreter's result is meaningless. * TCL_CONTINUE Go on to the next iteration of the current loop; the interpreter's result is meaningless. ---- '''Command Creation''' The next step is to create a command in the app_init proc that can be executed by a Tcl script. Tcl_CreateCommand (interp, "draw_test_cmd", draw_test_cmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Briefly, the first argument is the Tcl command name. The next is the command proc. ClientData would be any argument(s) that you wish to pass to the draw_test_cmd procedure. Tcl_CmdDeleteProc would be a procedure that does something before running the draw_test_cmd. Two are NULL, because they are not needed in this simple program. ---- The main window is the window created automatically by Tk. Tk_Window image_window; image_window = Tk_MainWindow (interp); ---- Some of the Xlib functions need to know what the display connection is. Tk automatically invokes XOpenDisplay, so all that you need to do is a assign the result of Tk_Display to a Display structure. Display *display; display = Tk_Display (image_window); ---- This creates the window, because Tk delays creating the window until an idle moment. Then the window is mapped, because Tk delays mapping until an idle moment. By mapping, I mean displaying the window on the screen. Tk_MakeWindowExist (image_window); Tk_MapWindow (image_window); ---- Some Xlib functions need to know the color depth. int depth; depth = Tk_Depth (image_window); ---- '''Color and Graphics Context Allocation''' This code creates two graphics contexts; which are used to draw a rectangle later on. You could also use Tk_GetGC instead. Colormap colormap; char green[] = "sea green"; XColor green_col; GC green_gc; char red [] = "#FF0000"; XColor red_col; GC red_gc; colormap = DefaultColormap (display, 0); XParseColor (display, colormap, green, &green_col); XAllocColor (display, colormap, &green_col); green_gc = XCreateGC (display, Tk_WindowId (image_window), 0, 0); XSetForeground (display, green_gc, green_col.pixel); XParseColor (display, colormap, red, &red_col); XAllocColor (display, colormap, &red_col); red_gc = XCreateGC (display, Tk_WindowId (image_window), 0, 0); XSetForeground (display, red_gc, red_col.pixel); ---- The code below creates the interface. Tcl_Eval (interp, "\n" "button .b -text \"Go\" -command {draw_test_cmd};\n" "pack .b\n" "wm geometry . 400x400\n" "canvas .c -height 200 -width 200\n" "pack .c -fill both -expand yes\n" "#Update is needed so that the canvas is created\n" "#before invoking draw_test_cmd. Without this\n" "#the program might crash with a bad drawable\n" "#error.\n" "update\n" "\n"); (Note: string literals may not fall off the end of the line, in ANSI C. They can, however, be concatenated. Corrected above example. -EE Mar/05/2001) ---- Tk_Window canvas; canvas = Tk_NameToWindow (interp, ".c", image_window); ---- This invokes the Xlib function when the window is exposed, moved, resized, and raised. Tcl_Eval (interp, "bind .c {draw_test_cmd}"); Tcl_Eval (interp, "bind .c {draw_test_cmd}"); ---- This is the simple event loop, which is actually just code from Tk_MainLoop. I feel that it's important that you understand how it works, because often calling Tcl_DoOneEvent is needed when working with two event loops. while (Tk_GetNumMainWindows () > 0) { Tcl_DoOneEvent (0); } ---- The actual draw_test_cmd is at the beginning of the C file. The first line below is a function prototype. int draw_test_cmd (ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]); ---- '''Drawing Into the Window''' Below is the entire draw_test_cmd. int draw_test_cmd (ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]) { int c; fprintf (stdout, "\ndraw_test_cmd has been invoked.\n"); /* This could make one call to Tk_WindowId and store the result in a X Window typedef, but I think that it makes it more understandable for new programmers to have it this way. */ XClearWindow (display, Tk_WindowId (canvas)); /* This updates the window, so that if the button was pressed, it will go up when released, before drawing the boxes. This also causes Tk to redraw before I draw, so that Tk doesn't overwrite what I have drawn. */ Tcl_Eval (interp, "update"); /*This is a little bit of code that draws a checker board type of thing.*/ for (c = 0; c < 150; c++) { XFillRectangle (display, Tk_WindowId (canvas), green_gc, 1, 1, c, c); XFlush (display); usleep (1); } for (c = 0; c < 150; c++) { XFillRectangle (display, Tk_WindowId (canvas), red_gc, 150, 150, c, c); XFlush (display); usleep (1); } return TCL_OK; } ---- [http://www.xmission.com/~georgeps/Xlib_TclTk.png] Download Xlib_TclTk.tgz Example [http://www.xmission.com/~georgeps/Xlib_TclTk.tgz] See also: [Drawing Into Foreign Windows]. Also, [BLT] has a "container" widget, about which [George Howlett] has written [http://groups.google.com/groups?hl=en&frame=right&th=550fbc8088915ab7].