[Home] [ToC] [Up] [Prev] [Next]
This section describes how to implement an iHTML language module, by examining the source code to implement a simple language module. The example implements a module that displays any text data type as a scrollable text view. The content type description for this module is:
Name: text Content-type: text/*
In order to keep the example as simple as possible, the module does not implement document script execution. Such a situation is typical for modules that display traditional data types, as scripts executing in a document context do not include a graphics context in which data can be displayed. In addition, the module makes use of low-level Xt widgets for displaying the actual text. Because of this, it needs to be modified to work with browsers running under different platforms, such as Macintosh or Windows.
The first thing the module must do is include the header files for the parts of the X system it uses. This includes the basic X headers, along with the X Athena widgets needed to make a scrollable text area.
#include <X11/Intrinsic.h> #include <X11/StringDefs.h> #include <X11/Xmu/Converters.h> #include <X11/Xaw/Viewport.h> #include <X11/Xaw/AsciiText.h>
The iHTML header file declares a number of types used by the
system. Many of these types are defined as a black-box
(void*
) by the library, in order to hide their
implementation.
However, individual components need define the types they use
in their internal implementation.
Programs including the iHTML header file can conditionally
override the black-box definitions with their own, to have access
to the parts of the system they implement.
Here, the language module defines those types that are associated
with the language component; the #define
statements tell
the system which ones it has defined, so that other header files do
not later try to redefine them. (The structures referred to here are
defined later.)
#define TYPE_IHLangCode 1 typedef struct ih_int_lang_code_rec* IHLangCode; #define TYPE_IHLangEnv 1 typedef struct ih_int_lang_env_rec IHLangEnv; #define TYPE_IHWidgetObj 1 typedef struct ih_int_widget_obj_rec* IHWidgetObj;
One of the types that the browser component defines represents the underlying operating system's user interface objects it uses to construct an applet's display context on a document. Because the language module assumes that it is running under X, it knows that this type is a Widget and can use it to get low-level access to the display.
#define TYPE_IHWidgetRaw 1 typedef Widget IHWidgetRaw;
The module now includes the iHTML header file, which defines the interface to the rest of the system. In addition to the interface, the header file also defines a number of macros that will be used later in the module:
_NO_PROTO
bug()
D()
IH_PROTO
MARK
#include "IHScript.h"
A language module defines various data structures that are part of the iHTML system. The first is the IHLangCode, which contains state information for a single code block executing in the module.
A code block is not necessarily an actual program -- it is simply
the context for some data that has been retrieved with an HTML
<OBJECT>
tag. For this text module, it is any
ASCII text. The IHLangCode type keeps track of the width and height
specified in the initial <OBJECT>
tag, and the
widgets being used to display the text.
struct ih_int_lang_code_rec { /* Fields initialized when code block is first allocated. */ int width, height; /* The requested applet size. */ /* Fields initialized each time execution is started. */ IHWidgetObj obj; /* Top-level widget interface. */ Widget viewport; /* The scrollable area. */ Widget text; /* The text being displayed. */ };
The IHLangEnv type contains environment information associated with a single browser document. This includes global information used by all code blocks executing in that document, and the information needed by the document scripting context.
The iHTML system itself defines a common public part of this structure, which must appear at the front of a language module's internal definition of the IHLangEnv.
struct ih_int_lang_env_rec { struct ih_lang_env_rec env; /* Subclass from iHTML's language env. */ /* Static variables that are made at IH_PyAllocEnv() time. */ IHEvent* query_evt; /* Used to determine document dimension. */ /* Dynamic variables that are made at each run of document script. */ /* (none -- module does not implement a document context.) */ };
The graphical user interface managed by the iHTML system is composed of two parts. The IHWidgetRep is a browser-side object that defines a display context and low-level drawing operations. The second part, IHWidgetObj, is implemented by the language module and encapsulates the intelligence associated with that display context.
This language module goes directly to the operating system for rendering in its display. It only defines a simple IHWidgetObj, which is composed of a single child X Widget; the IHWidgetObj ensures that its dimensions match that Widget's dimensions.
struct ih_int_widget_obj_rec { IHWidgetRep rep; /* Browser widget associated with. */ IHWidgetRaw raw; /* Raw OS-level widget object. For the Xt interface, this is a Widget obj. */ Dimension width, height; /* Preferred dimensions. */ Widget child; /* X-level child widget. */ };
The public interface to a language module is defined by a set of structures that supply general information about the module and provide access to the module's functionality.
First, a #define
is cleared, indicating that the
module does not implement a document context. Setting the macro
will affect how
later portions of the code operate.
#undef DOCUMENT_CONTEXT
Before defining the interface structures, the functions they reference must be declared. Note that these function names are not directly visible outside of the module, but are accessed through the structures defined later on.
static void LM_Initialize IH_PROTO( (void) ); static void LM_Terminate IH_PROTO( (IHLanguage* lang) ); static IHLangEnv* LM_AllocLangEnv IH_PROTO( (IHLangEnv* le) ); static void LM_FreeLangEnv IH_PROTO( (IHLangEnv* env) ); static int LM_ExecLangEnv IH_PROTO( (IHLangEnv* le, IHModuleInfo* main) ); static void LM_StopLangEnv IH_PROTO( (IHLangEnv* le, IHModuleInfo* main) ); static IHModuleInfo* LM_AllocModule IH_PROTO( (IHModuleInfo* mi) ); static void LM_FreeModule IH_PROTO( (IHModuleInfo* mi) ); static IHEmbeddedInfo* LM_AllocEmbedded IH_PROTO( (IHEmbeddedInfo* ei) ); static void LM_FreeEmbedded IH_PROTO( (IHEmbeddedInfo* ei) ); static int LM_ExecEmbedded IH_PROTO( (IHEmbeddedInfo* ei) ); static void LM_StopEmbedded IH_PROTO( (IHEmbeddedInfo* ei) ); static int LM_HandleEvent IH_PROTO( (IHLangEnv* env, IHEvent* event) ); #if DOCUMENT_CONTEXT static HTMLNode LM_Markup_HeadNode IH_PROTO( (IHMarkup markup) ); static HTMLNode LM_Markup_TailNode IH_PROTO( (IHMarkup markup) ); static void LM_Markup_SetHead IH_PROTO( (IHMarkup markup, HTMLNode node) ); static void LM_Markup_Ref IH_PROTO( (IHMarkup markup) ); static void LM_Markup_Deref IH_PROTO( (IHMarkup markup) ); #endif
The IHLanguageFunc structure defines the functional interface to the module. It is composed of pointers to the various functions that make up its public interface.
If a module does not implement a document context, it does not need to define its HTML parse tree functions. Instead, it may set those to NULL, informering the rest of the system of this fact. Similarly, setting the terminate function to NULL will keep the system from unloading the module when it is no longer in use.
static IHLanguageFunc lang_functions = { LM_Terminate, LM_AllocLangEnv, LM_FreeLangEnv, LM_ExecLangEnv, LM_StopLangEnv, LM_AllocModule, LM_FreeModule, LM_AllocEmbedded, LM_FreeEmbedded, LM_ExecEmbedded, LM_StopEmbedded, LM_HandleEvent, #if DOCUMENT_CONTEXT LM_Markup_HeadNode, LM_Markup_TailNode, LM_Markup_SetHead, LM_Markup_Ref, LM_Markup_Deref #else NULL, NULL, NULL, NULL, NULL #endif };
The IHLanguage structure is the top-level interface to the module. It includes a pointer to its functional interface, and various other pieces of information.
static IHLanguage lm_language = { &lang_functions, MODULE_VERSION, MODULE_REVISION, MODULE_SUBMAKE, /* Version info */ MODULE_BUILD_DATE, /* (char*) build date */ "text", /* Name of this module */ sizeof(IHLangEnv) /* Size of module's language env. */ };
Finally, this module declares a number of internal variables that it uses to store global state information.
/* The name of the browser executable. */ static char* lm_cache_argv0 = NULL; /* The directory name from which we were loaded. */ static char* lm_name = NULL; /* The path to find external files for this language module. */ static char** lm_path = NULL;
The initialization function is the only publically defined symbol in a module. It takes two parameters: browser is the name of the executable file the browser was loaded from, and language is the name of the language path the module was loaded from. If the initialization is okay, it returns a pointer to the module's IHLanguage; otherwise, it returns NULL.
The initialization function can be defined with a couple of symbol
names. The iHTML system first looks for a symbol named
IHTML_language
,
where language is the name of
the language path from which the module was loaded. If that symbol
is not found, it then falls back on the symbol
IHTMLinterface
, as is used here.
IHLanguage* #ifdef _NO_PROTO IHTMLinterface(browser, language) char* browser; char* language; #else IHTMLinterface(char* browser, char* language) #endif { char* rawpath; MARK; D(bug("Initializing language: %s\n",language)); if( !(lm_name=strdup(language)) ) { BR_ReportError(NULL,"Unable to get memory for name '%s'\n",language); return NULL; } MARK; lm_cache_argv0 = browser; /* Set up the module path. */ MARK; lm_path = BR_ParsePath(rawpath=BR_GetBasePath(language,NULL),language); MARK; if( !rawpath ) { BR_ReportError(NULL, "Unable to construct iHTML search path for language '%s'\n", language); free(lm_name); lm_name = NULL; return NULL; } MARK; if( !lm_path ) { BR_ReportError(NULL,"Unable to parse iHTML search path:\n%s\n",rawpath); free(rawpath); free(lm_name); lm_name = NULL; return NULL; } free(rawpath); MARK; D(bug("Returning lang=%x, fn=%x, func=%x\n",&lm_language,lm_language.Fn, LM_AllocLangEnv)); return &lm_language; }
A module's Term() function is called by the system prior to unloading the entire module from memory.
static void #ifdef _NO_PROTO LM_Terminate(language) IHLanguage* language; #else LM_Terminate(IHLanguage* language) #endif { MARK; D(bug("Terminating language: %s\n",lm_name)); if(lm_path) BR_FreeParsePath(lm_path); if(lm_name) free(lm_name); }
Modules that include a document context should define functions for handling HTML parse trees. To do this, they define an IHMarkup type that stores information about the first and last nodes in a tree and a reference count, and define the module's IHMarkup_HeadNode(), IHMarkup_TailNode(), IHMarkup_SetHead(), IHMarkup_Ref() and IHMarkup_Deref(), functions to manipulate this type.
#if DOCUMENT_CONTEXT static HTMLNode #ifdef _NO_PROTO LM_Markup_HeadNode(obj) IHMarkup obj; #else LM_Markup_HeadNode(IHMarkup obj) #endif { return NULL; } static HTMLNode #ifdef _NO_PROTO LM_Markup_TailNode(obj) IHMarkup obj; #else LM_Markup_TailNode(IHMarkup obj) #endif { return NULL; } static void #ifdef _NO_PROTO LM_Markup_SetHead(obj,node) IHMarkup obj; HTMLNode node; #else LM_Markup_SetHead(IHMarkup obj,HTMLNode node) #endif { } static void #ifdef _NO_PROTO LM_Markup_Ref(obj) IHMarkup obj; #else LM_Markup_Ref(IHMarkup obj) #endif { } static void #ifdef _NO_PROTO LM_Markup_Deref(obj) IHMarkup obj; #else LM_Markup_Deref(IHMarkup obj) #endif { } #endif
Every module must define the HandleEvent() function to handle system IHEvent objects that are sent to it. When called with an event, the function needs to distinguish whether it occurred in a document and applet context, and route it to the correct part of the module.
The module does not implement a document context, so it simply returns zero for any events it receives in that context, indicating it did nothing with them. Otherwise, it sends the event to its widget event handling code.
static int #ifdef _NO_PROTO LM_HandleEvent(env,event) IHLangEnv* env; IHEvent* event; #else LM_HandleEvent(IHLangEnv* le,IHEvent* event) #endif { MARK; D(bug("Handling event for '%s'\n",lm_name)); /* Sanity checks. */ if( !le ) { BR_ReportError(NULL, "LM_HandleEvent() for '%s': IHLangEnv is NULL.\n", lm_name); return 0; } if( !event ) { BR_ReportError(NULL, "LM_HandleEvent() for '%s': IHEvent is NULL.\n", lm_name); return 0; } if( le->env.language != &lm_language ) { BR_ReportError(NULL, "LM_HandleEvent() for '%s': Not for correct language.\n", lm_name); return 0; } MARK; if( event->class == IECLASS_DOCUMENT_ID ) { /* Process document events. */ return 0; } else if( event->class == IECLASS_WIDGET_ID ) { return widget_event(event,(IHWidgetObj)event->object); } return 0; }
The implementation of widget objects is a fundamental part of most language modules. These determine how the user interacts with the module and what information it displays in the document.
This module defines a number of private functions for managing these objects. The first deallocates an IHWidgetObj and any X widgets attached to it.
void #ifdef _NO_PROTO free_widget(obj) IHWidgetObj obj; #else free_widget(IHWidgetObj obj) #endif { if( obj ) { IHMemberValue set[] = { { IWM_WIDGETOBJ_ID, 0 }, { IWM_NULL_ID, 0 } }; if( obj->rep ) BR_SetWidgetMembers(obj->rep,&set[0]); if( obj->child ) { XtDestroyWidget(obj->child); } free(obj); } }
The next function allocates a new IHWidgetObj object, attaching it to a browser-side IHWidgetRep object. In the process, it queries the browser-side widget for its default dimensions.
IHWidgetObj #ifdef _NO_PROTO alloc_widget(widget) IHWidgetRep widget; #else alloc_widget(IHWidgetRep widget) #endif { IHWidgetObj obj = malloc(sizeof(*obj)); int w=0, h=0; IHMemberValue get[] = { { IWM_WIDGETRAW_ID, 0 }, { IWM_WIDTH_ID, 0 }, { IWM_HEIGHT_ID, 0 }, { IWM_NULL_ID, 0 } }; if( !obj ) return NULL; obj->rep = widget; obj->raw = NULL; obj->child = NULL; get[0].value = &obj->raw; get[1].value = &w; get[2].value = &h; BR_GetWidgetMembers(obj->rep,&get[0]); obj->width = w; obj->height = h; return obj; }
In order to use its display area, the module defines a function that attaches a low-level X widget to its IHWidgetObj object. Once it has done this, it copies the X widget dimensions into the parent IHWidgetRep object.
void #ifdef _NO_PROTO set_widget_child(obj,child) IHWidgetObj obj; Widget child; #else set_widget_child(IHWidgetObj obj,Widget child) #endif { IHMemberValue set[] = { { IWM_WIDGETOBJ_ID, 0 }, { IWM_WIDTH_ID, 0 }, { IWM_HEIGHT_ID, 0 }, { IWM_NULL_ID, 0 } }; if( !obj ) return; obj->child = child; if( obj->child ) { XtVaGetValues(obj->child, XtNwidth,&obj->width, XtNheight,&obj->height, NULL); } set[0].value = obj; set[1].value = (void*)obj->width; set[2].value = (void*)obj->height; BR_SetWidgetMembers(obj->rep,&set[0]); }
Finally, a function is defined for handling events that are sent to the module's widget objects. Since most of the user interaction is down through the low-level X widgets, this module doesn't need to worry about many events. The only one it concerns itself with is a resize event; upon receiving one, it changes the child X widget to be the same size as the IHWidgetRep.
int #ifdef _NO_PROTO widget_event(evt,obj) IHEvent* evt; IHWidgetObj obj; #else widget_event(IHEvent* evt,IHWidgetObj obj) #endif { D(bug("Handling widget event $%x for '%s'\n",evt,lm_name)); /* Sanity checks. */ if( !evt ) { BR_ReportError(NULL, "widget_event() for '%s': IHEvent is NULL.\n", lm_name); return 0; } if( !obj ) { BR_ReportError(evt->document, "widget_event() for '%s': IHWidgetObj is NULL.\n", lm_name); return 0; } switch( evt->code ) { case IECODE_RESIZE_ID: { int w, h; IHMemberValue get[] = { { IWM_WIDTH_ID, 0 }, { IWM_HEIGHT_ID, 0 }, { IWM_NULL_ID, 0 } }; get[0].value = &w; get[1].value = &h; BR_GetWidgetMembers(obj->rep,&get[0]); if( obj->child ) { XtVaSetValues(obj->child, XtNwidth,obj->width=w, XtNheight,obj->height=h, NULL); } return 1; } } return 0; }
IHLangCode
RoutinesEvery embedded applet or document script has a code block associated with it that is defined by the language module. Typically, this code block is the same for both types of contexts, so it is a good idea to factor out the logic for handling them with something like these two functions.
The first function deallocates a IHLangCode object, along with any resources attached to it.
static void free_code(ci,code) IHCodeInfo* ci; IHLangCode code; { D(bug("Freeing code info $%lx for '%s'\n",ci,lm_name)); if(code) { free(code); } }
The second function allocates a new
IHLangCode object. In the
process, it parses and saves the parameters associated with that
code block. (I.e., the <PARAM>
tag and certain
attributes of the <OBJECT>
tag.) It also uses a
document context event allocated by the language environment to
compute a default size for its display area.
static IHLangCode #ifdef _NO_PROTO alloc_code(ci) IHCodeInfo* ci; #else alloc_code(IHCodeInfo* ci) #endif { IHLangCode code = NULL; IHEmbeddedParam* param = NULL; D(bug("Allocating code info %lx for '%s'\n",ci,lm_name)); /* Sanity checks. */ if( !ci ) { BR_ReportError(NULL, "alloc_code() for '%s': IHCodeInfo is NULL.\n", lm_name); return NULL; } if( !ci->source ) { BR_ReportError(ci->interface->document, "alloc_code() for '%s': No source data.\n", lm_name); return NULL; } if( !ci->langenv ) { BR_ReportError(ci->interface->document, "alloc_code() for '%s': IHCodeInfo.langenv is NULL.\n", lm_name); return NULL; } if(code = malloc(sizeof(*code))) { IHAttrType type; IHAttrVal val=0; memset(code,0,sizeof(*code)); if( (type=BR_GetEventAttr(ci->langenv->query_evt,IEATTR_VIEW_WIDTH_ID, &val)) == ATTRTYPE_INTEGER_ID ) { code->width = (int)val; } BR_FreeEventAttr(ci->langenv->query_evt,IEATTR_VIEW_WIDTH_ID,type,val); if( (type=BR_GetEventAttr(ci->langenv->query_evt,IEATTR_VIEW_HEIGHT_ID, &val)) == ATTRTYPE_INTEGER_ID ) { code->height = (int)val; } BR_FreeEventAttr(ci->langenv->query_evt,IEATTR_VIEW_HEIGHT_ID,type,val); D(bug("Code '%s' default dimens: %d x %d\n",lm_name, code->width,code->height)); /* Parse the parameters. */ while( param = IH_NextNode(&ci->params,¶m->node) ) { D(bug("Param: %s = %s\n",param->param,param->value)); if( strcmp(param->param,"__WIDTH__") == 0 ) { code->width = atoi(param->value); } else if( strcmp(param->param,"__HEIGHT__") == 0 ) { code->height = atoi(param->value); } } D(bug("Code '%s' final dimens: %d x %d\n",lm_name, code->width,code->height)); } /* Link in the language code block, and return. */ ci->code = code; return code; }
A language module must provide two functions for allocating and deallocating document context scripts. Since this module doesn't implement a document, these functions don't need to do anything. However, here they allocate a code block for the document context, just because its easy to do.
The first function implements the module's FreeModule() functionality, deallocating all language resources attached to a IHModuleInfo structure.
static void #ifdef _NO_PROTO LM_FreeModule(mi) IHModuleInfo* mi; #else LM_FreeModule(IHModuleInfo* mi) #endif { MARK; D(bug("Deallocating module %lx for '%s'\n",mi,lm_name)); if(mi && mi->code.code) { MARK; free_code(&mi->code,mi->code.code); } D(bug("done.\n")); }
Second is the implementation of the module's AllocModule() functionality, which allocates the code block for the module.
static IHModuleInfo* #ifdef _NO_PROTO LM_AllocModule(mi) IHModuleInfo* mi; #else LM_AllocModule(IHModuleInfo* mi) #endif { IHLangCode code; MARK; D(bug("Allocating module %lx for '%s'\n",mi,lm_name)); /* Sanity checks. */ if( !mi ) { BR_ReportError(NULL, "LM_AllocModule() for '%s': IHModuleInfo is NULL.\n", lm_name); return NULL; } if(code = alloc_code(&mi->code)) { return mi; } return NULL; }
Similar to the document scripts above, functions must be provided for managing any embedded applets in a document. The first function implements the FreeEmbedded() functionality, deallocating the code block associated with an embedded applet.
static void #ifdef _NO_PROTO LM_FreeEmbedded(ei) IHEmbeddedInfo* ei; #else LM_FreeEmbedded(IHEmbeddedInfo* ei) #endif { MARK; D(bug("Deallocating embedded object %lx for '%s'\n",ei,lm_name)); if(ei && ei->code.code) { free_code(&ei->code,ei->code.code); } D(bug("done.\n")); }
Also very similar to document scripts, the module implements its embedded applets' AllocEmbedded() functionality by allocating a new code block for the applet.
static IHEmbeddedInfo* #ifdef _NO_PROTO LM_AllocEmbedded(ei) IHEmbeddedInfo* ei; #else LM_AllocEmbedded(IHEmbeddedInfo* ei) #endif { IHLangCode code; MARK; D(bug("Allocating embedded object for '%s'.\n",lm_name)); /* Sanity checks. */ if( !ei ) { BR_ReportError(NULL, "LM_AllocEmbedded() for '%s': IHEmbeddedInfo is NULL.\n", lm_name); return NULL; } if(code = alloc_code(&ei->code)) { return ei; } return NULL; }
An applet's execution is separate from the execution of its encompassing language environment. The module's ExecEmbedded() function starts the execution of a single applet. In this language module, the function is implemented by allocating a widget object for the applet and creating the X widgets that are used to display its text.
static int #ifdef _NO_PROTO LM_ExecEmbedded(ei) IHEmbeddedInfo* ei; #else LM_ExecEmbedded(IHEmbeddedInfo* ei) #endif { IHLangCode code; MARK; D(bug("Executing embedded object for '%s'.\n",lm_name)); /* Sanity checks. */ if( !ei ) { BR_ReportError(NULL, "LM_ExecEmbedded() for '%s': IHEmbeddedInfo is NULL.\n", lm_name); return 0; } if( !(code=ei->code.code) ) { BR_ReportError(ei->code.interface->document, "LM_ExecEmbedded() for '%s': IHLangCode is NULL.\n", lm_name); return 0; } if( !(ei->code.langenv) ) { BR_ReportError(mi->code.interface->document, "LM_ExecEmbedded() for '%s': IHLangEnv is NULL.\n", lm_name); return 0; } D(bug("ei=%x, code=%x, langenv=%x\n",ei,code,ei->code.langenv)); if( ei && (code=ei->code.code) && ei->code.langenv ) { if( !(code->obj=alloc_widget(ei->widget)) ) return 0; code->viewport = XtVaCreateManagedWidget("viewport", viewportWidgetClass, code->obj->raw, XtNwidth, code->width, XtNheight, code->height, NULL); code->text = XtVaCreateManagedWidget("text", asciiTextWidgetClass, code->viewport, XtNstring, ( ei->code.source && ei->code.source->buffer ) ? ei->code.source->buffer : "<empty>", XtNdisplayCaret, False, NULL); widget_set_child(code->obj,code->viewport); return 1; /* Success */ } return 0; /* Failure */ }
Finally, the module's StopEmbedded() function stops the execution of a single applet. When called, this module deallocates the widgets associated with the applet.
static void #ifdef _NO_PROTO LM_StopEmbedded(ei) IHEmbeddedInfo* ei; #else LM_StopEmbedded(IHEmbeddedInfo* ei) #endif { IHLangCode code; MARK; D(bug("Stopping embedded object $%x for '%s'.\n",ei,lm_name)); if( !ei || !(code=ei->code.code) ) return; free_widget(code->obj); code->obj = NULL; code->viewport = NULL; code->text = NULL; }
The final part of a language module's interface are those associated with its entire language environment. One IHLangEnv object is associated with each document the module has one or more objects on.
First, the module's FreeLangEnv() function is called after all objects have been removed from a document. It deallocates all of the module resources associated with that document.
static void #ifdef _NO_PROTO LM_FreeLangEnv(le) IHLangEnv* le; #else LM_FreeLangEnv(IHLangEnv* le) #endif { MARK; D(bug("Freeing environment $%lx for '%s'\n",le,lm_name)); if( le != NULL ) { if( le->query_evt ) BR_FreeEvent(le->query_evt); le->query_evt = NULL; } /* The iHTML library takes care of deallocating the actual IHLangEnv structure. */ }
When the AllocLangEnv() function is called to create a new language environment, this module allocates an IHEvent object is allocated in the browser's document context. During execution in the environment, queries can be performed on the object for information about the dimensions of the document associated with it.
static IHLangEnv* #ifdef _NO_PROTO LM_AllocLangEnv(le) IHLangEnv* le; #else LM_AllocLangEnv(IHLangEnv* le) #endif { MARK; D(bug("Allocating environment $%lx for '%s'\n",le,lm_name)); /* Sanity checks. */ if( !le ) { BR_ReportError(NULL, "LM_AllocLangEnv() for '%s': IHLangEnv is NULL.\n", lm_name); return NULL; } /* Fill in custom data for language environment here. */ if( !(le->query_evt=BR_AllocEvent(le->env.interface, le->env.interface->document, le,IECLASS_DOCUMENT_ID, IECODE_SCROLL_ID,NULL)) ) { return NULL; } return le; }
A module's ExecLangEnv() function is called when a document is being displayed and needs to start executing the scripts associated with it.
static int #ifdef _NO_PROTO LM_ExecLangEnv(le,main) IHLangEnv* le; IHModuleInfo* main; #else LM_ExecLangEnv(IHLangEnv* le,IHModuleInfo* main) #endif { IHLangCode code; MARK; D(bug("Executing language environment $%x for '%s'.\n",le,lm_name)); /* Sanity checks. */ if( !le ) { BR_ReportError(NULL, "LM_ExecLangEnv() for '%s': IHLangEnv is NULL.\n", lm_name); return 0; } /* Check if this environment has a main (document) program; if so, start it up. */ if( main && (code=main->code.code) ) { D(bug("Starting document program...\n")); } return 1; }
Finally, the module's StopLangEnv() function terminates document context execution within a language module.
static void #ifdef _NO_PROTO LM_StopLangEnv(le,main) IHLangEnv* le; IHModuleInfo* main; #else LM_StopLangEnv(IHLangEnv* le,IHModuleInfo* main) #endif { IHLangCode code; MARK; D(bug("Stopping language environment $%x for '%s'.\n",le,lm_name)); if( !le ) return; if( main && (code=main->code.code) ) { D(bug("Stopping document program...\n")); } }
[Home] [ToC] [Up] [Prev] [Next]
Dianne Kyra Hackborn <hackbod@angryredplanet.com> | Last modified: Sat Oct 26 14:58:06 PDT 1996 |