Previous Next Contents

Implementor's Manual

Tk is still evolving, and also offers numerous user-contributed extensions. To ensure the compatibility of CamlTk with this evolution, we chose to divide the library in two parts:

This chapter defines the syntax and semantics of the CamlTk description language, as well as some technical details of the protocol.

The CamlTk description language

The language allows the specification of

widgets
of the standard Tk library, with options and associated commands,
functions
not associated with a specific widget class
modules
grouping families of functions.

A source file is simply a collection of entries, each entry describing an element of the interface. The tkcompiler provided in this distribution takes the source file and produces Caml Light modules forming the interface library.

Tk tokens

In the heart of the interface, Caml Light needs to call TclTk procedures with arguments. Essentially, arguments are lists of tokens (strings). In some cases an argument will itself be a list of strings. It is mandatory that the descriptions of types and procedure respects the ``tokenization'' of arguments.

The syntax for describing translations respecting tokens is called a template. A template is a list of tokens, each token being

a string,
e.g. "foobar", used as is by Tk.
a type,
e.g. Color. The translation of a value of this type may result in several tokens
a list of tokens

Types

A type entry contains information to translate data from Caml Light to Tk and the converse. Each data type that needs to be exchanged by Caml Light and Tk must be described by a type entry. For each type, a conversion function from Caml Light to Tk is generated, as well as a parsing function from Tk to Caml Light if required (because values of the type are returned by some function in the library) and feasible.

A type is given by a set of value constructors, as in Caml Light. For each constructor, one must give the Caml Light name, and its template.

Example
type State { 
   Normal ["normal"]
   Active ["active"]
   Disabled ["disabled"]
}
defines the Caml Light type
type State =
          Disabled
        | Active
        | Normal
;;
and the conversion function
let cCAMLtoTKState = function
	Disabled -> TkToken"disabled"
	| Active -> TkToken"active"
	| Normal -> TkToken"normal"
;;

The parser can be generated only in restricted cases: zeroary constructors, and at most one anonymous int constructor and one anonymous string constructor (anonymous means that the template is composed only with this token). Otherwise, the compiler will produce a warning message, and you have to write the parser yourself.

Subtypes

As customary, types will be statically verified in Caml Light programs. However, in order to reduce the number of value constructors required by the interface (and associated naming problems), we introduced a notion of subtypes. A subtype is a named subset of the set of constructors of a type. In this case, type-checking (i.e. verifiying that a constructor belongs to a subtype) will occur at run-time.

If a type requires subtyping, then one should not declare the type itself, but instead define each of its subtypes. The compiler will consider the whole set of constructors for the definition of the type. Note also that the definition of a constructor may be omitted if it has already been declared.

Example
subtype option(standard) {
   ActiveBackground		["-activebackground"; Color]
   ActiveBorderWidth		["-activeborderwidth"; Units]
   ActiveForeground		["-activeforeground"; Color]
   Anchor			["-anchor"; Anchor]
   ...
   }
subtype option (bitmap) {
   Anchor
   Background
   ...
   }
defines two subtypes (standard and bitmap) of the type option.

Subtypes may be referred to (in argument types of functions) with the same syntax: type(subtype).

Example
   function () delete [widget(listbox); "delete"; Index(listbox); Index(listbox)]

Widgets

A widget class description is composed of its name, the set of valid options for the creation of widgets of this class, and the set of functions and commands associated to the class.

Options descriptions follow the same syntax as value constructors in type or subtype declarations, except for the presence of the option keyword. Actually, the options declared inside a widget description form a subtype (with the name of the class) of the option type. option is not really hardwired in the compiler, in that it obeys the same rules for subtyping as user-defined types. However the option type is implicitly used when producing the widget creation functions.

Function declarations are formed of

The Caml Light function will have as many arguments as types appearing in the template, in the same order.

Example
widget message {
   option Anchor
   option Background
   option Borderwidth
   option Cursor
   option Font
   option Foreground
   option PadX
   option PadY
   option Relief
   option Text
   option TextVariable
   option Width

   option Aspect ["-aspect"; int]
   option Justify ["-justify"; Justification]

   function () configure [widget(message); "configure"; option(message) list]
   function (string) configure_get [widget(message); "configure"]
   }
Note that the first options are given implicitly, since they belong to ``standard'' options, defined elsewhere. The two others are given in their full form.

Each widget class is compiled into a separate module (bearing the name of the widget class). Besides the functions described in the entry, the compiler produces two creation functions

value create : Widget -> option list -> Widget ;;
value create_named : Widget -> string -> option list -> Widget ;;
with, in fact, run-time verification of options, who must belong to the subtype option(widgetname). The first argument of create is the parent of the created widget. The second argument of create_named is the new path component of the widget.

Modules

