Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2 | mjames | 1 | <html><head> |
2 | <title>Mktclapp: A tool for building C/C++ programs that use Tcl/Tk</title> |
||
3 | </head> |
||
4 | <body bgcolor=white> |
||
5 | |||
6 | <h1>Contents:</h1> |
||
7 | <ul> |
||
8 | <li><a href="#toc1"> |
||
9 | Introduction |
||
10 | </a></li> |
||
11 | <li><a href="#toc2"> |
||
12 | A Quick Overview |
||
13 | </a></li> |
||
14 | <li><a href="#toc3"> |
||
15 | Setting Up Your Development Environment |
||
16 | </a></li> |
||
17 | <li><a href="#toc4"> |
||
18 | Hello, World! |
||
19 | </a></li> |
||
20 | <li><a href="#toc5"> |
||
21 | If You Are Having Trouble |
||
22 | </a></li> |
||
23 | <li><a href="#toc6"> |
||
24 | Adding More Tcl Code |
||
25 | </a></li> |
||
26 | <li><a href="#toc7"> |
||
27 | Making The Program Standalone |
||
28 | </a></li> |
||
29 | <li><a href="#toc8"> |
||
30 | Common Mistakes |
||
31 | </a></li> |
||
32 | <li><a href="#toc9"> |
||
33 | Adding In Some C Code |
||
34 | </a></li> |
||
35 | <li><a href="#toc10"> |
||
36 | Implementing New Tcl Commands In C |
||
37 | </a></li> |
||
38 | <li><a href="#toc11"> |
||
39 | Using The Tcl_Obj Interface For Tcl New Commands |
||
40 | </a></li> |
||
41 | <li><a href="#toc12"> |
||
42 | Using Tcl Namespaces |
||
43 | </a></li> |
||
44 | <li><a href="#toc13"> |
||
45 | Executing Tcl/Tk Commands From C Code |
||
46 | </a></li> |
||
47 | <li><a href="#toc14"> |
||
48 | Other Functions Provided By Mktclapp |
||
49 | </a></li> |
||
50 | <li><a href="#toc15"> |
||
51 | The <tt>%q</tt> Format Field |
||
52 | </a></li> |
||
53 | <li><a href="#toc16"> |
||
54 | Putting A <tt>main()</tt> In Your C Code |
||
55 | </a></li> |
||
56 | <li><a href="#toc17"> |
||
57 | C Functions For Application Initialization |
||
58 | </a></li> |
||
59 | <li><a href="#toc17a"> |
||
60 | Bundling Binary Data Files With The Executable |
||
61 | </a></li> |
||
62 | <li><a href="#toc17b"> |
||
63 | Building Tcl Extensions With Mktclapp |
||
64 | </a></li> |
||
65 | <li><a href="#toc18"> |
||
66 | Running Mktclapp From The Command Line |
||
67 | Or In A Makefile |
||
68 | </a></li> |
||
69 | <li><a href="#toc18b"> |
||
70 | Using MkTclApp With The MingW32 Compiler For Windows |
||
71 | </a></li> |
||
72 | <li><a href="#toc19"> |
||
73 | History Of Mktclapp And Summary |
||
74 | </a></li> |
||
75 | <li><a href="#toc20"> |
||
76 | Bibliography |
||
77 | </a></li> |
||
78 | </ul> |
||
79 | <hr> |
||
80 | |||
81 | <h1 align=center> |
||
82 | Mktclapp: A Tool For Building C/C++ Programs |
||
83 | That Use Tcl/Tk |
||
84 | </h1> |
||
85 | <a name="toc1"><!-- AUTO --> |
||
86 | <h2 align=center> |
||
87 | Introduction |
||
88 | </h2> |
||
89 | |||
90 | <p>Many people think that Tcl/Tk is just a scripting language. |
||
91 | They think the only way to use Tcl/Tk is to |
||
92 | write a script of Tcl commands, then run that script using |
||
93 | either the "tclsh" or "wish" interpreters.</p> |
||
94 | |||
95 | <p>But this perception is false. |
||
96 | At its heart, |
||
97 | Tcl/Tk is really a C library, just like Motif, Gtk or MFC. |
||
98 | To use Tcl/Tk as it was originally intended, you have to write |
||
99 | a C program that calls the library. "Tclsh" and "wish" are |
||
100 | just two programs that happen to be implemented using the |
||
101 | Tcl/Tk library.</p> |
||
102 | |||
103 | <p>This is not to disparage the use of scripts that are |
||
104 | interpreted by "tclsh" or "wish". |
||
105 | The use of scripts is a very powerful idea and has many important |
||
106 | applications. |
||
107 | But sometimes problems work out better if you approach them with |
||
108 | a C or C++ program rather than a script. |
||
109 | Unfortunately, the mixing of C or C++ and Tcl/Tk into the same |
||
110 | program is a topic that has been neglected in the Tcl/Tk |
||
111 | literature and documentation.</p> |
||
112 | |||
113 | <p>The article is about a utility called <b>mktclapp</b>. |
||
114 | Mktclapp simplifies the task of building a program |
||
115 | that uses both C/C++ code and Tcl/Tk. |
||
116 | Using mktclapp, you can quickly write programs that:</p> |
||
117 | <ul> |
||
118 | <p><li> Implement powerful GUIs or command-line interfaces using Tcl or |
||
119 | Tcl/Tk,</li></p> |
||
120 | <p><li> Do speedy computation on complex data structures using C or C++, |
||
121 | </li></p> |
||
122 | <p><li> Compile under both Unix and Windows98/NT with no source code changes, |
||
123 | </li></p> |
||
124 | <p><li> Are realized in a single standalone executable file,</li></p> |
||
125 | <p><li> Run on machines without "tclsh" or "wish" installed, and</li></p> |
||
126 | <p><li> Are difficult for end-users to read and reverse-engineer.</li></p> |
||
127 | </ul> |
||
128 | |||
129 | <p>Mktclapp is very easy to learn to use. |
||
130 | If you already know how to program in C and you are familiar |
||
131 | with writing Tcl/Tk scripts, then you should be able to start |
||
132 | using mktclapp in just a few minutes. |
||
133 | If you are inexperienced, it might take you just a little longer, |
||
134 | but it still is not hard.</p> |
||
135 | |||
136 | <a name="toc2"><!-- AUTO --> |
||
137 | <h2 align=center> |
||
138 | A Quick Overview |
||
139 | </h2> |
||
140 | |||
141 | <p>This is what mktclapp does for you: |
||
142 | You begin with a collection of source files, some written in C or C++ |
||
143 | and others written in pure Tcl or Tcl/Tk. |
||
144 | (See figure 1 below). The mktclapp program scans these source files |
||
145 | and uses the information it gleans to build an |
||
146 | <b>application initialization</b> module, shown as <b>appinit.c</b> |
||
147 | in the figure. |
||
148 | You then use your regular C compiler to turn the application initialization |
||
149 | module and your C/C++ code into a standalone executable.</p> |
||
150 | |||
151 | <p> |
||
152 | <a name=fig1> |
||
153 | <hr><center><img src="fig1.jpg" alt="Figure 1"><br> |
||
154 | <b>Figure 1</b></center></p><p><hr></p> |
||
155 | |||
156 | <p>The mktclapp program performs a number of services for you:</p> |
||
157 | |||
158 | <ul> |
||
159 | <p><li> It takes care of the messy details of creating and |
||
160 | initializing a Tcl or Tcl/Tk interpreter.</li></p> |
||
161 | <p><li> It converts all your Tcl/Tk scripts into static C strings inside |
||
162 | the application initialization module so they |
||
163 | will be compiled into the executable.</li></p> |
||
164 | <p><li> It optionally "shrouds" your Tcl/Tk scripts so that they are |
||
165 | not easily visible to end users.</li></p> |
||
166 | <p><li> It provides some extra library functions that make it easier |
||
167 | to write C code that invokes Tcl/Tk subroutines.</li></p> |
||
168 | <p><li> It registers certain C functions as Tcl/Tk commands so that |
||
169 | your Tcl/Tk scripts can directly execute those C functions.</li></p> |
||
170 | </ul> |
||
171 | |||
172 | <p>It short, mktclapp takes care of a lot of the mundate |
||
173 | details of writing a mixed C/C++/Tcl/Tk program so that you have more |
||
174 | time left over to focus on solving the interesting problems.</p> |
||
175 | |||
176 | <p>Mktclapp is a command-line program which you can call from your |
||
177 | project's Makefile. But there is also a GUI wrapper for mktclapp |
||
178 | (written in Tcl/Tk, of course) that makes the program easier |
||
179 | to operate. The GUI is called |
||
180 | <b>xmktclapp.tcl</b>. With xmktclapp.tcl, all you have to do is |
||
181 | select the options you want, choose your C, C++ |
||
182 | and Tcl/Tk source files from a file list, and press "Build". |
||
183 | The application initialization file will be built for your automatically. |
||
184 | Then just run your compiler as you normally would and the job is |
||
185 | done. A snapshot of the xmktclapp.tcl GUI is show in figure 2.</p> |
||
186 | |||
187 | <p> |
||
188 | <a name=fig2> |
||
189 | <hr><center><img src="xmta1.jpg" alt="Figure 2"><br> |
||
190 | <b>Figure 2</b></center></p><p><hr></p> |
||
191 | |||
192 | <a name="toc3"><!-- AUTO --> |
||
193 | <h2 align=center> |
||
194 | Setting Up Your Development Environment |
||
195 | </h2> |
||
196 | |||
197 | <p>Are you ready to get started? This section will guide |
||
198 | you step-by-step into setting up your development environment to |
||
199 | use mktclapp. The process is not difficult and should not take |
||
200 | very long. There are three simple steps:</p> |
||
201 | |||
202 | <ol> |
||
203 | <li><p> |
||
204 | Make sure you have a suitable ANSI C compiler |
||
205 | and POSIX compliant development environment handy. |
||
206 | You must have an ANSI-C compiler. Older K&R C compilers will |
||
207 | not work.</li></p> |
||
208 | |||
209 | <p>For the development environment, I like to use some kind |
||
210 | of Unix, especially Linux. |
||
211 | Unix was originally written by and for software developers, and is |
||
212 | an excellent environment for getting a lot of work done in a short |
||
213 | amount of time. But mktclapp also works on Windows platforms. |
||
214 | For use on Windows, though, you'll need to use the Cygwin |
||
215 | (or Mingw32) compiler and development environment. You can download |
||
216 | this package for free from |
||
217 | <a href="http://sourceware.cygnus.com/cygwin/"> |
||
218 | http://sourceware.cygnus.com/cygwin/</a>. |
||
219 | Mktclapp will work with VC++, but the use of mktclapp with VC++ is |
||
220 | not supported. |
||
221 | Please do not send me e-mail asking for help using mktclapp with VC++ or |
||
222 | Borland C++. |
||
223 | </li> |
||
224 | |||
225 | <p> |
||
226 | <img src=righthand.jpg align=left> |
||
227 | <a href="mailto:dlabelle@albany.net">Dennis LaBelle</a> |
||
228 | has gotten mktclapp to work with VC++ 6.0. |
||
229 | He uses it to build his "Freewrap" program. |
||
230 | See <a href="http://www.albany.net/~dlabelle/freewrap/freewrap.html"> |
||
231 | http://www.albany.net/~dlabelle/freewrap/freewrap.html</a> for additional |
||
232 | Information. |
||
233 | </p> |
||
234 | |||
235 | <li><p> |
||
236 | Next, you'll want to download and compile the Tcl/Tk source code. |
||
237 | You can find the sources at several sites, including</p> |
||
238 | |||
239 | <p><ul> |
||
240 | <li> <a href="http://www.tclconsortium.org/software/index.vet"> |
||
241 | http://www.tclconsortium.org/software/index.vet</a></li> |
||
242 | <li> <a href="http://www.scriptics.com/software/download.html"> |
||
243 | http://www.scriptics.com/software/download.html</a></li> |
||
244 | <li> <a href="http://www.neosoft.com/tcl/"> |
||
245 | http://www.neosoft.com/tcl/</a></li> |
||
246 | </ul></p> |
||
247 | |||
248 | <p>The Cygwin environment comes with a copy of Tcl/Tk already built |
||
249 | and installed. If you want to compile it yourself (or if you want |
||
250 | to use a version of Tcl/Tk other than the version that |
||
251 | comes with Cygwin) you may need to |
||
252 | download and apply some patches from</p> |
||
253 | <ul> |
||
254 | <p><li> <a href="http://www.xraylith.wisc.edu/~khan/software/tcl"> |
||
255 | http://www.xraylith.wisc.edu/~khan/software/tcl</a></li></p> |
||
256 | </ul> |
||
257 | |||
258 | <p> |
||
259 | <img src=righthand.jpg align=left> |
||
260 | The Tcl/Tk that comes with the Cygwin compiler requires a special |
||
261 | DLL named <b>cygwin1.dll</b>. This DLL is covered by GPL (not |
||
262 | the LGPL) and can not be distributed with proprietary software |
||
263 | without first paying a $6K licensing fee to Cygnus. If this |
||
264 | is a problem for you, you can use the standard Tcl/Tk DLLs from |
||
265 | Scriptics that do not require a licensing fee. See |
||
266 | <a href="#toc18b">below</a> for details. |
||
267 | </p> |
||
268 | |||
269 | <p>After you download the code, untar it and change to the directory |
||
270 | named "tcl*/unix". In that directory, type "./configure" and |
||
271 | then type "make". This will build Tcl for you. After building |
||
272 | Tcl, cd to "../../tk*/unix" and there type "./configure" and "make". |
||
273 | This will build Tk.</p> |
||
274 | |||
275 | <p> |
||
276 | <a name=RecordLibraries> |
||
277 | <img src=righthand.jpg align=left> |
||
278 | <b>IMPORTANT:</b> While building both Tcl and Tk, notice the sequence of |
||
279 | library directives that the Makefile gives to the C compiler when it |
||
280 | is linking "tclsh" and "wish". These directives will look something |
||
281 | like the following:</p> |
||
282 | <blockquote> |
||
283 | <b><tt>-ltk8.0 -ltcl8.0 -L/usr/X11R6/lib -lX11 -lm -ldl</tt></b> |
||
284 | </blockquote> |
||
285 | <p>The exact sequence of libraries varies from one system to another. |
||
286 | Write down the libraries that your systems uses. You will need to |
||
287 | type in the exact same sequence of libraries when you compile your |
||
288 | own applications later on.</p> |
||
289 | |||
290 | <p>If you are using Cygwin beta19, the compiler options you need |
||
291 | to remember look like the line shown below. Note that several extra |
||
292 | libraries have been added to this list since version 2.1 of Mktclapp!</p> |
||
293 | <blockquote> |
||
294 | <b><tt>-Wl,--subsystem,windows -ltk80 -ltcl80 -lm -lkernel32 -lgdi32 -luser32 -comdlg32</tt></b> |
||
295 | </blockquote> |
||
296 | <p>With Cygwin beta20 or later, the libraries you will need are like this:</p> |
||
297 | <blockquote> |
||
298 | <b><tt>-mwindows -ltk80 -ltcl80 -lm</tt></b> |
||
299 | </blockquote> |
||
300 | </li></p> |
||
301 | |||
302 | |||
303 | <p><li> |
||
304 | Download and compile mktclapp. You can get the sources to mktclapp |
||
305 | from |
||
306 | <ul> |
||
307 | <p><li> <a href="http://www.hwaci.com/sw/mktclapp/"> |
||
308 | http://www.hwaci.com/sw/mktclapp/</a>.</li></p> |
||
309 | </ul> |
||
310 | <p>The source code to mktclapp is a single file of C code named |
||
311 | <b>mktclapp.c</b>. There is no makefile. There really isn't a |
||
312 | need for one. To build mktclapp you type this command:</p> |
||
313 | <blockquote> |
||
314 | <b>cc -o mktclapp mktclapp.c</b> |
||
315 | </blockquote> |
||
316 | <p>The source code to mktclapp is very portable and should compile |
||
317 | without modification and without the need for special compiler |
||
318 | switches on any ANSI C compiler.</p> |
||
319 | |||
320 | <p>The source code to xmktclapp.tcl is also a single file. But xmktclapp.tcl |
||
321 | is a Tcl/Tk script, so it requires no compilation. All you have |
||
322 | to do is download it.</li></p> |
||
323 | </ol> |
||
324 | |||
325 | <p>Now your environment should be setup and ready to build some |
||
326 | great programs using mktclapp. Let's get starting.</p> |
||
327 | |||
328 | <a name="toc4"><!-- AUTO --> |
||
329 | <h2 align=center> |
||
330 | Hello, World! |
||
331 | </h2> |
||
332 | |||
333 | <p>We'll begin by using xmktclapp.tcl to build a simple program that |
||
334 | involves just 9 lines of Tcl/Tk and no C/C++. |
||
335 | The Tcl/Tk code is contained in a single file named <b>hello.tcl</b> |
||
336 | and looks like this:</p> |
||
337 | |||
338 | <blockquote><pre> |
||
339 | <b>button .b -text Go -command Hello |
||
340 | button .e -text Quit -command exit |
||
341 | pack .b .e -side left |
||
342 | proc Hello {} { |
||
343 | catch {destroy .hi} |
||
344 | toplevel .hi |
||
345 | button .hi.b -text {Hello, World!} -command {destroy .hi} |
||
346 | pack .hi.b |
||
347 | }</b> |
||
348 | </pre></blockquote> |
||
349 | |||
350 | <p>This code creates a small window containing two buttons labeled |
||
351 | "Go" and "Quit". If you press the Quit button, the program exits. |
||
352 | If you press "Go", it pops up another small window containing a |
||
353 | single button labeled "Hello, World!". If you press the Hello, World! |
||
354 | button, the new window disappears.</p> |
||
355 | |||
356 | <p>To build your first mktclapp program, first type the code above |
||
357 | into a file named <b>hello.tcl</b>. Then, just to make sure you |
||
358 | didn't mistype anything, run the script by double-clicking the |
||
359 | icon in Windows or in Unix by typing:</p> |
||
360 | |||
361 | <blockquote><b>wish hello.tcl</b></blockquote> |
||
362 | |||
363 | <p>Once you are satisfied that the script is right, launch xmktclapp.tcl |
||
364 | by typing</p> |
||
365 | |||
366 | <blockquote><b>wish xmktclapp.tcl</b></blockquote> |
||
367 | |||
368 | <p>If you are using windows and have installed a binary release |
||
369 | of Tcl/Tk from Scriptics, then you can double-click on the |
||
370 | xmktclapp.tcl icon to launch it. But when you do, you will |
||
371 | not be running the Cygwin version of Tcl/Tk. This can cause |
||
372 | some problems. It is best to run xmktclapp.tcl using the |
||
373 | Cygwin version of the Tcl/Tk interpreter. To do so, bring |
||
374 | up a Cygwin DOS box and type</p> |
||
375 | |||
376 | <blockquote><b>cygwish80 xmktclapp.tcl</b></blockquote> |
||
377 | |||
378 | <p>After you get xmktclapp.tcl running, you should see a screen like |
||
379 | <a href="#fig2">figure 2</a>. |
||
380 | Now change the <b>Settings</b> page of xmktclapp.tcl to look exactly like |
||
381 | figure 2. Specifically, set the Application Mode to Tcl/Tk, |
||
382 | set Fork Into Background, Standalone |
||
383 | and Shroud all to No, set Command Line Input to None, |
||
384 | set the name of the Configuration File to <b>hello.mta</b> and |
||
385 | the name of the Output File to <b>hello.c</b>.</p> |
||
386 | |||
387 | <p>Don't worry about the contents of the <b>Libraries</b> page on |
||
388 | the xmktclapp.tcl screen at this point. The libraries only |
||
389 | come into play if you set Standalone to "Yes" or "Strict". |
||
390 | But do go over to the |
||
391 | <b>C/C++ Modules</b> pages and make sure |
||
392 | it is blank. Use the "Delete" button if necessary to |
||
393 | clear it out.</p> |
||
394 | |||
395 | <p>On the "Tcl Scripts" page, you have to insert the name of your |
||
396 | Tcl script in two places, as shown in figure 3.</p> |
||
397 | |||
398 | <p><center> |
||
399 | <img src="xmta-fig3.jpg" alt="figure 3"><br> |
||
400 | Figure 3 |
||
401 | </center></p> |
||
402 | |||
403 | <p>Be sure the hello.tcl script appears in both the list box on top and |
||
404 | in the "Startup Script" entry box down below. |
||
405 | When everything looks right, select the File/Build menu option, |
||
406 | or go back to the "Settings" page and press the "Build" button. |
||
407 | The build will create files named <b>hello.c</b> and <b>hello.h</b>.</p> |
||
408 | |||
409 | <p>Referring back to <a href="#fig1">figure 1</a>, what you have just |
||
410 | done is the first step of the compilation process. You have run |
||
411 | mktclapp on your C, C++ and Tcl/Tk source files in order to generate |
||
412 | an application initialization file. In this particular instance you |
||
413 | don't happen to be using any C or C++ source files, only Tcl/Tk |
||
414 | files. But the idea is still the same. The |
||
415 | next step is to run the C compiler.</p> |
||
416 | |||
417 | <p>The command to compile your program will be something like this:</p> |
||
418 | |||
419 | <blockquote> |
||
420 | <b>gcc hello.c -ltk8.0 -ltcl8.0 -L/usr/X11R6/lib -lX11 -lm -ldl</b> |
||
421 | </blockquote> |
||
422 | |||
423 | <p>The above works on RedHat Linux. If you are using Cygwin version 20 on |
||
424 | a Windows machine, the following command is what you need:</p> |
||
425 | |||
426 | <blockquote> |
||
427 | <b>gcc hello.c -ltk80 -ltcl80 -lm -mwindows</b> |
||
428 | </blockquote> |
||
429 | |||
430 | <p>Other platforms will have slightly different variations. |
||
431 | But the basic recipe is simple: Start with the name of your C compiler |
||
432 | (<b>gcc</b> in the example) and add to this the name of all your C |
||
433 | or C++ source |
||
434 | files. (We have none for this example.) |
||
435 | Then add on the name of the application initialization file: |
||
436 | <b>hello.c</b>. |
||
437 | Next, add on all those library directives that you wrote down |
||
438 | <a href="#RecordLibraries">above</a> when you were compiling Tcl/Tk. |
||
439 | Finally, press return and wait for the compiler to do its thing.</p> |
||
440 | |||
441 | <p>The result is your executable in a file named <b>a.out</b> |
||
442 | (or <b>a.exe</b> if you are using Cygwin on Windows.) Type</p> |
||
443 | |||
444 | <blockquote><b>./a.out</b></blockquote> |
||
445 | |||
446 | <p>to give it a try (or double click the <b>a.exe</b> icon if you |
||
447 | are using Windows.)</p> |
||
448 | |||
449 | <a name="toc5"><!-- AUTO --> |
||
450 | <h2 align=center> |
||
451 | If You Are Having Trouble |
||
452 | </h2> |
||
453 | |||
454 | <p>Are you having difficulty getting your first mktclapp program to compile |
||
455 | or run? |
||
456 | This section is designed to help you fix the problem.</p> |
||
457 | |||
458 | <p><img src=righthand.jpg align=left> |
||
459 | <b>Problem:</b> |
||
460 | The compiler complains that it can't find library <i>ABC</i>.<br clear=both></p> |
||
461 | |||
462 | <p>First, make sure you entered the library options to the compiler exactly |
||
463 | as they were used when compiling Tcl/Tk. See the |
||
464 | discussion <a href="#RecordLibraries">above</a> for details.</p> |
||
465 | |||
466 | <p>If it still doesn't work, it may be because your a compiling in a different |
||
467 | directory from the one in which Tcl/Tk was built. Try adding a |
||
468 | couple of <b>-L</b> |
||
469 | options to the beginning of the library switches that defines the directories |
||
470 | that contain your Tcl and Tk libraries. Perhaps something like this:</p> |
||
471 | |||
472 | <blockquote> |
||
473 | <b>gcc appinit.c -L../tk8.0.3/unix -ltk8.0 -L../tcl8.0.3/unix/ \<br> |
||
474 | -ltcl8.0 -L/usr/X11R6/lib -lX11 -lm -ldl</b> |
||
475 | </blockquote> |
||
476 | |||
477 | <p>The <b>-L</b> option tells the compiler what directories to look in for |
||
478 | the libraries you specify. The compiler is often able to figure out |
||
479 | these directories on its own, but sometimes you have to give it hints.</p> |
||
480 | |||
481 | <p>If that still isn't working, try typing the filename of the libraries |
||
482 | themselves instead of using the <b>-l</b> options. Like this:</p> |
||
483 | |||
484 | <blockquote> |
||
485 | <b>gcc appinit.c ../tk8.0.3/unix/libtk8.0.a ../tcl8.0.3/unix/libtcl8.0.a \<br> |
||
486 | -L/usr/X11R6/lib -lX11 -lm -ldl</b> |
||
487 | </blockquote> |
||
488 | |||
489 | <p> |
||
490 | <img src=righthand.jpg align=left> |
||
491 | <b>Problem:</b> |
||
492 | The program compiles, but when I try to run it a message |
||
493 | says that Tcl/Tk was installed incorrectly.<br clear=both></p> |
||
494 | |||
495 | <p>This problem can arise if you have two or more incompatible versions of |
||
496 | Tcl/Tk installed on your development machine. The error message occurs |
||
497 | when you try to use the C library from one version of Tcl/Tk and the Tcl/Tk |
||
498 | Script Library from an incompatible version.</p> |
||
499 | |||
500 | <p>A quick fix is to set your TCL_LIBRARY and TK_LIBRARY environment variables |
||
501 | to point to the appropriate versions of the Tcl/Tk Library scripts for the |
||
502 | version of the C library you are using. If you are using the C library |
||
503 | at <b>../tk8.0.3/unix/libtk8.0.a</b>, then an appropriate setting for |
||
504 | the TK_LIBRARY environment variable would be <b>../tk8.0.3/library</b>. |
||
505 | The TCL_LIBRARY variable is set analogously.</p> |
||
506 | |||
507 | <p>A more permanent fix (and one that you should use for all your |
||
508 | deliverables) is to make your program standalone. To do this, change |
||
509 | the "Standalone?" option on the Settings page of xmktclapp.tcl to either |
||
510 | "Yes" or "Strict". |
||
511 | Then go to the "Libraries" page and enter an appropriate path for both |
||
512 | your Tcl and your Tk script libraries. These paths will be exactly the |
||
513 | same paths you used for the TCL_LIBRARY and TK_LIBRARY environment varibles |
||
514 | in the quick fix described by the previous paragraph. Then press |
||
515 | the "Build" button, exit xmktclapp.tcl, and recompile.</p> |
||
516 | |||
517 | <p>This problem occurs most often on Windows because people tend to |
||
518 | have two versions of Tcl/Tk installed there. There is probably a |
||
519 | binary release of Tcl/Tk installed and the version of Tcl/Tk that |
||
520 | came with the Cygwin compile. If this is your situation, you need |
||
521 | to make absolutely sure that the Tcl/Tk Libraries you are using come |
||
522 | from the Cygwin compiler release and not the other Tcl/Tk installation.</p> |
||
523 | |||
524 | <p><img src=righthand.jpg align=left> |
||
525 | <b>Problem:</b> On windows, I double click on the program icon, but |
||
526 | nothing happens.<br clear=both></p> |
||
527 | |||
528 | <p>This could be one of several things. Mostly likely it is because Windows |
||
529 | cannot find the right DLLs to run your program. You need to make a |
||
530 | copy of the following three files from the "bin" directory of your |
||
531 | Cygwin installation into the same directory where your new executable |
||
532 | is found: |
||
533 | <ul> |
||
534 | <li> <b>cygwin1.dll</b> (or <b>cygwin19.dll</b> under Cygwin version 19) |
||
535 | <li> <b>cygtcl80.dll</b> |
||
536 | <li> <b>cygtk80.dll</b> |
||
537 | </ul> |
||
538 | If putting these three files in the same directory as the executable |
||
539 | doesn't help, then try running the program manually from the Cygwin |
||
540 | shell prompt. You might get a better error message then. If you |
||
541 | still cannot figure out what is going wrong, try using the remedy to |
||
542 | the previous problem -- make the program standalone and/or set your |
||
543 | TCL_LIBRARY and TK_LIBRARY environment variables. If all else fails, |
||
544 | run your program in the debugger to see where it is going astray.</p> |
||
545 | |||
546 | <p><img src=righthand.jpg align=left> |
||
547 | <b>Problem:</b> When I compile using the Cygwin compiler I get an error |
||
548 | message that says "cannot find entry symbol _winMainCRTStartup". |
||
549 | <br clear=both></p> |
||
550 | |||
551 | <p>The Cygwin compiler always gives this error message when you compile |
||
552 | using the "-mwindows" option. But it isn't really an error. If you |
||
553 | didn't see any other error messages then you executable should still |
||
554 | work.</p> |
||
555 | |||
556 | <a name="toc6"><!-- AUTO --> |
||
557 | <h2 align=center> |
||
558 | Adding More Tcl Code |
||
559 | </h2> |
||
560 | |||
561 | <p>Now let's consider the case where your program consists of two |
||
562 | or more Tcl files. Typically the way this works is that the |
||
563 | first Tcl file (the "main" Tcl file) uses the "source" command of |
||
564 | Tcl to load the contents of all the other Tcl files.</p> |
||
565 | |||
566 | <p>Suppose, for example, we what to add ballon help to the "Hello World" |
||
567 | program we constructed above. I like to use |
||
568 | <a href="mailto:d.roche@lectra.com">Daniel Roche's</a> excellent |
||
569 | <b>balloon.tcl</b> code. You can get a copy directly from |
||
570 | Daniel's website at</p> |
||
571 | |||
572 | <blockquote> |
||
573 | <a href="http://www.multimania.com/droche/tkballoon/index.html"> |
||
574 | http://www.multimania.com/droche/tkballoon/index.html</a> |
||
575 | </blockquote> |
||
576 | |||
577 | <p>Or you can grab a <a href="balloon.tcl">mirrored copy</a> directly |
||
578 | from the mktclapp website. |
||
579 | However you get it, add in |
||
580 | the <b>balloon.tcl</b> package by altering <b>hello.tcl</b> |
||
581 | to look something like this:</p> |
||
582 | |||
583 | <blockquote><pre> |
||
584 | <b>source balloon.tcl |
||
585 | button .b -text Go -command Hello |
||
586 | set_balloon .b {Press for a new window} |
||
587 | button .e -text Quit -command exit |
||
588 | set_balloon .e {Exit this program} |
||
589 | pack .b .e -side left |
||
590 | proc Hello {} { |
||
591 | catch {destroy .hi} |
||
592 | toplevel .hi |
||
593 | button .hi.b -text {Hello, World!} -command {destroy .hi} |
||
594 | set_balloon .hi.b {Make this window disappear} |
||
595 | pack .hi.b |
||
596 | }</b> |
||
597 | </pre></blockquote> |
||
598 | |||
599 | <p>The key thing you need to notice |
||
600 | is the first line. We are using the "source" |
||
601 | command of Tcl to load in the balloon package. (We also added |
||
602 | various calls to "set_balloon". |
||
603 | But that isn't the point of this exercise. You should |
||
604 | focus on the "source" command on the first line of the script.)</p> |
||
605 | |||
606 | <p>In a normal Tcl script, the "source" command looks to the disk, |
||
607 | finds the file named in its argument, and read the text of that |
||
608 | file in as a Tcl script. But with |
||
609 | mktclapp, the "source" command works a little differently. |
||
610 | With mktclapp, the "source" command first checks to see if the |
||
611 | file named in the argument has been compiled into the executable. |
||
612 | If the named file is part of the executable, then it is executed |
||
613 | directly out of memory, not off of the disk. This feature is |
||
614 | the magic of mktclapp. It eliminates the need to have various |
||
615 | Tcl scripts on the local disk drive and thus allows you to build a |
||
616 | standalone application.</p> |
||
617 | |||
618 | <p>To compile our revised program, bring up xmktclapp.tcl again |
||
619 | and add the <b>balloon.tcl</b> file to the "Tcl Scripts" page. |
||
620 | When you are done, it should look something like this:</p> |
||
621 | |||
622 | <p><center> |
||
623 | <img src="xmta-fig5.jpg" alt="unnumbered figure"> |
||
624 | </center><p> |
||
625 | |||
626 | <p>Notice that both Tcl source files, <b>hello.c</b> and |
||
627 | <b>balloon.tcl</b>, are listed in the top listbox. This means |
||
628 | both files will be compiled into the executable as strings. |
||
629 | But <b>hello.tcl</b> is still the main script, the script that |
||
630 | runs first and gets everything else running, so it alone is |
||
631 | shown below in the "Startup Script" entry.</p> |
||
632 | |||
633 | <p>After you get xmktclapp.tcl to look like the figure above, use |
||
634 | the File/Build menu option to construct the <b>hello.c</b> and |
||
635 | <b>hello.h</b> output files. Then recompile the example |
||
636 | program just like we did above.</p> |
||
637 | |||
638 | <blockquote> |
||
639 | <b>gcc hello.c -ltk80 -ltcl80 -lm -mwindows</b> |
||
640 | </blockquote> |
||
641 | |||
642 | <p>The result is a executable named <b>a.exe</b> (or |
||
643 | <b>a.out</b> on Unix) that contains both the <b>hello.tcl</b> |
||
644 | and <b>balloon.tcl</b> scripts built in. You can run this |
||
645 | program and it will "source" the balloon.tcl script even if |
||
646 | the balloon.tcl script doesn't really exist on the target |
||
647 | machine.</p> |
||
648 | |||
649 | <a name="toc7"><!-- AUTO --> |
||
650 | <h2 align=center> |
||
651 | Making The Program Standalone |
||
652 | </h2> |
||
653 | |||
654 | <p>The programs we built above do not depend on the files <b>hello.tcl</b> |
||
655 | and <b>balloon.tcl</b>. The contents of those files have been compiled |
||
656 | into the executable, so the program will run on machines that do have |
||
657 | those files resident. But the program still will not run on machines |
||
658 | that do not have Tcl/Tk installed. This is because every Tcl/Tk |
||
659 | program depends on a couple dozen Tcl script files that are part |
||
660 | of the Tcl/Tk installation. These files are sometimes call "Tcl/Tk |
||
661 | Initialization Scripts" or the "Tcl/Tk Library".</p> |
||
662 | |||
663 | <p>You can see a list of the Tcl/Tk Library scripts on your machine |
||
664 | be starting up a copy of "wish" and entering the following command:</p> |
||
665 | |||
666 | <blockquote><pre> |
||
667 | <b>lsort [concat [glob $tcl_library/*] [glob $tk_library/*]] |
||
668 | </b></pre></blockquote> |
||
669 | |||
670 | <p>If you have more than one version of Tcl/Tk installed on your |
||
671 | machine, then you will have more than one Tcl/Tk Library. On |
||
672 | my main development machine, I have both Tk7.6 and Tk8.0 installed. |
||
673 | So I will get a different response to the above command depending |
||
674 | on whether I run "wish" or "wish8.0".</p> |
||
675 | |||
676 | <p>Here's the issue: In order to make your programs completely standalone, |
||
677 | so that they will run on machines that do not have Tcl/Tk installed, you |
||
678 | have to make sure all of the Tcl/Tk Library scripts are compiled in.</p> |
||
679 | |||
680 | <p>To accomplish this goal, |
||
681 | all you have to do is enter the name of |
||
682 | the directories that contain your Tcl and Tk libraries on the |
||
683 | "Libraries" page of xmktclapp.tcl, then on the "Settings" page set |
||
684 | Standalone to either "Yes" or "Strict". |
||
685 | When you do this, all the Tcl/Tk Library scripts will be added to your |
||
686 | program automatically.</p> |
||
687 | |||
688 | <p>You may want to avoid making your program standalone during early |
||
689 | development. There are a lot of Tcl/Tk Library scripts. Their |
||
690 | total size approaches a half megabyte. Your code will compile a lot |
||
691 | faster if you leave them out at the beginning. Just be sure to |
||
692 | include the library scripts before you ship your program so that |
||
693 | people that do not have Tcl/Tk installed will be able to run your |
||
694 | code.</p> |
||
695 | |||
696 | <p>There's more: To be truely standalone, your program should also |
||
697 | be statically linked. This means you will need to link against |
||
698 | static libraries for Tcl and Tk instead of the usual shared libraries. |
||
699 | Otherwise, your program will not run on machines that do not have |
||
700 | the Tcl/Tk shared libraries installed.</p> |
||
701 | |||
702 | <p>On Unix systems, the usual way to link against static libraries |
||
703 | is to add an option like <b>-static</b> or perhaps <b>-Bstatic</b> |
||
704 | to the last compiler command line. This will do the trick if |
||
705 | static libraries are available on your system. If static libraries |
||
706 | are not available, you may need to recompile Tcl/Tk to generate |
||
707 | them. You might also need to specify the name of the static |
||
708 | library directly, as in <b>../tcl8.0/unix/libtcl.a</b> instead |
||
709 | of using the "-l" option like this: <b>-ltcl8.0</b>. |
||
710 | Most Unix systems have an "ldd" command which will tell you what |
||
711 | shared libraries a program needs. Do whatever it takes to get |
||
712 | this list down to only those libraries you know will be on |
||
713 | every system.</p> |
||
714 | |||
715 | <p>You may notice that statically linking your program |
||
716 | causes it to be much larger. A typical |
||
717 | "Hello, World" Tk program will grow to be a couple of megabytes |
||
718 | or more in size. It takes a lot longer to compile, and uses |
||
719 | more disk space. So, again, you might want to hold |
||
720 | off on making your program fully standalone until just before |
||
721 | final testing and delivery.</p> |
||
722 | |||
723 | <p>Statically linking a program on Windows is more problematic. |
||
724 | I usually deal with the problem by ignoring it altogether. |
||
725 | For Windows builds, I just ship the resulting EXE file together |
||
726 | with the DLLs it needs in the same directory on a CD-ROM. |
||
727 | This works on Windows because Windows programs look for the |
||
728 | DLLs they need in the same directory as the EXE file. |
||
729 | (Unix systems do not work this way for security reasons. Windows |
||
730 | can get away with it because it has no security, other than |
||
731 | the fact that it is a single-user operating system.) |
||
732 | After you compile your EXE on Windows, you can find out what |
||
733 | DLLs it needs using this command:</p> |
||
734 | |||
735 | <blockquote><pre> |
||
736 | <b>objdump -p a.exe | grep DLL |
||
737 | </b></pre></blockquote> |
||
738 | |||
739 | <p>The <b>kernel32.dll</b> file is part of Windows and doesn't need |
||
740 | to be included with your program. I typically ship with just |
||
741 | these extra DLLs: <b>cygwin1.dll</b>, <b>cygtcl80.dll</b> and |
||
742 | <b>cygtk80.dll</b>.</p> |
||
743 | |||
744 | <a name="toc8"><!-- AUTO --> |
||
745 | <h2 align=center> |
||
746 | Common Mistakes |
||
747 | </h2> |
||
748 | |||
749 | <p>Here are some mistakes people commonly make when |
||
750 | they are first beginning to use mktclapp. |
||
751 | Take care to avoid these mistakes yourself.</p> |
||
752 | |||
753 | <ul> |
||
754 | <p><li> |
||
755 | The name of the argument to the "source" command must |
||
756 | match exactly, character-for-character, the name that |
||
757 | appears in the upper "Tcl Scripts" listbox of xmktclapp.tcl. |
||
758 | If xmktclapp.tcl shows an absolute pathname, then you |
||
759 | should give an absolute pathname when you "source" the |
||
760 | file. If the name in the listbox is relative, then the |
||
761 | argument to "source" must be relative. |
||
762 | Note also that case is significant. Even though |
||
763 | Windows will match names with differing case, mktclapp |
||
764 | uses the strcmp() function from the C library to compare |
||
765 | names, so the names really do have to be identical.</li></p> |
||
766 | |||
767 | <p><li> |
||
768 | Be sure that every file that you "source" really is listed |
||
769 | on the "Tcl Scripts" page. If a file isn't listed in |
||
770 | "Tcl Scripts" your program will fall back and read it from the disk |
||
771 | on your development machine. But when you move the program |
||
772 | to a different computer, that script won't be there anymore |
||
773 | and the program will fail. |
||
774 | |||
775 | <p>You can guard against this error by setting the Standalone |
||
776 | option on the "Settings" page to "Strict". In strict mode, |
||
777 | the "source" command of mktclapp will check for built-in files, |
||
778 | but if it doesn't find one it won't fall back to the disk. |
||
779 | Instead it just returns an error.</li></p> |
||
780 | |||
781 | <p><li> |
||
782 | Be sure to include your main script (the Tcl script that |
||
783 | runs first) both in the upper listbox and the lower entry |
||
784 | box on the "Tcl Scripts" page. It has to be in both places.</li></lp> |
||
785 | |||
786 | <p><li> |
||
787 | In order to conserve memory and run faster, mktclapp tries |
||
788 | to remove comments and extra whitespace from the Tcl scripts |
||
789 | before it compiles them into the program. But on some rare |
||
790 | occasions, these changes can break scripts. If you are having |
||
791 | problems, try turning off the comment removal and see if that |
||
792 | helps. To turn off comment removal for a particular script, |
||
793 | click on the script so that it is highlighted, then press |
||
794 | the "Don't Strip Comments" button.</li></p> |
||
795 | |||
796 | <p><li> |
||
797 | When you set Standalone to "Yes" or "Strict", the two Tcl/Tk |
||
798 | Library directories specified on the Libraries page of |
||
799 | xmktclapp.tcl must be compatible with each other and with |
||
800 | the Tcl/Tk C library that you link against. If this isn't |
||
801 | the case, the Tcl/Tk initialization will fail and your |
||
802 | program will not run. </li></p> |
||
803 | </ul> |
||
804 | |||
805 | <a name="addingccode"> |
||
806 | <a name="toc9"><!-- AUTO --> |
||
807 | <h2 align=center> |
||
808 | Adding In Some C Code |
||
809 | </h2> |
||
810 | |||
811 | <p>We've seen how to link Tcl/Tk scripts into your program. Now let's look |
||
812 | at how you can add in some C or C++ code.</p> |
||
813 | |||
814 | <p>As an example, create a C source code file named <b>hw.c</b> and |
||
815 | put in the following text. (Omit the line numbers. They are for |
||
816 | reference only.)</p> |
||
817 | |||
818 | <a name=code1> |
||
819 | <blockquote><pre> |
||
820 | <b>0001 /* A C module */ |
||
821 | 0002 #include <stdio.h> |
||
822 | 0003 #include "hello.h" |
||
823 | 0004 |
||
824 | 0005 int ET_COMMAND_print_hello(ET_TCLARGS){ |
||
825 | 0006 printf("Hello, out there!\n"); |
||
826 | 0007 return TCL_OK; |
||
827 | 0008 } |
||
828 | </b></pre></blockquote> |
||
829 | |||
830 | <p>To compile this C module into your program, bring up xmktclapp.tcl |
||
831 | again and go to the C/C++ Modules page. Click on the Insert button |
||
832 | and select "hw.c" from the menu. The C/C++ Modules pages should |
||
833 | look like figure 4.</p> |
||
834 | |||
835 | <p> |
||
836 | <a name=fig4> |
||
837 | <hr><center><img src="xmta-fig4.jpg" alt="Figure 4"><br> |
||
838 | <b>Figure 4</b></center><br><hr></p> |
||
839 | |||
840 | <p>Next return to the Settings page, press the "Build" button and |
||
841 | exit.</p> |
||
842 | |||
843 | <p>The C code in the hw.c file implements a new Tcl command named |
||
844 | <b>print_hello</b>. Adding the name of the C source code file |
||
845 | to the C/C++ Modules page of xmktclapp.tcl instructs mktclapp to |
||
846 | scan the source code looking for function definitions of the |
||
847 | the form</p> |
||
848 | |||
849 | <blockquote><pre> |
||
850 | <b>int ET_COMMAND_aaaaa(ET_TCLARGS){ |
||
851 | ... |
||
852 | return TCL_OK; |
||
853 | } |
||
854 | </b></pre></blockquote> |
||
855 | |||
856 | <p>where the string "aaaaa" in the function name can be any valid |
||
857 | C identifier. For every such function found, mktclapp will create |
||
858 | a new Tcl command named "aaaaa". And whenever that Tcl command |
||
859 | is invoked from within a script, the function will be called.</p> |
||
860 | |||
861 | <p>This is one of the primary means of communication between the |
||
862 | C/C++ side of your application and the Tcl/Tk scripts. Whenever |
||
863 | you want your Tcl/Tk script to execute some C code, you put the |
||
864 | C code inside a function whose name begins |
||
865 | with "ET_COMMAND_". The C code can then be executed by calling |
||
866 | the Tcl command whose name matches the suffix of the new C function.</p> |
||
867 | |||
868 | <p>In our example, we've created a new Tcl command named "print_hello", |
||
869 | but that command is never being called. Let's modify the "hello.tcl" |
||
870 | script a bit to change that. Edit hello.tcl so that it looks like |
||
871 | this:</p> |
||
872 | |||
873 | <blockquote><pre> |
||
874 | <b>button .b -text Go -command print_hello |
||
875 | button .e -text Quit -command exit |
||
876 | pack .b .e -side left |
||
877 | </b></pre></blockquote> |
||
878 | After making this edit, rerun xmktclapp.tcl and press the "Build" button |
||
879 | again in order to rebuild the application initialization module. |
||
880 | Then recompile everything as follows: |
||
881 | <blockquote><pre> |
||
882 | <b>gcc -o hello2 hw.c hello.c -ltk8.0 -ltcl8.0 -L/usr/X11R6/lib -lX11 -lm -ldl |
||
883 | </pre></b></blockquote> |
||
884 | |||
885 | <p>Notice that we now have to add the "hw.c" file to the compiler command |
||
886 | line. If you have a lot of C source files, it is probably best to |
||
887 | construct a Makefile that compiles each C modules separately then |
||
888 | links them all together in the end. Like this:</p> |
||
889 | |||
890 | <blockquote><pre> |
||
891 | <b>gcc -c hw.c |
||
892 | gcc -c hello.c |
||
893 | gcc -o hello hw.o hello.o -ltk8.0 -ltcl8.0 -L/usr/X11R6/lib -lX11 -lm -ldl |
||
894 | </pre></b></blockquote> |
||
895 | |||
896 | <p>That way, there is less to recompile if you make changes to a subset of |
||
897 | your C modules.</p> |
||
898 | |||
899 | <p>Now run the program. Click on the "Print" button and verify that the |
||
900 | message "Hello, out there!" is printed on standard output. |
||
901 | (Note that "printf" is a no-op on Windows. If you want to try |
||
902 | this example in Windows, you'll need to alter hw.c to write its |
||
903 | message to a file that you explicitly open instead of |
||
904 | writing to standard output.)</p> |
||
905 | |||
906 | <a name="toc10"><!-- AUTO --> |
||
907 | <h2 align=center> |
||
908 | Implementing New Tcl Commands In C |
||
909 | </h2> |
||
910 | |||
911 | <p>If you've never looked at Tcl's C API before, then you need to |
||
912 | take note of the interface details for a function that implements |
||
913 | a Tcl command. |
||
914 | The first thing to notice is that the function must return an |
||
915 | integer result code. |
||
916 | <a name="retcode"> |
||
917 | This result code must be one of the following values:</p> |
||
918 | |||
919 | <ul> |
||
920 | <p><li> <b>TCL_OK</b>. |
||
921 | This means the command completed successfully.</p> |
||
922 | |||
923 | <p><li> <b>TCL_ERROR</b>. |
||
924 | This means that an error occurred while executing the |
||
925 | command. In this case, the result string that is returned is |
||
926 | an error message describing what when wrong.</p> |
||
927 | |||
928 | <p><li> <b>TCL_RETURN</b>. |
||
929 | This means that the interpreter should immediately return |
||
930 | from the Tcl procedure it is currently executing. Returning this |
||
931 | value from a C function is the same as executing the <b>return</b> |
||
932 | command in Tcl.</p> |
||
933 | |||
934 | <p><li> <b>TCL_BREAK</b>. |
||
935 | This means that the interpreter should immediately exit |
||
936 | the inner-most loop that is currently active. This is the same as |
||
937 | executing the <b>break</b> command in Tcl.</p> |
||
938 | |||
939 | <p><li> <b>TCL_CONTINUE</b>. |
||
940 | This means that the interpreter should skip the rest of |
||
941 | the body of the inner-most loop and continue with the next iteration |
||
942 | of that loop. This is the same as the <b>continue</b> command in Tcl.</p> |
||
943 | </ul> |
||
944 | |||
945 | <p>In practice, you rarely ever use any but the first two values, |
||
946 | TCL_OK and TCL_ERROR. |
||
947 | All of these values are really integers. The symbolic |
||
948 | names given here are for C preprocessor macros that evaluate to the |
||
949 | appropriate integer. You should make it a habit to always use the |
||
950 | symbolic name rather than the raw integer value in your code. |
||
951 | The symbolic names are defined in the include file <tcl.h> which |
||
952 | is included by the header file that mktclapp generates. In the |
||
953 | code example <a href="#code1">above</a>, line 0003 includes the |
||
954 | header file that mktclapp generates and hence the <tcl.h> file gets |
||
955 | included and the symbolic names for the return values are defined.</p> |
||
956 | |||
957 | <p>The second thing to notice about C functions that implement Tcl commands |
||
958 | is that they require exactly four parameters, as follows:</p> |
||
959 | |||
960 | <ul> |
||
961 | <li> <b>ClientData clientData</b> |
||
962 | <li> <b>Tcl_Interp *interp</b> |
||
963 | <li> <b>int argc</b> |
||
964 | <li> <b>char **argv</b> |
||
965 | </ul> |
||
966 | |||
967 | <p>The <b>interp</b> parameter is a pointer to the Tcl interpreter. The |
||
968 | <b>argc</b> and <b>argv</b> parameters contain, respectively, the |
||
969 | number of arguments to the Tcl command and the text of each argument. |
||
970 | For Tcl commands created automatically by mktclapp, the <b>clientData</b> |
||
971 | parameter is always NULL.</p> |
||
972 | |||
973 | <p>It can be a chore to type in all four parameters |
||
974 | to every C function that implements a Tcl command. |
||
975 | Every such C function takes exactly the same four parameters. |
||
976 | So as a convenience, mktclapp supplies a macro named <b>ET_TCLARGS</b> |
||
977 | which contains the appropriate parameter definitions. Mktclapp |
||
978 | writes this macro into the header file it generates. In the case of our |
||
979 | example, the file is called <b>hello.h</b>. Notice again that we |
||
980 | include this file on line 0003 of the <a href="#code1">C code above</a> |
||
981 | so we are able to take the shortcut of using the macro.</p> |
||
982 | |||
983 | <p>The third important point about C functions that implement Tcl commands |
||
984 | is how they return their results. By default, a Tcl command will |
||
985 | return an empty string. But you can specify an different result using |
||
986 | one or more of the following Tcl API functions:</p> |
||
987 | |||
988 | <p><ul> |
||
989 | <li> <b>Tcl_SetResult</b> |
||
990 | <li> <b>Tcl_AppendResult</b> |
||
991 | <li> <b>Tcl_AppendElement</b> |
||
992 | <li> <b>Tcl_ResetResult</b> |
||
993 | </ul></p> |
||
994 | |||
995 | <p>The operation of these functions is well documented in the |
||
996 | Tcl manpages, and will not be repeated here.</p> |
||
997 | |||
998 | <p>Mktclapp provides another method for setting the return value of |
||
999 | a Tcl command that is sometimes easier to use than the standard |
||
1000 | Tcl API functions. This is the function named <b>Et_ResultF()</b>. |
||
1001 | The Et_ResultF function works very much like printf() from the |
||
1002 | standard C library. You give it a format string and a variable number |
||
1003 | of additional arguments whose values are substituted into specified |
||
1004 | places of the format string. But while printf() writes its result |
||
1005 | on standard output, Et_ResultF puts its result into the return value |
||
1006 | of the Tcl command.</p> |
||
1007 | |||
1008 | <p>An example will help to illustrate how Et_ResultF() works. Let's |
||
1009 | implement a Tcl command that adds the value of two arguments and |
||
1010 | returns the result. The file that will contain this new command |
||
1011 | will be <b>add.c</b>. The code looks like this:</p> |
||
1012 | |||
1013 | <blockquote><pre> |
||
1014 | <b>0001 /* Add two numbers and return the result */ |
||
1015 | 0002 #include "hello.h" |
||
1016 | 0003 |
||
1017 | 0004 int ET_COMMAND_add(ET_TCLARGS){ |
||
1018 | 0005 if( argc!=3 ){ |
||
1019 | 0006 Et_ResultF(interp, "wrong # args: should be \"%s NUM1 NUM2\"",argv[0]); |
||
1020 | 0007 return TCL_ERROR; |
||
1021 | 0008 } |
||
1022 | 0009 Et_ResultF(interp,"%d", atoi(argv[1]) + atoi(argv[2])); |
||
1023 | 0010 return TCL_OK; |
||
1024 | 0011 } |
||
1025 | </b></pre></blockquote> |
||
1026 | |||
1027 | <p>If you want to test this routine out, add the "add.c" file to the list |
||
1028 | of C/C++ Modules in xmktclapp.tcl, make some changes to "hello.tcl" that will |
||
1029 | call the new "add" Tcl command, and recompile. By now, you should be |
||
1030 | comfortable with doing this kind of thing, so we won't go into the details, |
||
1031 | but will instead focus on describing how the code works.</p> |
||
1032 | |||
1033 | <p>Line 0002 of add.c includes the header file that mktclapp generated for us. |
||
1034 | This header file contains a prototype for the Et_ResultF() function, and it |
||
1035 | includes the <tcl.h> header file so that we can access macros like |
||
1036 | TCL_OK and TCL_ERROR.</p> |
||
1037 | |||
1038 | <p>The implementation of the new "add" Tcl command is on lines 0004 through |
||
1039 | 0011. Lines 0005 through 0008 check to make sure the command is given |
||
1040 | exactly 2 arguments. Notice that we compare the argument count in |
||
1041 | <b>argc</b> to 3 instead of 2. That is because <b>argc</b> contains the |
||
1042 | number of arguments to the command, including the name of the command |
||
1043 | itself. If the number of arguments is not 2, then we will return an |
||
1044 | error condition (line 0007) but first we have to set the return value to |
||
1045 | be an appropriate error message. The call to Et_ResultF() on line |
||
1046 | 0006 does this. The first argument is a pointer to the Tcl interpreter. |
||
1047 | The second argument is a format string. Additional arguments are added |
||
1048 | as required by the format string.</p> |
||
1049 | |||
1050 | <p>If the number of arguments is correct, we fall through to lines 0009 and |
||
1051 | 0010. The return value is set by the Et_ResultF() call in line 0009. |
||
1052 | Since the command succeeded in this case, we return TCL_OK on line 0010.</p> |
||
1053 | |||
1054 | <a name="toc11"><!-- AUTO --> |
||
1055 | <h2 align=center> |
||
1056 | Using The Tcl_Obj Interface For Tcl New Commands |
||
1057 | </h2> |
||
1058 | |||
1059 | <p>Beginning with version 8.0, Tcl/Tk supports a new interface to commands |
||
1060 | that is faster in some circumstances. We won't go into the details |
||
1061 | of how the new interface works. (You can get that information from |
||
1062 | the Tcl/Tk documentation) |
||
1063 | But we would like to point out that mktclapp |
||
1064 | can create Tcl commands that use the new Tcl_Obj interface. |
||
1065 | All you have to do is preface the name of your function with |
||
1066 | ET_OBJCOMMAND instead of ET_COMMAND, and use ET_OBJARGS instead |
||
1067 | of ET_TCLARGS as the argument. So instead of</p> |
||
1068 | |||
1069 | <blockquote><pre> |
||
1070 | <b>int ET_COMMAND_print_hello(ET_TCLARGS){ |
||
1071 | printf("Hello, out there!\n"); |
||
1072 | return TCL_OK; |
||
1073 | } |
||
1074 | </b></pre></blockquote> |
||
1075 | |||
1076 | <p>write code like this:</p> |
||
1077 | |||
1078 | <blockquote><pre> |
||
1079 | <b>int ET_OBJCOMMAND_print_hello(ET_OBJARGS){ |
||
1080 | printf("Hello, out there!\n"); |
||
1081 | return TCL_OK; |
||
1082 | } |
||
1083 | </b></pre></blockquote> |
||
1084 | |||
1085 | <p>The new Tcl_Obj command interface is a little faster, but |
||
1086 | not that much faster. The big advantage to the Tcl_Obj interface |
||
1087 | is that it cleanly handles binary data. The main disadvantage is |
||
1088 | that the Tcl_Obj interface is harder to use. I recommend using |
||
1089 | the Tcl_Obj interface only for commands that deal with binary data |
||
1090 | and continuing to use the older string interface for all other |
||
1091 | commands.</p> |
||
1092 | |||
1093 | <a name="toc12"><!-- AUTO --> |
||
1094 | <h2 align=center> |
||
1095 | Using Tcl Namespaces |
||
1096 | </h2> |
||
1097 | |||
1098 | <p>The names of C functions may contain only alphanumeric and |
||
1099 | underscore characters. But in order to generate a Tcl command in |
||
1100 | a namespace, you need to embed colons in the name. |
||
1101 | To accomodate this, whenever mktclapp sees an ET_COMMAND_ function |
||
1102 | name that contains two underscores in a row, it changes both |
||
1103 | underscores to colons in the corresponding Tcl command. |
||
1104 | This allows you to create new Tcl commands in a namespace.</p> |
||
1105 | |||
1106 | <p>For example, suppose you wanted to created a new Tcl command |
||
1107 | named <b>ns1::func1</b>. Your C code would look something like |
||
1108 | this:</p> |
||
1109 | |||
1110 | <blockquote><pre> |
||
1111 | <b>int ET_COMMAND_ns1__func1(ET_TCLARGS){ |
||
1112 | ... |
||
1113 | return TCL_OK; |
||
1114 | } |
||
1115 | </b></pre></blockquote> |
||
1116 | |||
1117 | <p>The two underscores between "ns1" and "func1" in the C function |
||
1118 | name will be converted into colons for the corresponding Tcl command, |
||
1119 | thus giving the desired result.</p> |
||
1120 | |||
1121 | <a name="toc13"><!-- AUTO --> |
||
1122 | <h2 align=center> |
||
1123 | Executing Tcl/Tk Commands From C Code |
||
1124 | </h2> |
||
1125 | |||
1126 | <p>The previous sections described how you can transfer control from |
||
1127 | Tcl/Tk over to C. Now let's see how to go the other direction: how |
||
1128 | to execute Tcl/Tk code from within a C function.</p> |
||
1129 | |||
1130 | <p>The Tcl API provides several functions that will cause a string |
||
1131 | to be interpreted as a Tcl/Tk script. We find:</p> |
||
1132 | |||
1133 | <p><ul> |
||
1134 | <li> <b>Tcl_Eval</b> |
||
1135 | <li> <b>Tcl_VarEval</b> |
||
1136 | <li> <b>Tcl_GlobalEval</b> |
||
1137 | <li> <b>Tcl_EvalFile</b> |
||
1138 | </ul></p> |
||
1139 | |||
1140 | <p>All of these functions are fully documented by the Tcl manpages, |
||
1141 | so we won't go into a lot of detail here about how they work. But |
||
1142 | a quick example won't hurt.</p> |
||
1143 | |||
1144 | <p>Consider the Tcl_Eval() function. This function takes two argument. |
||
1145 | <ol> |
||
1146 | <li> A pointer to a Tcl interpreter, and |
||
1147 | <li> A string that contains Tcl code to be executed. |
||
1148 | </ol> |
||
1149 | The Tcl_Eval() function returns an integer which is one of the |
||
1150 | return codes (TCL_OK, TCL_ERROR, etc.) described |
||
1151 | <a href="#retcode">above.</a>. The way Tcl_Eval() works is |
||
1152 | this: it breaks up the string you give it into one or more |
||
1153 | Tcl commands. It parses each command up into a command name |
||
1154 | and its arguments. It then calls a C function to execute that |
||
1155 | command. If the C function returns TCL_OK, then Tcl_Eval() procedes |
||
1156 | to execute the next command in the sequence. And so forth until |
||
1157 | all commands have been executed. But if any implementation |
||
1158 | function returns something other than TCL_OK, the commands |
||
1159 | that follow are skipped and Tcl_Eval() returns immediately. |
||
1160 | The return code of Tcl_Eval() is always the return code of |
||
1161 | the last command executed. The result of the last Tcl |
||
1162 | command executed (or the error message if TCL_ERROR is returned) |
||
1163 | is stored in <b>interp->result</b> where <b>interp</b> is the |
||
1164 | pointer to the Tcl interpreter.</p> |
||
1165 | |||
1166 | <p>We will illustrate the use of Tcl_Eval() by implementing a Tcl |
||
1167 | command in C that invokes another Tcl command as part of its |
||
1168 | execution. Call our example command <b>factor</b>. It has |
||
1169 | two argument.</p> |
||
1170 | |||
1171 | <blockquote> |
||
1172 | <b>factor</b> <i>NUMBER PROC</i> |
||
1173 | </blockquote> |
||
1174 | |||
1175 | <p>The factor command will compute all factors of <i>NUMBER</i> and |
||
1176 | for each factor <i>F</i> it will invoke the Tcl procedure |
||
1177 | named <i>PROC</i> with a single argument <i>F</i>. |
||
1178 | So, for example, if we execute the command</p> |
||
1179 | |||
1180 | <blockquote><pre> |
||
1181 | <b>factor 12 puts</b> |
||
1182 | </pre></blockquote> |
||
1183 | |||
1184 | <p>the implementation of factor will invoke the <b>puts</b> Tcl command |
||
1185 | six times, once each with the arguments "1", "2", "3", "4", "6" and "12".</p> |
||
1186 | |||
1187 | <a name=code2> |
||
1188 | <blockquote><pre> |
||
1189 | <b>0001 /* Implementation of the factor command */ |
||
1190 | 0002 #include "hello.h" |
||
1191 | 0003 |
||
1192 | 0004 int ET_COMMAND_factor(ET_TCLARGS){ |
||
1193 | 0005 int i, n; |
||
1194 | 0006 char *zCmd; |
||
1195 | 0007 if( argc!=3 ){ |
||
1196 | 0008 Et_ResultF(interp,"wrong # args: should be \"%s NUM PROC\"",argv[0]); |
||
1197 | 0009 return TCL_ERROR; |
||
1198 | 0010 } |
||
1199 | 0011 zCmd = Tcl_Alloc( strlen(argv[2]) + 100 ); |
||
1200 | 0012 if( zCmd==0 ){ |
||
1201 | 0013 Et_ResultF(interp,"out of memory"); |
||
1202 | 0014 return TCL_ERROR; |
||
1203 | 0015 } |
||
1204 | 0016 n = atoi(argv[1]); |
||
1205 | 0017 for(i=1; i<=n; i++){ |
||
1206 | 0018 if( (n/i)*i!=n ) continue; |
||
1207 | 0019 sprintf(zCmd, "%s %d", argv[2], i); |
||
1208 | 0020 if( Tcl_Eval(interp, zCmd)!=TCL_OK ){ |
||
1209 | 0021 Tcl_Free(zCmd); |
||
1210 | 0022 return TCL_ERROR; |
||
1211 | 0023 } |
||
1212 | 0024 } |
||
1213 | 0025 Tcl_Free(zCmd); |
||
1214 | 0026 return TCL_OK; |
||
1215 | 0027 } |
||
1216 | </b></pre></blockquote> |
||
1217 | |||
1218 | <p>Let's have a look at the code. We begin, as always, by including |
||
1219 | the necessary header files on line 0002. The rest of the code is |
||
1220 | an implementation of the "factor" Tcl command on lines 0004 through |
||
1221 | 0027. The first thing the factor command does is make sure it was |
||
1222 | called with exactly two arguments. If not, an error message is |
||
1223 | created and the function returns with an error. Next, on line 0011, |
||
1224 | we allocate space to hold the Tcl commands that we will execute for |
||
1225 | each factor. We have to dynamically allocate this space, since we |
||
1226 | cannot know in advance how big the name of the callback procedure |
||
1227 | will be. The Tcl library imposes no length limitations on |
||
1228 | strings and you should strive to do the same with the |
||
1229 | code you add.</p> |
||
1230 | |||
1231 | <p>The loop from lines 0017 to 0024 iterates over all values between |
||
1232 | 1 and the target number, and line 0018 discards those that are |
||
1233 | not factors. (This is not a very efficient way to compute the |
||
1234 | factors of a number, by the way. But a better |
||
1235 | algorithm would require considerably more code which would obsure |
||
1236 | the point of the example.) On line 0019, we construct the text of |
||
1237 | a Tcl command to execute for the single factor in the variable <b>i</b>. |
||
1238 | Then the command is executed using Tcl_Eval on line 0020. Notice |
||
1239 | that we test to make sure Tcl_Eval returns TCL_OK, and we abort |
||
1240 | immediately if it does not. Finally, after the loop exits, we |
||
1241 | free the dynamically allocated memory and return successfully.</p> |
||
1242 | |||
1243 | <p>You will notice in this example that a considerable amount of code |
||
1244 | was used to dynamically allocate space for the Tcl command that |
||
1245 | was to be executed. A lot of this extra could can be avoided by |
||
1246 | using a special function provided by mktclapp that does the same |
||
1247 | work as Tcl_Eval, but with more flexible calling parameters. |
||
1248 | The <b>Et_EvalF</b> function executes Tcl code like Tcl_Eval, but |
||
1249 | it constructs the Tcl code using printf-style arguments. Take |
||
1250 | a look at the same "factor" command coded using Et_EvalF instead |
||
1251 | of Tcl_Eval:</p> |
||
1252 | |||
1253 | <a name=code3> |
||
1254 | <blockquote><pre> |
||
1255 | <b>0001 /* Implementation of the factor command */ |
||
1256 | 0002 #include "hello.h" |
||
1257 | 0003 |
||
1258 | 0004 int ET_COMMAND_factor(ET_TCLARGS){ |
||
1259 | 0005 int i, n; |
||
1260 | 0006 if( argc!=3 ){ |
||
1261 | 0007 Et_ResultF(interp,"wrong # args: should be \"%s NUM PROC\"",argv[0]); |
||
1262 | 0008 return TCL_ERROR; |
||
1263 | 0009 } |
||
1264 | 0010 n = atoi(argv[1]); |
||
1265 | 0011 for(i=1; i<=n; i++){ |
||
1266 | 0012 if( (n/i)*i!=n ) continue; |
||
1267 | 0013 if( Et_EvalF(interp, "%s %d", argv[2], i)!=TCL_OK ){ |
||
1268 | 0014 return TCL_ERROR; |
||
1269 | 0015 } |
||
1270 | 0016 } |
||
1271 | 0017 return TCL_OK; |
||
1272 | 0018 } |
||
1273 | </b></pre></blockquote> |
||
1274 | |||
1275 | <p>What a difference! The use of Et_EvalF reduced the size of the |
||
1276 | factor command by one third. It also helped reduce |
||
1277 | the danger of memory leaks that would result if (for example) line |
||
1278 | 0021 were accidently omitted from the Tcl_Eval implementation. Et_EvalF |
||
1279 | executes Tcl code in the same way as Tcl_Eval, but with a much more |
||
1280 | convenient interface. Experience with Et_EvalF shows |
||
1281 | it can greatly decrease coding effort and the number of coding errors.</p> |
||
1282 | |||
1283 | <a name="toc14"><!-- AUTO --> |
||
1284 | <h2 align=center> |
||
1285 | Other Functions Provided By Mktclapp |
||
1286 | </h2> |
||
1287 | |||
1288 | <p>We've already seen two functions that are provided by mktclapp that |
||
1289 | do not appear in the standard Tcl library: <b>Et_ResultF</b> and |
||
1290 | <b>Et_EvalF</b>. There are others which haven't been mentioned.</p> |
||
1291 | |||
1292 | <ul> |
||
1293 | <li> <p><b>Et_GlobalEvalF</b>. |
||
1294 | This works just like Et_EvalF, except that all variables are evaluated |
||
1295 | in the global context. Et_GlobalEvalF is to Et_EvalF what |
||
1296 | Tcl_GlobalEval is to Tcl_Eval.</p> |
||
1297 | |||
1298 | <li> <p><b>Et_DStringAppendF</b>. |
||
1299 | This routine works like Tcl_DStringAppend with the addition that the |
||
1300 | string to be appended is constructed using printf-style arguments. |
||
1301 | For more information on what Tcl_DStringAppend does, refer to the |
||
1302 | Tcl manpages.</p> |
||
1303 | |||
1304 | <li> <p><b>mprintf</b>. |
||
1305 | This helpful routine works like the <b>sprintf</b> routine from the |
||
1306 | standard C library. Except, instead of taking the buffer into which |
||
1307 | the results are written as its first parameter, the mprintf routine |
||
1308 | mallocs for sufficient space to hold the result and returns a pointer |
||
1309 | to the malloced space. The calling function is responsible for |
||
1310 | freeing the memory to avoid a memory leak. |
||
1311 | This implementation of mprintf actually |
||
1312 | uses Tcl_Alloc to obtain its memory, so the memory really should |
||
1313 | be freed by calling Tcl_Free. Tcl_Alloc and Tcl_Free are fully |
||
1314 | documented in the Tcl manpages.</p> |
||
1315 | |||
1316 | <li> <p><b>vmprintf</b>. |
||
1317 | This is a varargs version of mprintf().</p> |
||
1318 | </ul> |
||
1319 | |||
1320 | <p>In addition to these function, mktclapp also provides a global variable |
||
1321 | named <b>Et_Interp</b> which is a pointer to the main Tcl interpreter |
||
1322 | for your application. A pointer to an interpreter is required as the |
||
1323 | first parameter to many routines in the Tcl library, as well as to |
||
1324 | mktclapp routines like Et_EvalF. If your program only uses a single |
||
1325 | Tcl interpreter (most programs fulfill this constraint) then you can |
||
1326 | use the global variable <b>Et_Interp</b> rather than pass a |
||
1327 | pointer to the interpreter as a parameter to every C subroutine you |
||
1328 | write.</p> |
||
1329 | |||
1330 | <a name="toc15"><!-- AUTO --> |
||
1331 | <h2 align=center> |
||
1332 | The <tt>%q</tt> Format Field |
||
1333 | </h2> |
||
1334 | |||
1335 | <p>There is subtle danger in using the <b>%s</b> format field within |
||
1336 | the Et_EvalF function and its kin. Consider what would happen if |
||
1337 | the string that is inserted in place of the %s contains characters |
||
1338 | with special meaning to Tcl.</p> |
||
1339 | |||
1340 | <p>For example, suppose you have a Tcl/Tk command named <b>PopupMessage</b> |
||
1341 | that takes a text string as its only argument and displays that string |
||
1342 | as a message in a popup dialog box. If the your C code frequently |
||
1343 | needs to pop up messages, you might consider writing a C subroutine |
||
1344 | to do the work for you, like this:</p> |
||
1345 | |||
1346 | <blockquote><pre> |
||
1347 | <b>void DoPopup(const char *zMsg){ |
||
1348 | Et_EvalF(Et_Interp,"PopupMessage \"%s\"", zMsg); |
||
1349 | } |
||
1350 | </b></pre></blockquote> |
||
1351 | |||
1352 | <p>If you invoke this subroutine with a message that read |
||
1353 | "Hello, World!", then the Et_EvalF function would construct |
||
1354 | a Tcl command that said</p> |
||
1355 | |||
1356 | <blockquote><pre> |
||
1357 | <b>PopupMessage "Hello, World!" |
||
1358 | </b></pre></blockquote> |
||
1359 | |||
1360 | <p>which would do what you intend. But now consider what would happen |
||
1361 | if you invoke DoPopup with a string like this:</p> |
||
1362 | |||
1363 | <blockquote><pre> |
||
1364 | <b>DoPopup("Missing \";\" on line 11"); |
||
1365 | </b></pre></blockquote> |
||
1366 | |||
1367 | <p>In this case, Et_EvalF would construct its Tcl command to read |
||
1368 | as follows:</p> |
||
1369 | |||
1370 | <blockquote><pre> |
||
1371 | <b>PopupMessage "Missing ";" on line 11" |
||
1372 | </b></pre></blockquote> |
||
1373 | |||
1374 | <p>And this command will not work. The Tcl interpreter will break |
||
1375 | this string into two commands. The first command will invoke |
||
1376 | the PopupMessage procedure with the string "Missing ", and the |
||
1377 | second command will consist of the text " on line 11". Certainly |
||
1378 | not what you intended.</p> |
||
1379 | |||
1380 | <p>You might try to work around this problem by using curly braces |
||
1381 | rather than double quotes to enclose the argument to PopupMessage, |
||
1382 | like this:</p> |
||
1383 | |||
1384 | <blockquote><pre> |
||
1385 | <b>void DoPopup(const char *zMsg){ |
||
1386 | Et_EvalF(Et_Interp,"PopupMessage {%s}", zMsg); |
||
1387 | } |
||
1388 | </b></pre></blockquote> |
||
1389 | |||
1390 | <p>This changes the problem, but does not make it go away. Now the |
||
1391 | function fails when the input string is something like</p> |
||
1392 | |||
1393 | <blockquote><pre> |
||
1394 | <b>DoPopup("Missing \"}\" on line 11"); |
||
1395 | </b></pre></blockquote> |
||
1396 | |||
1397 | <p>The solution to this conundrum is to never use the %s format |
||
1398 | directive when the string to be inserted can possibly contain |
||
1399 | characters that are special to Tcl. Et_EvalF provides an |
||
1400 | alternative formatting directived called <b>%q</b> (the "q" stands |
||
1401 | for "quote") that works just like %s except that it inserts a |
||
1402 | backslash character before every character in the inserted string |
||
1403 | that has special meaning to Tcl. So what we have to do is |
||
1404 | change the DoPopup function to read as follows:</p> |
||
1405 | |||
1406 | <blockquote><pre> |
||
1407 | <b>void DoPopup(const char *zMsg){ |
||
1408 | Et_EvalF(Et_Interp,"PopupMessage \"%q\"", zMsg); |
||
1409 | } |
||
1410 | </b></pre></blockquote> |
||
1411 | |||
1412 | <p>Now when the DoPopup function is called with an input string |
||
1413 | that contains special characters, like this:</p> |
||
1414 | |||
1415 | <blockquote><pre> |
||
1416 | <b>DoPopup("Missing \";\" on line 11"); |
||
1417 | </b></pre></blockquote> |
||
1418 | |||
1419 | <p>the Tcl command that Et_EvalF constructs reads as follows:</p> |
||
1420 | |||
1421 | <blockquote><pre> |
||
1422 | <b>PopupMessage "Missing \";\" on line 11" |
||
1423 | </b></pre></blockquote> |
||
1424 | |||
1425 | <p>The Et_EvalF function has inserted a backslash before each double-quote |
||
1426 | character in the string. |
||
1427 | This Tcl command gives the intended result.</p> |
||
1428 | |||
1429 | <p>The %q formatting directive is available in all of the extension |
||
1430 | functions provided by mktclapp: Et_EvalF, Et_ResultF, Et_GlobalEvalF, |
||
1431 | Et_DStringAppendF, mprintf, and vmprintf. |
||
1432 | Some general guidelines on how and when to use %q instead of %s |
||
1433 | follow:</p> |
||
1434 | |||
1435 | <ul> |
||
1436 | <li> <p>Always use %q for strings whose content can be influenced by |
||
1437 | the user or by input data. To do otherwise will open security |
||
1438 | holes in your program.</p> |
||
1439 | |||
1440 | <li> <p>Use %q rather than %s for any string that might contain characters |
||
1441 | that are special to Tcl.</p> |
||
1442 | |||
1443 | <li> <p>The %q formatter does not escape spaces, so it is best to put you |
||
1444 | %q inside of double-quotes.</p> |
||
1445 | |||
1446 | <li> <p>%q is designed for use within double-quotes, not curly braces. |
||
1447 | Use the %q like this: <b>\"%q\"</b> Not like this: <b>{%q}</b>.</p> |
||
1448 | |||
1449 | </ul> |
||
1450 | |||
1451 | <a name="toc16"><!-- AUTO --> |
||
1452 | <h2 align=center> |
||
1453 | Putting A <tt>main()</tt> In Your C Code |
||
1454 | </h2> |
||
1455 | |||
1456 | <p>Every C program requires a <b>main()</b> function. If you don't |
||
1457 | supply one in the C code you link with mktclapp, then mktclapp will |
||
1458 | put its own main() in the application initialization module that |
||
1459 | it builds. |
||
1460 | This is the recommended practice. |
||
1461 | But for special circumstances, you may want to provide your own |
||
1462 | main() function. |
||
1463 | That's fine. The code that mktclapp generates will still work. |
||
1464 | But you need to remember two things:</p> |
||
1465 | |||
1466 | <ol> |
||
1467 | <li> <p>At some point, you need to call the routine <b>Et_Init()</b> |
||
1468 | and pass it the value of <b>argc</b> and <b>argv</b> from |
||
1469 | main(), in order to initialize the Tcl/Tk interpreter. |
||
1470 | Your code might look something like this:</p> |
||
1471 | |||
1472 | <blockquote><pre> |
||
1473 | <b>int main(int argc, argv){ |
||
1474 | /* Your code goes here */ |
||
1475 | Et_Init(argc, argv); |
||
1476 | return 0; |
||
1477 | } |
||
1478 | </b></pre></blockquote> |
||
1479 | |||
1480 | <p>The Et_Init() function doesn't return until the Tcl/Tk interpreter |
||
1481 | has been terminated. For a GUI application, this means it |
||
1482 | never returns. So don't put any code after your call to |
||
1483 | Et_Init() that you want to execute.</p> |
||
1484 | |||
1485 | <li> <p>The "Fork Into Background" feature that can be optionally |
||
1486 | enabled using mktclapp will not work if you supply your own |
||
1487 | main() routine.</p> |
||
1488 | </ol> |
||
1489 | |||
1490 | <a name="appinit"> |
||
1491 | <a name="toc17"><!-- AUTO --> |
||
1492 | <h2 align=center> |
||
1493 | C Functions For Application Initialization |
||
1494 | </h2> |
||
1495 | |||
1496 | <p>If the C code that you link using mktclapp contains a function |
||
1497 | named <b>Et_AppInit()</b>, then that function will be called right |
||
1498 | after the Tcl/Tk interpreter has been initialized. So if you need |
||
1499 | to do any additional setup to the Tcl/Tk interpreter, such |
||
1500 | as loading an extension, the Et_AppInit() function |
||
1501 | is a great place to do it.</p> |
||
1502 | |||
1503 | <p>For example, suppose you need to include the BLT extensions in |
||
1504 | your application. You could simply add C code that looks |
||
1505 | like the following:</p> |
||
1506 | |||
1507 | <blockquote><pre> |
||
1508 | <b>0001 #include "appinit.h" |
||
1509 | 0002 #include <blt.h> |
||
1510 | 0003 |
||
1511 | 0004 int Et_AppInit(Tcl_Interp *interp){ |
||
1512 | 0005 Blt_Init(interp); |
||
1513 | 0006 return TCL_OK; |
||
1514 | 0007 } |
||
1515 | </b></pre></blockquote> |
||
1516 | |||
1517 | <p>Let's look more closely at this code. Line 0001 sources the header |
||
1518 | file that mktclapp generates. (Depending on what name you choose for |
||
1519 | the generated files, you might need to alter this line.) We also |
||
1520 | need to include the header file for BLT on line 0002. The implementation |
||
1521 | of Et_AppInit begins on line 0004. Notice that it takes a single parameter |
||
1522 | which is a pointer to the Tcl interpreter and returns an integer. |
||
1523 | On Line 0005, the BLT extension is initialized. Finally on line |
||
1524 | 0006 we return from Et_AppInit(). Notice the Et_AppInit() should |
||
1525 | return TCL_OK if there were no errors.</p> |
||
1526 | |||
1527 | <p>Other Tcl/Tk extensions are initialized in a similar way. To initialize |
||
1528 | an extension named <b>ABC</b> you typically invoke a function named |
||
1529 | <b>Abc_Init(interp)</b> inside of Et_AppInit(). Refer to the |
||
1530 | documentation for the particular extension you want to initialize for |
||
1531 | details. Some Tcl/Tk extensions (such as TclX) include additional |
||
1532 | Tcl Script files that must be loaded with the application in order |
||
1533 | for the application to run standalone. For these extensions, you |
||
1534 | will need to list those script files on the "Tcl Scripts" page of |
||
1535 | xmktclapp.tcl so that they will be compiled into the initialization module. |
||
1536 | Or (beginning with version 3.0 of mktclapp) you can list the directories |
||
1537 | containing the extra scripts in the "Other Libraries" entry field on |
||
1538 | the "Libraries" page of xmktclapp.tcl. I'm told that you might also |
||
1539 | need to make some change to the "auto_path" variable in order to |
||
1540 | get Tix and IncrTcl to work right. But the only major extension I ever |
||
1541 | use is BLT, so I can not say from personal experience.</p> |
||
1542 | |||
1543 | <p>The Et_AppInit() function is also a convenient place in which to |
||
1544 | make calls the the <b>Tcl_LinkVar</b> function in order to create |
||
1545 | a link between Tcl variables and C variables. Refer to the documentation |
||
1546 | of the Tcl_LinkVar() function for additional information.</p> |
||
1547 | |||
1548 | <p>In addition to Et_AppInit(), the code generated by mktclapp will |
||
1549 | also call a function named <b>Et_PreInit()</b> if it is present. |
||
1550 | The difference between Et_AppInit() and Et_PreInit() is this: |
||
1551 | Et_PreInit() is called before the core Tcl/Tk modules are initialized, |
||
1552 | but Et_AppInit() is called afterwards. It is very unusual to need |
||
1553 | to use Et_PreInit(). If you do not clearly understand the difference, |
||
1554 | it is safer to stick with Et_AppInit().</p> |
||
1555 | |||
1556 | <a name="toc17a"><!-- AUTO --> |
||
1557 | <h2 align=center> |
||
1558 | Bundling Binary Data Files With The Executable |
||
1559 | </h2> |
||
1560 | |||
1561 | <p>The principal magic of mktclapp is that it compiles your Tcl scripts |
||
1562 | into the executable as static character strings. Beginning with version |
||
1563 | 3.8 of mktclapp, you can do the same thing with binary data files, such |
||
1564 | as GIFs. Just add the name of each data file you want to insert on the |
||
1565 | Data Files page of the application wizard and mktclapp will add the contents |
||
1566 | of each file to the executable as an array of characters. You can access |
||
1567 | the data file using the normal file access commands of Tcl.</p> |
||
1568 | |||
1569 | <p>As an example, suppose you have the following Tcl/Tk code |
||
1570 | in a file named <b>ckmail.tcl</b>:</p> |
||
1571 | |||
1572 | <blockquote><pre> |
||
1573 | <b>image create photo mailicon -file mailicon.gif |
||
1574 | button .b -image mailicon -command {exec checkmail &} |
||
1575 | pack .b</b> |
||
1576 | </pre></blockquote> |
||
1577 | |||
1578 | <p>This program shows a single button containing a GIF icon where the |
||
1579 | GIF is loaded from a separate file <b>mailicon.gif</b>. To turn this |
||
1580 | script into a standalone program using mktclapp, bring up xmktclapp.tcl |
||
1581 | and enter the ckmail.tcl script in two places on the Tcl Scripts page. |
||
1582 | Then go over to the Data Files pages of xmktclapp.tcl and enter |
||
1583 | the name of the GIF icon. Like this:</p> |
||
1584 | |||
1585 | <p><center> |
||
1586 | <img src="xmta-fig6.jpg" alt="unnumbered figure"> |
||
1587 | </center><p> |
||
1588 | |||
1589 | <p>Then select File/Build from the menu and exit xmktclapp.tcl. |
||
1590 | Mktclapp will include both |
||
1591 | the Tcl script ckmail.tcl and the image mailicon.gif in the |
||
1592 | generated application initialization file (<b>ckmail.c</b>). |
||
1593 | All you have to do now is compile that file.</p> |
||
1594 | |||
1595 | <blockquote><pre> |
||
1596 | <b>gcc -o ckmail ckmail.c -ltk8.0 -L/usr/X11R6/lib -lX11 -ltcl8.0 -lm -ldl</b> |
||
1597 | </pre></blockquote> |
||
1598 | |||
1599 | <p>The ability to access data files as if they were normal files on |
||
1600 | the disk depends on certain API functions that appeared in Tcl beginning |
||
1601 | with release 8.0.3. So this feature will not work if you are using an |
||
1602 | earlier version of Tcl.</p> |
||
1603 | |||
1604 | <a name="toc17b"><!-- AUTO --> |
||
1605 | <h2 align=center> |
||
1606 | Building Tcl Extensions Using Mktclapp |
||
1607 | </h2> |
||
1608 | |||
1609 | <p>Another new feature to mktclapp as of version 3.8 is the ability to |
||
1610 | generate an application initialization file that works as a Tcl extension |
||
1611 | rather than as a standalone program. To build a Tcl extension, enter |
||
1612 | the names of C/C++ modules, Tcl scripts, and Data Files into xmktclapp.tcl |
||
1613 | as you normally would. But on the Settings page select <b>Extension</b> |
||
1614 | for the Application Mode. Then select File/Build from the menu. |
||
1615 | Assuming the name of the generated application initialization file is |
||
1616 | <b>hello.c</b>, you can compile the extension for Unix as follows:</p> |
||
1617 | |||
1618 | <blockquote><pre> |
||
1619 | <b>gcc -shared -o hello.so hello.c -ltk -L/usr/X11R6/lib -lX11 -ltcl -lm -ldl |
||
1620 | </b></pre></blockquote> |
||
1621 | |||
1622 | <p>For Windows, the compilation process is more involved. These are |
||
1623 | the steps:</p> |
||
1624 | |||
1625 | <blockquote><pre> |
||
1626 | <b>gcc -c hello.c |
||
1627 | echo EXPORTS >hello.def |
||
1628 | echo Hello_Init >>hello.def |
||
1629 | dllwrap --def hello.def -v --export-all -dllname hello.dll \ |
||
1630 | hello.o -ltk -ltcl</b> |
||
1631 | </pre></blockquote> |
||
1632 | |||
1633 | <p>The above will generate an extension that can be loaded only by |
||
1634 | the <b>cygwish</b> version of wish that comes with Cygwin. If you |
||
1635 | want to make the extension usable by any version of wish, you have |
||
1636 | to enable stubs support. Do so by adding the options |
||
1637 | "-DUSE_TCL_STUBS" and "-mno-cygwin" to the compiler command line and |
||
1638 | link against a compatible Tcl stubs library. You'll have to build a |
||
1639 | version of the |
||
1640 | stubs libraries that is compatible with cygwin in order for this |
||
1641 | to work. The sources to this library are in files named <b>tclStubLib.c</b> |
||
1642 | and <b>tkStubLib.c</b> in the Tcl/Tk source tree.</p> |
||
1643 | |||
1644 | <p>The name of the initialization function within the generated extension |
||
1645 | is based on the name of the initialization file. In the example above, |
||
1646 | the initialization file was named <b>hello.c</b> so the initialization |
||
1647 | function for the extension will be named <b>Hello_Init</b>. The initialization |
||
1648 | function name is formed by taking the root name of the initialization file, |
||
1649 | making the first letter upper case and all other letters lower case, and |
||
1650 | appending "_Init".</p> |
||
1651 | |||
1652 | <p>You can compile Tcl scripts and Data Files into your extension just |
||
1653 | like you can with standalone applications. If your extension contains |
||
1654 | an Et_AppInit() function, that function will be executed as soon as the |
||
1655 | extension is loaded. If you specify a Startup Script, the script will |
||
1656 | be executed when the extension is loaded, right after the Et_AppInit() |
||
1657 | function has run.</p> |
||
1658 | |||
1659 | <a name="toc18"><!-- AUTO --> |
||
1660 | <h2 align=center> |
||
1661 | Running Mktclapp From The Command Line<br> |
||
1662 | Or In A Makefile |
||
1663 | </h2> |
||
1664 | |||
1665 | <p>The xmktclapp.tcl GUI really just collects data. The |
||
1666 | mktclapp command-line program is what does all the work. |
||
1667 | So if you want to fully understand mktclapp, you have to |
||
1668 | understand what the command-line program does.</p> |
||
1669 | |||
1670 | <p>The mktclapp command line program is what generates both |
||
1671 | the application initialization code and the corresponding |
||
1672 | header file. Mktclapp is controlled completely by command-line |
||
1673 | arguments. Its output appears on standard output.</p> |
||
1674 | |||
1675 | <p>To generate a header file, use the following command:</p> |
||
1676 | |||
1677 | <blockquote><pre> |
||
1678 | <b>mktclapp -header >appinit.h</b> |
||
1679 | </pre></blockquote> |
||
1680 | |||
1681 | <p>If you want to generate an application initialization file |
||
1682 | for <b>hello.tcl</b>, then do this:</p> |
||
1683 | |||
1684 | <blockquote><pre> |
||
1685 | <b>mktclapp hello.tcl -main-script hello.tcl >appinit.c</b> |
||
1686 | </pre></blockquote> |
||
1687 | |||
1688 | <p>Everything is controlled by command-line options. But it |
||
1689 | doesn't take a large project for the number of command-line |
||
1690 | options to become excessive. So mktclapp allows you to specify |
||
1691 | the name of a file which is read to extract additional |
||
1692 | command-line options. In fact, the <b>.mta</b> configuration |
||
1693 | files that xmktclapp.tcl generates are just such files.</p> |
||
1694 | |||
1695 | <p>Suppose, for example, that you use xmktclapp.tcl to generate |
||
1696 | a configuration file named <b>hello.mta</b>. Then to use |
||
1697 | this configuration file to generate an application initialization |
||
1698 | file, you just type:</p> |
||
1699 | |||
1700 | <blockquote><pre> |
||
1701 | <b>mktclapp -f hello.mta >hello.c</b> |
||
1702 | </pre></blockquote> |
||
1703 | |||
1704 | <p>In fact, this is all xmktclapp.tcl does when you press the |
||
1705 | "Build" button. (Grep for "exec mktclapp" on the xmktclapp.tcl |
||
1706 | source code and you will see.) |
||
1707 | If you look at the content of a configuration file, you will see |
||
1708 | that it consists of comments and command-line options for |
||
1709 | mktclapp. Go ahead. Look at an <b>.mta</b> file now. The |
||
1710 | contents are instructive.</p> |
||
1711 | |||
1712 | <p>So one easy way to add mktclapp to a Makefile is to generate |
||
1713 | the configuration file (the <b>.mta</b> file) using xmktclapp.tcl, |
||
1714 | then add a rule like this to your Makefile:</p> |
||
1715 | |||
1716 | <blockquote><pre> |
||
1717 | <b>hello.c: hello.mta |
||
1718 | mktclapp -f hello.mta >hello.c</b> |
||
1719 | </pre></blockquote> |
||
1720 | |||
1721 | <p>That's all I'm going to say about running mktclapp from the |
||
1722 | command line. |
||
1723 | If you want to know more about the command-line options available |
||
1724 | with mktclapp and how they work, you should look at the output |
||
1725 | of</p> |
||
1726 | |||
1727 | <blockquote><pre> |
||
1728 | <b>mktclapp -help</b> |
||
1729 | </pre></blockquote> |
||
1730 | |||
1731 | <p>and study some of the configuration files that xmktclapp generates. |
||
1732 | It isn't difficult and you should have no trouble figuring it |
||
1733 | out given the above hints.</p> |
||
1734 | |||
1735 | <a name="toc18b"><!-- AUTO --> |
||
1736 | <h2 align=center> |
||
1737 | Using MkTclApp With The MingW32 Compiler For Windows |
||
1738 | </h2> |
||
1739 | |||
1740 | <!-- <p><i>This section was added on October 8, 1999</i></p> --> |
||
1741 | |||
1742 | <p>By default, the cygwin compiler generates an executable that |
||
1743 | requires a special DLL named <b>cygwin1.dll</b>. This DLL is |
||
1744 | covered by the GNU Public License. Consequently, you cannot |
||
1745 | distribute the DLL with your product unless you are also willing |
||
1746 | to distribute your source code. Many managers find this |
||
1747 | restruction unacceptable.</p> |
||
1748 | |||
1749 | <p>If you don't want to use the cygwin1.dll library, you can still |
||
1750 | use the cygwin compiler. You simply have to give the compiler a |
||
1751 | special command-line option: <b>-mno-cygwin</b>. With the -mno-cygwin |
||
1752 | command-line option, the cygwin compiler will generate an executable |
||
1753 | that uses only native Windows DLLs. You are free to distribute such |
||
1754 | an executable without having to make the source code available.</p> |
||
1755 | |||
1756 | <p>The only problem with this approach is that the Tcl/Tk libraries |
||
1757 | that come with cygwin depend on the cygwin1.dll DLL. So if you use |
||
1758 | the -mno-cygwin switch, you won't be able to use the Tcl/Tk libraries |
||
1759 | that come in the cygwin package. You'll have to either compile the |
||
1760 | Tcl/Tk libraries yourself (using VC++) or get a copy from Scriptics.</p> |
||
1761 | |||
1762 | <p>Here is how you can use the Tcl/Tk DLLs and libraries from the standard |
||
1763 | Scriptics distributions with the cygwin compiler. First get and install |
||
1764 | the binary distribution of Tcl/Tk from Scriptics. The binary distribution |
||
1765 | contains the DLLs you'll need along with C header files and |
||
1766 | interface libraries. The DLLs and header files you can use as is. |
||
1767 | However, the libraries (tcl82.lib and tk82.lib) will only work with |
||
1768 | VC++. You'll need to create new libraries named tcl82.a and tk82.a |
||
1769 | for use with cygwin. I'll describe how to do this using the Tcl |
||
1770 | library as an example.</p> |
||
1771 | |||
1772 | <p>The first step is to generate a suitable DEF file. |
||
1773 | Execute the following commands from a Cygwin shell:</p> |
||
1774 | |||
1775 | <blockquote><pre> |
||
1776 | <b>echo EXPORTS >tcl82.def |
||
1777 | nm tcl82.lib | grep 'T _' | sed 's/.* T _//' >>tcl82.def |
||
1778 | </b></pre></blockquote> |
||
1779 | |||
1780 | <p>If you do it right, the DEF file will begin with the |
||
1781 | keyword "EXPORTS" then contain the name of every API function |
||
1782 | in the TCL library, one function name per line. Use the |
||
1783 | DEF file to generate the library <b>libtcl82.a</b> as follows:</p> |
||
1784 | |||
1785 | <blockquote><pre> |
||
1786 | <b>dlltool --def tcl82.def --dllname tcl82.dll --output-lib libtcl82.a |
||
1787 | </b></pre></blockquote> |
||
1788 | |||
1789 | <p>Follow the same steps to generate libtk82.a, then link your |
||
1790 | program against the new libtcl82.a and libtk82.a library files. |
||
1791 | Put the tcl82.dll and tk82.dll DLLs from the standard distribution |
||
1792 | in the same directory as your executable and everything should |
||
1793 | work.</p> |
||
1794 | |||
1795 | <a name="toc19"><!-- AUTO --> |
||
1796 | <h2 align=center> |
||
1797 | History Of Mktclapp And Summary |
||
1798 | </h2> |
||
1799 | |||
1800 | <p>I first started programming Tcl/Tk back in 1994, with Tk3.6. |
||
1801 | Right away, I needed a simple way to mix C/C++ with Tcl/Tk so |
||
1802 | I wrote the "Embedded Tk" or "ET" package. ET was and continues |
||
1803 | to be widely used even if its interface is a bit clunky.</p> |
||
1804 | |||
1805 | <p>Mktclapp was written as a follow-on to ET. It has the same |
||
1806 | goal as ET, to make it easier to program with a mixture of C/C++ |
||
1807 | and Tcl/Tk, but the interface is very different. I think |
||
1808 | the interface is much cleaner and easier to use. It is |
||
1809 | certainly much easier to maintain. I encourage all ET |
||
1810 | users to transition to using mktclapp as soon as possible.</p> |
||
1811 | |||
1812 | <p>The development of mktclapp began in the early fall of 1998. |
||
1813 | I have personally used mktclapp to develop applications for |
||
1814 | three separate clients. And other users have had success with |
||
1815 | mktclapp too, to judge from my e-mail and from the number |
||
1816 | of downloads.</p> |
||
1817 | |||
1818 | <p>I hope you find mktclapp useful in your own endeavors. |
||
1819 | If you do, I would appreciate a brief |
||
1820 | <a href="mailto:drh@acm.org">e-mail</a> telling me |
||
1821 | how you are using mktclapp and how it has helped you. |
||
1822 | Better still, if you find a bug or a missing feature, |
||
1823 | please let me know. Mktclapp has already been much improved |
||
1824 | by user feedback.</p> |
||
1825 | |||
1826 | <p>Please note that the mktclapp program itself, and the |
||
1827 | xmktclapp.tcl GUI, are covered under the |
||
1828 | <a href="http://www.gnu.ai.mit.edu/copyleft/gpl.html">GNU Public License</a>. |
||
1829 | But the C code that mktclapp generates (the part you link with your |
||
1830 | program) is free of any copyright. You can use it however you |
||
1831 | wish and you do not have to give away the source.</p> |
||
1832 | |||
1833 | <a name="toc20"><!-- AUTO --> |
||
1834 | <h2 align=center> |
||
1835 | Bibliography |
||
1836 | </h2> |
||
1837 | |||
1838 | <dl compact> |
||
1839 | <p><dt>[1]</dt> |
||
1840 | <dd> The Mktclapp Homepage. |
||
1841 | <a href="http://www.hwaci.com/sw/mktclapp/"> |
||
1842 | http://www.hwaci.com/sw/mktclapp/ |
||
1843 | </a> |
||
1844 | </dd></p> |
||
1845 | <p><dt>[2]</dt> |
||
1846 | <dd> The Embedded Tk Homepage. |
||
1847 | <a href="http://www.hwaci.com/sw/et/"> |
||
1848 | http://www.hwaci.com/sw/et/ |
||
1849 | </a> |
||
1850 | </dd></p> |
||
1851 | <p><dt>[3]</dt> |
||
1852 | <dd> The Tcl Consortium Homepage |
||
1853 | <a href="http://www.tclconsortium.org/"> |
||
1854 | http://www.tclconsortium.org/ |
||
1855 | </a> |
||
1856 | </dd></p> |
||
1857 | <p><dt>[4]</dt> |
||
1858 | <dd> The homepage for Scriptics Corporations |
||
1859 | <a href="http://www.scriptics.com/"> |
||
1860 | http://www.scriptics.com/ |
||
1861 | </a> |
||
1862 | </dd></p> |
||
1863 | <p><dt>[5]</dt> |
||
1864 | <dd> The Cygwin Compiler Page at Cygnus. |
||
1865 | <a href="http://sourceware.cygnus.com/cygwin/"> |
||
1866 | http://sourceware.cygnus.com/cygwin/ |
||
1867 | </a> |
||
1868 | </dd></p> |
||
1869 | <p><dt>[6]</dt> |
||
1870 | <dd> Dennis LaBelle's Freewrap Program. |
||
1871 | <a href="http://www.albany.net/~dlabelle/freewrap/freewrap.html"> |
||
1872 | http://www.albany.net/~dlabelle/freewrap/freewrap.html |
||
1873 | </a> |
||
1874 | </dd></p> |
||
1875 | <p><dt>[7]</dt> |
||
1876 | <dd> Mumit Khan's Tcl/Tk Archives for the Cygwin and Mingw32 Compilers. |
||
1877 | <a href="http://www.xraylith.wisc.edu/~khan/software/tcl"> |
||
1878 | http://www.xraylith.wisc.edu/~khan/software/tcl |
||
1879 | </a> |
||
1880 | </dd></p> |
||
1881 | <p><dt>[8]</dt> |
||
1882 | <dd> Daniel Roche's balloon widget for Tcl/Tk |
||
1883 | <a href="http://www.multimania.com/droche/tkballoon/index.html"> |
||
1884 | http://www.multimania.com/droche/tkballoon/index.html |
||
1885 | </a> |
||
1886 | </dd></p> |
||
1887 | <p><dt>[9]</dt> |
||
1888 | <dd> Cameron Laird's Personal Notes On Tcl Compilers |
||
1889 | <a href="http://starbase.neosoft.com/~claird/comp.lang.tcl/tcl_compilers.html"> |
||
1890 | http://starbase.neosoft.com/~claird/comp.lang.tcl/tcl_compilers.html</a> |
||
1891 | </dd></p> |
||
1892 | <p><dt>[10]</dt> |
||
1893 | <dd> The BLT Extension |
||
1894 | <a href="http://www.tcltk.com/blt/"> |
||
1895 | http://www.tcltk.com/blt/ |
||
1896 | </a> |
||
1897 | </dd></p> |
||
1898 | <p><dt>[11]</dt> |
||
1899 | <dd> The GNU Public License |
||
1900 | <a href="http://www.gnu.ai.mit.edu/copyleft/gpl.html"> |
||
1901 | http://www.gnu.ai.mit.edu/copyleft/gpl.html |
||
1902 | </a> |
||
1903 | </dd></p> |
||
1904 | <p><dt>[12]</dt> |
||
1905 | <dd> Jan Nijtmans' Wrap Program |
||
1906 | <a href="http://home.wxs.nl/~nijtmans/wrap.html"> |
||
1907 | http://www.wxs.nl/~nijtmans/wrap.html |
||
1908 | </a> |
||
1909 | </dd></p> |
||
1910 | </dl> |
||
1911 | |||
1912 | <p><hr></p> |
||
1913 | <p align=right> |
||
1914 | D. Richard Hipp<br> |
||
1915 | <a href="mailto:drh@acm.org">drh@acm.org</a><br> |
||
1916 | Charlotte, NC<br> |
||
1917 | May 31, 1999 |
||
1918 | </p> |
||
1919 | |||
1920 | </body> |
||
1921 | </html> |