From fcbf63e62c627deae76c1b8cb8c0876c536ed811 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 16 Mar 2020 18:49:26 +0900 Subject: Fresh start --- .../ext/tk/sample/tkextlib/tkHTML/page3/index.html | 2787 ++++++++++++++++++++ 1 file changed, 2787 insertions(+) create mode 100644 jni/ruby/ext/tk/sample/tkextlib/tkHTML/page3/index.html (limited to 'jni/ruby/ext/tk/sample/tkextlib/tkHTML/page3/index.html') diff --git a/jni/ruby/ext/tk/sample/tkextlib/tkHTML/page3/index.html b/jni/ruby/ext/tk/sample/tkextlib/tkHTML/page3/index.html new file mode 100644 index 0000000..f4dc84b --- /dev/null +++ b/jni/ruby/ext/tk/sample/tkextlib/tkHTML/page3/index.html @@ -0,0 +1,2787 @@ + +
+

Embedding Tcl in C/C++ Applications

+ + + + + +
+ Presented At: +
+ The Tcl2K Conference
+ Austin, Texas
+ 9:00am, February 15, 2000
+
+
  + Instructor: +
+ D. Richard Hipp
+ drh@hwaci.com
+ http://www.hwaci.com/drh/
+ 704.948.4565 +
+

+

+ +
+

+ Copies of these notes, example source code,
and other + resources related to this tutorial
are available online at + + http://www.hwaci.com/tcl2k/

+

$Id: index.html 31689 2011-05-22 09:26:02Z nobu $

+
+

+ +


+

Tutorial Outline

+

+


+

Embedding Tcl in C/C++ Applications

+

+


+

Why Mix C With Tcl/Tk?

+

+


+

Why Mix C With Tcl/Tk?

+

+ "Use C for the things C is good at and use Tcl/Tk for the things + Tcl/Tk is good at." +

+ + + + + +
+ C is good at: +
    +
  • Speed
  • +
  • Complex data structures
  • +
  • Computation
  • +
  • Interacting with hardware
  • +
  • Byte-by-byte data analysis
  • +
+
  + Tcl/Tk is good at: +
    +
  • Building a user interface
  • +
  • Manipulation of strings
  • +
  • Portability
  • +
  • Opening sockets
  • +
  • Handling events
  • +
+
+


+

Programming Models

+ + + + + + + + + + + + + + + + +
+ +

Mainstream Tcl Programming Model:

+
  + +

Embedded Tcl Programming Model:  

+
+ +
  • Add bits of C code to a large Tcl program
+
  + +
  • Add bits of Tcl code to a large C program
+
+ +
  • Main Tcl script loads extensions written in C
+
  + +
  • Main C procedure invokes the Tcl interpreter
+
+ +
  • Tcl/Tk is a programming language
+
  + +
  • Tcl/Tk is a C library
+
+ +

+ Most of the Tcl2K conference is about
+
  + +

+ This tutorial is about
+
+ +


+

"Hello, World!" Using The Tcl Library

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+#include <tcl.h>    Always include <tcl.h>
+int main(int argc, char **argv){
+  Tcl_Interp *interp;
+  interp = Tcl_CreateInterp();    Create a new Tcl interpreter
+  Tcl_Eval(interp, "puts {Hello, World!}");    Execute a Tcl command.
+  return 0;
+}
+ +


+

Compiling "Hello, World!"

+

Unix:

+
+ $ gcc hello.c -ltcl -lm -ldl
+ $ ./a.out
+ Hello, World!
+ +

Windows using Cygwin:

+
+ C:> gcc hello.c -ltcl80 -lm
+ C:> a.exe
+ Hello, World!
+ +

Windows using Mingw32:

+
+ C:> gcc -mno-cygwin hello.c -ltcl82 -lm
+
+ +
Also works with VC++

+


+

Where Does -ltcl Come From On Unix?

+

Build it yourself using these steps:

+

+


+

What Other Libraries Are Required For Unix?

+

+


+

How To Compile Under Unix Without Installing Tcl

+

Specify the *.a file directly:

+
+  $ gcc -I../tcl8.2.2/generic hello.c \
+      ../tcl8.2.2/unix/libtcl8.2.a -lm -ldl
+  $ strip a.out
+  $ ./a.out
+  Hello, World!
+ +

Or, tell the C compiler where to look for *.a files:

+
+  $ gcc -I../tcl8.2.2/generic hello.c \
+      -L../tcl8.2.2/unix -ltcl -lm -ldl
+  $ strip a.out
+  $ ./a.out
+  Hello, World!
+ +
The -I../tcl8.2.2 argument + tells the compiler where to + find <tcl.h>.

+


+

What's "Cygwin"?

+

+


+

Where Does -ltcl82 Come From On Windows?

+

Build it like this:

+

+


+

Where Does Your Code Go?

+ + + + + + + + + + + + + +
+#include <tcl.h>

+int main(int argc, char **argv){
+  Tcl_Interp *interp;
+  interp = Tcl_CreateInterp();
+  /* Your application code goes here */    Insert C code here to do whatever it is your program is + suppose to do
+  return 0;
+}
+ +


+

Building A Simple TCLSH

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+#include <tcl.h>

+int main(int argc, char **argv){
+  Tcl_Interp *interp;
+  char *z;
+  char zLine[2000];
+  interp = Tcl_CreateInterp();
+  while( fgets(zLine,sizeof(zLine),stdin) ){    Get one line of input
+    Tcl_Eval(interp, zLine);    Execute the input as Tcl.
+    z = Tcl_GetStringResult(interp);
+    if( z[0] ){
+      printf("PX\n", z);
+    }
    Print result if not empty
+  }
+  return 0;
+}
+

+
What if user types more than 2000 characters?
+

+ +


+

Building A Simple TCLSH

+

