---
name: "Tk::pTk"
section: "3pm"
platform: "linux"
package: "perl-tk"
version: "804.036-9"
release: "Arch Linux 2026-11-01"
platforms: ["linux"]
url: "https://new.carta.tech/Tk::pTk"
---

# Tk::pTk(3pm)

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

## 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)](/merge/1.md) 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 *Lang*uages.

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