Provided by: perl-tk_804.036+dfsg1-1ubuntu3_amd64 bug

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.38.2                                       2024-04-01                                           pTk(3pm)