PTK(1) perl/Tk Documentation PTK(1)NAME
Tk2portableTk - how to make your Tk source portable to
other interpreted languages.
Author
Ilya Zakharevich <ilya@math.ohio-state.edu> has
contributed most of this document. Many thanks.
DESCRIPTION
PortableTk is an attempt to make Tk useful from other
languages. Currently tk4.0 runs under Perl using this
approach. Below, Lang is the notation for an external
language to which PortableTk glues Tk code.
The main problem with using the code developed for TCL
with different languages is the absence of data types:
almost anything is char*. It makes automatic translation
hopeless. However, if you typedef several new symbols to
be char*, you can still use your code in TCL, and it will
make the automatic translation possible.
Another problem with the approach that "everything is a
string" is impossibility to have a result that says
"NotApplicable" without setting an error. Thus different
Tk command return different string values that mean "error
happened", like "", " " or "??". Other languages can be
more flexible, so in portableTk you should inform the
compiler that what you want to return means "error" (see
the section on Setting variables).
Currently PortableTk uses several different approachs to
simplify translation: several TCL functions that are
especially dangerous to use are undefined, so you can
easily find places that need to be updated to use
Language-independent functions based on compiler warnings.
Eventually a way to use these Language-independent
functions under proper TCL will be also provided. The end
of this document provides a starting point for such a
project.
Structure of pTk, porting your code
pTk, that is a port of Tk, is very special with respect to
porting of other code to portableTk. The problem is that
currently there is very little hope to merge the
modifications back into Tk, so a special strategy is
needed to maintain this port. Do not use this strategy to
port your own code.
pTk is produced from Tk via a two-step process: first,
some manual editing (the result is in the subdirectory
mTk), and second, automatic conversion by the munge script
(written in Perl). Thus the subdirectory pTk/mTk contains
code with minimal possible difference from the virgin Tk
code, so it is easier to merge(1) the differences between
25/Aug/1997 Tk400.202 1
PTK(1) perl/Tk Documentation PTK(1)
Tk versions into modified code.
It looks like the strategy for a portable code should be
exactly opposite: starting from TCL-based code, apply
munge, and then hand-edit the resulting code. Probably it
is also possible to target your code to portableTk from
scratch, since this will make it possible to run it under
a lot of Languages.
The only reason anyone would like to look into contents of
pTk/mTk directory is to find out which constructs are not
supported by munge. On the other hand, pTk directory
contains code that is conformant to portableTk, so you can
look there to find example code.
munge is the script that converts most common Tk
constructs to their portableTk equivalent. For your code
to qualify, you should follow Tk conventions on
indentation and names of variables, in particular, the
array of arguments for the ...CmdProc should be called
argv.
For details on what munge can do, see the section on
Translation of some TCL functions.
PortableTk API
Checking what you are running under
PortableTk provides a symbol ????. If this symbol is
defined, your source is compiled with it.
New types of configuration options
PortableTk defines several new types of configuration
options:
TK_CONFIG_CALLBACK
TK_CONFIG_LANGARG
TK_CONFIG_SCALARVAR
TK_CONFIG_HASHVAR
TK_CONFIG_ARRAYVAR
TK_CONFIG_IMAGE
You should use them instead of TK_CONFIG_STRING whenever
appropriate. This allows your application to receive a
direct representation of the corresponding resource
instead of the string representation, if this is possible
under given language.
???? It looks like TK_CONFIG_IMAGE and TK_CONFIG_SCALARVAR
set variables of type char*.
25/Aug/1997 Tk400.202 2
PTK(1) perl/Tk Documentation PTK(1)
Language data
The following data types are defined:
Arg is the main datatype of the language. This
is a type that your C function gets
pointers to for arguments when the
corresponding Lang function is called. The
corresponding config type is
TK_CONFIG_LANGARG.
This is also a type that keeps information
about contents of Lang variable.
Var Is a substitute for a char * that contains
name of variable. In Lang it is an object
that contains reference to another Lang
variable.
LangResultSave ????
LangCallback LangCallback* a substitute for a char *
that contains command to call. The
corresponding config type is
TK_CONFIG_CALLBACK.
LangFreeProc It is the type that the Lang_SplitList
sets. Before you call it, declare
Args *args;
LangFreeProc *freeProc = NULL;
...
code = Lang_SplitList(interp, value,
&argc, &args, &freeProc);
After you use the split values, call
if (args != NULL && freeProc) (*freeProc)(argc,args);
It is not guaranteed that the args can
survive deletion of value.
Conversion
The following macros and functions are used for conversion
between strings and the additional types:
LangCallback * LangMakeCallback(Arg)
Arg LangCallbackArg(LangCallback *)
char * LangString(Arg)
After you use the result of LangCallbackArg(), you should
free it with freeProc LANG_DYNAMIC (it is not guaranteed
that any change of Arg will not be reflected in
25/Aug/1997 Tk400.202 3
PTK(1) perl/Tk Documentation PTK(1)
<LangCallback>, so you cannot do LangSet...() in between,
and you should reset it to NULL if you want to do any
further assignments to this Arg).
The following function returns the Arg that is a reference
to Var:
Arg LangVarArg(Var)
???? It is very anti-intuitive, I hope the name is
changed.
int LangCmpCallback(LangCallback *a,Arg b)
(currently only a stub), and, at last,
LangCallback * LangCopyCallback(LangCallback *)
Callbacks
Above we have seen the new datatype LangCallback and the
corresponding Config option TK_CONFIG_CALLBACK. The
following functions are provided for manipulation of
LangCallbacks:
void LangFreeCallback(LangCallback *)
int LangDoCallback(Tcl_Interp *,LangCallback *,
int result,int argc, char *format,...)
The argument format of LangDoCallback should contain a
string that is suitable for sprintf with optional
arguments of LangDoCallback. result should be false if
result of callback is not needed.
int LangMethodCall(Tcl_Interp *,Arg,char *method,
int result,int argc,...)
????
Conceptually, LangCallback* is a substitute for ubiquitous
char * in TCL. So you should use LangFreeCallback instead
of ckfree or free if appropriate.
Setting variables
void LangFreeArg (Arg, Tcl_FreeProc *freeProc)
Arg LangCopyArg (Arg);
void Tcl_AppendArg (Tcl_Interp *interp, Arg)
void LangSetString(Arg *, char *s)
void LangSetDefault(Arg *, char *s)
These two are equivalent unless s is an empty string. In
25/Aug/1997 Tk400.202 4
PTK(1) perl/Tk Documentation PTK(1)
this case LangSetDefault behaves like LangSetString with
s==NULL, i.e., it sets the current value of the Lang
variable to be false.
void LangSetInt(Arg *,int)
void LangSetDouble(Arg *,double)
The Lang functions separate uninitialized and initialized
data comparing data with NULL. So the declaration for an
Arg should look like
Arg arg = NULL;
if you want to use this arg with the above functions.
After you are done, you should use LangFreeArg with
TCL_DYNAMIC as freeProc.
Language functions
Use
int LangNull(Arg) to check that an object is false;
int LangStringMatch(char *string, Arg match)
????
void LangExit(int) to make a proper shutdown;
int LangEval(Tcl_Interp *interp, char *cmd, int global)
to call Lang eval;
void Lang_SetErrorCode(Tcl_Interp *interp,char *code)
char *Lang_GetErrorCode(Tcl_Interp *interp)
char *Lang_GetErrorInfo(Tcl_Interp *interp)
void LangCloseHandler(Tcl_Interp *interp,Arg arg,FILE
*f,Lang_FileCloseProc *proc)
currently stubs only;
int LangSaveVar(Tcl_Interp *,Arg arg,Var *varPtr,int type)
to save the structure arg into
Lang variable *varPtr;
void LangFreeVar(Var var)
to free the result;
int LangEventCallback(Tcl_Interp *,LangCallback *,XEvent
*,KeySym)
????
int LangEventHook(int flags)
25/Aug/1997 Tk400.202 5
PTK(1) perl/Tk Documentation PTK(1)
void LangBadFile(int fd)
int LangCmpConfig(char *spec, char *arg, size_t length)
unsupported????;
void Tcl_AppendArg (Tcl_Interp *interp, Arg)
Another useful construction is
Arg variable = LangFindVar(interp, Tk_Window tkwin, char *name);
After using the above function, you should call
LangFreeVar(Var variable);
???? Note discrepancy in types!
If you want to find the value of a variable (of type Arg)
given the variable name, use Tcl_GetVar(interp, varName,
flags). If you are interested in the string value of this
variable, use LangString(Tcl_GetVar(...)).
To get a C array of Arg of length n, use
Arg *args = LangAllocVec(n);
...
LangFreeVec(n,args);
You can set the values of the Args using LangSet...
functions, and get string value using LangString.
If you want to merge an array of Args into one Arg (that
will be an array variable), use
result = Tcl_Merge(listLength, list);
Translation of some TCL functions
We mark items that can be dealt with by munge by
Autoconverted.
Tcl_AppendResult does not take (char*)NULL, but NULL as
delimiter. Autoconverted.
Tcl_CreateCommand, Tcl_DeleteCommand
Tk_CreateWidget, Tk_DeleteWidget, the
second argument is the window itself,
not the pathname. Autoconverted.
sprintf(interp->result,
Tcl_IntResults(interp,4,0,...).
Autoconverted.
25/Aug/1997 Tk400.202 6
PTK(1) perl/Tk Documentation PTK(1)
interp->result =
Tcl_SetResult(interp,"1", TCL_STATIC). Autoconverted.
Reading interp->result
Tcl_GetResult(interp). Autoconverted.
interp->result = Tk_PathName(textPtr->tkwin);
Tk_WidgetResult(interp,textPtr->tkwin).
Autoconverted.
Sequence Tcl_PrintDouble, Tcl_PrintDouble, ...,
Tcl_AppendResult
Use a single command
void Tcl_DoubleResults(Tcl_Interp *interp, int append,
int argc,...);
append governs whether it is required
to clear the result first.
A similar command for int arguments is
Tcl_IntResults.
Tcl_SplitList Use Lang_SplitList (see the
description above).
Translation back to TCL
To use your portableTk program with TCL, put
#include "ptcl.h"
before inclusion of tk.h, and link the resulting code with
ptclGlue.c.
These files currently implement the following:
Additional config types:
TK_CONFIG_CALLBACK
TK_CONFIG_LANGARG
TK_CONFIG_SCALARVAR
TK_CONFIG_HASHVAR
TK_CONFIG_ARRAYVAR
TK_CONFIG_IMAGE
Types:
Var, Arg, LangCallback, LangFreeProc.
Functions and macros:
25/Aug/1997 Tk400.202 7
PTK(1) perl/Tk Documentation PTK(1)
Lang_SplitList, LangString, LangSetString, LangSetDefault,
LangSetInt, LangSetDouble Tcl_ArgResult, LangCallbackArg,
LangSaveVar, LangFreeVar,
LangFreeSplitProc, LangFreeArg, Tcl_DoubleResults, Tcl_IntResults,
LangDoCallback, Tk_WidgetResult, Tcl_CreateCommand,
Tcl_DeleteCommand, Tcl_GetResult.
Current implementation contains enough to make it possible
to compile mTk/tkText*.[ch] with the virgin Tk.
New types of events ????
PortableTk defines following new types of events:
TK_EVENTTYPE_NONE
TK_EVENTTYPE_STRING
TK_EVENTTYPE_NUMBER
TK_EVENTTYPE_WINDOW
TK_EVENTTYPE_ATOM
TK_EVENTTYPE_DISPLAY
TK_EVENTTYPE_DATA
and a function
char * Tk_EventInfo(int letter,
Tk_Window tkwin, XEvent *eventPtr,
KeySym keySym, int *numPtr, int *isNum, int *type,
int num_size, char *numStorage)
Checking for trouble
If you start with working TCL code, you can start
convertion using the above hints. Good indication that you
are doing is OK is absence of sprintf and sscanf in your
code (at least in the part that is working with
interpreter).
Additional API
What is described here is not included into base
portableTk distribution. Currently it is coded in TCL and
as Perl macros (core is coded as functions, so
theoretically you can use the same object files with
different interpreted languages).
ListFactory
Dynamic arrays in TCL are used for two different purposes:
to construct strings, and to construct lists. These two
usages will have separate interfaces in other languages
(since list is a different type from a string), so you
should use a different interface in your code.
The type for construction of dynamic lists is ListFactory.
25/Aug/1997 Tk400.202 8
PTK(1) perl/Tk Documentation PTK(1)
The API below is a counterpart of the API for construction
of dynamic lists in TCL:
void ListFactoryInit(ListFactory *)
void ListFactoryFinish(ListFactory *)
void ListFactoryFree(ListFactory *)
Arg * ListFactoryArg(ListFactory *)
void ListFactoryAppend(ListFactory *, Arg *arg)
void ListFactoryAppendCopy(ListFactory *, Arg *arg)
ListFactory * ListFactoryNewLevel(ListFactory *)
ListFactory * ListFactoryEndLevel(ListFactory *)
void ListFactoryResult(Tcl_Interp *, ListFactory *)
The difference is that a call to ListFactoryFinish should
precede the actual usage of the value of ListFactory, and
there are two different ways to append an Arg to a
ListFactory: ListFactoryAppendCopy() guarantees that the
value of arg is copied to the list, but
ListFactoryAppend() may append to the list a reference to
the current value of arg. If you are not going to change
the value of arg after appending, the call to
ListFactoryAppend may be quicker.
As in TCL, the call to ListFactoryFree() does not free the
ListFactory, only the objects it references.
The functions ListFactoryNewLevel() and
ListFactoryEndLevel() return a pointer to a ListFactory to
fill. The argument of ListFactoryEndLevel() cannot be used
after a call to this function.
DStrings
Production of strings are still supported in portableTk.
Accessing Args
The following functions for getting a value of an Arg may
be provided:
double LangDouble(Arg)
int LangInt(Arg)
long LangLong(Arg)
int LangIsList(Arg arg)
The function LangIsList() is supported only partially
under TCL, since there is no data types. It checks whether
there is a space inside the string arg.
Assigning numbers to Args
While LangSetDouble() and LangSetInt() are supported ways
to assign numbers to assign an integer value to a
variable, for the sake of efficiency under TCL it is
25/Aug/1997 Tk400.202 9
PTK(1) perl/Tk Documentation PTK(1)
supposed that the destination of these commands was
massaged before the call so it contains a long enough
string to sprintf() the numbers inside it. If you are
going to immediately use the resulting Arg, the best way
to do this is to declare a buffer in the beginning of a
block by
dArgBuffer;
and assign this buffer to the Arg by
void LangSetDefaultBuffer(Arg *)
You can also create the buffer(s) manually and assign them
using
void LangSetBuffer(Arg *, char *)
This is the only choice if you need to assign numeric
values to several Args simultaneously. The advantage of
the first approach is that the above declarations can be
made nops in different languages.
Note that if you apply LangSetDefaultBuffer to an Arg that
contains some value, you can create a leak if you do not
free that Arg first. This is a non-problem in real
languages, but can be a trouble in TCL, unless you use
only the above API.
Creating new Args
The API for creating a new Arg is
void LangNewArg(Arg *, LangFreeProc *)
The API for creating a new Arg is absent. Just initialize
Arg to be NULL, and apply one of LangSet... methods.
After you use this Arg, it should be freed thusly:
LangFreeArg(arg, freeProc).
Evaluating a list
Use
int LangArgEval(Tcl_Interp *, Arg arg)
Here arg should be a list to evaluate, in particular, the
first element should be a LangCallback massaged to be an
Arg. The arguments can be send to the subroutine by
reference or by value in different languages.
25/Aug/1997 Tk400.202 10
PTK(1) perl/Tk Documentation PTK(1)
Getting result as Arg
Use Tcl_ArgResult. It is not guaranteed that result
survives this operation, so the Arg you get should be the
only mean to access the data from this moment on. After
you use this Arg, you should free it with freeProc
LANG_DYNAMIC (you can do LangSet...() in between).
interpreted languages."
25/Aug/1997 Tk400.202 11
PTK(1) perl/Tk Documentation PTK(1)25/Aug/1997 Tk400.202 12