Use TCL to handle input. Allows input lines of unlimited length.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+#include <tcl.h>

+/* Tcl code to implement the
+** input loop */
+static char zLoop[] = 
+  "while {![eof stdin]} {\n"
+  "  set line [gets stdin]\n"    Get one line of input
+  "  set result [eval $line]\n"    Execute input as Tcl
+  "  if {$result!=\"\"} {puts $result}\n"    Print result
+  "}\n"
+;

+
+int main(int argc, char **argv){
+  Tcl_Interp *interp;
+  interp = Tcl_CreateInterp();
+  Tcl_Eval(interp, zLoop);    Run the Tcl input loop
+  return 0;
+}
+

+
But what about commands that span multiple lines of input?
+

+ +


+

Better Handling Of Command-Line Input

+

The file "input.tcl"

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+set line {}
+while {![eof stdin]} {
+  if {$line!=""} {
+    puts -nonewline "> "
+  } else {
+    puts -nonewline "% "
+  }
+  flush stdout
    Prompt for user input. The prompt is normally "%" + but changes to ">" if the current line is a continuation.
+  append line [gets stdin]
+  if {[info complete $line]} {
+    if {[catch {uplevel #0 $line} result]} {    If the command is complete, execute it.
+      puts stderr "Error: $result"
+    } elseif {$result!=""} {
+      puts $result
+    }
+    set line {}
+  } else {
+    append line \n
+  }
    If the command is incomplete, append a newline and get + another line of text.
+}
+ +


+

Better Handling Of Command-Line Input

+

The file "input.c"

+ + + + + + + + + + + + + +
+#include <tcl.h>

+int main(int argc, char **argv){
+  Tcl_Interp *interp;
+  interp = Tcl_CreateInterp();
+  Tcl_Eval(interp, "source input.tcl");    Read and execute the input loop
+  return 0;
+}
+

+
But now the program is not standalone!
+

+ +


+

Converting Scripts Into C Strings

+ + + + +
+static char zInputLoop[] = 
+  "set line {}\n"
+  "while {![eof stdin]} {\n"
+  "  if {$line!=\"\"} {\n"
+  "    puts -nonewline \"> \"\n"
+  "  } else {\n"
+  "    puts -nonewline \"% \"\n"
+  "  }\n"
+  "  flush stdout\n"
+  "  append line [gets stdin]\n"
+  "  if {[info complete $line]} {\n"
+  "    if {[catch {uplevel #0 $line} result]} {\n"
+  "      puts stderr \"Error: $result\"\n"
+  "    } elseif {$result!=\"\"} {\n"
+  "      puts $result\n"
+  "    }\n"
+  "    set line {}\n"
+  "  } else {\n"
+  "    append line \\n\n"
+  "  }\n"
+  "}\n"
+;
+ +


+

Compile Tcl Scripts Into C Programs

+ + + + + + + + + + + + + + + + + + + + + + +
+#include <tcl.h>
+
+
+static char zInputLoop[] = 
+  /* Actual code omitted */
+;
    Copy and paste the converted Tcl script here
+
+int main(int argc, char **argv){
+  Tcl_Interp *interp;
+  interp = Tcl_CreateInterp();
+  Tcl_Eval(interp, zInputLoop);    Execute the Tcl code
+  return 0;
+}
+ +


+

Converting Scripts To Strings
Using SED Or TCLSH

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+sed -e 's/\\/\\\\/g' \     Convert \ into \\
+  -e 's/"/\\"/g' \     Convert " into \"
+  -e 's/^/  "/' \     Add " to start of each line
+  -e 's/$/\\n"/' input.tcl    Add \n" to end of each line
+

+

+
+while {![eof stdin]} {
+  set line [gets stdin]
+  regsub -all {\} $line {&&} line    Convert \ into \\
+  regsub -all {"} $line {\"} line    Convert " into \"
+  puts "\"$line\\n\""    Add " in front and \n" at the end
+}
+ +


+

Converting Scripts Into C Strings

+

You may want to save space by removing comments and extra whitespace + from scripts.

+ + + + +
+static char zInputLoop[] = 
+  "set line {}\n"
+  "while {![eof stdin]} {\n"
+  "if {$line!=\"\"} {\n"
+  "puts -nonewline \"> \"\n"
+  "} else {\n"
+  "puts -nonewline \"% \"\n"
+  "}\n"
+  "flush stdout\n"
+  "append line [gets stdin]\n"
+  "if {[info complete $line]} {\n"
+  "if {[catch {uplevel #0 $line} result]} {\n"
+  "puts stderr \"Error: $result\"\n"
+  "} elseif {$result!=\"\"} {\n"
+  "puts $result\n"
+  "}\n"
+  "set line {}\n"
+  "} else {\n"
+  "append line \\n\n"
+  "}\n"
+  "}\n"
+;
+ +


+

Converting Scripts To Strings

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+sed -e 's/\\/\\\\/g' \ 
+  -e 's/"/\\"/g' \ 
+  -e '/^ *#/d' \     Delete lines that begin with #
+  -e '/^ *$/d' \     Delete blank lines
+  -e 's/^ */  "/' \     Delete leading spaces
+  -e 's/$/\\n"/' input.tcl

+

+

+while {![eof stdin]} {
+  set line [gets stdin]
+  set line [string trimleft $line]    Remove leading space
+  if {$line==""} continue    Delete blank lines
+  if {[string index $line 0]=="#"} {
+    continue
+  }
    Delete lines starting with #
+  regsub -all {\} $line {&&} line
+  regsub -all {"} $line {\"} line
+  puts "\"$line\\n\""
+}
+ +


+

Removing Comments Or Leading Space
Will Break Some Tcl Scripts!

+ + + + + + + + + + + + + + + + + + + + + + +
+image create bitmap smiley -data {
+#define smile_width 15
+#define smile_height 15
    These lines begin with # but are not comment
+static unsigned char smile_bits[] = {
+   0xc0, 0x01, 0x30, 0x06, 0x0c, 0x18,
+   0x04, 0x10, 0x22, 0x22, 0x52, 0x25,
+   0x01, 0x40, 0x01, 0x40, 0x01, 0x40,
+   0x12, 0x24, 0xe2, 0x23, 0x04, 0x10,
+   0x0c, 0x18, 0x30, 0x06, 0xc0, 0x01};
+}

+

+text .t
+pack .t
+.t insert end [string trim {
+She walks in beauty, like the night
+     Of cloudless climes and starry skies;
+And all that's best of dark and bright
+     Meet in her aspect and her eyes;
    Indentation is deleted on lines 2 + and 4
+}] 

+
+

+
Problems like these are rare
+

+ +


+

Adding A "continue" Command

+ + + + + + + + + + + + + +
+set line {}
+while {![eof stdin]} {
+  if {$line!=""} {
+    puts -nonewline "> "
+  } else {
+    puts -nonewline "% "
+  }
+  flush stdout
+  append line [gets stdin]
+  if {[info complete $line]} {
+    if {[lindex $line 0]=="continue"} {
+      break;
    Break out of the loop if the command + is "continue"
+    } elseif {[catch {uplevel #0 $line} result]} {
+      puts stderr "Error: $result"
+    } elseif {$result!=""} {
+      puts $result
+    }
+    set line {}
+  } else {
+    append line \n
+  }
+}
+ +


+

Stop For Tcl Input At Various Points
In A C Program

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+#include <tcl.h>

+static char zInputLoop[] = 
+  /* Tcl Input loop as a C string */
+;

+int main(int argc, char **argv){
+  Tcl_Interp *interp;
+  interp = Tcl_CreateInterp();
+  /* Application C code */    Do some computation
+  Tcl_Eval(interp, zInputLoop);    Stop for some Tcl input
+  /* More application C code */    Do more computation
+  Tcl_Eval(interp, zInputLoop);    Stop for more Tcl input
+  /* Finish up the application */    Finish the computation
+  return 0;
+}
+ +


+

Using Tcl For Testing

+ + + + + + + + + + + + + + + + + + + + + + +
+#include <tcl.h>

+static char zInputLoop[] = 
+  /* Tcl Input loop as a C string */
+;

+
+int main(int argc, char **argv){
+#ifdef TESTING
+  Tcl_Interp *interp;
    Create interpreter only if TESTING + is defined
+  interp = Tcl_CreateInterp();
+#endif
+  /* Application C code */
+#ifdef TESTING
+  Tcl_Eval(interp, zInputLoop);
+#endif
    Accept command-line input only if TESTING + is defined
+  /* More application C code */
+#ifdef TESTING
+  Tcl_Eval(interp, zInputLoop);
+#endif
+  /* Finish up the application */
+  return 0;
+}
+ +


+

Creating A New Tcl Command In C

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+#include <tcl.h>

+int NewCmd(
+  void *clientData,
+  Tcl_Interp *interp,
+  int argc,
+  char **argv
    The Tcl command is implemented as + a C function with four arguments.
+){
+  printf("Hello, World!\n");
+  return TCL_OK;    Returns TCL_OK or TCL_ERROR
+}

+static char zInputLoop[] = 
+  /* Tcl code omitted... */
+;

+int main(int argc, char **argv){
+  Tcl_Interp *interp;
+  interp = Tcl_CreateInterp();
+  Tcl_CreateCommand(interp, "helloworld",
+                    NewCmd, 0, 0);
    Tell the interpreter which C function to call when the + "helloworld" Tcl command is executed
+  Tcl_Eval(interp, zInputLoop);
+  return 0;
+}
+ +


+

Linkage From Tcl To C

+

+ +


+

Linkage From Tcl To C

+

+ +


+

When To Use A Delete Proc

+

Examples of where the delete proc is used in standard Tcl/Tk:

+ + + + + + + + + + + + + + + + + + + + + + +
+button .b -text Hello
+pack .b
+rename .b {}    Deleting the .b command causes the button to be destroyed
+

+
+image create photo smiley \ 
+    -file smiley.gif
+rename smiley {}    Deleting the smiley command destroys the image and reclaims the + memory used to hold the image
+

+ +


+

Linkage From Tcl To C

+

The argc and argv parameters work just like in + main()

+ + + + + + + +
+helloworld one {two three} four    argc = 4
+ argv[0] = "helloworld"
+ argv[1] = "one"
+ argv[2] = "two three"
+ argv[3] = "four"
+ argv[4] = NULL
+ +


+

A Short-Cut

+

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.

+ + + + + + + + + + + + + + + + + + + +
+#define TCLARGS \ 
+    void *clientData, \ 
+    Tcl_Interp *interp, \ 
+    int argc, \ 
+    char *argv
    Define TCLARGS once in a header file
+ 

+int NewCmd(TCLARGS){    Use the TCLARGS macro to define new C functions + that implement Tcl commands.
+   /* implementation... */
+}
+

+
For brevity, we will use the TCLARGS macro during the + rest of this talk.
+

+ +


+

Returning A Value From C Back To Tcl

+ + + + + + + + + + + + + + + + +
+int NewCmd(TCLARGS){    Note that the C function returns an "int"
+  return TCL_OK;    Return value is TCL_OK or TCL_ERROR
+}
+

+ +


+

Returning A Value From C Back To Tcl

+ + + + + + + + + + + + + +
+int NewCmd(TCLARGS){
+  Tcl_SetResult(interp,"Hello!",TCL_STATIC);    Set the result to "Hello!"
+  return TCL_OK;
+}
+

+ +


+

The Tcl_Obj Interface

+

+


+

The Tcl_Obj Interface

+ + + + + + + + + + + + + + + + + + + + + + +
+int NewObjCmd(
+  void *clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *const* objv    4th parameter is an array Tcl_Objs, not an array of strings
+){
+  /* Implementation... */
+  return TCL_OK;
+}

+static char zInputLoop[] = 
+  /* Tcl code omitted... */
+;

+int main(int argc, char **argv){
+  Tcl_Interp *interp;
+  interp = Tcl_CreateInterp();
+  Tcl_CreateObjCommand(interp, "newcmd",
+                       NewObjCmd, 0, 0);
    Use a different function to register the command
+  Tcl_Eval(interp, zInputLoop);
+  return 0;
+}
+ +


+

The Tcl_Obj Interface

+

+


+

Nickel Tour Of The Tcl API

+

Memory allocation functions

+
+ + + +
+ Tcl_Alloc
+
+ Tcl_Free
+
+ Tcl_Realloc
+

Functions useful in the implementation of new Tcl commands

+
+ + + +
+ Tcl_AppendElement
+ Tcl_AppendResult
+ Tcl_GetBoolean
+
+ Tcl_GetDouble
+ Tcl_GetInt
+ Tcl_GetStringResult
+
+ Tcl_ResetResult
+ Tcl_SetResult
+

Functions for controlling the Tcl interpreter

+
+ + + +
+ Tcl_CreateCommand
+ Tcl_CreateInterp
+
+ Tcl_CreateObjCommand
+ Tcl_DeleteCommand
+
+ Tcl_DeleteInterp
+ Tcl_Exit
+

+


+

Nickel Tour Of The Tcl API

+

I/O functions

+
+ + + +
+ Tcl_Close
+ Tcl_Eof
+ Tcl_Flush
+ Tcl_GetChannel
+ Tcl_GetChannelMode
+ Tcl_GetChannelName
+
+ Tcl_Gets
+ Tcl_OpenCommandChannel
+ Tcl_OpenFileChannel
+ Tcl_OpenTcpClient
+ Tcl_OpenTcpServer
+ Tcl_Read
+
+ Tcl_Seek
+ Tcl_Tell
+ Tcl_Ungets
+ Tcl_Write
+ Tcl_WriteChars
+

Names and meanings of system error codes

+
+ + + +
+ Tcl_ErrnoId
+ Tcl_ErrnoMsg
+
+ Tcl_GetErrno
+ Tcl_SetErrno
+
+ Tcl_SignalId
+ Tcl_SignalMsg
+

+


+

Nickel Tour Of The Tcl API

+

General Operating System Calls

+
+ + + +
+ Tcl_Access
+ Tcl_Chdir
+ Tcl_GetCwd
+
+ Tcl_GetHostName
+ Tcl_GetNameOfExecutable
+ Tcl_Sleep
+
+ Tcl_Stat
+

String Manipulation And Comparison

+
+ + + +
+ Tcl_Concat
+ Tcl_Merge
+
+ Tcl_SplitList
+ Tcl_StringCaseMatch
+
+ Tcl_StringMatch
+

Dynamically Resizable Strings

+
+ + +
+ Tcl_DStringAppend
+ Tcl_DStringAppendElement
+ Tcl_DStringEndSublist
+ Tcl_DStringInit
+ Tcl_DStringLength
+
+ Tcl_DStringResult
+ Tcl_DStringSetLength
+ Tcl_DStringStartSublist
+ Tcl_DStringValue
+

+


+

Nickel Tour Of The Tcl API

+

Event Handlers

+
+ + +
+ Tcl_CancelIdleCall
+ Tcl_CreateChannelHandler
+ Tcl_CreateTimerHandler
+ Tcl_DeleteChannelHandler
+
+ Tcl_DeleteTimerHandler
+ Tcl_DoOneEvent
+ Tcl_DoWhenIdle
+

Functions For Reading And Writing Tcl Variables

+
+ + + +
+ Tcl_GetVar
+ Tcl_GetVar2
+ Tcl_LinkVar
+ Tcl_SetVar
+ Tcl_SetVar2
+
+ Tcl_TraceVar
+ Tcl_TraceVar2
+ Tcl_UnlinkVar
+ Tcl_UnsetVar
+ Tcl_UnsetVar2
+
+ Tcl_UntraceVar
+ Tcl_UntraceVar2
+ Tcl_UpdateLinkedVar
+

Functions For Executing Tcl Code

+
+ + + +
+ Tcl_Eval
+ Tcl_EvalFile
+
+ Tcl_EvalObj
+ Tcl_GlobalEval
+
+ Tcl_GlobalEvalObj
+ Tcl_VarEval
+

+


+

Nickel Tour Of The Tcl API

+

Functions For Dealing With Unicode

+
+ + +
+ Tcl_NumUtfChars
+ Tcl_UniCharAtIndex
+ Tcl_UniCharIsAlnum
+ Tcl_UniCharIsAlpha
+ Tcl_UniCharIsControl
+ Tcl_UniCharIsDigit
+ Tcl_UniCharIsGraph
+ Tcl_UniCharIsLower
+ Tcl_UniCharIsPrint
+ Tcl_UniCharIsPunct
+ Tcl_UniCharIsSpace
+ Tcl_UniCharIsUpper
+ Tcl_UniCharIsWordChar
+ Tcl_UniCharLen
+ Tcl_UniCharNcmp
+ Tcl_UniCharToLower
+ Tcl_UniCharToTitle
+
+ Tcl_UniCharToUpper
+ Tcl_UniCharToUtf
+ Tcl_UniCharToUtfDString
+ Tcl_UtfAtIndex
+ Tcl_UtfBackslash
+ Tcl_UtfCharComplete
+ Tcl_UtfFindFirst
+ Tcl_UtfFindLast
+ Tcl_UtfNcasecmp
+ Tcl_UtfNcmp
+ Tcl_UtfNext
+ Tcl_UtfPrev
+ Tcl_UtfToLower
+ Tcl_UtfToTitle
+ Tcl_UtfToUniChar
+ Tcl_UtfToUniCharDString
+ Tcl_UtfToUpper
+
+

Functions For Dealing With Tcl_Objs

+
Too numerous to list...

+


+

Documentation Of The Tcl API

+

+


+

Initialization Scripts

+

+
Invoke the Tcl_Init() function to locate and read the + Tcl initialization scripts.

+


+

The Tcl_Init() Function

+ + + + + + + + + + + + + +
+#include <tcl.h>

+static char zInputLoop[] = 
+  /* Tcl code omitted... */
+;

+int main(int argc, char **argv){
+  Tcl_Interp *interp;
+  interp = Tcl_CreateInterp();
+  Tcl_Init(interp);    Locate and read the initialization scripts
+  /* Call Tcl_CreateCommand()? */
+  Tcl_Eval(interp, zInputLoop);
+  return 0;
+}
+

+
But Tcl_Init() can fail. We need to check its return value...
+

+ +


+

The Tcl_Init() Function

+ + + + + + + + + + + + + +
+#include <tcl.h>

+static char zInputLoop[] = 
+  /* Tcl code omitted... */
+;

+int main(int argc, char **argv){
+  Tcl_Interp *interp;
+  interp = Tcl_CreateInterp();
+  if( Tcl_Init(interp)!=TCL_OK ){
+    fprintf(stderr,"Tcl_Init() failed: PX",
+       Tcl_GetStringResult(interp));
+  }
    Print error message if Tcl_Init() fails
+  /* Call Tcl_CreateCommand()? */
+  Tcl_Eval(interp, zInputLoop);
+  return 0;
+}
+

+
But now the program is not standalone.
+

+ +


+

How Tcl_Init() Works

+

+


+

The Default initTcl Procedure

+ + + + +
+set errors {}
+set dirs {}
+if {[info exists tcl_library]} {
+  lappend dirs $tcl_library
+} else {
+  if {[info exists env(TCL_LIBRARY)]} {
+    lappend dirs $env(TCL_LIBRARY)
+  }
+  lappend dirs $tclDefaultLibrary
+  unset tclDefaultLibrary
+  set dirs [concat $dirs $tcl_libPath]
+}
+foreach i $dirs {
+  set tcl_library $i
+  set tclfile [file join $i init.tcl]
+  if {[file exists $tclfile]} {
+    if {![catch {uplevel #0 [list source $tclfile]} msg]} {
+      return
+    } else {
+      append errors "$tclfile: $msg\n$errorInfo\n"
+    }
+  }
+}
+error "Can't find a usable init.tcl ..."
+ +


+

The Default Initialization Sequence

+

+
Commands defined in the initialization scripts are loaded + on demand.

+


+

Standalone Initialization Techniques

+

Manually execute all initialization scripts

+ +
This approach is not recommended

+


+

Standalone Initialization Techniques

+

Redefining the builtin source command

+

+


+

Redefining source

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+static char zInitTcl[] = "...";
+static char zParrayTcl[] = "...";
    Scripts init.tcl and parray.tcl
+
+int NewSourceCmd(TCLARGS){
+  if( !strcmp(argv[1],"/builtin/init.tcl") )
+    return Tcl_Eval(interp, zInitTcl);
+  if( !strcmp(argv[1],"/builtin/parray.tcl") )
+    return Tcl_Eval(interp, zParrayTcl);
    Call Tcl_Eval() on builtin strings if the names match
+  return Tcl_EvalFile(interp, argv[1]);    Call Tcl_EvalFile() if no match
+}

+int main(int argc, char **argv){
+  Tcl_Interp *interp;
+  setenv("TCL_LIBRARY","/builtin");    Causes tclInit to look for init.tcl in /builtin
+  interp = Tcl_CreateInterp();
+  Tcl_CreateCommand(interp, "source",
+                    NewSourceCmd, 0, 0);
    Redefine source
+  Tcl_Init(interp);
+  Tcl_Eval(interp, zInputLoop);
+  return 0;
+}
+ +


+

Redefining source

+

+


+

Standalone Initialization Techniques

+

Use the Tcl*InsertProc() functions

+

+


+

The TclStatInsertProc() Function

+

+


+

The TclStatInsertProc() Function

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+#include <tclInt.h>    Rather than <tcl.h>!
+
+static int
+BltinFileStat(char *path,struct stat *buf){
+  char *zData;
+  int nData;
+  zData = FindBuiltinFile(path, 0, &nData);    Check if path is a builtin
+  if( zData==0 ){
+    return -1;
+  }
    Fail if path is not a builtin
+  memset(buf, 0, sizeof(*buf));
+  buf->st_mode = 0400;
+  buf->st_size = nData;
+  return 0;    Success if it is builtin
+}

+int main(int argc, char **argv){
+  Tcl_Interp *interp;
+  TclStatInsertProc(BltinFileStat);    Register new stat function
+  interp = Tcl_CreateInterp();
+  Tcl_Init(interp);
+  Tcl_Eval(interp, zInputLoop);
+  return 0;
+}
+ +


+

The TclAccessInsertProc() Function

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+#include <tclInt.h>    Rather than <tcl.h>!
+
+/* BltinFileStat() not shown... */

+static int
+BltinFileAccess(char *path, int mode){
+  char *zData;
+  if( mode & 3 ) return -1;    All builtins are read-only
+  zData = FindBuiltinFile(path, 0, &nData);    Check if path is a builtin
+  if( zData==0 ) return -1;    Fail if path is not a builtin
+  return 0;    Success if it is builtin
+}

+int main(int argc, char **argv){
+  Tcl_Interp *interp;
+  TclStatInsertProc(BltinFileStat);
+  TclAccessInsertProc(BltinFileAccess);
    Register new stat and access functions
+  interp = Tcl_CreateInterp();
+  Tcl_Init(interp);
+  Tcl_Eval(interp, zInputLoop);
+  return 0;
+}
+ +


+

The TclOpenFileChannelInsertProc() Function

+ + + + +
+static Tcl_Channel BuiltinFileOpen(
+  Tcl_Interp *interp,   /* The TCL interpreter doing the open */
+  char *zFilename,      /* Name of the file to open */
+  char *modeString,     /* Mode string for the open (ignored) */
+  int permissions       /* Permissions for a newly created file (ignored) */
+){
+  char *zData;
+  BuiltinFileStruct *p;
+  int nData;
+  char zName[50];
+  Tcl_Channel chan;
+  static int count = 1;

+  zData = FindBuiltinFile(zFilename, 1, &nData);
+  if( zData==0 ) return NULL;
+  p = (BuiltinFileStruct*)Tcl_Alloc( sizeof(BuiltinFileStruct) );
+  if( p==0 ) return NULL;
+  p->zData = zData;
+  p->nData = nData;
+  p->cursor = 0;
+  sprintf(zName,"etbi_bffffc7c_8049b04",((int)BuiltinFileOpen)>>12,count++);
+  chan = Tcl_CreateChannel(&builtinChannelType, zName, 
+                           (ClientData)p, TCL_READABLE);
+  return chan;
+}
+ +


+

The TclOpenFileChannelInsertProc() Function

+ + + + +
+static Tcl_ChannelType builtinChannelType = {
+  "builtin",          /* Type name. */
+  NULL,               /* Always non-blocking.*/
+  BuiltinFileClose,   /* Close proc. */
+  BuiltinFileInput,   /* Input proc. */
+  BuiltinFileOutput,  /* Output proc. */
+  BuiltinFileSeek,    /* Seek proc. */
+  NULL,               /* Set option proc. */
+  NULL,               /* Get option proc. */
+  BuiltinFileWatch,   /* Watch for events on console. */
+  BuiltinFileHandle,  /* Get a handle from the device. */
+};
+

+

For additional information see:

+ +

+ +


+

Initializing Tk

+

+


+

Implementing An Event Loop

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+button .b -text Hello -command exit
+pack .b
    Create a Tk interface
+
+
+bind . <Destroy> {
+  if {![winfo exists .]} exit
+}
    Close the application when the main window + is destroyed
+
+
+while 1 {vwait forever}    The event loop
+ +


+

"Hello, World!" Using Tk

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+#include <tk.h>

+
+static char zHello[] =     The application code
+  "button .b "
+    "-text {Hello, World} "
+    "-command exit\n"
+  "pack .b\n";

+
+static char zEventLoop[] =    The event loop
+  "bind . <Destroy> {\n"
+  "  if {![winfo exists .]} exit\n"
+  "}\n"
+  "while 1 {vwait forever}\n";

+
+int main(int argc, char **argv){
+  Tcl_Interp *interp;
+  interp = Tcl_CreateInterp();
+  Tcl_Init(interp);
+  Tk_Init(interp);
    We really should check the return values of the init functions...
+  Tcl_Eval(interp, zHello);
+  Tcl_Eval(interp, zEventLoop);    The event loop never returns
+  /*NOTREACHED*/
+}
+ +


+

Compiling "Hello, World!" For Tk

+

Unix:

+
+  $ gcc hello.c -ltk -L/usr/X11R6/lib \
+        -lX11 -ltcl -lm -ldl
+  $ ./a.out
+ +

Windows using Cygwin:

+
+  C:> gcc hello.c -mwindows -ltk80 -ltcl80 -lm
+  C:> a.exe
+ +

Windows using Mingw32:

+
+  C:> gcc -mno-cygwin hello.c -mwindows \
+           -ltk82 -ltcl82 -lm
+  C:> a.exe

+


+

Making The Program Standalone

+

To make a Tcl application standalone you have to convert the following + initialization scripts to C strings and compile them into the + executable:

+ + + + + +
+   auto.tcl
+   history.tcl
+   init.tcl +
+   ldAout.tcl
+   package.tcl +
+   parray.tcl
+   safe.tcl +
+   tclIndex
+   word.tcl +
+ +

To make a Tk application standalone requires these additional + initialization scripts from the Tk Library:

+ + + + + +
+   bgerror.tcl
+   button.tcl
+   clrpick.tcl
+   comdlg.tcl
+   console.tcl
+   dialog.tcl +
+   entry.tcl
+   focus.tcl
+   listbox.tcl
+   menu.tcl
+   msgbox.tcl
+   optMenu.tcl +
+   palette.tcl
+   safetk.tcl
+   scale.tcl
+   scrlbar.tcl
+   tclIndex
+   tearoff.tcl +
+   text.tcl
+   tk.tcl
+   tkfbox.tcl
+   xmfbox.tcl +
+ +

Total of about 13K lines and 400K bytes of text or 9K lines and + 250K bytes if you strip comments and leading spaces

+


+

A Review Of The Features We Want

+

    +
  1. + Combine C/C++ with Tcl/Tk into a single executable. +
+ +
    +
  1. + The executable should be standalone. It must not depend + on files not normally found on the system. +
+ +
    +
  1. + It should be difficult for end users to alter the program + (and introduce bugs). +

+


+

Available Programming Aids

+

Several tools are available. The chart below shows which tools + help achieve which objectives.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Features The Tool Helps To Achieve
Tool NameMix C and TclStandaloneHide Source
SWIG  
TclPro Wrapper 
FreeWrap 
Wrap  
mktclapp

+


+

SWIG

+ +

  • Creates an interface between an existing C/C++ library and a high-level + programming language. Support for: +
      +
    • Tcl/Tk
    • +
    • Perl
    • +
    • Python
    • +
    • Java
    • +
    • Eiffel
    • +
    • Guile
    • +
  • No changes required to C/C++ code. Can be used with legacy libraries.
  • Generates an extension, not a standalone binary
  • The tutorial on SWIG was yesterday afternoon.
  • http://www.swig.org/

+ +


+

Wrapper Programs

+ +

  • Convert a pure Tcl/Tk program into a standalone binary
  • Several wrapper programs are available: +
      +
    • TclPro Wrapper - http://www.scriptics.com/
    • +
    • FreeWrap - http://www.albany.net/~dlabelle/freewrap/freewrap.html
    • +
    • Wrap - http://members1.chello.nl/~j.nijtmans/wrap.html
    • +
  • No C compiler required!
  • TclPro will convert Tcl script into bytecode so that it cannot be + easily read by the end user. FreeWrap encrypts the scripts.
  • FreeWrap uses compression on its executable. + Wrap uses compression on both the executable and on the bundled script files.
  • Usually include extensions like winico and/or BLT

+ +


+

mktclapp

+ +

  • Mix C/C++ with Tcl/Tk into a standalone binary
+
  • mktclapp generates an application initialization file + that contains Tcl scripts as strings and makes all necessary calls + to Tcl_Init, Tcl_CreateCommand, + Tcl*InsertProc, etc.
  • Features to make it easier to write new Tcl command in C
  • xmktclapp.tcl provides a GUI interface to mktclapp
  • http://www.hwaci.com/sw/mktclapp/

+ +


+

"Hello, World!" Using Mktclapp

+

+


+

"Hello, World!" Using Mktclapp

+ +

  • Set "Command Line Input?" to "None"
  • Set "Standalone?" to "Yes"
  • Enter "hw.mta" for the Configuration File
  • Enter "hw.c" for the Output C File

+ +


+

"Hello, World!" Using Mktclapp

+ +

  • Go to the "Tcl Scripts" page
  • Press "Insert" and add hw.tcl to the list of + Tcl scripts
  • Change the "Startup Script" to be hw.tcl.
  • Select File/Build and File/Exit

+ +


+

"Hello, World!" Using Mktclapp

+

+


+

Adding C Code To Your Program

+

Put the new C code in a new source file named "add.c"

+ + + + + + + + + + + + + + + + + + + +
+#include "hw.h"    Generated by mktclapp
+
+int ET_COMMAND_add(ET_TCLARGS){    ET_TCLARGS is a macro defined in hw.h
+  int a, b;
+  char zResult[30];
+  a = atoi(argv[1]);
+  b = atoi(argv[2]);
+  sprintf(zResult, "-1073742724", a+b);
+  Tcl_SetResult(interp, zResult, TCL_VOLATILE);
+  return TCL_OK;
+}
+ +


+

Adding C Code To Your Program

+ +

  • Go to the "C/C++ Modules" page of xmktclapp.tcl
+
  • Press "Insert" and add add.c to the list of + C/C++ modules

  • Select File/Build and File/Exit

+ +


+

Adding C Code To Your Program

+

+
Don't have to worry with Tcl_CreateCommand() - Mktclapp takes + care of that automatically.

+


+

Checking Parameters In The add Command

+

Modify add.c to insure the add command + is called with exactly two integer arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+#include "hw.h"

+int ET_COMMAND_add(ET_TCLARGS){
+  int a, b;
+  char zResult[30];
+  if( argc!=3 ){
+    Tcl_AppendResult(interp,
+      "wrong # args: should be: \"",
+      argv[0], " VALUE VALUE\"", 0);
+    return TCL_ERROR;
+  }
    Report an error if there are not exactly + 2 arguments
+  if( Tcl_GetInt(interp, argv[1], &a)!=TCL_OK ){
+    return TCL_ERROR;
+  }
    Report an error if the first argument is + not an integer
+  if( Tcl_GetInt(interp, argv[2], &b)!=TCL_OK ){
+    return TCL_ERROR;
+  }
    Do the same for the second argument
+  sprintf(zResult, "-1073742724", a+b);
+  Tcl_SetResult(interp, zResult, TCL_VOLATILE);
+  return TCL_OK;
+}
+ +


+

Using The Tcl_Obj Interface

+

In the file objadd.c put this code:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+#include "hw.h"
+
+int ET_OBJCOMMAND_add2(ET_OBJARGS){
+  int a, b;
    Use "ET_OBJCOMMAND" instead of "ET_COMMAND" and + "ET_OBJARGS" instead of "ET_TCLARGS"
+  if( objc!=3 ){
+    Tcl_WrongNumArgs(interp, 1, objv,
+      "number number");
+    return TCL_ERROR;
+  }
    A special routine for "wrong # args" error
+  if( Tcl_GetIntFromObj(interp, objv[1], &a) ){    Instead of Tcl_GetInt
+    return TCL_ERROR;
+  }
+  if( Tcl_GetIntFromObj(interp, objv[2], &b) ){
+    return TCL_ERROR;
+  }
+  Tcl_SetIntObj(Tcl_GetObjResult(interp), a+b);    Result stored as integer, not a string
+  return TCL_OK;
+}
+ +


+

Speed Of Tcl_Obj Versus "char*" Interfaces

+

+
In many real-world problems, the Tcl_Obj interface has no noticeable + speed advantage over the string interface.

+


+

More About Built-in Tcl Scripts

+ +

  • Comments and leading white-space are removed from the + script by default. Use the "Don't Strip Comments" + button to change this.
  • The file name must exactly match the name that is + used by the source command.

+ +


+

Locations Of Libraries

+ +

  • Tells mktclapp where to look for script libraries.
  • All Tcl scripts in the indicated directories are + compiled into the appinit.c file.
  • Comments and extra white-space are removed. + There is no way to turn this off.

+ +


+

Built-in Binary Data Files

+ +

  • Arbitrary files become part of the virtual filesystem
  • No comment or white-space removal is attempted
  • Useful for images or other binary data

+ +


+

New Commands In Namespaces

+

Two underscores (__) are replaced by two colons (::) in + command names, thus giving the ability to define new commands + in a namespace

+ + + + + + + + + + + + + +
+#include <hw.h>
+
+int ET_COMMAND_adder__add(ET_TCLARGS){
+  int a, b;
    Creates the Tcl command called "adder::add"
+  char *zResult[30];
+  if( argc!=3 ){
+    Tcl_AppendResult(interp,
+      "wrong # args: should be: \"",
+      argv[0], " VALUE VALUE\"", 0);
+    return TCL_ERROR;
+  }
+  if( Tcl_GetInt(interp, argv[1], &a)!=TCL_OK ){
+    return TCL_ERROR;
+  }
+  if( Tcl_GetInt(interp, argv[1], &b)!=TCL_OK ){
+    return TCL_ERROR;
+  }
+  sprintf(zResult, "-1073742724", a+b);
+  Tcl_SetResult(interp, zResult, TCL_VOLATILE);
+  return TCL_OK;
+}
+ +


+

Adding Your Own main()

+ + + + + + + + + + + + + +
+int main(int argc, char **argv){
+  /* Application specific initialization */
+  Et_Init(argc, argv);    Never returns!
+  /*NOTREACHED*/
+  return 0;
+}
+

+
The "Autofork" feature is disabled if you supply your own main()
+

+ +


+

Initializing The Tcl Interpreter

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+#include <tcl.h>

+int counter = 0;

+int main(int argc, char **argv){
+   Et_Init(argc, argv);
+   /*NOTREACHED*/
+   return 0;
+}

+int Et_AppInit(Tcl_Interp *interp){
+  if( Blt_Init(Interp) ){
+    return TCL_ERROR;
+  }
    Example: Initialize an extension
+  Tcl_LinkVar(interp, "counter", &counter,
+              TCL_LINK_INT);
    Or link a C variable to a Tcl variable
+  return TCL_OK;    Return TCL_OK if successful
+}
+ +


+

Writing Your Own Event Loop

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+#include <tcl.h>
+
+void Et_CustomMainLoop(Tcl_Interp *interp){    Replaces the default event loop
+  return;    Ex: Return without handling any events.
+}

+int main(int argc, char **argv){
+  Et_Init(argc, argv);    This now returns after initializing Tcl
+  /* Application code here */
+  return 0;
+}
+ +


+

Writing Your Own Event Loop

+ + + + + + + + + + + + + + + + + + + + + + +
+#include <tcl.h>

+void Et_CustomMainLoop(Tcl_Interp *interp){
+  for(;;){
+    Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT);
+    /* Other processing... */
+  }
    Intermix processing and event handling
+}

+int main(int argc, char **argv){
+  Et_Init(argc, argv);    Never returns
+  /*NOTREACHED*/
+  return 0;
+}
+ +


+

Mktclapp Initialization Sequence

+

+


+

Invoking Tcl From C

+

+


+

Invoking Tcl From C

+

Example: A C function that pops up an error message dialog box

+ + + + +
+#include "appinit.h"

+void ErrMsg(char *zMsg){
+  Tcl_SetVar(Et_Interp, "zMsg", zMsg, TCL_GLOBAL_ONLY);
+  Tcl_GlobalEval(Et_Interp, 
+    "tk_messageBox -icon error -msg $zMsg -type ok");
+  Tcl_UnsetVar(Et_Interp, "zMsg", TCL_GLOBAL_ONLY);
+}
+ +


+

Invoking Tcl From C

+

The same C function implemented using Et_EvalF() instead + of Tcl_GlobalEval()

+ + + + +
+#include "appinit.h"

+void ErrMsg(char *zMsg){
+  Et_EvalF(Et_Interp, 
+    "tk_messageBox -icon error -msg {PX} -type ok",
+    zMsg);
+}
+

+

+ + + + +

+ +


+

Invoking Tcl From C

+

Use the "" format to generate a quoted string

+ + + + +
+#include "appinit.h"

+void ErrMsg(char *zMsg){
+  Et_EvalF(Et_Interp, 
+    "tk_messageBox -icon error -msg \"%\" -type ok",
+    zMsg);
+}
+

+ +


+

Other Functions Provided By Mktclapp

+

+


+

Operating Mktclapp From The Command Line

+

+


+

Format Of An MTA File

+ + + + + + + + + + + + + + + + + + + +
+# Configuration file generated by xmktclapp
+# Hand editing is not recommended
+#
    Comments begin with one #
+## Autofork No
+## CFile:add.c 1
+## CFile:objadd.c 1
+## CmdLine Console
+## ConfigFile hw.mta
+## Data:check.gif 1
+## MainScript hw.tcl
+## Mode Tcl/Tk
+## NoSource No
+## OutputFile hw.c
+## Shroud No
+## Standalone Yes
+## TclFile:hw.tcl 1
+## TclLib /usr/lib/tcl8.0
+## TkLib /usr/lib/tk8.0
    Lines beginning with two #s are used + by xmktclapp.tcl and ignored by mktclapp
+-console
+-main-script "hw.tcl"
+-tcl-library "/usr/lib/tcl8.0"
+-tk-library "/usr/lib/tk8.0"
+"add.c"
+"objadd.c"
+-i "check.gif"
+-strip-tcl "hw.tcl"
    All other lines are read by mktclapp and + ignored by xmktclapp.tcl
+ +


+

Summary

+

+


-- cgit v1.2.3-70-g09d2