mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
ffcedd7950
* ext/tk/lib/tk : bug fix and add Tcl/Tk extension support libraries git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@6559 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2787 lines
105 KiB
HTML
2787 lines
105 KiB
HTML
<html><body bgcolor="white">
|
||
<hr>
|
||
<h1 align="center">Embedding Tcl in C/C++ Applications</h1>
|
||
|
||
<table width="100%">
|
||
<tr><td valign="top" align="left" width="46%">
|
||
<b>Presented At:</b>
|
||
<blockquote>
|
||
The Tcl2K Conference<br>
|
||
Austin, Texas<br>
|
||
<nobr>9:00am, February 15, 2000</nobr><br>
|
||
</blockquote>
|
||
</td>
|
||
<td width="5%"> </td>
|
||
<td valign="top" align="left" width="46%">
|
||
<b>Instructor:</b>
|
||
<blockquote>
|
||
D. Richard Hipp<br>
|
||
drh@hwaci.com<br>
|
||
http://www.hwaci.com/drh/<br>
|
||
704.948.4565
|
||
</blockquote>
|
||
</td></tr>
|
||
</table><p>
|
||
<center><table border="2">
|
||
<tr><td>
|
||
<p align="center">
|
||
Copies of these notes, example source code,<br>and other
|
||
resources related to this tutorial<br>are available online at
|
||
<a href="http://www.hwaci.com/tcl2k/">
|
||
http://www.hwaci.com/tcl2k/</a></p>
|
||
<p align="center"><small>$Id$</small></p></td></tr>
|
||
</table>
|
||
</center>
|
||
</p>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Tutorial Outline</h2>
|
||
<p><ul><li>Introduction</li>
|
||
<li>Building It Yourself</li>
|
||
<ul><li>"Hello, World!" using Tcl</li>
|
||
<li>Tcl scripts as C strings</li>
|
||
<li>Adding new Tcl commands</li>
|
||
<li>A tour of the Tcl API</li>
|
||
<li>Tcl initialization scripts</li>
|
||
<li>Adding Tk</li>
|
||
</ul><li>Tools Survey</li>
|
||
<li>Mktclapp</li>
|
||
<ul><li>"Hello World" using mktclapp</li>
|
||
<li>Adding C code</li>
|
||
<li>Other Features</li>
|
||
<li>Invoking Tcl from C</li>
|
||
<li>Running mktclapp directly</li>
|
||
<li>Real-world examples</li>
|
||
</ul><li>Summary</li>
|
||
</ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Embedding Tcl in C/C++ Applications</h2>
|
||
<p><ul><li>You know how to program in Tcl/Tk</li></ul><ul><li>You know how to program in C/C++</li></ul><ul><li>This tutorial is about how to do both at the same time.</li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Why Mix C With Tcl/Tk?</h2>
|
||
<p><ul><li>Use C for the things C is good at and Tcl for the things
|
||
Tcl is good at.</li></ul><ul><li>Generate standalone executables.
|
||
<ul><li>Eliminate the need to install Tcl/Tk.</li>
|
||
<li>Prevent problems when the wrong version of Tcl/Tk is installed.</li>
|
||
</ul></li></ul><ul><li>Prevent end users from changing the source code.
|
||
<ul><li>Keeps users from creating new bugs.</li>
|
||
<li>Protects proprietary code.</li>
|
||
</ul></li></ul><ul><li>Office politics</li></ul><ul><li>Use Tcl/Tk as a portability layer for a large C program</li></ul><ul><li>Use Tcl as a testing interface</li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Why Mix C With Tcl/Tk?</h2>
|
||
<p><blockquote><big><b>
|
||
"Use C for the things C is good at and use Tcl/Tk for the things
|
||
Tcl/Tk is good at."
|
||
</b></blockquote></p><p>
|
||
|
||
<table width="100%">
|
||
<tr><td valign="top" align="left" width="46%">
|
||
<b>C is good at:</b>
|
||
<ul>
|
||
<li>Speed</li>
|
||
<li>Complex data structures</li>
|
||
<li>Computation</li>
|
||
<li>Interacting with hardware</li>
|
||
<li>Byte-by-byte data analysis</li>
|
||
</ul>
|
||
</td>
|
||
<td width="5%"> </td>
|
||
<td valign="top" align="left" width="46%">
|
||
<b>Tcl/Tk is good at:</b>
|
||
<ul>
|
||
<li>Building a user interface</li>
|
||
<li>Manipulation of strings</li>
|
||
<li>Portability</li>
|
||
<li>Opening sockets</li>
|
||
<li>Handling events</li>
|
||
</ul>
|
||
</td></tr>
|
||
</table>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Programming Models</h2>
|
||
<table width="100%">
|
||
<tr><td valign="top" width="49%">
|
||
|
||
<p><b>Mainstream Tcl Programming Model:</b></p>
|
||
</td>
|
||
<td width="2%"> </td>
|
||
<td valign="top" width="49%">
|
||
|
||
<p><b>Embedded Tcl Programming Model: </b></p>
|
||
</td></tr>
|
||
<tr><td valign="top" width="49%">
|
||
|
||
<ul><li>Add bits of C code to a large Tcl program</li></ul>
|
||
</td>
|
||
<td width="2%"> </td>
|
||
<td valign="top" width="49%">
|
||
|
||
<ul><li>Add bits of Tcl code to a large C program</li></ul>
|
||
</td></tr>
|
||
<tr><td valign="top" width="49%">
|
||
|
||
<ul><li>Main Tcl script loads extensions written in C</li></ul>
|
||
</td>
|
||
<td width="2%"> </td>
|
||
<td valign="top" width="49%">
|
||
|
||
<ul><li>Main C procedure invokes the Tcl interpreter</li></ul>
|
||
</td></tr>
|
||
<tr><td valign="top" width="49%">
|
||
|
||
<ul><li>Tcl/Tk is a programming language</li></ul>
|
||
</td>
|
||
<td width="2%"> </td>
|
||
<td valign="top" width="49%">
|
||
|
||
<ul><li>Tcl/Tk is a C library</li></ul>
|
||
</td></tr>
|
||
<tr><td valign="top" width="49%">
|
||
|
||
<center><img src="image1"><br>
|
||
Most of the Tcl2K conference is about</center>
|
||
</td>
|
||
<td width="2%"> </td>
|
||
<td valign="top" width="49%">
|
||
|
||
<center><img src="image1"><br>
|
||
This tutorial is about</center>
|
||
</td></tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">"Hello, World!" Using The Tcl Library</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include <tcl.h></tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Always include <tcl.h></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>int main(int argc, char **argv){<br>
|
||
Tcl_Interp *interp;</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> interp = Tcl_CreateInterp();</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Create a new Tcl interpreter</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_Eval(interp, "puts {Hello, World!}");</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Execute a Tcl command.</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> return 0;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Compiling "Hello, World!"</h2>
|
||
<p><p><b>Unix:</b></p>
|
||
<blockquote><tt>
|
||
$ gcc hello.c -ltcl -lm -ldl<br>
|
||
$ ./a.out<br>
|
||
Hello, World!</tt></blockquote>
|
||
|
||
<p><b>Windows using Cygwin:</b></p>
|
||
<blockquote><tt>
|
||
C:> gcc hello.c -ltcl80 -lm<br>
|
||
C:> a.exe<br>
|
||
Hello, World!</tt></blockquote>
|
||
|
||
<p><b>Windows using Mingw32:</b></p>
|
||
<blockquote><tt>
|
||
C:> gcc -mno-cygwin hello.c -ltcl82 -lm<br>
|
||
</tt></blockquote>
|
||
<table><tr><td valign="top"><img src="image3"></td>
|
||
<td valign="top"><b>Also works with VC++</b></td></tr></table></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Where Does <tt>-ltcl</tt> Come From On Unix?</h2>
|
||
<p><p>Build it yourself using these steps:</p></p><p>
|
||
<p><ul><li>Get tcl8.2.2.tar.gz from Scriptics</li></ul><ul><li><tt>zcat tcl8.2.2.tar.gz | tar vx </tt></li></ul><ul><li><tt>cd tcl8.2.2/unix</tt></li></ul><ul><li><tt>./configure --disable-shared</tt></li></ul><ul><li><tt>make</tt></li></ul><ul><li>Move <b>libtcl8.2.a</b> to your lib directory.</li></ul><ul><li>Copy <b>../generic/tcl.h</b> into /usr/include.</li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">What Other Libraries Are Required For Unix?</h2>
|
||
<p><ul><li>The sequence of <b>-l</b> options after <b>-ltcl</b>
|
||
varies from system to system</li></ul><ul><li>Observe what libraries the TCL makefile inserts when
|
||
it is building <b>tclsh</b></li></ul><ul><li>Examples in this talk are for RedHat Linux 6.0 for Intel</li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">How To Compile Under Unix Without Installing Tcl</h2>
|
||
<p><p>Specify the *.a file directly:</p>
|
||
<blockquote><pre>
|
||
$ gcc -I../tcl8.2.2/generic hello.c \
|
||
../tcl8.2.2/unix/libtcl8.2.a -lm -ldl
|
||
$ strip a.out
|
||
$ ./a.out
|
||
Hello, World!</pre></blockquote>
|
||
|
||
<p>Or, tell the C compiler where to look for *.a files:</p>
|
||
<blockquote><pre>
|
||
$ gcc -I../tcl8.2.2/generic hello.c \
|
||
-L../tcl8.2.2/unix -ltcl -lm -ldl
|
||
$ strip a.out
|
||
$ ./a.out
|
||
Hello, World!</pre></blockquote>
|
||
<table><tr><td valign="top"><img src="image3"></td>
|
||
<td valign="top"><b>The <tt>-I../tcl8.2.2</tt> argument
|
||
tells the compiler where to
|
||
find <tt><tcl.h></tt>.</p></b></td></tr></table></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">What's "Cygwin"?</h2>
|
||
<p><ul><li>An implementation of GCC/G++ and all development tools
|
||
for Windows95/98/NT/2000</li></ul><ul><li>Available for free download at
|
||
<blockquote>
|
||
<tt>http://sourceware.cygnus.com/cygwin/</tt>
|
||
</blockquote></li></ul><ul><li>Also available shrink-wrapped at your local software retailer or
|
||
online at
|
||
<blockquote>
|
||
<tt>http://www.cygnus.com/cygwin/index.html</tt>
|
||
</blockquote></li></ul><ul><li>Programs compiled using Cygwin require a special
|
||
DLL (<b>cygwin1.dll</b>) that provides a POSIX system API</li></ul><ul><li>Cygwin1.dll cannot be shipped with proprietary programs
|
||
without purchasing a license from Cygnus.</li></ul><ul><li>Mingw32 is the same compiler as Cygwin, but generates
|
||
binaries that do not use cygwin1.dll</li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Where Does <tt>-ltcl82</tt> Come From On Windows?</h2>
|
||
<p><p>Build it like this:</p></p><p>
|
||
<p><ul><li>Get <b>tcl82.lib</b> and <b>tcl82.dll</b> from Scriptics.</li></ul><ul><li><tt>echo EXPORTS >tcl82.def</tt></li></ul><ul><li><tt>nm tcl82.lib | grep 'T _' | sed 's/.* T _//' >>tcl82.def</tt></li></ul><ul><li><tt>dlltool --def tcl82.def --dllname tcl82.dll --output-lib libtcl82.a</tt></li></ul><ul><li>Move <b>libtcl82.a</b> to the lib directory and <b>tcl82.dll</b>
|
||
to the bin directory.</li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Where Does Your Code Go?</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include <tcl.h><br>
|
||
<br>
|
||
int main(int argc, char **argv){<br>
|
||
Tcl_Interp *interp;<br>
|
||
interp = Tcl_CreateInterp();</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> /* Your application code goes here */</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Insert C code here to do whatever it is your program is
|
||
suppose to do</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> return 0;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Building A Simple TCLSH</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include <tcl.h><br>
|
||
<br>
|
||
int main(int argc, char **argv){<br>
|
||
Tcl_Interp *interp;<br>
|
||
char *z;<br>
|
||
char zLine[2000];<br>
|
||
interp = Tcl_CreateInterp();</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> while( fgets(zLine,sizeof(zLine),stdin) ){</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Get one line of input</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_Eval(interp, zLine);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Execute the input as Tcl.</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> z = Tcl_GetStringResult(interp);<br>
|
||
if( z[0] ){<br>
|
||
printf("<22><><EFBFBD><EFBFBD>P<EFBFBD>X<>\n", z);<br>
|
||
}</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Print result if not empty</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> }<br>
|
||
return 0;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
<p><table><tr><td valign="top"><img src="image3"></td>
|
||
<td valign="top"><b>What if user types more than 2000 characters?</b></td></tr></table>
|
||
</p>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Building A Simple TCLSH</h2>
|
||
<p>Use TCL to handle input. Allows input lines of unlimited length.</p><p>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include <tcl.h><br>
|
||
<br>
|
||
/* Tcl code to implement the<br>
|
||
** input loop */<br>
|
||
static char zLoop[] = <br>
|
||
"while {![eof stdin]} {\n"</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> " set line [gets stdin]\n"</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Get one line of input</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> " set result [eval $line]\n"</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Execute input as Tcl</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> " if {$result!=\"\"} {puts $result}\n"</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Print result</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> "}\n"<br>
|
||
;<br>
|
||
<br>
|
||
<br>
|
||
int main(int argc, char **argv){<br>
|
||
Tcl_Interp *interp;<br>
|
||
interp = Tcl_CreateInterp();</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_Eval(interp, zLoop);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Run the Tcl input loop</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> return 0;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
<p><table><tr><td valign="top"><img src="image3"></td>
|
||
<td valign="top"><b>But what about commands that span multiple lines of input?</b></td></tr></table>
|
||
</p>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Better Handling Of Command-Line Input</h2>
|
||
<p>The file "input.tcl"</p><p>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>set line {}<br>
|
||
while {![eof stdin]} {</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> if {$line!=""} {<br>
|
||
puts -nonewline "> "<br>
|
||
} else {<br>
|
||
puts -nonewline "% "<br>
|
||
}<br>
|
||
flush stdout</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Prompt for user input. The prompt is normally "%"
|
||
but changes to ">" if the current line is a continuation.</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> append line [gets stdin]<br>
|
||
if {[info complete $line]} {</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> if {[catch {uplevel #0 $line} result]} {</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">If the command is complete, execute it.</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> puts stderr "Error: $result"<br>
|
||
} elseif {$result!=""} {<br>
|
||
puts $result<br>
|
||
}<br>
|
||
set line {}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> } else {<br>
|
||
append line \n<br>
|
||
}</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">If the command is incomplete, append a newline and get
|
||
another line of text.</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Better Handling Of Command-Line Input</h2>
|
||
<p>The file "input.c"</p><p>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include <tcl.h><br>
|
||
<br>
|
||
int main(int argc, char **argv){<br>
|
||
Tcl_Interp *interp;<br>
|
||
interp = Tcl_CreateInterp();</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_Eval(interp, "source input.tcl");</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Read and execute the input loop</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> return 0;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
<p><table><tr><td valign="top"><img src="image3"></td>
|
||
<td valign="top"><b>But now the program is not standalone!</b></td></tr></table>
|
||
</p>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Converting Scripts Into C Strings</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>static char zInputLoop[] = <br>
|
||
"set line {}\n"<br>
|
||
"while {![eof stdin]} {\n"<br>
|
||
" if {$line!=\"\"} {\n"<br>
|
||
" puts -nonewline \"> \"\n"<br>
|
||
" } else {\n"<br>
|
||
" puts -nonewline \"% \"\n"<br>
|
||
" }\n"<br>
|
||
" flush stdout\n"<br>
|
||
" append line [gets stdin]\n"<br>
|
||
" if {[info complete $line]} {\n"<br>
|
||
" if {[catch {uplevel #0 $line} result]} {\n"<br>
|
||
" puts stderr \"Error: $result\"\n"<br>
|
||
" } elseif {$result!=\"\"} {\n"<br>
|
||
" puts $result\n"<br>
|
||
" }\n"<br>
|
||
" set line {}\n"<br>
|
||
" } else {\n"<br>
|
||
" append line \\n\n"<br>
|
||
" }\n"<br>
|
||
"}\n"<br>
|
||
;</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Compile Tcl Scripts Into C Programs</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include <tcl.h><br>
|
||
</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt><br>
|
||
static char zInputLoop[] = <br>
|
||
/* Actual code omitted */<br>
|
||
;</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Copy and paste the converted Tcl script here</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt><br>
|
||
int main(int argc, char **argv){<br>
|
||
Tcl_Interp *interp;<br>
|
||
interp = Tcl_CreateInterp();</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_Eval(interp, zInputLoop);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Execute the Tcl code</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> return 0;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Converting Scripts To Strings<br>Using SED Or TCLSH</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>sed -e 's/\\/\\\\/g' \ </tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Convert <b>\</b> into <b>\\</b></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> -e 's/"/\\"/g' \ </tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Convert <b>"</b> into <b>\"</b></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> -e 's/^/ "/' \ </tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Add <b>"</b> to start of each line</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> -e 's/$/\\n"/' input.tcl</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Add <b>\n"</b> to end of each line</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt><br>
|
||
<br>
|
||
<br>
|
||
<br>
|
||
<br>
|
||
while {![eof stdin]} {<br>
|
||
set line [gets stdin]</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> regsub -all {\} $line {&&} line</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Convert <b>\</b> into <b>\\</b></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> regsub -all {"} $line {\"} line</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Convert <b>"</b> into <b>\"</b></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> puts "\"$line\\n\""</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Add <b>"</b> in front and <b>\n"</b> at the end</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Converting Scripts Into C Strings</h2>
|
||
<p>You may want to save space by removing comments and extra whitespace
|
||
from scripts.</p><p>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>static char zInputLoop[] = <br>
|
||
"set line {}\n"<br>
|
||
"while {![eof stdin]} {\n"<br>
|
||
"if {$line!=\"\"} {\n"<br>
|
||
"puts -nonewline \"> \"\n"<br>
|
||
"} else {\n"<br>
|
||
"puts -nonewline \"% \"\n"<br>
|
||
"}\n"<br>
|
||
"flush stdout\n"<br>
|
||
"append line [gets stdin]\n"<br>
|
||
"if {[info complete $line]} {\n"<br>
|
||
"if {[catch {uplevel #0 $line} result]} {\n"<br>
|
||
"puts stderr \"Error: $result\"\n"<br>
|
||
"} elseif {$result!=\"\"} {\n"<br>
|
||
"puts $result\n"<br>
|
||
"}\n"<br>
|
||
"set line {}\n"<br>
|
||
"} else {\n"<br>
|
||
"append line \\n\n"<br>
|
||
"}\n"<br>
|
||
"}\n"<br>
|
||
;</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Converting Scripts To Strings</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>sed -e 's/\\/\\\\/g' \ <br>
|
||
-e 's/"/\\"/g' \ </tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> -e '/^ *#/d' \ </tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Delete lines that begin with #</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> -e '/^ *$/d' \ </tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Delete blank lines</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> -e 's/^ */ "/' \ </tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Delete leading spaces</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> -e 's/$/\\n"/' input.tcl<br>
|
||
<br>
|
||
<br>
|
||
<br>
|
||
<br>
|
||
<br>
|
||
while {![eof stdin]} {<br>
|
||
set line [gets stdin]</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> set line [string trimleft $line]</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Remove leading space</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> if {$line==""} continue</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Delete blank lines</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> if {[string index $line 0]=="#"} {<br>
|
||
continue<br>
|
||
}</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Delete lines starting with #</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> regsub -all {\} $line {&&} line<br>
|
||
regsub -all {"} $line {\"} line<br>
|
||
puts "\"$line\\n\""<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Removing Comments Or Leading Space<br>Will Break Some Tcl Scripts!</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>image create bitmap smiley -data {</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>#define smile_width 15<br>
|
||
#define smile_height 15</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">These lines begin with # but are not comment</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>static unsigned char smile_bits[] = {<br>
|
||
0xc0, 0x01, 0x30, 0x06, 0x0c, 0x18,<br>
|
||
0x04, 0x10, 0x22, 0x22, 0x52, 0x25,<br>
|
||
0x01, 0x40, 0x01, 0x40, 0x01, 0x40,<br>
|
||
0x12, 0x24, 0xe2, 0x23, 0x04, 0x10,<br>
|
||
0x0c, 0x18, 0x30, 0x06, 0xc0, 0x01};<br>
|
||
}<br>
|
||
<br>
|
||
<br>
|
||
<br>
|
||
text .t<br>
|
||
pack .t<br>
|
||
.t insert end [string trim {</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>She walks in beauty, like the night<br>
|
||
Of cloudless climes and starry skies;<br>
|
||
And all that's best of dark and bright<br>
|
||
Meet in her aspect and her eyes;</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Indentation is deleted on lines 2
|
||
and 4</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>}] <br>
|
||
<br>
|
||
</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
<p><table><tr><td valign="top"><img src="image3"></td>
|
||
<td valign="top"><b>Problems like these are rare</b></td></tr></table>
|
||
</p>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Adding A "continue" Command</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>set line {}<br>
|
||
while {![eof stdin]} {<br>
|
||
if {$line!=""} {<br>
|
||
puts -nonewline "> "<br>
|
||
} else {<br>
|
||
puts -nonewline "% "<br>
|
||
}<br>
|
||
flush stdout<br>
|
||
append line [gets stdin]<br>
|
||
if {[info complete $line]} {</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> if {[lindex $line 0]=="continue"} {<br>
|
||
break;</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Break out of the loop if the command
|
||
is "continue"</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> } elseif {[catch {uplevel #0 $line} result]} {<br>
|
||
puts stderr "Error: $result"<br>
|
||
} elseif {$result!=""} {<br>
|
||
puts $result<br>
|
||
}<br>
|
||
set line {}<br>
|
||
} else {<br>
|
||
append line \n<br>
|
||
}<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Stop For Tcl Input At Various Points<br>In A C Program</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include <tcl.h><br>
|
||
<br>
|
||
static char zInputLoop[] = <br>
|
||
/* Tcl Input loop as a C string */<br>
|
||
;<br>
|
||
<br>
|
||
int main(int argc, char **argv){<br>
|
||
Tcl_Interp *interp;<br>
|
||
interp = Tcl_CreateInterp();</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> /* Application C code */</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Do some computation</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_Eval(interp, zInputLoop);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Stop for some Tcl input</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> /* More application C code */</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Do more computation</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_Eval(interp, zInputLoop);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Stop for more Tcl input</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> /* Finish up the application */</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Finish the computation</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> return 0;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Using Tcl For Testing</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include <tcl.h><br>
|
||
<br>
|
||
static char zInputLoop[] = <br>
|
||
/* Tcl Input loop as a C string */<br>
|
||
;<br>
|
||
<br>
|
||
</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>int main(int argc, char **argv){<br>
|
||
#ifdef TESTING<br>
|
||
Tcl_Interp *interp;</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Create interpreter only if TESTING
|
||
is defined</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> interp = Tcl_CreateInterp();<br>
|
||
#endif<br>
|
||
/* Application C code */</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>#ifdef TESTING<br>
|
||
Tcl_Eval(interp, zInputLoop);<br>
|
||
#endif</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Accept command-line input only if TESTING
|
||
is defined</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> /* More application C code */<br>
|
||
#ifdef TESTING<br>
|
||
Tcl_Eval(interp, zInputLoop);<br>
|
||
#endif<br>
|
||
/* Finish up the application */<br>
|
||
return 0;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Creating A New Tcl Command In C</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include <tcl.h><br>
|
||
<br>
|
||
int NewCmd(</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> void *clientData,<br>
|
||
Tcl_Interp *interp,<br>
|
||
int argc,<br>
|
||
char **argv</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">The Tcl command is implemented as
|
||
a C function with four arguments.</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>){<br>
|
||
printf("Hello, World!\n");</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> return TCL_OK;</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Returns TCL_OK or TCL_ERROR</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>}<br>
|
||
<br>
|
||
static char zInputLoop[] = <br>
|
||
/* Tcl code omitted... */<br>
|
||
;<br>
|
||
<br>
|
||
int main(int argc, char **argv){<br>
|
||
Tcl_Interp *interp;<br>
|
||
interp = Tcl_CreateInterp();</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_CreateCommand(interp, "helloworld",<br>
|
||
NewCmd, 0, 0);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Tell the interpreter which C function to call when the
|
||
"helloworld" Tcl command is executed</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_Eval(interp, zInputLoop);<br>
|
||
return 0;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Linkage From Tcl To C</h2>
|
||
<p><p align="center"><img src="image4"></p></p><p><ul><li>3rd parameter of Tcl_CreateCommand() is a pointer to the C subroutine
|
||
that implements the command.</li></ul><ul><li>4th parameter to Tcl_CreateCommand() becomes the 1st parameter to
|
||
the C routine whenever the Tcl command is executed.</li></ul><ul><li>1st parameter to Tcl_CreateCommand() must be a valid Tcl interpreter.
|
||
The same pointer appears as the second parameter to the C routine
|
||
whenever the Tcl command is executed.</li></ul></p>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Linkage From Tcl To C</h2>
|
||
<p><p align="center"><img src="image5"></p></p><p><ul><li>5th parameter of Tcl_CreateCommand() is a pointer to the C subroutine
|
||
that is called when the Tcl command is deleted.</li></ul><ul><li>4th parameter to Tcl_CreateCommand() becomes the 1st parameter to
|
||
the C routine.</li></ul></p>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">When To Use A Delete Proc</h2>
|
||
<p>Examples of where the delete proc is used in standard Tcl/Tk:</p><p>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>button .b -text Hello<br>
|
||
pack .b</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>rename .b {}</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Deleting the <b>.b</b> command causes the button to be destroyed</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt><br>
|
||
<br>
|
||
</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>image create photo smiley \ <br>
|
||
-file smiley.gif</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>rename smiley {}</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Deleting the <b>smiley</b> command destroys the image and reclaims the
|
||
memory used to hold the image</td>
|
||
</tr>
|
||
</table>
|
||
<p><ul><li>Always use a delete proc if the clientData is a pointer to
|
||
malloced memory or some other resource that needs freeing</li></ul><ul><li>Delete procs are never used in the Tcl core but are used
|
||
extensively in Tk</li></ul></p>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Linkage From Tcl To C</h2>
|
||
<p>The <tt>argc</tt> and <tt>argv</tt> parameters work just like in
|
||
<tt>main()</tt></p><p>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>helloworld one {two three} four</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center"><tt>argc = 4<br>
|
||
argv[0] = "helloworld"<br>
|
||
argv[1] = "one"<br>
|
||
argv[2] = "two three"<br>
|
||
argv[3] = "four"<br>
|
||
argv[4] = NULL</tt></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">A Short-Cut</h2>
|
||
<p>In a program with many new Tcl commands implemented in C, it becomes
|
||
tedious to type the same four parameters over and over again. So
|
||
we define a short-cut.</p><p>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#define TCLARGS \ <br>
|
||
void *clientData, \ <br>
|
||
Tcl_Interp *interp, \ <br>
|
||
int argc, \ <br>
|
||
char *argv</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Define TCLARGS once in a header file</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> <br>
|
||
<br>
|
||
</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>int NewCmd(TCLARGS){</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Use the TCLARGS macro to define new C functions
|
||
that implement Tcl commands.</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> /* implementation... */<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
<p><table><tr><td valign="top"><img src="image3"></td>
|
||
<td valign="top"><b>For brevity, we will use the TCLARGS macro during the
|
||
rest of this talk.</b></td></tr></table>
|
||
</p>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Returning A Value From C Back To Tcl</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>int NewCmd(TCLARGS){</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Note that the C function returns an "int"</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> return TCL_OK;</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Return value is TCL_OK or TCL_ERROR</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
<p><ul><li>TCL_OK and TCL_ERROR are defined in <tcl.h></li></ul><ul><li>Other valid return values TCL_RETURN, TCL_BREAK and TCL_CONTINUE
|
||
are rarely used</li></ul><ul><li>Common mistake: forgetting to return TCL_OK</li></ul></p>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Returning A Value From C Back To Tcl</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>int NewCmd(TCLARGS){</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_SetResult(interp,"Hello!",TCL_STATIC);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Set the result to "Hello!"</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> return TCL_OK;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
<p><ul><li>Result should be the text of an error message if you
|
||
return TCL_ERROR.</li></ul><ul><li>3rd argument to Tcl_SetResult() can be TCL_STATIC,
|
||
TCL_DYNAMIC, TCL_VOLATILE, or a function pointer.</li></ul><ul><li>Also consider using Tcl_AppendResult().</li></ul><ul><li>Direct access to <tt>interp->result</tt> is deprecated.</li></ul><ul><li>See the man pages for details.</li></ul></p>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">The Tcl_Obj Interface</h2>
|
||
<p><ul><li>A new way to write Tcl commands in C code</li></ul><ul><li>First introduced in Tcl8.0</li></ul><ul><li>Can be much faster, especially for lists or numeric values.</li></ul><ul><li>Able to handle arbitrary binary data.</li></ul><ul><li>More difficult to program.</li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">The Tcl_Obj Interface</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>int NewObjCmd(<br>
|
||
void *clientData,<br>
|
||
Tcl_Interp *interp,<br>
|
||
int objc,</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_Obj *const* objv</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">4th parameter is an array Tcl_Objs, not an array of strings</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>){<br>
|
||
/* Implementation... */<br>
|
||
return TCL_OK;<br>
|
||
}<br>
|
||
<br>
|
||
static char zInputLoop[] = <br>
|
||
/* Tcl code omitted... */<br>
|
||
;<br>
|
||
<br>
|
||
int main(int argc, char **argv){<br>
|
||
Tcl_Interp *interp;<br>
|
||
interp = Tcl_CreateInterp();</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_CreateObjCommand(interp, "newcmd",<br>
|
||
NewObjCmd, 0, 0);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Use a different function to register the command</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_Eval(interp, zInputLoop);<br>
|
||
return 0;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">The Tcl_Obj Interface</h2>
|
||
<p><ul><li>There are countless access methods for reading information from and
|
||
placing information in Tcl_Objs. Always use the access methods.</li></ul><ul><li>Details provided at Lee Bernhard's talk this afternoon.</li></ul><ul><li>Definitely use Tcl_Objs if you are writing a new Tcl extension.</li></ul><ul><li>Tcl_Objs address some of the weaknesses of Tcl relative to C/C++.
|
||
<ul>
|
||
<li> Tcl_Objs are faster </li>
|
||
<li> Tcl_Objs work with binary data </li>
|
||
</ul>
|
||
But C/C++ is faster still and better for working with binary data.</li></ul><ul><li>When mixing C/C++ with Tcl/Tk the benefits of Tcl_Objs are
|
||
less important. Using Tcl_Objs in this context may not be
|
||
worth the extra trouble.</li></ul><ul><li>This talk will focus on the string interface.</li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Nickel Tour Of The Tcl API</h2>
|
||
<p><p><b>Memory allocation functions</b></p>
|
||
<center><table width="90%"><tr>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_Alloc<br>
|
||
</tt></small></td>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_Free<br>
|
||
</tt></small></td>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_Realloc<br>
|
||
</tt></small></td>
|
||
</table></center><p><b>Functions useful in the implementation of new Tcl commands</b></p>
|
||
<center><table width="90%"><tr>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_AppendElement<br>
|
||
Tcl_AppendResult<br>
|
||
Tcl_GetBoolean<br>
|
||
</tt></small></td>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_GetDouble<br>
|
||
Tcl_GetInt<br>
|
||
Tcl_GetStringResult<br>
|
||
</tt></small></td>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_ResetResult<br>
|
||
Tcl_SetResult<br>
|
||
</tt></small></td>
|
||
</table></center><p><b>Functions for controlling the Tcl interpreter</b></p>
|
||
<center><table width="90%"><tr>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_CreateCommand<br>
|
||
Tcl_CreateInterp<br>
|
||
</tt></small></td>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_CreateObjCommand<br>
|
||
Tcl_DeleteCommand<br>
|
||
</tt></small></td>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_DeleteInterp<br>
|
||
Tcl_Exit<br>
|
||
</tt></small></td>
|
||
</table></center></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Nickel Tour Of The Tcl API</h2>
|
||
<p><p><b>I/O functions</b></p>
|
||
<center><table width="90%"><tr>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_Close<br>
|
||
Tcl_Eof<br>
|
||
Tcl_Flush<br>
|
||
Tcl_GetChannel<br>
|
||
Tcl_GetChannelMode<br>
|
||
Tcl_GetChannelName<br>
|
||
</tt></small></td>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_Gets<br>
|
||
Tcl_OpenCommandChannel<br>
|
||
Tcl_OpenFileChannel<br>
|
||
Tcl_OpenTcpClient<br>
|
||
Tcl_OpenTcpServer<br>
|
||
Tcl_Read<br>
|
||
</tt></small></td>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_Seek<br>
|
||
Tcl_Tell<br>
|
||
Tcl_Ungets<br>
|
||
Tcl_Write<br>
|
||
Tcl_WriteChars<br>
|
||
</tt></small></td>
|
||
</table></center><p><b>Names and meanings of system error codes</b></p>
|
||
<center><table width="90%"><tr>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_ErrnoId<br>
|
||
Tcl_ErrnoMsg<br>
|
||
</tt></small></td>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_GetErrno<br>
|
||
Tcl_SetErrno<br>
|
||
</tt></small></td>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_SignalId<br>
|
||
Tcl_SignalMsg<br>
|
||
</tt></small></td>
|
||
</table></center></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Nickel Tour Of The Tcl API</h2>
|
||
<p><p><b>General Operating System Calls</b></p>
|
||
<center><table width="90%"><tr>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_Access<br>
|
||
Tcl_Chdir<br>
|
||
Tcl_GetCwd<br>
|
||
</tt></small></td>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_GetHostName<br>
|
||
Tcl_GetNameOfExecutable<br>
|
||
Tcl_Sleep<br>
|
||
</tt></small></td>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_Stat<br>
|
||
</tt></small></td>
|
||
</table></center><p><b>String Manipulation And Comparison</b></p>
|
||
<center><table width="90%"><tr>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_Concat<br>
|
||
Tcl_Merge<br>
|
||
</tt></small></td>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_SplitList<br>
|
||
Tcl_StringCaseMatch<br>
|
||
</tt></small></td>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_StringMatch<br>
|
||
</tt></small></td>
|
||
</table></center><p><b>Dynamically Resizable Strings</b></p>
|
||
<center><table width="90%"><tr>
|
||
<td width="49%" valign="top"><small><tt>
|
||
Tcl_DStringAppend<br>
|
||
Tcl_DStringAppendElement<br>
|
||
Tcl_DStringEndSublist<br>
|
||
Tcl_DStringInit<br>
|
||
Tcl_DStringLength<br>
|
||
</tt></small></td>
|
||
<td width="49%" valign="top"><small><tt>
|
||
Tcl_DStringResult<br>
|
||
Tcl_DStringSetLength<br>
|
||
Tcl_DStringStartSublist<br>
|
||
Tcl_DStringValue<br>
|
||
</tt></small></td>
|
||
</table></center></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Nickel Tour Of The Tcl API</h2>
|
||
<p><p><b>Event Handlers</b></p>
|
||
<center><table width="90%"><tr>
|
||
<td width="49%" valign="top"><small><tt>
|
||
Tcl_CancelIdleCall<br>
|
||
Tcl_CreateChannelHandler<br>
|
||
Tcl_CreateTimerHandler<br>
|
||
Tcl_DeleteChannelHandler<br>
|
||
</tt></small></td>
|
||
<td width="49%" valign="top"><small><tt>
|
||
Tcl_DeleteTimerHandler<br>
|
||
Tcl_DoOneEvent<br>
|
||
Tcl_DoWhenIdle<br>
|
||
</tt></small></td>
|
||
</table></center><p><b>Functions For Reading And Writing Tcl Variables</b></p>
|
||
<center><table width="90%"><tr>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_GetVar<br>
|
||
Tcl_GetVar2<br>
|
||
Tcl_LinkVar<br>
|
||
Tcl_SetVar<br>
|
||
Tcl_SetVar2<br>
|
||
</tt></small></td>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_TraceVar<br>
|
||
Tcl_TraceVar2<br>
|
||
Tcl_UnlinkVar<br>
|
||
Tcl_UnsetVar<br>
|
||
Tcl_UnsetVar2<br>
|
||
</tt></small></td>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_UntraceVar<br>
|
||
Tcl_UntraceVar2<br>
|
||
Tcl_UpdateLinkedVar<br>
|
||
</tt></small></td>
|
||
</table></center><p><b>Functions For Executing Tcl Code</b></p>
|
||
<center><table width="90%"><tr>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_Eval<br>
|
||
Tcl_EvalFile<br>
|
||
</tt></small></td>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_EvalObj<br>
|
||
Tcl_GlobalEval<br>
|
||
</tt></small></td>
|
||
<td width="32%" valign="top"><small><tt>
|
||
Tcl_GlobalEvalObj<br>
|
||
Tcl_VarEval<br>
|
||
</tt></small></td>
|
||
</table></center></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Nickel Tour Of The Tcl API</h2>
|
||
<p><p><b>Functions For Dealing With Unicode</b></p>
|
||
<center><table width="90%"><tr>
|
||
<td width="49%" valign="top"><small><tt>
|
||
Tcl_NumUtfChars<br>
|
||
Tcl_UniCharAtIndex<br>
|
||
Tcl_UniCharIsAlnum<br>
|
||
Tcl_UniCharIsAlpha<br>
|
||
Tcl_UniCharIsControl<br>
|
||
Tcl_UniCharIsDigit<br>
|
||
Tcl_UniCharIsGraph<br>
|
||
Tcl_UniCharIsLower<br>
|
||
Tcl_UniCharIsPrint<br>
|
||
Tcl_UniCharIsPunct<br>
|
||
Tcl_UniCharIsSpace<br>
|
||
Tcl_UniCharIsUpper<br>
|
||
Tcl_UniCharIsWordChar<br>
|
||
Tcl_UniCharLen<br>
|
||
Tcl_UniCharNcmp<br>
|
||
Tcl_UniCharToLower<br>
|
||
Tcl_UniCharToTitle<br>
|
||
</tt></small></td>
|
||
<td width="49%" valign="top"><small><tt>
|
||
Tcl_UniCharToUpper<br>
|
||
Tcl_UniCharToUtf<br>
|
||
Tcl_UniCharToUtfDString<br>
|
||
Tcl_UtfAtIndex<br>
|
||
Tcl_UtfBackslash<br>
|
||
Tcl_UtfCharComplete<br>
|
||
Tcl_UtfFindFirst<br>
|
||
Tcl_UtfFindLast<br>
|
||
Tcl_UtfNcasecmp<br>
|
||
Tcl_UtfNcmp<br>
|
||
Tcl_UtfNext<br>
|
||
Tcl_UtfPrev<br>
|
||
Tcl_UtfToLower<br>
|
||
Tcl_UtfToTitle<br>
|
||
Tcl_UtfToUniChar<br>
|
||
Tcl_UtfToUniCharDString<br>
|
||
Tcl_UtfToUpper<br>
|
||
</tt></small></td>
|
||
</table></center>
|
||
<p><b>Functions For Dealing With Tcl_Objs</b></p>
|
||
<blockquote><i>Too numerous to list...</i></blockquote></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Documentation Of The Tcl API</h2>
|
||
<p><ul><li>Tcl comes with excellent man pages</li></ul><ul><li>"Use the source, Luke"</li></ul><ul><li>See <tt>tclDecl.h</tt> for a list of API functions</li></ul><ul><li>The header comments on the implementation of API functions usually
|
||
gives a good description of what the function does and how it should
|
||
be used.</li></ul><ul><li>Most API functions are used within Tcl and Tk. Use grep to locate
|
||
examples.</li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Initialization Scripts</h2>
|
||
<p><ul><li>Run the mini TCLSH implemented above and execute the <tt>parray</tt> command</li></ul><ul><li>It doesn't work! What's wrong? </p></li></li></ul><ul><li><tt>parray</tt> is really a Tcl proc that is read in when the
|
||
interpreter is initialized. </p></li></li></ul><ul><li><tt>parray</tt> (and several other commands) are stored in a
|
||
handful of "Initialization Scripts" </p></li></li></ul><ul><li>All the initialization scripts are stored in the
|
||
"Tcl Library" - a directory on the host
|
||
computer. </p></li></li></ul><table><tr><td valign="top"><img src="image3"></td>
|
||
<td valign="top"><b>Invoke the Tcl_Init() function to locate and read the
|
||
Tcl initialization scripts.</b></td></tr></table></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">The <tt>Tcl_Init()</tt> Function</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include <tcl.h><br>
|
||
<br>
|
||
static char zInputLoop[] = <br>
|
||
/* Tcl code omitted... */<br>
|
||
;<br>
|
||
<br>
|
||
int main(int argc, char **argv){<br>
|
||
Tcl_Interp *interp;<br>
|
||
interp = Tcl_CreateInterp();</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_Init(interp);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Locate and read the initialization scripts</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> /* Call Tcl_CreateCommand()? */<br>
|
||
Tcl_Eval(interp, zInputLoop);<br>
|
||
return 0;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
<p><table><tr><td valign="top"><img src="image3"></td>
|
||
<td valign="top"><b>But Tcl_Init() can fail. We need to check its return value...</b></td></tr></table>
|
||
</p>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">The <tt>Tcl_Init()</tt> Function</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include <tcl.h><br>
|
||
<br>
|
||
static char zInputLoop[] = <br>
|
||
/* Tcl code omitted... */<br>
|
||
;<br>
|
||
<br>
|
||
int main(int argc, char **argv){<br>
|
||
Tcl_Interp *interp;<br>
|
||
interp = Tcl_CreateInterp();</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> if( Tcl_Init(interp)!=TCL_OK ){<br>
|
||
fprintf(stderr,"Tcl_Init() failed: <EFBFBD><EFBFBD><EFBFBD><EFBFBD>P<EFBFBD>X<>",<br>
|
||
Tcl_GetStringResult(interp));<br>
|
||
}</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Print error message if Tcl_Init() fails</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> /* Call Tcl_CreateCommand()? */<br>
|
||
Tcl_Eval(interp, zInputLoop);<br>
|
||
return 0;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
<p><table><tr><td valign="top"><img src="image3"></td>
|
||
<td valign="top"><b>But now the program is not standalone.</b></td></tr></table>
|
||
</p>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">How <tt>Tcl_Init()</tt> Works</h2>
|
||
<p><ul><li>Computes the value of variable <tt>tcl_libPath</tt>.</li></ul><ul><li>Invokes the procedure named "<tt>tclInit</tt>"</li></ul><ul><li>A default <tt>tclInit</tt> procedure is built into Tcl.
|
||
You can define an alternative <tt>tclInit</tt> procedure
|
||
prior to calling <tt>Tcl_Init()</tt>.</li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">The Default <tt>initTcl</tt> Procedure</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>set errors {}<br>
|
||
set dirs {}<br>
|
||
if {[info exists tcl_library]} {<br>
|
||
lappend dirs $tcl_library<br>
|
||
} else {<br>
|
||
if {[info exists env(TCL_LIBRARY)]} {<br>
|
||
lappend dirs $env(TCL_LIBRARY)<br>
|
||
}<br>
|
||
lappend dirs $tclDefaultLibrary<br>
|
||
unset tclDefaultLibrary<br>
|
||
set dirs [concat $dirs $tcl_libPath]<br>
|
||
}<br>
|
||
foreach i $dirs {<br>
|
||
set tcl_library $i<br>
|
||
set tclfile [file join $i init.tcl]<br>
|
||
if {[file exists $tclfile]} {<br>
|
||
if {![catch {uplevel #0 [list source $tclfile]} msg]} {<br>
|
||
return<br>
|
||
} else {<br>
|
||
append errors "$tclfile: $msg\n$errorInfo\n"<br>
|
||
}<br>
|
||
}<br>
|
||
}<br>
|
||
error "Can't find a usable init.tcl ..."</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">The Default Initialization Sequence</h2>
|
||
<p><ul><li>The <tt>tclInit</tt> procedure locates and sources the <tt>init.tcl</tt>
|
||
script. The directory that contains <tt>init.tcl</tt> is stored in
|
||
the <tt>tcl_library</tt> variable.</li></ul><ul><li>The <tt>init.tcl</tt> script creates an <tt>unknown</tt> procedure.
|
||
The <tt>unknown</tt> procedure will run whenever Tcl encounters an
|
||
unknown command.</li></ul><ul><li>The <tt>unknown</tt> procedure consults the file <tt>tclIndex</tt> in the
|
||
<tt>tcl_library</tt> directory to see if the command is defined by one of
|
||
the initialization scripts.</li></ul><ul><li>The <tt>unknown</tt> procedure sources any needed initialization scripts
|
||
and retries the command.</li></ul><table><tr><td valign="top"><img src="image3"></td>
|
||
<td valign="top"><b>Commands defined in the initialization scripts are loaded
|
||
on demand.</b></td></tr></table></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Standalone Initialization Techniques</h2>
|
||
<p><p><b>Manually execute all initialization scripts</b></p>
|
||
<ul><li>Convert all initialization scripts into C strings and
|
||
put them in the executable.</li></ul><ul><li>Call <tt>Tcl_Eval()</tt> on each initialization script and omit the
|
||
call to <tt>Tcl_Init()</tt></li></ul><ul><li>Or, redefine <tt>tclInit</tt> so that it does not attempt to source
|
||
<tt>init.tcl</tt> then call <tt>Tcl_Eval()</tt> on each initialization
|
||
script after <tt>Tcl_Init()</tt> returns.</li></ul><table><tr><td valign="top"><img src="image3"></td>
|
||
<td valign="top"><b>This approach is not recommended</b></td></tr></table></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Standalone Initialization Techniques</h2>
|
||
<p><p><b>Redefining the builtin <tt>source</tt> command</b></p>
|
||
<ul><li>Convert all initialization scripts into C strings and
|
||
put them in the executable.</li></ul><ul><li>Create a new <tt>source</tt> command that
|
||
calls <tt>Tcl_Eval()</tt> on the appropriate built-in string
|
||
instead of reading from the disk.</li></ul><ul><li>Read from disk if the named file is not one that is built in.</li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Redefining <tt>source</tt></h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>static char zInitTcl[] = "...";<br>
|
||
static char zParrayTcl[] = "...";</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Scripts <tt>init.tcl</tt> and <tt>parray.tcl</tt></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt><br>
|
||
int NewSourceCmd(TCLARGS){</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> if( !strcmp(argv[1],"/builtin/init.tcl") )<br>
|
||
return Tcl_Eval(interp, zInitTcl);<br>
|
||
if( !strcmp(argv[1],"/builtin/parray.tcl") )<br>
|
||
return Tcl_Eval(interp, zParrayTcl);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Call <tt>Tcl_Eval()</tt> on builtin strings if the names match</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> return Tcl_EvalFile(interp, argv[1]);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Call <tt>Tcl_EvalFile()</tt> if no match</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>}<br>
|
||
<br>
|
||
int main(int argc, char **argv){<br>
|
||
Tcl_Interp *interp;</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> setenv("TCL_LIBRARY","/builtin");</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Causes <tt>tclInit</tt> to look for <tt>init.tcl</tt> in <tt>/builtin</tt></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> interp = Tcl_CreateInterp();</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_CreateCommand(interp, "source",<br>
|
||
NewSourceCmd, 0, 0);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Redefine <tt>source</tt></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_Init(interp);<br>
|
||
Tcl_Eval(interp, zInputLoop);<br>
|
||
return 0;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Redefining <tt>source</tt></h2>
|
||
<p><ul><li>This approach works for all versions of Tcl and Tk.</li></ul><ul><li>Also need to redefine the "<tt>file exists</tt>" Tcl command since it
|
||
too is used by <tt>tclInit</tt>.</li></ul><ul><li>To verify that the program is really standalone, remove the call
|
||
to <tt>Tcl_EvalFile()</tt>.</li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Standalone Initialization Techniques</h2>
|
||
<p><p><b>Use the <tt>Tcl</tt>*<tt>InsertProc()</tt> functions</b></p>
|
||
<ul><li>Three routines that overload basic file I/O operations:
|
||
<ul>
|
||
<li> <tt>TclStatInsertProc()</tt> </li>
|
||
<li> <tt>TclAccessInsertProc()</tt> </li>
|
||
<li> <tt>TclOpenFileChannelInsertProc()</tt> </li>
|
||
</ul></li></ul><ul><li>Allows us to implement a virtual filesystem that overlays the
|
||
real filesystem.</li></ul><ul><li>The virtual filesystem contains all the initialization scripts
|
||
as compiled-in strings. The initialization scripts look like
|
||
they are resident on disk even though they are built in.</li></ul><ul><li>These functions first appeared in Tcl8.0.3.
|
||
Presumably to support TclPro Wrapper.</li></ul><ul><li>The only documentation is comments on the code.
|
||
See the Tcl source file <tt>generic/tclIOUtil.c</tt></li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">The <tt>TclStatInsertProc()</tt> Function</h2>
|
||
<p><ul><li>Sole argument is a pointer to a function whose interface is the
|
||
same as <tt>stat()</tt></li></ul><ul><li>Functions are stacked. Tcl tries each <tt>stat</tt> function on the
|
||
list, beginning with the most recently inserted, until one succeeds.</li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">The <tt>TclStatInsertProc()</tt> Function</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include <tclInt.h></tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Rather than <tt><tcl.h></tt>!</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt><br>
|
||
static int<br>
|
||
BltinFileStat(char *path,struct stat *buf){<br>
|
||
char *zData;<br>
|
||
int nData;</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> zData = FindBuiltinFile(path, 0, &nData);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Check if <tt>path</tt> is a builtin</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> if( zData==0 ){<br>
|
||
return -1;<br>
|
||
}</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Fail if <tt>path</tt> is not a builtin</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> memset(buf, 0, sizeof(*buf));<br>
|
||
buf->st_mode = 0400;<br>
|
||
buf->st_size = nData;</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> return 0;</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Success if it is builtin</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>}<br>
|
||
<br>
|
||
int main(int argc, char **argv){<br>
|
||
Tcl_Interp *interp;</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> TclStatInsertProc(BltinFileStat);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Register new <tt>stat</tt> function</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> interp = Tcl_CreateInterp();<br>
|
||
Tcl_Init(interp);<br>
|
||
Tcl_Eval(interp, zInputLoop);<br>
|
||
return 0;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">The <tt>TclAccessInsertProc()</tt> Function</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include <tclInt.h></tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Rather than <tt><tcl.h></tt>!</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt><br>
|
||
/* BltinFileStat() not shown... */<br>
|
||
<br>
|
||
static int<br>
|
||
BltinFileAccess(char *path, int mode){<br>
|
||
char *zData;</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> if( mode & 3 ) return -1;</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">All builtins are read-only</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> zData = FindBuiltinFile(path, 0, &nData);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Check if <tt>path</tt> is a builtin</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> if( zData==0 ) return -1;</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Fail if <tt>path</tt> is not a builtin</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> return 0;</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Success if it is builtin</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>}<br>
|
||
<br>
|
||
int main(int argc, char **argv){<br>
|
||
Tcl_Interp *interp;</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> TclStatInsertProc(BltinFileStat);<br>
|
||
TclAccessInsertProc(BltinFileAccess);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Register new <tt>stat</tt> and <tt>access</tt> functions</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> interp = Tcl_CreateInterp();<br>
|
||
Tcl_Init(interp);<br>
|
||
Tcl_Eval(interp, zInputLoop);<br>
|
||
return 0;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">The <tt>TclOpenFileChannelInsertProc()</tt> Function</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>static Tcl_Channel BuiltinFileOpen(<br>
|
||
Tcl_Interp *interp, /* The TCL interpreter doing the open */<br>
|
||
char *zFilename, /* Name of the file to open */<br>
|
||
char *modeString, /* Mode string for the open (ignored) */<br>
|
||
int permissions /* Permissions for a newly created file (ignored) */<br>
|
||
){<br>
|
||
char *zData;<br>
|
||
BuiltinFileStruct *p;<br>
|
||
int nData;<br>
|
||
char zName[50];<br>
|
||
Tcl_Channel chan;<br>
|
||
static int count = 1;<br>
|
||
<br>
|
||
zData = FindBuiltinFile(zFilename, 1, &nData);<br>
|
||
if( zData==0 ) return NULL;<br>
|
||
p = (BuiltinFileStruct*)Tcl_Alloc( sizeof(BuiltinFileStruct) );<br>
|
||
if( p==0 ) return NULL;<br>
|
||
p->zData = zData;<br>
|
||
p->nData = nData;<br>
|
||
p->cursor = 0;<br>
|
||
sprintf(zName,"etbi_bffffc7c_8049b04",((int)BuiltinFileOpen)>>12,count++);<br>
|
||
chan = Tcl_CreateChannel(&builtinChannelType, zName, <br>
|
||
(ClientData)p, TCL_READABLE);<br>
|
||
return chan;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">The <tt>TclOpenFileChannelInsertProc()</tt> Function</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>static Tcl_ChannelType builtinChannelType = {<br>
|
||
"builtin", /* Type name. */<br>
|
||
NULL, /* Always non-blocking.*/<br>
|
||
BuiltinFileClose, /* Close proc. */<br>
|
||
BuiltinFileInput, /* Input proc. */<br>
|
||
BuiltinFileOutput, /* Output proc. */<br>
|
||
BuiltinFileSeek, /* Seek proc. */<br>
|
||
NULL, /* Set option proc. */<br>
|
||
NULL, /* Get option proc. */<br>
|
||
BuiltinFileWatch, /* Watch for events on console. */<br>
|
||
BuiltinFileHandle, /* Get a handle from the device. */<br>
|
||
};</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
<p>
|
||
<p>For additional information see:</p>
|
||
<ul>
|
||
<li>The man page for <tt>Tcl_CreateChannel()</tt></li>
|
||
<li>Tk source code file <tt>generic/tkConsole.c</tt></li>
|
||
</ul>
|
||
</p>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Initializing Tk</h2>
|
||
<p><ul><li>All the same initialization script issues as Tcl</li></ul><ul><li>Tk initialization scripts are in a different directory
|
||
than the Tcl initialization scripts - the "Tk Library"</li></ul><ul><li>Call <tt>Tk_Init()</tt> after <tt>Tcl_Init()</tt></li></ul><ul><li>Must have an event loop or Tk will not work!</li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Implementing An Event Loop</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>button .b -text Hello -command exit<br>
|
||
pack .b</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Create a Tk interface</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt><br>
|
||
</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>bind . <Destroy> {<br>
|
||
if {![winfo exists .]} exit<br>
|
||
}</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Close the application when the main window
|
||
is destroyed</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt><br>
|
||
</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>while 1 {vwait forever}</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">The event loop</td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">"Hello, World!" Using Tk</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include <tk.h><br>
|
||
<br>
|
||
</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>static char zHello[] = </tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">The application code</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> "button .b "<br>
|
||
"-text {Hello, World} "<br>
|
||
"-command exit\n"<br>
|
||
"pack .b\n";<br>
|
||
<br>
|
||
</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>static char zEventLoop[] =</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">The event loop</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> "bind . <Destroy> {\n"<br>
|
||
" if {![winfo exists .]} exit\n"<br>
|
||
"}\n"<br>
|
||
"while 1 {vwait forever}\n";<br>
|
||
<br>
|
||
<br>
|
||
int main(int argc, char **argv){<br>
|
||
Tcl_Interp *interp;<br>
|
||
interp = Tcl_CreateInterp();</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_Init(interp);<br>
|
||
Tk_Init(interp);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">We really should check the return values of the init functions...</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_Eval(interp, zHello);</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_Eval(interp, zEventLoop);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">The event loop never returns</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> /*NOTREACHED*/<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Compiling "Hello, World!" For Tk</h2>
|
||
<p><p><b>Unix:</b></p>
|
||
<blockquote><pre>
|
||
$ gcc hello.c -ltk -L/usr/X11R6/lib \
|
||
-lX11 -ltcl -lm -ldl
|
||
$ ./a.out</pre></blockquote>
|
||
|
||
<p><b>Windows using Cygwin:</b></p>
|
||
<blockquote><pre>
|
||
C:> gcc hello.c -mwindows -ltk80 -ltcl80 -lm
|
||
C:> a.exe</pre></blockquote>
|
||
|
||
<p><b>Windows using Mingw32:</b></p>
|
||
<blockquote><pre>
|
||
C:> gcc -mno-cygwin hello.c -mwindows \
|
||
-ltk82 -ltcl82 -lm
|
||
C:> a.exe</pre></blockquote></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Making The Program Standalone</h2>
|
||
<p><p>To make a Tcl application standalone you have to convert the following
|
||
initialization scripts to C strings and compile them into the
|
||
executable:</p>
|
||
<table><tr>
|
||
<td valign="top"><tt>
|
||
auto.tcl<br>
|
||
history.tcl<br>
|
||
init.tcl
|
||
</tt></td>
|
||
<td valign="top"><tt>
|
||
ldAout.tcl<br>
|
||
package.tcl
|
||
</tt></td>
|
||
<td valign="top"><tt>
|
||
parray.tcl<br>
|
||
safe.tcl
|
||
</tt></td>
|
||
<td valign="top"><tt>
|
||
tclIndex<br>
|
||
word.tcl
|
||
</tt></td>
|
||
</tr></table>
|
||
|
||
<p>To make a Tk application standalone requires these additional
|
||
initialization scripts from the Tk Library:</p>
|
||
<table><tr>
|
||
<td valign="top"><tt>
|
||
bgerror.tcl<br>
|
||
button.tcl<br>
|
||
clrpick.tcl<br>
|
||
comdlg.tcl<br>
|
||
console.tcl<br>
|
||
dialog.tcl
|
||
</tt></td>
|
||
<td valign="top"><tt>
|
||
entry.tcl<br>
|
||
focus.tcl<br>
|
||
listbox.tcl<br>
|
||
menu.tcl<br>
|
||
msgbox.tcl<br>
|
||
optMenu.tcl
|
||
</tt></td>
|
||
<td valign="top"><tt>
|
||
palette.tcl<br>
|
||
safetk.tcl<br>
|
||
scale.tcl<br>
|
||
scrlbar.tcl<br>
|
||
tclIndex<br>
|
||
tearoff.tcl
|
||
</tt></td>
|
||
<td valign="top"><tt>
|
||
text.tcl<br>
|
||
tk.tcl<br>
|
||
tkfbox.tcl<br>
|
||
xmfbox.tcl
|
||
</tt></td>
|
||
</tr></table>
|
||
|
||
<p>Total of about 13K lines and 400K bytes of text or 9K lines and
|
||
250K bytes if you strip comments and leading spaces</p></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">A Review Of The Features We Want</h2>
|
||
<p><ol type="A">
|
||
<li value="1">
|
||
Combine C/C++ with Tcl/Tk into a single executable.</dd>
|
||
</li></ol>
|
||
|
||
<ol type="A">
|
||
<li value="2">
|
||
The executable should be standalone. It must not depend
|
||
on files not normally found on the system.
|
||
</li></ol>
|
||
|
||
<ol type="A">
|
||
<li value="3">
|
||
It should be difficult for end users to alter the program
|
||
(and introduce bugs).
|
||
</li></ol></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Available Programming Aids</h2>
|
||
<p><p>Several tools are available. The chart below shows which tools
|
||
help achieve which objectives.</p>
|
||
|
||
<center><table border="2">
|
||
<tr>
|
||
<td></td>
|
||
<td colspan="3" align="center">
|
||
<b>Features The Tool Helps To Achieve</b></td>
|
||
</tr>
|
||
<tr>
|
||
<td align="center"><b>Tool Name</b></td>
|
||
<td align="center">Mix C and Tcl</td>
|
||
<td align="center">Standalone</td>
|
||
<td align="center">Hide Source</td>
|
||
</tr>
|
||
<tr>
|
||
<td>SWIG</td>
|
||
<td align="center"><img src="image6"></td>
|
||
<td> </td>
|
||
<td> </td>
|
||
</tr>
|
||
<tr>
|
||
<td>TclPro Wrapper</td>
|
||
<td> </td>
|
||
<td align="center"><img src="image6"></td>
|
||
<td align="center"><img src="image6"></td>
|
||
</tr>
|
||
<tr>
|
||
<td>FreeWrap</td>
|
||
<td> </td>
|
||
<td align="center"><img src="image6"></td>
|
||
<td align="center"><img src="image6"></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Wrap</td>
|
||
<td> </td>
|
||
<td align="center"><img src="image6"></td>
|
||
<td> </td>
|
||
</tr>
|
||
<tr>
|
||
<td>mktclapp</td>
|
||
<td align="center"><img src="image6"></td>
|
||
<td align="center"><img src="image6"></td>
|
||
<td align="center"><img src="image6"></td>
|
||
</tr>
|
||
</table></center></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">SWIG</h2>
|
||
<table><tr><td valign="top"><img src="image7"></td>
|
||
<td valign="top"><p><ul><li>Creates an interface between an existing C/C++ library and a high-level
|
||
programming language. Support for:
|
||
<ul>
|
||
<li> Tcl/Tk </li>
|
||
<li> Perl </li>
|
||
<li> Python </li>
|
||
<li> Java </li>
|
||
<li> Eiffel </li>
|
||
<li> Guile </li>
|
||
</ul></li></ul><ul><li>No changes required to C/C++ code. Can be used with legacy libraries.</li></ul><ul><li>Generates an extension, not a standalone binary</li></ul><ul><li>The tutorial on SWIG was yesterday afternoon.</li></ul><ul><li>http://www.swig.org/</li></ul></p></td></tr></table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Wrapper Programs</h2>
|
||
<table><tr><td valign="top"><img src="image8"></td>
|
||
<td valign="top"><p><ul><li>Convert a pure Tcl/Tk program into a standalone binary</li></ul><ul><li>Several wrapper programs are available:
|
||
<ul>
|
||
<li> TclPro Wrapper - http://www.scriptics.com/ </li>
|
||
<li> FreeWrap - http://www.albany.net/~dlabelle/freewrap/freewrap.html </li>
|
||
<li> Wrap - http://members1.chello.nl/~j.nijtmans/wrap.html </li>
|
||
</ul></li></ul><ul><li>No C compiler required!</li></ul><ul><li>TclPro will convert Tcl script into bytecode so that it cannot be
|
||
easily read by the end user. FreeWrap encrypts the scripts.</li></ul><ul><li>FreeWrap uses compression on its executable.
|
||
Wrap uses compression on both the executable and on the bundled script files.</li></ul><ul><li>Usually include extensions like winico and/or BLT</li></ul></p></td></tr></table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">mktclapp</h2>
|
||
<table><tr><td valign="top"><img src="image9"></td>
|
||
<td valign="top"><p><ul><li>Mix C/C++ with Tcl/Tk into a standalone binary</li></ul>
|
||
<ul><li><tt>mktclapp</tt> generates an application initialization file
|
||
that contains Tcl scripts as strings and makes all necessary calls
|
||
to <tt>Tcl_Init</tt>, <tt>Tcl_CreateCommand</tt>,
|
||
<tt>Tcl</tt>*<tt>InsertProc</tt>, etc.</li></ul><ul><li>Features to make it easier to write new Tcl command in C</li></ul><ul><li><tt>xmktclapp.tcl</tt> provides a GUI interface to <tt>mktclapp</tt></li></ul><ul><li>http://www.hwaci.com/sw/mktclapp/</li></ul></p></td></tr></table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">"Hello, World!" Using Mktclapp</h2>
|
||
<p><ul><li>Download <tt>mktclapp.c</tt> and <tt>xmktclapp.tcl</tt> from
|
||
http://www.hwaci.com/sw/mktclapp/</li></ul><ul><li>Compile <tt>mktclapp</tt>:
|
||
<blockquote><pre>
|
||
cc -o mktclapp mktclapp.c
|
||
</pre></blockquote></li></ul><ul><li>Create "Hello, World!" as a Tcl script in file <tt>hw.tcl</tt>:
|
||
<blockquote><pre>
|
||
button .b -text {Hello, World!} -command exit
|
||
pack .b
|
||
</pre></blockquote></li></ul><ul><li>Launch xmktclapp:
|
||
<blockquote><pre>
|
||
wish xmktclapp.tcl
|
||
</pre></blockquote></li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">"Hello, World!" Using Mktclapp</h2>
|
||
<table width="100%"><tr><td valign="top"><p><ul><li>Set "Command Line Input?" to "None"</li></ul><ul><li>Set "Standalone?" to "Yes"</li></ul><ul><li>Enter "<tt>hw.mta</tt>" for the Configuration File</li></ul><ul><li>Enter "<tt>hw.c</tt>" for the Output C File</li></ul></p></td>
|
||
<td valign="top" align="right"><img src="image10"></td></tr></table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">"Hello, World!" Using Mktclapp</h2>
|
||
<table width="100%"><tr><td valign="top"><p><ul><li>Go to the "Tcl Scripts" page</li></ul><ul><li>Press "Insert" and add <tt>hw.tcl</tt> to the list of
|
||
Tcl scripts</li></ul><ul><li>Change the "Startup Script" to be <tt>hw.tcl</tt>.</li></ul><ul><li>Select File/Build and File/Exit</li></ul></p></td>
|
||
<td valign="top" align="right"><img src="image11"></td></tr></table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">"Hello, World!" Using Mktclapp</h2>
|
||
<p><ul><li>Mktclapp generates <tt>hw.c</tt>.
|
||
Compile it something like this:
|
||
<pre>
|
||
cc hw.c -ltk -L/usr/X11R6/lib -lX11 -ltcl -lm -ldl
|
||
</pre></li></ul><ul><li>Or, if using Cygwin:
|
||
<pre>
|
||
gcc hw.c -mwindows -ltk80 -ltcl80 -lm
|
||
</pre></li></ul><ul><li>Or, if using Mingw32:
|
||
<pre>
|
||
gcc -mno-cygwin hw.c -mwindows -ltk82 -ltcl82 -lm
|
||
</pre></li></ul><ul><li>And you're done!</li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Adding C Code To Your Program</h2>
|
||
<p>Put the new C code in a new source file named "<tt>add.c</tt>"</p><p>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include "hw.h"</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Generated by mktclapp</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt></tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>int ET_COMMAND_add(ET_TCLARGS){</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center"><tt>ET_TCLARGS</tt> is a macro defined in <tt>hw.h</tt></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> int a, b;<br>
|
||
char zResult[30];<br>
|
||
a = atoi(argv[1]);<br>
|
||
b = atoi(argv[2]);<br>
|
||
sprintf(zResult, "-1073742724", a+b);<br>
|
||
Tcl_SetResult(interp, zResult, TCL_VOLATILE);<br>
|
||
return TCL_OK;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Adding C Code To Your Program</h2>
|
||
<table width="100%"><tr><td valign="top"><p><ul><li>Go to the "C/C++ Modules" page of xmktclapp.tcl</li></ul>
|
||
<ul><li>Press "Insert" and add <tt>add.c</tt> to the list of
|
||
C/C++ modules</p></li></ul></li></ul><ul><li>Select File/Build and File/Exit</li></ul></p></td>
|
||
<td valign="top" align="right"><img src="image12"></td></tr></table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Adding C Code To Your Program</h2>
|
||
<p><ul><li>Compile as follows:
|
||
<pre>
|
||
cc add.c hw.c -ltk -L/usr/X11R6/lib -ltcl -lm -ldl
|
||
</pre></li></ul><ul><li>Or construct a Makefile that compiles <tt>add.c</tt> into <tt>add.o</tt>
|
||
and <tt>hw.c</tt> into <tt>hw.o</tt> and then links them.</li></ul><ul><li>Compile the same way for Windows except use the usual Windows
|
||
libraries and options...</li></ul><table><tr><td valign="top"><img src="image3"></td>
|
||
<td valign="top"><b>Don't have to worry with <tt>Tcl_CreateCommand()</tt> - Mktclapp takes
|
||
care of that automatically.</b></td></tr></table></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Checking Parameters In The <tt>add</tt> Command</h2>
|
||
<p>Modify <tt>add.c</tt> to insure the <tt>add</tt> command
|
||
is called with exactly two integer arguments</p><p>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include "hw.h"<br>
|
||
<br>
|
||
int ET_COMMAND_add(ET_TCLARGS){<br>
|
||
int a, b;<br>
|
||
char zResult[30];</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> if( argc!=3 ){<br>
|
||
Tcl_AppendResult(interp,<br>
|
||
"wrong # args: should be: \"",<br>
|
||
argv[0], " VALUE VALUE\"", 0);<br>
|
||
return TCL_ERROR;<br>
|
||
}</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Report an error if there are not exactly
|
||
2 arguments</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> if( Tcl_GetInt(interp, argv[1], &a)!=TCL_OK ){<br>
|
||
return TCL_ERROR;<br>
|
||
}</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Report an error if the first argument is
|
||
not an integer</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> if( Tcl_GetInt(interp, argv[2], &b)!=TCL_OK ){<br>
|
||
return TCL_ERROR;<br>
|
||
}</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Do the same for the second argument</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> sprintf(zResult, "-1073742724", a+b);<br>
|
||
Tcl_SetResult(interp, zResult, TCL_VOLATILE);<br>
|
||
return TCL_OK;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Using The Tcl_Obj Interface</h2>
|
||
<p>In the file <tt>objadd.c</tt> put this code:</p><p>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include "hw.h"</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt><br>
|
||
int ET_OBJCOMMAND_add2(ET_OBJARGS){<br>
|
||
int a, b;</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Use "<tt>ET_OBJCOMMAND</tt>" instead of "<tt>ET_COMMAND</tt>" and
|
||
"<tt>ET_OBJARGS</tt>" instead of "<tt>ET_TCLARGS</tt>"</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> if( objc!=3 ){<br>
|
||
Tcl_WrongNumArgs(interp, 1, objv,<br>
|
||
"number number");<br>
|
||
return TCL_ERROR;<br>
|
||
}</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">A special routine for "wrong # args" error</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> if( Tcl_GetIntFromObj(interp, objv[1], &a) ){</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Instead of <tt>Tcl_GetInt</tt></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> return TCL_ERROR;<br>
|
||
}<br>
|
||
if( Tcl_GetIntFromObj(interp, objv[2], &b) ){<br>
|
||
return TCL_ERROR;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_SetIntObj(Tcl_GetObjResult(interp), a+b);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Result stored as integer, not a string</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> return TCL_OK;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Speed Of Tcl_Obj Versus "char*" Interfaces</h2>
|
||
<p><ul><li>Compile both <tt>add</tt> and <tt>add2</tt> into the same executable.</li></ul><ul><li>Compare their speeds:
|
||
<pre>
|
||
time {add 123456 654321} 10000
|
||
<font color="blue">26 microseconds per iteration</font>
|
||
time {add2 123456 654321} 10000
|
||
<font color="blue">4 microseconds per iteration</font>
|
||
</pre></li></ul><ul><li>The Tcl_Obj version is 650 faster!</li></ul><ul><li>Replace the addition with a "real" computation that takes
|
||
10 milliseconds.</li></ul><ul><li>Now the Tcl_Obj version is only 0.2 faster!</li></ul><table><tr><td valign="top"><img src="image3"></td>
|
||
<td valign="top"><b>In many real-world problems, the Tcl_Obj interface has no noticeable
|
||
speed advantage over the string interface.</b></td></tr></table></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">More About Built-in Tcl Scripts</h2>
|
||
<table><tr><td valign="top"><img src="image11"></td>
|
||
<td valign="top"><p><ul><li>Comments and leading white-space are removed from the
|
||
script by default. Use the "Don't Strip Comments"
|
||
button to change this.</li></ul><ul><li>The file name must exactly match the name that is
|
||
used by the <tt>source</tt> command.</li></ul></p></td></tr></table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Locations Of Libraries</h2>
|
||
<table><tr><td valign="top"><img src="image13"></td>
|
||
<td valign="top"><p><ul><li>Tells mktclapp where to look for script libraries.</li></ul><ul><li>All Tcl scripts in the indicated directories are
|
||
compiled into the <tt>appinit.c</tt> file.</li></ul><ul><li>Comments and extra white-space are removed.
|
||
There is no way to turn this off.</li></ul></p></td></tr></table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Built-in Binary Data Files</h2>
|
||
<table><tr><td valign="top"><img src="image14"></td>
|
||
<td valign="top"><p><ul><li>Arbitrary files become part of the virtual filesystem</li></ul><ul><li>No comment or white-space removal is attempted</li></ul><ul><li>Useful for images or other binary data</li></ul></p></td></tr></table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">New Commands In Namespaces</h2>
|
||
<p>Two underscores (__) are replaced by two colons (::) in
|
||
command names, thus giving the ability to define new commands
|
||
in a namespace</p><p>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include <hw.h></tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt><br>
|
||
int ET_COMMAND_adder__add(ET_TCLARGS){<br>
|
||
int a, b;</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Creates the Tcl command called "<tt>adder::add</tt>"</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> char *zResult[30];<br>
|
||
if( argc!=3 ){<br>
|
||
Tcl_AppendResult(interp,<br>
|
||
"wrong # args: should be: \"",<br>
|
||
argv[0], " VALUE VALUE\"", 0);<br>
|
||
return TCL_ERROR;<br>
|
||
}<br>
|
||
if( Tcl_GetInt(interp, argv[1], &a)!=TCL_OK ){<br>
|
||
return TCL_ERROR;<br>
|
||
}<br>
|
||
if( Tcl_GetInt(interp, argv[1], &b)!=TCL_OK ){<br>
|
||
return TCL_ERROR;<br>
|
||
}<br>
|
||
sprintf(zResult, "-1073742724", a+b);<br>
|
||
Tcl_SetResult(interp, zResult, TCL_VOLATILE);<br>
|
||
return TCL_OK;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Adding Your Own <tt>main()</tt></h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>int main(int argc, char **argv){<br>
|
||
/* Application specific initialization */</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Et_Init(argc, argv);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Never returns!</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> /*NOTREACHED*/<br>
|
||
return 0;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
<p><table><tr><td valign="top"><img src="image3"></td>
|
||
<td valign="top"><b>The "Autofork" feature is disabled if you supply your own <tt>main()</tt></b></td></tr></table>
|
||
</p>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Initializing The Tcl Interpreter</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include <tcl.h><br>
|
||
<br>
|
||
int counter = 0;<br>
|
||
<br>
|
||
int main(int argc, char **argv){<br>
|
||
Et_Init(argc, argv);<br>
|
||
/*NOTREACHED*/<br>
|
||
return 0;<br>
|
||
}<br>
|
||
<br>
|
||
int Et_AppInit(Tcl_Interp *interp){</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> if( Blt_Init(Interp) ){<br>
|
||
return TCL_ERROR;<br>
|
||
}</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Example: Initialize an extension</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Tcl_LinkVar(interp, "counter", &counter,<br>
|
||
TCL_LINK_INT);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Or link a C variable to a Tcl variable</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> return TCL_OK;</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Return TCL_OK if successful</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Writing Your Own Event Loop</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include <tcl.h><br>
|
||
</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>void Et_CustomMainLoop(Tcl_Interp *interp){</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Replaces the default event loop</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> return;</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Ex: Return without handling any events.</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>}<br>
|
||
<br>
|
||
int main(int argc, char **argv){</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Et_Init(argc, argv);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">This now returns after initializing Tcl</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> /* Application code here */<br>
|
||
return 0;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Writing Your Own Event Loop</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include <tcl.h><br>
|
||
<br>
|
||
void Et_CustomMainLoop(Tcl_Interp *interp){</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> for(;;){<br>
|
||
Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT);<br>
|
||
/* Other processing... */<br>
|
||
}</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Intermix processing and event handling</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>}<br>
|
||
<br>
|
||
int main(int argc, char **argv){</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> Et_Init(argc, argv);</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Never returns</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt> /*NOTREACHED*/<br>
|
||
return 0;<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Mktclapp Initialization Sequence</h2>
|
||
<p><ul><li>Initialization starts when the <tt>Et_Init()</tt>
|
||
function is called either by client code or by
|
||
the <tt>main()</tt> that mktclapp generates</li></ul><ul><li>Create the main Tcl interpreter</li></ul><ul><li>Construct the virtual filesystem overlay by redefining
|
||
the <tt>source</tt> command and by using the
|
||
<tt>Tcl</tt>*<tt>InsertProc()</tt> functions</li></ul><ul><li>Call <tt>Et_PreInit()</tt> if the client defines it</li></ul><ul><li>Call <tt>Tcl_Init()</tt> and <tt>Tk_Init()</tt></li></ul><ul><li>Call <tt>Tcl_CreateCommand()</tt> and <tt>Tcl_CreateObjCommand()</tt>
|
||
for every <tt>ET_COMMAND_</tt>* and <tt>ET_OBJCOMMAND_</tt>* function
|
||
in the client code</li></ul><ul><li>Call <tt>Et_AppInit()</tt> if the client defines it</li></ul><ul><li>Run the main Tcl script if there is one</li></ul><ul><li>Call <tt>Et_CustomMainLoop()</tt> if defined by client code or
|
||
else run the built-in event loop</li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Invoking Tcl From C</h2>
|
||
<p><ul><li>Use one of the built-in evaluation functions:
|
||
<center><table width="80%">
|
||
<tr><td valign="top" width="50%"><ul>
|
||
<li> Tcl_Eval() </li>
|
||
<li> Tcl_VarEval() </li>
|
||
<li> Tcl_EvalFile() </li>
|
||
<li> Tcl_GlobalEval() </li>
|
||
</ul></td>
|
||
<td valign="top" width="50%"><ul>
|
||
<li> Tcl_EvalObj() </li>
|
||
<li> Tcl_GlobalEvalObj() </li>
|
||
</ul></td></tr>
|
||
</table></center></li></ul><ul><li>Mktclapp provides evaluation functions with variable argument
|
||
lists as in <tt>printf()</tt>:
|
||
<ul>
|
||
<li> Et_EvalF() </li>
|
||
<li> Et_GlobalEvalF() </li>
|
||
</ul></li></ul><ul><li>Mktclapp provides a global variable <tt>Et_Interp</tt> which is
|
||
a pointer to the main interpreter</li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Invoking Tcl From C</h2>
|
||
<p>Example: A C function that pops up an error message dialog box</p><p>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include "appinit.h"<br>
|
||
<br>
|
||
void ErrMsg(char *zMsg){<br>
|
||
Tcl_SetVar(Et_Interp, "zMsg", zMsg, TCL_GLOBAL_ONLY);<br>
|
||
Tcl_GlobalEval(Et_Interp, <br>
|
||
"tk_messageBox -icon error -msg $zMsg -type ok");<br>
|
||
Tcl_UnsetVar(Et_Interp, "zMsg", TCL_GLOBAL_ONLY);<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Invoking Tcl From C</h2>
|
||
<p>The same C function implemented using <tt>Et_EvalF()</tt> instead
|
||
of <tt>Tcl_GlobalEval()</tt></p><p>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include "appinit.h"<br>
|
||
<br>
|
||
void ErrMsg(char *zMsg){<br>
|
||
Et_EvalF(Et_Interp, <br>
|
||
"tk_messageBox -icon error -msg {<7B><><EFBFBD><EFBFBD>P<EFBFBD>X<>} -type ok",<br>
|
||
zMsg);<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
<p>
|
||
<ul><li>
|
||
Suppose the function is called as follows:
|
||
<blockquote>
|
||
<tt>ErrMsg("Syntax error near \"}\"");</tt>
|
||
</blockquote>
|
||
</li></ul>
|
||
|
||
<ul><li>
|
||
The command that gets executed is:
|
||
<pre>
|
||
tk_messageBox -icon error -msg \
|
||
{Syntax error near "}"} -type ok
|
||
</pre>
|
||
</li></ul>
|
||
|
||
<ul><li>
|
||
But this is an ill-formed Tcl command!
|
||
</li></ul>
|
||
</p>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Invoking Tcl From C</h2>
|
||
<p>Use the "<tt></tt>" format to generate a quoted string</p><p>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt>#include "appinit.h"<br>
|
||
<br>
|
||
void ErrMsg(char *zMsg){<br>
|
||
Et_EvalF(Et_Interp, <br>
|
||
"tk_messageBox -icon error -msg \"%\" -type ok",<br>
|
||
zMsg);<br>
|
||
}</tt></small></td>
|
||
<td></td><td></td><td></td><td></td>
|
||
</tr>
|
||
</table>
|
||
<p><ul><li>The <tt></tt> puts a backslash before all characters that
|
||
are special to Tcl</li></ul><ul><li>The Tcl command becomes:
|
||
<pre>
|
||
tk_messageBox -icon error -msg \
|
||
"Syntax error near \"\}\"" -type ok
|
||
</pre></li></ul></p>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Other Functions Provided By Mktclapp</h2>
|
||
<p><ul><li><tt>void Et_ResultF(Tcl_Interp*, ...);</tt></li></ul><ul><li><tt>char *Et_DStringAppendF(Tcl_DString*, ...);</tt></li></ul><ul><li><tt>int Et_AppendObjF(Tcl_Obj*, ...);</tt></li></ul><ul><li><tt>char *mprintf(const char *format, ...);<br>
|
||
char *vmprintf(const char *format, va_list);</tt></li></ul><ul><li><tt>void Et_NewBuiltinFile(char *filename, char *data, int amt);</tt></li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Operating Mktclapp From The Command Line</h2>
|
||
<p><ul><li>Generate the <tt>appinit.h</tt> header file like this:
|
||
<blockquote>
|
||
<tt>mktclapp -header >appinit.h</tt>
|
||
</blockquote></li></ul><ul><li>Generate the <tt>appinit.c</tt> file like this:
|
||
<blockquote>
|
||
<tt>mktclapp -f appinit.mta >appinit.c</tt>
|
||
</blockquote></li></ul><ul><li>The <tt>*.mta</tt> file is just a list of command-line options</li></ul><ul><li>Enter
|
||
<blockquote>
|
||
<tt>mktclapp -help</tt>
|
||
</blockquote>
|
||
to get a list of available options</li></ul><ul><li>Look at MTA files generated by xmktclapp.tcl for examples</li></ul></p>
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Format Of An MTA File</h2>
|
||
<table cellspacing="0" cellpadding="0" border="0">
|
||
<tr><td valign="center">
|
||
<small><tt># Configuration file generated by xmktclapp<br>
|
||
# Hand editing is not recommended<br>
|
||
#</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Comments begin with one #</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>## Autofork No<br>
|
||
## CFile:add.c 1<br>
|
||
## CFile:objadd.c 1<br>
|
||
## CmdLine Console<br>
|
||
## ConfigFile hw.mta<br>
|
||
## Data:check.gif 1<br>
|
||
## MainScript hw.tcl<br>
|
||
## Mode Tcl/Tk<br>
|
||
## NoSource No<br>
|
||
## OutputFile hw.c<br>
|
||
## Shroud No<br>
|
||
## Standalone Yes<br>
|
||
## TclFile:hw.tcl 1<br>
|
||
## TclLib /usr/lib/tcl8.0<br>
|
||
## TkLib /usr/lib/tk8.0</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">Lines beginning with two #s are used
|
||
by xmktclapp.tcl and ignored by mktclapp</td>
|
||
</tr>
|
||
<tr><td valign="center">
|
||
<small><tt>-console<br>
|
||
-main-script "hw.tcl"<br>
|
||
-tcl-library "/usr/lib/tcl8.0"<br>
|
||
-tk-library "/usr/lib/tk8.0"<br>
|
||
"add.c"<br>
|
||
"objadd.c"<br>
|
||
-i "check.gif"<br>
|
||
-strip-tcl "hw.tcl"</tt></small></td>
|
||
<td> </td>
|
||
<td valign="center"><img src="image2"></td>
|
||
<td> </td>
|
||
<td valign="center">All other lines are read by mktclapp and
|
||
ignored by xmktclapp.tcl</td>
|
||
</tr>
|
||
</table>
|
||
|
||
<br clear="both"><p><hr></p>
|
||
<h2 align="center">Summary</h2>
|
||
<p><ul><li>Use Tcl for the things Tcl is good at and use C/C++ for the things that
|
||
C/C++ is good at</li></ul><ul><li>Use wrapper programs to make pure Tcl programs standalone</li></ul><ul><li>Use mktclapp to combine Tcl/Tk with C/C++ into a standalone</li></ul></p>
|
||
<br clear="both"><p><hr></p>
|