How to Create ToonTalk Extensions
For very advanced users only:
Programmers in languages like C or Pascal can define new extensions to ToonTalk. A ToonTalk user thinks of an extension is a faraway place that cannot be visited. But a user can obtain a bird which will "fly" there. The bird can even bring along other birds who can fly back with things made by the extension. Extensions can make any capability of the underlying operating system (e.g. Windows) available to users inside of ToonTalk. Examples include files, window management, Internet access, producing music or 3D graphics, and much more. If you do make a ToonTalk extension, please contribute it to the larger ToonTalk community by sending it to support@toontalk.com.
Extensions are implemented as Microsoft Windows DLLs (dynamic link libraries). The library needs only to export one procedure. The procedure's type should be:
BOOL __declspec(dllexport) receive(HANDLE handle, void **data_in, char *types_in, char *label, char *country_code, void ***data_out, char **types_out, char **to_say, BOOL *ok_to_speak, HINSTANCE string_library);
If a bird connected to an extension is given a box then the receiver associated with that bird is called with the following arguments:
HANDLE handle.
A handle associated with the bird. Will be NULL unless the bird was created by an extension. Used, for example, by the file extension to hold the file handle.
void **data_in
data_in is an array of pointers. It is interpreted relative to types described in types_in. data_in is flat, i.e. elements of data_in are not themselves pointers to arrays of pointers.
char *types_in
A null-terminated string describing the ToonTalk types of the box received by the bird. L is for a long integer from a ToonTalk number pad. S is for a string for a ToonTalk text pad. H is for a handle of a bird to an extension. Square brackets ([]) enclose the elements of a box. A minus sign (-) indicates an empty hole of a box. B indicates a normal bird. ? is for all other ToonTalk objects. For L, S, B, - and ? there is a corresponding data item in the data argument described above. The elements of a box inside a box occur in place in data_in. A handle (H) needs 3 data items: the receiver procedure, the associated handle, and a string that labels the handle.
char *label
A null-terminated string labeling this bird. Typically can be seen on the T-shirt of the bird.
char *country_code
A null-terminated 2 letter string indicating the country code of this version of ToonTalk. Examples include "US", "UK", "SE", "DE", and "BR". This argument is provided in case the extension wants Marty to speak or to put up a dialog box in a language sensitive manner.
void ***data_out
This is an array of pointers -- one for each bird in the box given to the extension bird. This enables an extension to specify what items should be given to birds that it receives. Each element of data_out should be set to another array of pointers whose length and elements are consistent with the corresponding elements in types_out (described below). Note that storage allocated for the values of types_out should be allocated in Windows global heap. (E.g. using GlobalAlloc.)
char **types_out
This is an array of pointers -- one for each bird in the box given to the extension bird. To specify what a bird should receive set the corresponding element (its occurrence while scanning from left to right) to a globally allocated null-terminated string that describes the types in exactly the same manner as types_in. For each element in types_out, a ToonTalk item is created using the data in data_out.
char **to_say
Should be set to a null-terminated globally allocated string if the extension wishes Marty to say something.
BOOL *ok_to_speak
This should be set to non-zero if, when Marty is to speak the to_say string, he should use a text-to-speech engine. If not set, then Marty will use talk balloons.
HINSTANCE string_library
This is a handle on the currently loaded ToonTalk string resource DLL. It should be ignored unless the writer of the extension has access to ToonTalk's string table.
Returns.
The receiver procedure should return non-zero if the data_out and types_out variables were set.
Remarks.
If types_in is NULL then the procedure is being called so that Marty can describe what this bird does. If types_in is "H", then the last bird associated with handle is being destroyed. Here is an opportunity to clean up (e.g. close file handles).
The DEF file for the DLL should contain the line:
EXPORTS receive@1
The DLL should be named "TT" followed by the extension name. Simply placing the DLL file in the ToonTalk directory installs it.
Sample code.
Here is an example definition of a file extension defined in C++ (though with only minor differences could have been defined in C).
// Copyright (c) 1992,1998. Ken Kahn, Animated Programs,
All rights reserved.
// You may copy and modify this file so long as you keep
this copyright notice.
#include <windows.h>
extern "C" int __declspec(dllexport) WEP (int
nParam);
extern "C" BOOL __declspec(dllexport)
receive(HANDLE handle, void **data, char *types, void ***out, char **out_types, char
**to_say, HINSTANCE string_library);
int FAR PASCAL LibMain (HANDLE , WORD , WORD , LPSTR ) {
return 1 ;
}
int __declspec(dllexport) WEP (int) {
return 1 ;
}
char *copy_string(char *source, int length) {
if (source == NULL) return(NULL);
if (length <= 0) length = strlen(source);
char *destination = (char *) GlobalAlloc(0,length+1); //
Can't use library's local storage
memcpy(destination,source,length);
destination[length] = '\0'; // terminate the string
return(destination);
};
// This receiver procedure is associated with opened files
BOOL file_receive(HANDLE handle, void **data, char *types,
char *label, char *country_code,
void ***out, char **out_types, char **to_say, BOOL
*ok_to_speak, HINSTANCE string_library) {
if (types == NULL) { // want help
*to_say = copy_string("This is a file handle.",0);
// could give better help
return(TRUE);
};
if (strcmp(types,"[SLB]") == 0) {
if (stricmp((char *) data[0],"Read") == 0) { //
box contains "Read", a number, followed by a bird
long number_of_bytes_to_read = (long) data[1];
DWORD number_of_bytes_read;
char *buffer = (char *)
GlobalAlloc(0,number_of_bytes_to_read+1);
if
(!ReadFile(handle,buffer,number_of_bytes_to_read,&number_of_bytes_read,NULL)) {
// set *to_say to describe the error.
};
// give the bird in the third hole of the box a string
containing the characters just read
out_types[0] = copy_string("S",1);
out[0] = (void * *) GlobalAlloc(0,sizeof(void*));
buffer[number_of_bytes_read] = '\0'; // terminate string
out[0][0] = (void *) buffer;
return(TRUE);
} else if (stricmp((char *) data[0],"Read Bytes")
== 0) {
long number_of_bytes_to_read = (long) data[1];
DWORD number_of_bytes_read;
char *buffer = (char *)
GlobalAlloc(0,number_of_bytes_to_read+1);
if
(!ReadFile(handle,buffer,number_of_bytes_to_read,&number_of_bytes_read,NULL)) {
// set *to_say to describe the error.
};
// give the bird a box which for each character read
contains an integer whose value is the ASCII encoding of the character
out_types[0] = (char *)
GlobalAlloc(0,number_of_bytes_read+3); // 3 extra for [] and terminator
out[0] = (void * *)
GlobalAlloc(0,sizeof(void*)*number_of_bytes_read);
int out_type_index = 0;
out_types[0][out_type_index++] = '[';
for (DWORD i = 0; i < number_of_bytes_read; i++) {
out_types[0][out_type_index++] = 'L';
out[0][i] = (void *) buffer[i];
};
out_types[0][out_type_index++] = ']';
out_types[0][out_type_index++] = '\0'; // terminate string
return(TRUE);
};
} else if (strcmp(types,"[SSB]") == 0) {
if (stricmp((char *) data[0],"Write") == 0) { //
box is Write, followed by a text pad, followed by a bird
char *buffer = (char *) data[1];
long number_of_bytes_to_write = strlen(buffer);
DWORD number_of_bytes_written;
if
(WriteFile(handle,buffer,number_of_bytes_to_write,&number_of_bytes_written,NULL)) {
out_types[0] = copy_string("L",1);
out[0] = (void * *) GlobalAlloc(0,sizeof(void*));
out[0][0] = (void *) number_of_bytes_written;
return(TRUE);
} else {
// set *to_say to describe error
return(FALSE);
};
};
} else if (strncmp(types,"[S[",3) == 0) { //
looking for [S[LL...LL]B] where S is Write Bytes
if (stricmp((char *) data[0],"Write Bytes") == 0)
{
int index = 3; // already checked first 3 characters of type
while (types[index] != '\0') {
if (types[index] == ']') { // OK so far
if (types[index+1] == 'B' && types[index+2] == ']')
{ // everything fine
index -= 3; // index is now the size of the box of numbers
unsigned char *buffer = new unsigned char[index];
for (int i = 0; i < index; i++) {
buffer[i] = (unsigned char) data[i+1];
};
DWORD number_of_bytes_written;
if
(WriteFile(handle,buffer,index,&number_of_bytes_written,NULL)) {
// give the bird a number showing how many characters were
written
out_types[0] =
copy_string("L",1);
out[0] = (void * *) GlobalAlloc(0,sizeof(void*));
out[0][0] = (void *) number_of_bytes_written;
return(TRUE);
} else {
// Set *to_say to describe error
return(FALSE);
};
} else break; // fall through
} else if (types[index] == 'L') {
index++;
} else { // no good
break; // fall through to code which should set *to_say
};
};
};
} else if (strcmp(types,"H") == 0) { // this
means that the last bird associated with this handle has been destroyed
if (!CloseHandle(handle)) {
// Set *to_say to describe problem
};
return(FALSE);
};
// Set *to_say to give help on how to use this extension
return(FALSE);
};
// Here is the exported procedure for opening file handles
BOOL __declspec(dllexport) receive(HANDLE handle, void
**data, char *types, char *label, char *country_code,
void ***out, char **out_types, char **to_say, BOOL
*ok_to_speak, HINSTANCE string_library) {
if (types == NULL) { // asking for help
// Set *to_say to help string
return(TRUE);
};
if (strcmp(types,"[SSB]") == 0) {
char *selector = (char *) data[0];
DWORD creation;
char *device_control_string = NULL;
char name[MAX_PATH];
if (stricmp(selector,"Create File") == 0) { //
received a box with "Create File", followed by the file name, followed by a bird
creation = CREATE_NEW;
strcpy(name,(char
*) data[1]);
} else if (stricmp(selector,"Open") == 0) { //
received a box with "Open", followed by the file name, followed by a bird
creation = OPEN_EXISTING;
strcpy(name,(char *) data[1]);
} else {
// set *to_say to describe problem
return(FALSE);
};
HANDLE file =
CreateFile(name,GENERIC_READ|GENERIC_WRITE,0,NULL,creation,FILE_ATTRIBUTE_NORMAL,NULL);
if (file == INVALID_HANDLE_VALUE) {
// set *to_say to describe problem
return(FALSE);
};
// give the bird in the third hole a box with a new bird
which is associated with the file handle
out_types[0] = copy_string("[H]",3);
out[0] = (void * *) GlobalAlloc(0,3*sizeof(void*));
out[0][0] = (void *) file_receive; // behavior defined
above
out[0][1] = (void *) file; // file handle
out[0][2] = copy_string(name,0); // use the file name as a
label
return(TRUE);
} else if (strcmp(types,"H") == 0) { // destroyed
return(FALSE);
};
// Set *to_say to give help
return(FALSE);
};
home | search | purchase | manual | news | info | games | faq | support | downloads | endorsements | press | contact us