Programming for X can be cumbersome, in comparison tcl/tk provides a quick and easy way of getting a graphical application up and running. Tcl/tk is a scripting language, and as such there are some things you just can't do, or can't do easily. The answer is to use a hybrid of C and tcl/tk. The tcl/tk system comes with libraries which allow a C program to call the tcl/tk interpreter and run tcl/tk scripts.
The provided library includes methods for initializing the environment, calling different scripts, and accessing variables. Using this hybrid environment also benefits the programmer by giving them access to features which are inherent in X. Simple call back and timer functions allow the programmer to schedule events, and the ability to register a C function as a procedure in tcl/tk creates a powerful tool.
This document covers the basics of integrating tcl/tk scripts with C. The Compile Options section describes the various libraries and include files necessary to create a program. The Initialization and Registering Commands section explains how to get started, and how to call a C function from a tcl/tk script. The last section Accessing Variables explains how to read and write tcl/tk variables from functions in C.
In order to access the tcl/tk library routines several things have to be
done to your source code and how you compile it. There are two include files
which have the declarations for the calls to the library:
Compiling for a hybrid application requires pointing the compiler at the right
include directories, the right libraries, and setting the right link flags.
On top of settings for tcl/tk it is also necessary to include files
and libraries for X. The following flags are those to be set when using
g++, your own system may vary depending
on compiler and file locations.
Creating a hybrid tcl/tk & C application centres around a few choice commands. The first of these is the "Tk_Main" function, which is used to hand over control of the program to the tcl/tk interpreter. This command does not return, so it should be the last line in your "main" function, once all of your program's initialization is done.
The "Tk_Main" function takes three parameters. The second parameter is an array of strings, each string having a special meaning. The first parameter is the number of indices in this array of strings. The third parameter is a pointer to a function which is called for initialization. This initialization function is where most of the work gets done.
The array of strings which is passed to "Tk_Main" informs the tcl/tk interpreter of the name of the application and the location of the script which has the tcl/tk commands in it. This array is actually the command line parameters which are passed to the wish-like interpreter. The first item in the array gives the name of the application, and the second is the location of the script to be run. If the script is not in the same directory as the executable, it is wise to use a fully qualified path.
Due to legacy reasons, tcl/tk requires that the strings passed into most
of its functions be modifiable, it also has the occasional problem with
function scope. The easiest way to avoid these problems is to dynamically
allocate the strings being passed in. The following code fragment shows a
call to "Tk_Main" using the application "Hello World" and the script
"hello.tcl".
The call to "Tk_Main" hands over control of your program to the tcl/tk interpreter, but after some base initialization is done and before the tcl/tk script is run a user defined function can be executed. The above example shows a function of this type: "InitProc". This user defined initialization function must return an integer and takes one parameter Tcl_Interp *, a pointer to an interpreter.
Inside of the initialization function is where you create an actual
interpreter with a call to "Tk_Init". The "Tk_Init" function takes one
parameter and that is a pointer to an interpreter, this should just be the
pointer passed into your initialization function. The following code is a bare
bones initialization function, more will be added later.
By now you are familiar with procedure calls inside of tcl/tk script. It is possible when programming a hybrid application to have a tcl/tk procedure call a C function. To accomplish this requires a call to the "Tcl_CreateCommand" function. This is normally done inside of the initialization function. Calling the function as a procedure in tcl/tk is just like calling any other procedure. No declaration for the procedure should exist in the tcl/tk script.
Functions which are to be registered as procedures have a very specific
prototype. They must return an integer, and take four arguments. The first
argument is of type "ClientData" which is a tcl/tk library type. The second
argument is a pointer to the interpreter. The last two arguments are similar
to the "argc" and "argv" arguments in a C "main" function. These two arguments
are used to pass the parameters given to the tcl/tk procedure. The "argc"
argument contains the number parameters passed to the tcl/tk procedure, and
the "argv" is an array of strings, each string containing a parameter.
When a function is registered to be used as a tcl/tk procedure it can have a
pointer associated with it, this pointer is passed in as the "ClientData". The
concept of "ClientData" allows the programmer to associate a data structure
with a tcl/tk object, and calls to procedures can reference this object. This
structure is not often needed.
As was mentioned earlier the registration process requires a call to the
"Tcl_CreateCommand" function. This function takes five arguments. The first
argument is a pointer to an interpreter. The second argument is a string which
is the name of the tcl/tk procedure. The third argument is a pointer to the
function which is called when the tcl/tk procedure is invoked. The last two
arguments are the "ClientData" item, and a pointer to a deletion routine. The
deletion routine allows a C function to be called when the program exits in
order to clean up structures associated with objects. Like "ClientData" the
pointer to the deletion function is not often necessary. The following is a
sample registration of the tcl/tk procedure called "hey_there" which is
to call the above declared "Myfunc"
Being able to call a C function by invoking a tcl/tk procedure allows you to have tcl/tk get help from C. In order to have C get help from tcl/tk there are a series of functions, the ones covered here will deal with getting information from, and putting information into a tcl/tk variable.
The "Tcl_GetVar" function returns a pointer to a string which contains the
contents of the specified tcl/tk variable. The function takes three arguments,
a pointer to the interpreter, a string with the name of the tcl/tk variable,
and a flag. The variable that is accessed is at the current scope in the
executing script associated with the interpreter. If there is no variable at
the current scope level with the given name then global variables are checked.
If no matching global variable is found an error is returned. The flags
argument allows you to specify TCL_GLOBAL_ONLY, in order to force the function
to only check for global variables with the given name. The following code
is part of a tcl/tk script that will be accessed.
The following code is a the call in C to access the tcl/tk variable
"say_hello_to".
The "Tcl_SetVar" function allows the programmer to change the contents of
a tcl/tk variable. The function takes four arguments, the first is a pointer
to the interpreter, the second a string indicating the name of the tcl/tk
variable to change, the third is a string with the new value for the
variable, and the last argument is for flags. The "Tcl_SetVar" flags are the
same as for "Tcl_GetVar". The "Tcl_SetVar" function returns NULL if an error
occurred during the setting. If the variable does not exist, this function
will create the variable locally to the scope currently in execution in the
script referenced by the interpreter pointer. The following code sets a
tcl/tk variable called "say_hello_to" to contain the value "World".