A module is simply a set of functions to be grouped in a separate module.
Example
module selection {
   function () clear ["selection"; "clear"; widget]
   function (string) get ["selection"; "get"]
   }

Builtins

Some datatypes cannot be described in the syntax of the interface description language, because they require custom converters. Thus it is possible to write them directly in Caml Light and place them in a builtin_*.ml file. To avoid a warning message from the compiler, one may also declare the type as external
Example
type Units external     # builtin_GetPixel
For each builtin type foo, one should provide a cCAMLtoTKfoo function, of type foo -> protocol__TkArgs, respecting the tokenization. If data of this type is to be returned by a function, one should also write a parser cTKtoCAMLfoo. For example, the file builtin_GetPixel.ml contains the Units type, used extensively in Tk for specifying distances or coordinates. It requires both a pretty-printer and a parser (which emulates the tkGetPixel function in the Tk library).

Compiling

The interface description source is compiled with
$ tkcompiler filename
(the file compiled defaults to Widgets.src if filename is omitted). The compiler requires the existence of a lib subdirectory, where it will produce tkgen.ml, modules, and various Caml Light files corresponding to entries in the interface description source.

The compiler will report the following errors

lexical errors
syntax errors
see the grammar of the language below
duplicate definitions
illegal implicit use of constructors
cyclic dependancy on types

Producing the library requires the presence of several files in the lib directory. For more details, check the Makefile in lib.

Run-time support

Apart from the modules produced by compiling the widget description file, the CamlTk interface uses low-level support for some basic data types and communication protocol between Caml Light and Tcl.

Widget support

Widget naming
: in the contrary of the Tcl/Tk approach, CamlTk keeps the naming of widgets internal to the library. Moreover, there is no particular data associated to widgets in the Caml Light half of the world. A widget is essentially its Tk path. The public functions are:

value default_toplevel_widget : Widget
      	(* The default toplevel widget is ".", of type "toplevel" *)
;;
value widget_atom : Widget -> string -> Widget
;;

And some functions used internally are

value widget_name : Widget -> string
      (* Return the name (tk "path") of a widget *)
and  widget_class : Widget-> string
      (* Returns the class of a widget *)
;;
value new_widget_atom : string -> Widget -> Widget
and   new_named_widget : string -> Widget -> string -> Widget
      (* Abstract creation functions *)
;;

Widget typing
: CamlTk attempts to check widget types before sending code to Tcl. We chose to have a unique Caml Light type for widgets, Widget, and to check dynamically when necessary if a widget is of the required class. Thus, internally,
type Widget =
  Untyped of string
| Typed of string * string
;;
We also maintain a table associating widget paths to Widgets. The reason for this table is that we may have to dynamically retype a widget path returned by a Tcl function.

Transferring control

Caml Light has been extended with primitives for evaluating Tcl procedures.

tcl_eval
evaluates a Tcl source phrase. The implementation of tcl_eval is straightforward. During this evaluation, Tcl will perform all customary substitutions and argument parsing. Thus, one needs to be careful with Tcl special characters. This function is deprecated, and tcl_direct_eval is preferred.

tcl_direct_eval
evaluates a Tcl procedure call. It takes as argument a value of type TkArgs vect, which could be thought of as an argv parameter. Note however that the vector may still contain token lists that have to be expanded.

Tcl has been extended with a new command camlcb to call Caml Light (or more precisely a given Caml Light function). camlcb relies on the callback primitive of the Caml Light runtime. Currently, the Caml Light runtime does not allow storage of Caml Light values in C global variables, because global variables cannot be roots for the garbage collector. The implementation of camlcb uses the same trick as signal handling.

Other CamlTk primitives

The other primitives, written in C, added to the Caml Light runtime deal with

initialising a Tcl interpreter
: straightforward
file descriptor callbacks
: the mechanism of file descriptor callbacks is available in Tk, although not as Tcl commands. We simply provide a Caml Light primitive to deal with this form of callbacks.

Transferring data

We provide extensible buffers to simplify the production of the Tcl source code to be evaluated by Tcl.

Callback handling

Callbacks are Caml Light functions. They have to be invoked by Tcl, using the camlcb command. The link between Tcl and Caml Light is made through a hash-table, associating an identifier (string) to a Caml Light function.

In order to avoid space leaks due to storing callback closures in the table, we implement a memo-mechanism associating callbacks to widgets. Except for class wide callbacks, each callback is defined for a given widget. When this widget is destroyed, the callback cannot be invoked any more, and we can remove it from the table. We thus provide a general binding for all widgets, on event Destroy, that will remove all callbacks associated to a widget being destroyed. If a user bindings masks the default binding for the Destroy event, its callback should call the function protocol__remove_callbacks with argument the widget being destroyed.


Previous Next Contents