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