diff options
Diffstat (limited to 'tests/page3/index.html')
-rw-r--r-- | tests/page3/index.html | 2787 |
1 files changed, 2787 insertions, 0 deletions
diff --git a/tests/page3/index.html b/tests/page3/index.html new file mode 100644 index 0000000..ce92e8a --- /dev/null +++ b/tests/page3/index.html @@ -0,0 +1,2787 @@ +<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("PX\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: PX",<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 {PX} -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> |