1pTk(3) User Contributed Perl Documentation pTk(3)
2
3
4
6 Tk2portableTk - how to make your Tk source portable to other
7 interpreted languages.
8
10 Ilya Zakharevich <ilya@math.ohio-state.edu> has contributed most of
11 this document. Many thanks.
12
14 PortableTk is an attempt to make Tk useful from other languages.
15 Currently tk4.0 runs under Perl using this approach. Below, Lang is the
16 notation for an external language to which PortableTk glues Tk code.
17
18 The main problem with using the code developed for TCL with different
19 languages is the absence of data types: almost anything is "char*". It
20 makes automatic translation hopeless. However, if you "typedef" several
21 new symbols to be "char*", you can still use your code in TCL, and it
22 will make the automatic translation possible.
23
24 Another problem with the approach that "everything is a string" is
25 impossibility to have a result that says "NotApplicable" without
26 setting an error. Thus different Tk command return different string
27 values that mean "error happened", like "", " " or "??". Other
28 languages can be more flexible, so in portableTk you should inform the
29 compiler that what you want to return means "error" (see "Setting
30 variables").
31
32 Currently PortableTk uses several different approachs to simplify
33 translation: several TCL functions that are especially dangerous to use
34 are undefined, so you can easily find places that need to be updated to
35 use Language-independent functions based on compiler warnings.
36 Eventually a way to use these Language-independent functions under
37 proper TCL will be also provided. The end of this document provides a
38 starting point for such a project.
39
41 pTk, that is a port of Tk, is very special with respect to porting of
42 other code to portableTk. The problem is that currently there is very
43 little hope to merge the modifications back into Tk, so a special
44 strategy is needed to maintain this port. Do not use this strategy to
45 port your own code.
46
47 pTk is produced from Tk via a two-step process: first, some manual
48 editing (the result is in the subdirectory "mTk"), and second,
49 automatic conversion by the "munge" script (written in Perl). Thus the
50 subdirectory "pTk/mTk" contains code with minimal possible difference
51 from the virgin Tk code, so it is easier to merge(1) the differences
52 between Tk versions into modified code.
53
54 It looks like the strategy for a portable code should be exactly
55 opposite: starting from TCL-based code, apply "munge", and then hand-
56 edit the resulting code. Probably it is also possible to target your
57 code to portableTk from scratch, since this will make it possible to
58 run it under a lot of Languages.
59
60 The only reason anyone would like to look into contents of "pTk/mTk"
61 directory is to find out which constructs are not supported by "munge".
62 On the other hand, "pTk" directory contains code that is conformant to
63 portableTk, so you can look there to find example code.
64
65 "munge" is the script that converts most common Tk constructs to their
66 "portableTk" equivalent. For your code to qualify, you should follow Tk
67 conventions on indentation and names of variables, in particular, the
68 array of arguments for the "...CmdProc" should be called "argv".
69
70 For details on what "munge" can do, see "Translation of some TCL
71 functions".
72
74 Checking what you are running under
75 PortableTk provides a symbol "????". If this symbol is defined, your
76 source is compiled with it.
77
78 New types of configuration options
79 PortableTk defines several new types of configuration options:
80
81 TK_CONFIG_CALLBACK
82 TK_CONFIG_LANGARG
83 TK_CONFIG_SCALARVAR
84 TK_CONFIG_HASHVAR
85 TK_CONFIG_ARRAYVAR
86 TK_CONFIG_IMAGE
87
88 You should use them instead of TK_CONFIG_STRING whenever appropriate.
89 This allows your application to receive a direct representation of the
90 corresponding resource instead of the string representation, if this is
91 possible under given language.
92
93 ???? It looks like "TK_CONFIG_IMAGE" and "TK_CONFIG_SCALARVAR" set
94 variables of type "char*".
95
96 Language data
97 The following data types are defined:
98
99 "Tcl_Obj *"
100 is the main datatype of the language. This is a type that your C
101 function gets pointers to for arguments when the corresponding Lang
102 function is called. The corresponding config type is
103 "TK_CONFIG_LANGARG".
104
105 This is also a type that keeps information about contents of Lang
106 variable.
107
108 "Var"
109 Is a substitute for a "char *" that contains name of variable. In
110 Lang it is an object that contains reference to another Lang
111 variable.
112
113 "LangResultSave"
114 ????
115
116 "LangCallback"
117 "LangCallback*" a substitute for a "char *" that contains command
118 to call. The corresponding config type is "TK_CONFIG_CALLBACK".
119
120 "LangFreeProc"
121 It is the type that the "Lang_SplitList" sets. Before you call it,
122 declare
123
124 Args *args;
125 LangFreeProc *freeProc = NULL;
126 ...
127 code = Lang_SplitList(interp, value,
128 &argc, &args, &freeProc);
129
130 After you use the split values, call
131
132 if (args != NULL && freeProc) (*freeProc)(argc,args);
133
134 It is not guaranteed that the "args" can survive deletion of
135 "value".
136
137 Conversion
138 The following macros and functions are used for conversion between
139 strings and the additional types:
140
141 LangCallback * LangMakeCallback(Tcl_Obj *)
142 Tcl_Obj * LangCallbackArg(LangCallback *)
143 char * LangString(Tcl_Obj *)
144
145 After you use the result of LangCallbackArg(), you should free it with
146 "freeProc" "LANG_DYNAMIC" (it is not guaranteed that any change of
147 "Tcl_Obj *" will not be reflected in <LangCallback>, so you cannot do
148 LangSet...() in between, and you should reset it to "NULL" if you want
149 to do any further assignments to this "Tcl_Obj *").
150
151 The following function returns the "Tcl_Obj *" that is a reference to
152 "Var":
153
154 Tcl_Obj * LangVarArg(Var)
155
156 ???? It is very anti-intuitive, I hope the name is changed.
157
158 int LangCmpCallback(LangCallback *a,Tcl_Obj * b)
159
160 (currently only a stub), and, at last,
161
162 LangCallback * LangCopyCallback(LangCallback *)
163
164 Callbacks
165 Above we have seen the new datatype "LangCallback" and the
166 corresponding Config option "TK_CONFIG_CALLBACK". The following
167 functions are provided for manipulation of "LangCallback"s:
168
169 void LangFreeCallback(LangCallback *)
170 int LangDoCallback(Tcl_Interp *,LangCallback *,
171 int result,int argc, char *format,...)
172
173 The argument "format" of "LangDoCallback" should contain a string that
174 is suitable for "sprintf" with optional arguments of "LangDoCallback".
175 "result" should be false if result of callback is not needed.
176
177 int LangMethodCall(Tcl_Interp *,Tcl_Obj *,char *method,
178 int result,int argc,...)
179
180 ????
181
182 Conceptually, "LangCallback*" is a substitute for ubiquitous "char *"
183 in TCL. So you should use "LangFreeCallback" instead of "ckfree" or
184 "free" if appropriate.
185
186 Setting variables
187 void LangFreeArg (Tcl_Obj *, Tcl_FreeProc *freeProc)
188 Tcl_Obj * LangCopyArg (Tcl_Obj *);
189 void Tcl_AppendArg (Tcl_Interp *interp, Tcl_Obj *)
190 void LangSetString(Tcl_Obj * *, char *s)
191 void LangSetDefault(Tcl_Obj * *, char *s)
192
193 These two are equivalent unless s is an empty string. In this case
194 "LangSetDefault" behaves like "LangSetString" with "s==NULL", i.e., it
195 sets the current value of the Lang variable to be false.
196
197 void LangSetInt(Tcl_Obj * *,int)
198 void LangSetDouble(Tcl_Obj * *,double)
199
200 The Lang functions separate uninitialized and initialized data
201 comparing data with "NULL". So the declaration for an "Tcl_Obj *"
202 should look like
203
204 Tcl_Obj * arg = NULL;
205
206 if you want to use this "arg" with the above functions. After you are
207 done, you should use "LangFreeArg" with "TCL_DYNAMIC" as "freeProc".
208
209 Language functions
210 Use
211
212 "int LangNull(Tcl_Obj *)"
213 to check that an object is false;
214
215 "int LangStringMatch(char *string, Tcl_Obj * match)"
216 ????
217
218 "void LangExit(int)"
219 to make a proper shutdown;
220
221 "int LangEval(Tcl_Interp *interp, char *cmd, int global)"
222 to call Lang "eval";
223
224 "void Lang_SetErrorCode(Tcl_Interp *interp,char *code)"
225 "char *Lang_GetErrorCode(Tcl_Interp *interp)"
226 "char *Lang_GetErrorInfo(Tcl_Interp *interp)"
227 "void LangCloseHandler(Tcl_Interp *interp,Tcl_Obj * arg,FILE
228 *f,Lang_FileCloseProc *proc)"
229 currently stubs only;
230
231 "int LangSaveVar(Tcl_Interp *,Tcl_Obj * arg,Var *varPtr,int type)"
232 to save the structure "arg" into Lang variable *varPtr;
233
234 "void LangFreeVar(Var var)"
235 to free the result;
236
237 "int LangEventCallback(Tcl_Interp *,LangCallback *,XEvent *,KeySym)"
238 ????
239
240 "int LangEventHook(int flags)"
241 "void LangBadFile(int fd)"
242 "int LangCmpConfig(char *spec, char *arg, size_t length)"
243 unsupported????;
244
245 "void Tcl_AppendArg (Tcl_Interp *interp, Tcl_Obj *)"
246
247 Another useful construction is
248
249 Tcl_Obj * variable = LangFindVar(interp, Tk_Window tkwin, char *name);
250
251 After using the above function, you should call
252
253 LangFreeVar(Var variable);
254
255 ???? Note discrepancy in types!
256
257 If you want to find the value of a variable (of type "Tcl_Obj *") given
258 the variable name, use "Tcl_GetVar(interp, varName, flags)". If you are
259 interested in the string value of this variable, use
260 "LangString(Tcl_GetVar(...))".
261
262 To get a C array of "Tcl_Obj *" of length "n", use
263
264 Tcl_Obj * *args = LangAllocVec(n);
265 ...
266 LangFreeVec(n,args);
267
268 You can set the values of the "Tcl_Obj *"s using "LangSet..."
269 functions, and get string value using "LangString".
270
271 If you want to merge an array of "Tcl_Obj *"s into one "Tcl_Obj *"
272 (that will be an array variable), use
273
274 result = Tcl_Merge(listLength, list);
275
276 Translation of some TCL functions
277 We mark items that can be dealt with by "munge" by Autoconverted.
278
279 "Tcl_AppendResult"
280 does not take "(char*)NULL", but "NULL" as delimiter.
281 Autoconverted.
282
283 "Tcl_CreateCommand", "Tcl_DeleteCommand"
284 "Tk_CreateWidget", "Tk_DeleteWidget", the second argument is the
285 window itself, not the pathname. Autoconverted.
286
287 "sprintf(interp->result, "%d %d %d %d",...)"
288 "Tcl_IntResults(interp,4,0,...)". Autoconverted.
289
290 "interp->result = "1";"
291 "Tcl_SetResult(interp,"1", TCL_STATIC)". Autoconverted.
292
293 Reading "interp->result"
294 "Tcl_GetResult(interp)". Autoconverted.
295
296 "interp->result = Tk_PathName(textPtr->tkwin);"
297 "Tk_WidgetResult(interp,textPtr->tkwin)". Autoconverted.
298
299 Sequence "Tcl_PrintDouble, Tcl_PrintDouble, ..., Tcl_AppendResult"
300 Use a single command
301
302 void Tcl_DoubleResults(Tcl_Interp *interp, int append,
303 int argc,...);
304
305 "append" governs whether it is required to clear the result first.
306
307 A similar command for "int" arguments is "Tcl_IntResults".
308
309 "Tcl_SplitList"
310 Use "Lang_SplitList" (see the description above).
311
313 To use your portableTk program with TCL, put
314
315 #include "ptcl.h"
316
317 before inclusion of "tk.h", and link the resulting code with
318 "ptclGlue.c".
319
320 These files currently implement the following:
321
322 Additional config types:
323 TK_CONFIG_CALLBACK
324 TK_CONFIG_LANGARG
325 TK_CONFIG_SCALARVAR
326 TK_CONFIG_HASHVAR
327 TK_CONFIG_ARRAYVAR
328 TK_CONFIG_IMAGE
329
330 Types:
331 Var, Tcl_Obj *, LangCallback, LangFreeProc.
332
333 Functions and macros:
334 Lang_SplitList, LangString, LangSetString, LangSetDefault,
335 LangSetInt, LangSetDouble Tcl_ArgResult, LangCallbackArg,
336 LangSaveVar, LangFreeVar,
337 LangFreeSplitProc, LangFreeArg, Tcl_DoubleResults, Tcl_IntResults,
338 LangDoCallback, Tk_WidgetResult, Tcl_CreateCommand,
339 Tcl_DeleteCommand, Tcl_GetResult.
340
341 Current implementation contains enough to make it possible to compile
342 "mTk/tkText*.[ch]" with the virgin Tk.
343
344 New types of events ????
345 PortableTk defines following new types of events:
346
347 TK_EVENTTYPE_NONE
348 TK_EVENTTYPE_STRING
349 TK_EVENTTYPE_NUMBER
350 TK_EVENTTYPE_WINDOW
351 TK_EVENTTYPE_ATOM
352 TK_EVENTTYPE_DISPLAY
353 TK_EVENTTYPE_DATA
354
355 and a function
356
357 char * Tk_EventInfo(int letter,
358 Tk_Window tkwin, XEvent *eventPtr,
359 KeySym keySym, int *numPtr, int *isNum, int *type,
360 int num_size, char *numStorage)
361
363 If you start with working TCL code, you can start conversion using the
364 above hints. Good indication that you are doing is OK is absence of
365 "sprintf" and "sscanf" in your code (at least in the part that is
366 working with interpreter).
367
369 What is described here is not included into base portableTk
370 distribution. Currently it is coded in TCL and as Perl macros (core is
371 coded as functions, so theoretically you can use the same object files
372 with different interpreted languages).
373
374 "ListFactory"
375 Dynamic arrays in TCL are used for two different purposes: to construct
376 strings, and to construct lists. These two usages will have separate
377 interfaces in other languages (since list is a different type from a
378 string), so you should use a different interface in your code.
379
380 The type for construction of dynamic lists is "ListFactory". The API
381 below is a counterpart of the API for construction of dynamic lists in
382 TCL:
383
384 void ListFactoryInit(ListFactory *)
385 void ListFactoryFinish(ListFactory *)
386 void ListFactoryFree(ListFactory *)
387 Tcl_Obj * * ListFactoryArg(ListFactory *)
388 void ListFactoryAppend(ListFactory *, Tcl_Obj * *arg)
389 void ListFactoryAppendCopy(ListFactory *, Tcl_Obj * *arg)
390 ListFactory * ListFactoryNewLevel(ListFactory *)
391 ListFactory * ListFactoryEndLevel(ListFactory *)
392 void ListFactoryResult(Tcl_Interp *, ListFactory *)
393
394 The difference is that a call to "ListFactoryFinish" should precede the
395 actual usage of the value of "ListFactory", and there are two different
396 ways to append an "Tcl_Obj *" to a "ListFactory":
397 ListFactoryAppendCopy() guarantees that the value of "arg" is copied to
398 the list, but ListFactoryAppend() may append to the list a reference to
399 the current value of "arg". If you are not going to change the value of
400 "arg" after appending, the call to ListFactoryAppend may be quicker.
401
402 As in TCL, the call to ListFactoryFree() does not free the
403 "ListFactory", only the objects it references.
404
405 The functions ListFactoryNewLevel() and ListFactoryEndLevel() return a
406 pointer to a "ListFactory" to fill. The argument of
407 ListFactoryEndLevel() cannot be used after a call to this function.
408
409 DStrings
410 Production of strings are still supported in portableTk.
411
412 Accessing "Tcl_Obj *"s
413 The following functions for getting a value of an "Tcl_Obj *" may be
414 provided:
415
416 double LangDouble(Tcl_Obj *)
417 int LangInt(Tcl_Obj *)
418 long LangLong(Tcl_Obj *)
419 int LangIsList(Tcl_Obj * arg)
420
421 The function LangIsList() is supported only partially under TCL, since
422 there is no data types. It checks whether there is a space inside the
423 string "arg".
424
425 Assigning numbers to "Tcl_Obj *"s
426 While LangSetDouble() and LangSetInt() are supported ways to assign
427 numbers to assign an integer value to a variable, for the sake of
428 efficiency under TCL it is supposed that the destination of these
429 commands was massaged before the call so it contains a long enough
430 string to sprintf() the numbers inside it. If you are going to
431 immediately use the resulting "Tcl_Obj *", the best way to do this is
432 to declare a buffer in the beginning of a block by
433
434 dArgBuffer;
435
436 and assign this buffer to the "Tcl_Obj *" by
437
438 void LangSetDefaultBuffer(Tcl_Obj * *)
439
440 You can also create the buffer(s) manually and assign them using
441
442 void LangSetBuffer(Tcl_Obj * *, char *)
443
444 This is the only choice if you need to assign numeric values to several
445 "Tcl_Obj *"s simultaneously. The advantage of the first approach is
446 that the above declarations can be made "nop"s in different languages.
447
448 Note that if you apply "LangSetDefaultBuffer" to an "Tcl_Obj *" that
449 contains some value, you can create a leak if you do not free that
450 "Tcl_Obj *" first. This is a non-problem in real languages, but can be
451 a trouble in "TCL", unless you use only the above API.
452
453 Creating new "Tcl_Obj *"s
454 The API for creating a new "Tcl_Obj *" is
455
456 void LangNewArg(Tcl_Obj * *, LangFreeProc *)
457
458 The API for creating a new "Tcl_Obj *" is absent. Just initialize
459 "Tcl_Obj *" to be "NULL", and apply one of "LangSet..." methods.
460
461 After you use this "Tcl_Obj *", it should be freed thusly:
462
463 "LangFreeArg(arg, freeProc)".
464
465 Evaluating a list
466 Use
467
468 int LangArgEval(Tcl_Interp *, Tcl_Obj * arg)
469
470 Here "arg" should be a list to evaluate, in particular, the first
471 element should be a "LangCallback" massaged to be an "Tcl_Obj *". The
472 arguments can be send to the subroutine by reference or by value in
473 different languages.
474
475 Getting result as "Tcl_Obj *"
476 Use "Tcl_ArgResult". It is not guaranteed that result survives this
477 operation, so the "Tcl_Obj *" you get should be the only mean to access
478 the data from this moment on. After you use this "Tcl_Obj *", you
479 should free it with "freeProc" "LANG_DYNAMIC" (you can do LangSet...()
480 in between).
481
482
483
484perl v5.32.1 2021-01-27 pTk(3)