dwww Home | Manual pages | Find package

pTk(3pm)              User Contributed Perl Documentation              pTk(3pm)

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

   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
           "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(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
       "Var":

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

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

       "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)"
           unsupported????;

       "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
       "LangString(Tcl_GetVar(...))".

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

           Tcl_Obj * *args = LangAllocVec(n);
           ...
           LangFreeVec(n,args);

       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.

       "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, "%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".

       "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, 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:

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

   DStrings
       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
       provided:

        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

          dArgBuffer;

       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
       Use

        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.40.1                       2025-04-13                          pTk(3pm)

Generated by dwww version 1.16 on Sat Oct 3 07:36:09 CEST 2026.