Skip site navigation (1)Skip section navigation (2)

FreeBSD Manual Pages


home | help
pTk(3)		      User Contributed Perl Documentation		pTk(3)

       Tk2portableTk - how to make your	Tk source portable to other
       interpreted languages.

       Ilya Zakharevich	<>  has	contributed most of
       this document. Many thanks.

       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	"Setting

       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 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 "Translation of some TCL

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:


       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*".

   Language data
       The following data types	are defined:

       "Tcl_Obj	*"
	   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

	   This	is also	a type that keeps information about contents of	Lang

	   Is a	substitute for a "char *" that contains	name of	variable. In
	   Lang	it is an object	that contains reference	to another Lang


	   "LangCallback*" a substitute	for a "char *" that contains command
	   to call. The	corresponding config type is "TK_CONFIG_CALLBACK".

	   It is the type that the "Lang_SplitList" sets. Before you call it,

	       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

       The following macros and	functions are used for conversion between
       strings and the additional types:

	LangCallback * LangMakeCallback(Tcl_Obj	*)
	Tcl_Obj	* LangCallbackArg(LangCallback *)
	char * LangString(Tcl_Obj *)

       After you use the result	of LangCallbackArg(), you should free it with
       "freeProc" "LANG_DYNAMIC" (it is	not guaranteed that any	change of
       "Tcl_Obj	*" will	not be reflected in <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 "Tcl_Obj *").

       The following function returns the "Tcl_Obj *" that is a	reference to

	Tcl_Obj	* LangVarArg(Var)

       ???? It is very anti-intuitive, I hope the name is changed.

	int LangCmpCallback(LangCallback *a,Tcl_Obj * b)

       (currently only a stub),	and, at	last,

	LangCallback * LangCopyCallback(LangCallback *)

       Above we	have seen the new datatype "LangCallback" and the
       corresponding Config option  "TK_CONFIG_CALLBACK". The following
       functions are provided for manipulation of "LangCallback"s:

	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 *,Tcl_Obj	*,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 (Tcl_Obj *, Tcl_FreeProc *freeProc)
	Tcl_Obj	*  LangCopyArg (Tcl_Obj	*);
	void Tcl_AppendArg (Tcl_Interp *interp,	Tcl_Obj	*)
	void LangSetString(Tcl_Obj * *,	char *s)
	void LangSetDefault(Tcl_Obj * *, char *s)

       These two are equivalent	unless s is an empty string. In	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(Tcl_Obj	* *,int)
	void LangSetDouble(Tcl_Obj * *,double)

       The Lang	functions separate uninitialized and initialized data
       comparing data with "NULL". So the declaration for an "Tcl_Obj *"
       should look like

	Tcl_Obj	* 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

       "int  LangNull(Tcl_Obj *)"
	   to check that an object is false;

       "int  LangStringMatch(char *string, Tcl_Obj * 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,Tcl_Obj * arg,FILE
       *f,Lang_FileCloseProc *proc)"
	   currently stubs only;

       "int LangSaveVar(Tcl_Interp *,Tcl_Obj * 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)"
       "void LangBadFile(int fd)"
       "int LangCmpConfig(char *spec, char *arg, size_t	length)"

       "void Tcl_AppendArg (Tcl_Interp *interp,	Tcl_Obj	*)"

       Another useful construction is

	Tcl_Obj	* 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 "Tcl_Obj *") given
       the variable name, use "Tcl_GetVar(interp, varName, flags)". If you are
       interested in the string	value of this variable,	use

       To get a	C array	of "Tcl_Obj *" of length "n", use

	   Tcl_Obj * *args = LangAllocVec(n);

       You can set the values of the "Tcl_Obj *"s using	"LangSet..."
       functions, and get string value using "LangString".

       If you want to merge an array of	"Tcl_Obj *"s into one "Tcl_Obj *"
       (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.

	   does	not take "(char*)NULL",	but "NULL" as delimiter.

       "Tcl_CreateCommand", "Tcl_DeleteCommand"
	   "Tk_CreateWidget", "Tk_DeleteWidget", the second argument is	the
	   window itself, not the pathname. Autoconverted.

       "sprintf(interp->result,	"%d %d %d %d",...)"
	   "Tcl_IntResults(interp,4,0,...)". Autoconverted.

       "interp->result = "1";"
	   "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".

	   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

       These files currently implement the following:

       Additional config types:

	    Var, Tcl_Obj *, LangCallback, LangFreeProc.

       Functions and macros:
	    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:


       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).

       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". The	API
       below is	a counterpart of the API for construction of dynamic lists in

	void ListFactoryInit(ListFactory *)
	void ListFactoryFinish(ListFactory *)
	void ListFactoryFree(ListFactory *)
	Tcl_Obj	* * ListFactoryArg(ListFactory *)
	void ListFactoryAppend(ListFactory *, Tcl_Obj *	*arg)
	void ListFactoryAppendCopy(ListFactory *, Tcl_Obj * *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 "Tcl_Obj *" 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.

       Production of strings are still supported in portableTk.

   Accessing "Tcl_Obj *"s
       The following functions for getting a value of an "Tcl_Obj *" may be

	double LangDouble(Tcl_Obj *)
	int LangInt(Tcl_Obj *)
	long LangLong(Tcl_Obj *)
	int LangIsList(Tcl_Obj * 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	"Tcl_Obj *"s
       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 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 "Tcl_Obj *", the best way to do this is
       to declare a buffer in the beginning of a block by


       and assign this buffer to the "Tcl_Obj *" by

	  void LangSetDefaultBuffer(Tcl_Obj * *)

       You can also create the buffer(s) manually and assign them using

	  void LangSetBuffer(Tcl_Obj * *, char *)

       This is the only	choice if you need to assign numeric values to several
       "Tcl_Obj	*"s simultaneously. The	advantage of the first approach	is
       that the	above declarations can be made "nop"s in different languages.

       Note that if you	apply "LangSetDefaultBuffer" to	an "Tcl_Obj *" that
       contains	some value, you	can create a leak if you do not	free that
       "Tcl_Obj	*" 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	"Tcl_Obj *"s
       The API for creating a new "Tcl_Obj *" is

	void LangNewArg(Tcl_Obj	* *, LangFreeProc *)

       The API for creating a new "Tcl_Obj *" is absent. Just initialize
       "Tcl_Obj	*" to be "NULL", and apply one of "LangSet..." methods.

       After you use this "Tcl_Obj *", it should be freed thusly:

       "LangFreeArg(arg, freeProc)".

   Evaluating a	list

	int LangArgEval(Tcl_Interp *, Tcl_Obj *	arg)

       Here "arg" should be a list to evaluate,	in particular, the first
       element should be a "LangCallback" massaged to be an "Tcl_Obj *". The
       arguments can be	send to	the subroutine by reference or by value	in
       different languages.

   Getting result as "Tcl_Obj *"
       Use "Tcl_ArgResult". It is not guaranteed that result survives this
       operation, so the "Tcl_Obj *" you get should be the only	mean to	access
       the data	from this moment on. After you use this	"Tcl_Obj *", you
       should free it with "freeProc" "LANG_DYNAMIC" (you can do LangSet...()
       in between).

perl v5.24.1			  2013-11-15				pTk(3)

NAME | Author | DESCRIPTION | Structure of pTk, porting your code | PortableTk API | Translation back to TCL | Checking for trouble | Additional API

Want to link to this manual page? Use this URL:

home | help