12: Appendices

12.1: The Dispatch Layer

The material described in this section is not part of the Glk API per se. It is an external layer, lying on top of Glk, which allows a program to invoke Glk dynamically -- determining the capabilities and interfaces of Glk at run-time.

This is most useful for virtual machines and other run-time systems, which want to use Glk without being bound to a particular version of the Glk API. In other words, a VM can export Glk to VM programs, without hard-wiring a list of Glk functions within itself. If a new Glk library is released, with new functions, the VM can simply link in the library; the new functions will be available to VM programs without further work.

If you are writing a C program which uses the Glk API, you can ignore this section entirely. If you are writing a VM which uses Glk, you need to read it. If you are implementing a Glk library, you should also read it. (There are some additional interfaces which your library must support for the dispatch layer to work right.)

12.1.1: How This Works

The dispatch layer is implemented in a C source file, gi_dispa.c, and its header, gi_dispa.h. This code is platform-independent -- it is identical in every library, just as the glk.h header file is identical in every library. Each library author should download the gi_dispa.c and gi_dispa.h files from the Glk web site, and compile them unchanged into the library.

This code is mostly external to Glk; it operates by calling the documented Glk API, not library internals. This is how gi_dispa.c can be platform-independent. However, the dividing line is not perfect. There are a few extra functions, not part of the Glk API, which the library must implement; gi_dispa.c (and no one else) calls these functions. These functions are simple and should not make life much harder for library implementors.

The dispatch layer then provides a dispatch API. The heart of this is the gidispatch_call() function, which allows you to call any Glk function (specified by number) and pass in a list of arguments in a standardized way. You may also make use of gidispatch_prototype(), which gives you the proper format of that list for each function. Ancilliary functions let you enumerate the functions and constants in the Glk API.

12.1.2: Interrogating the Interface

These are the ancilliary functions that let you enumerate.

glui32 gidispatch_count_classes(void);

This returns the number of opaque object classes used by the library. You will need to know this if you want to keep track of opaque objects as they are created; see section, "Opaque Object Registry".

As of Glk API 0.6.0, there are four classes: windows, streams, filerefs, and sound channels (numbered 0, 1, 2, and 3 respectively.)

glui32 gidispatch_count_intconst(void);

This returns the number of integer constants exported by the library.

gidispatch_intconst_t *gidispatch_get_intconst(glui32 index);

typedef struct gidispatch_intconst_struct {
    char *name;
    glui32 val;
} gidispatch_intconst_t;

This returns a structure describing an integer constant which the library exports. These are, roughly, all the constants defined in the glk.h file. index can range from 0 to N-1, where N is the value returned by gidispatch_count_intconst().

The structure simply contains a string and a value. The string is a symbolic name of the value, and can be re-exported to anyone interested in using Glk constants.

[In the current gi_dispa.c library, these structures are static and immutable, and will never be deallocated. However, it is safer to assume that the structure may be reused in future gidispatch_get_intconst() calls.]

glui32 gidispatch_count_functions(void);

This returns the number of functions exported by the library.

gidispatch_function_t *gidispatch_get_function(glui32 index);

typedef struct gidispatch_function_struct {
    glui32 id;
    void *fnptr;
    char *name;
} gidispatch_function_t;

This returns a structure describing a Glk function. index can range from 0 to N-1, where N is the value returned by gidispatch_count_functions().

The id field is a selector -- a numeric constant used to refer to the function in question. name is the function name, as it is given in the glk.h file, but without the "glk_" prefix. And fnptr is the address of the function itself. [This is included because it might be useful, but it is not recommended. To call an arbitrary Glk function, you should use gidispatch_call().] See section 12.1.6, "Table of Selectors" for the selector definitions. See section 12.1.3, "Dispatching" for more about calling Glk functions by selector.

gidispatch_function_t *gidispatch_get_function_by_id(glui32 id);

This returns a structure describing the Glk function with selector id. If there is no such function in the library, this returns NULL.

[Again, it is safest to assume that the structure is only valid until the next gidispatch_get_function() or gidispatch_get_function_by_id() call.]

12.1.3: Dispatching

void gidispatch_call(glui32 funcnum, glui32 numargs, gluniversal_t *arglist);

funcnum is the function number to invoke; see section 12.1.6, "Table of Selectors". arglist is the list of arguments, and numargs is the length of the list.

The arguments are all stored as gluniversal_t objects. This is a union, encompassing all the types that can be passed to Glk functions.

typedef union gluniversal_union {
    glui32 uint;
    glsi32 sint;
    void *opaqueref;
    unsigned char uch;
    signed char sch;
    char ch;
    char *charstr;
    void *array;
    glui32 ptrflag;
} gluniversal_t; Basic Types

Numeric arguments are passed in the obvious way -- one argument per gluniversal_t, with the uint or sint field set to the numeric value. Characters and strings are also passed in this way -- chars in the uch, sch, or ch fields (depending on whether the char is signed) and strings in the charstr field. Opaque objects (windows, streams, etc) are passed in the opaqueref field (which is void*, in order to handle all opaque pointer types.)

However, pointers (other than C strings), arrays, and structures complicate life. So do return values. References

A reference to a numeric type or object reference -- that is, glui32*, winid_t*, and so on -- takes one or two gluniversal_t objects. The first is a flag indicating whether the reference argument is NULL or not. The ptrflag field of this gluniversal_t should be FALSE (0) if the reference is NULL, and TRUE (1) otherwise. If FALSE, that is the end of the argument; you should not use a gluniversal_t to explicitly store the NULL reference. If the flag is TRUE, you must then put a gluniversal_t storing the base type of the reference.

For example, consider a hypothetical function, with selector 0xABCD:

void glk_glomp(glui32 num, winid_t win, glui32 *numref, strid_t *strref);

...and the calls:

glui32 value;
winid_t mainwin;
strid_t gamefile;
glk_glomp(5, mainwin, &value, &gamefile);

To perform this through gidispatch_call(), you would do the following:

gluniversal_t arglist[6];
arglist[0].uint = 5;
arglist[1].opaqueref = mainwin;
arglist[2].ptrflag = TRUE;
arglist[3].uint = value;
arglist[4].ptrflag = TRUE;
arglist[5].opaqueref = gamefile;
gidispatch_call(0xABCD, 6, arglist);
value = arglist[3].uint;
gamefile = arglist[5].opaqueref;

Note that you copy the value of the reference arguments into and out of arglist. Of course, it may be that glk_glomp() only uses these as pass-out references or pass-in references; if so, you could skip copying in or out.

For further examples:

glk_glomp(7, mainwin, NULL, NULL);
gluniversal_t arglist[4];
arglist[0].uint = 7;
arglist[1].opaqueref = mainwin;
arglist[2].ptrflag = FALSE;
arglist[3].ptrflag = FALSE;
gidispatch_call(0xABCD, 4, arglist);

glk_glomp(13, NULL, NULL, &gamefile);
gluniversal_t arglist[5];
arglist[0].uint = 13;
arglist[1].opaqueref = NULL;
arglist[2].ptrflag = FALSE;
arglist[3].ptrflag = TRUE;
arglist[4].opaqueref = gamefile;
gidispatch_call(0xABCD, 5, arglist);
gamefile = arglist[4].opaqueref;

glk_glomp(17, NULL, &value, NULL);
gluniversal_t arglist[5];
arglist[0].uint = 17;
arglist[1].opaqueref = NULL;
arglist[2].ptrflag = TRUE;
arglist[3].uint = value;
arglist[4].ptrflag = FALSE;
gidispatch_call(0xABCD, 5, arglist);
value = arglist[3].uint;

As you see, the length of arglist depends on how many of the reference arguments are NULL. Structures

A structure pointer is represented by a single ptrflag, possibly followed by a sequence of gluniversal_t objects (one for each field of the structure.) Again, if the structure pointer is non-NULL, the ptrflag should be TRUE and be followed by values; if not, the ptrflag should be NULL and stands alone.

For example, the function glk_select() can be invoked as follows:

event_t ev;
gluniversal_t arglist[5];
arglist[0].ptrflag = TRUE;
gidispatch_call(0x00C0, 5, arglist);
ev.type = arglist[1].uint;
ev.win = arglist[2].opaqueref;
ev.val1 = arglist[3].uint;
ev.val2 = arglist[4].uint;

Since the structure passed to glk_select() is a pass-out reference (the entry values are ignored), you don't need to fill in arglist[1..4] before calling gidispatch_call().

[Theoretically, you would invoke glk_select(NULL) by setting arglist[0].ptrflag to FALSE, and using a one-element arglist instead of five-element. But it's illegal to pass NULL to glk_select(). So you cannot actually do this.] Arrays

In the Glk API, an array argument is always followed by a numeric argument giving the array's length. These two C arguments are a single logical argument, which is represented by one or three gluniversal_t objects. The first is a ptrflag, indicating whether the argument is NULL or not. The second is a pointer, stored in the array field. The third is the array length, stored in the uint field. And again, if the ptrflag is NULL, the following two are omitted.

For example, the function glk_put_buffer() can be invoked as follows:

char buf[64];
glui32 len = 64;
glk_put_buffer(buf, len);
gluniversal_t arglist[3];
arglist[0].ptrflag = TRUE;
arglist[1].array = buf;
arglist[2].uint = len;
gidispatch_call(0x0084, 3, arglist);

Since you are passing a C char array to gidispatch_call(), the contents will be read directly from that. There is no need to copy data into arglist, as you would for a basic type.

If you are implementing a VM whose native representation of char arrays is more complex, you will have to do more work. You should allocate a C char array, copy your characters into it, make the call, and then free the array. [glk_put_buffer() does not modify the array passed to it, so there is no need to copy the characters out.] Return Values

The return value of a function is not treated specially. It is simply considered to be a pass-out reference argument which may not be NULL. It comes after all the other arguments of the function.

For example, the function glk_window_get_rock() can be invoked as follows:

glui32 rock;
winid_t win;
rock = glk_window_get_rock(win);
gluniversal_t arglist[3];
arglist[0].opaqueref = win;
arglist[1].ptrflag = TRUE;
gidispatch_call(0x0021, 3, arglist);
rock = arglist[2].uint;

12.1.4: Getting Argument Prototypes

There are many possible ways to set up a gluniversal_t array, and it's illegal to call gidispatch_call() with an array which doesn't match the function. Furthermore, some references are passed in, some passed out, and some both. How do you know how to handle the argument list?

One possibility is to recognize each function selector, and set up the arguments appropriately. However, this entails writing special code for each Glk function; which is exactly what we don't want to do.

Instead, you can call gidispatch_prototype().

char *gidispatch_prototype(glui32 funcnum);

This returns a string which encodes the proper argument list for the given function. If there is no such function in the library, this returns NULL.

The prototype string for the glk_glomp() function described above would be: "4IuQa&Iu&Qb:". The "4" is the number of arguments (including the return value, if there is one, which in this case there isn't.) "Iu" denotes an unsigned integer; "Qa" is an opaque object of class 0 (window). "&Iu" is a reference to an unsigned integer, and "&Qb" is a reference to a stream. The colon at the end terminates the argument list; the return value would follow it, if there was one.

Note that the initial number ("4" in this case) is the number of logical arguments, not the number of gluniversal_t objects which will be passed to gidispatch_call(). The glk_glomp() call uses anywhere from four to six gluniversal_t objects, as demonstrated above.

The basic type codes:

Any type code can be prefixed with one or more of the following characters:

The order of these characters and prefixes is not completely arbitrary. Here is a formal grammar for the prototype strings. [Thanks to Neil Cerutti for working this out.]

<prototype>   ->  ArgCount [ <arg_list> ] ':' [ <arg> ] EOL
<arg_list>    ->  <arg> { <arg> }
<arg>         ->  TypeName | <ref_type>
<ref_type>    ->  RefType [ '+' ] <target_type>
<target_type> ->  TypeName | <array> | <struct>
<array>       ->  '#' [ '!' ] TypeName
<struct>      ->  '[' ArgCount [ <arg_list> ] ']'

TypeName is "I[us]|C[nus]|S|U|F|Q[a-z]"
ArgCount is '\d+'
RefType is '&|<|>'
EOL is end of input

12.1.5: Functions the Library Must Provide

Ideally, the three layers -- program, dispatch layer, Glk library -- would be completely modular; each would refer only to the layers beneath it. Sadly, there are a few places where the library must notify the program that something has happened. Worse, these situations are only relevant to programs which use the dispatch layer, and then only some of those.

Since C is uncomfortable with the concept of calling functions which may not exist, Glk handles this with call-back function pointers. The program can pass callbacks in to the library; if it does, the library will call them, and if not, the library doesn't try.

These callbacks are optional, in the sense that the program may or may not set them. However, any library which wants to interoperate with the dispatch layer must allow the program to set them; it is the program's choice. The library does this by implementing set_registry functions -- the functions to which the program passes its callbacks.

[Even though these callbacks and the functions to set them are declared in gi_dispa.h, they are not defined in gi_dispa.c. The dispatch layer merely coordinates them. The program defines the callback functions; the library calls them.] Opaque Object Registry

The Glk API refers to opaque objects by pointer; but a VM probably cannot store pointers to native memory. Therefore, a VM program will want to keep a VM-accessible collection of opaque objects. [For example, it might keep a hash table for each opaque object class, mapping integer identifiers to object pointers.]

To make this possible, a Glk library must implement gidispatch_set_object_registry().

void gidispatch_set_object_registry(gidispatch_rock_t (*reg)(void *obj, glui32 objclass), void (*unreg)(void *obj, glui32 objclass, gidispatch_rock_t objrock));

Your program calls this early (before it begins actually executing VM code.) You pass in two function pointers, matching the following prototypes:

gidispatch_rock_t my_vm_reg_object(void *obj, glui32 objclass);
void my_vm_unreg_object(void *obj, glui32 objclass, gidispatch_rock_t objrock);

Whenever the Glk library creates an object, it will call my_vm_reg_object(). It will pass the object pointer and the class number (from 0 to N-1, where N is the value returned by gidispatch_count_classes().)

You can return any value in the gidispatch_rock_t object; the library will stash this away inside the object. [Note that this is entirely separate from the regular Glk rock, which is always a glui32 and can be set independently.]

typedef union glk_objrock_union {
    glui32 num;
    void *ptr;
} gidispatch_rock_t;

Whenever the Glk library destroys an object, it will call my_vm_unreg_object(). It passes you the object pointer, class number, and the object rock.

You can, at any time, get the object rock of an object. The library implements this function:

gidispatch_rock_t gidispatch_get_objrock(void *obj, glui32 objclass);

With this and your two callbacks, you can maintain (say) a hash table for each object class, and easily convert back and forth between hash table keys and Glk object pointers. A more sophisticated run-time system (such as Java) could create a typed VM object for every Glk object, thus allowing VM code to manipulate Glk objects intelligently.

One significant detail: It is possible that some Glk objects will already exist when your glk_main() function is called. [For example, MacGlk can open a stream when the user double-clicks a file; this occurs before glk_main().] So when you call gidispatch_set_object_registry(), it may immediately call your my_vm_reg_object() callback, notifying you of the existing objects. You must be prepared for this possibility. [If you are keeping hash tables, for example, create them before you call gidispatch_set_object_registry().] Retained Array Registry

A few Glk functions take an array and hold onto it. The memory is "owned" by the library until some future Glk call releases it. While the library retains the array, your program should not read, write, move, or deallocate it. When the library releases it, the contents are in their final form, and you can copy them out (if appropriate) and dispose of the memory as you wish.

To allow this, the library implements gidispatch_set_retained_registry().

void gidispatch_set_retained_registry(gidispatch_rock_t (*reg)(void *array, glui32 len, char *typecode), void (*unreg)(void *array, glui32 len, char *typecode, gidispatch_rock_t objrock));

Again, you pass in two function pointers:

gidispatch_rock_t my_vm_reg_array(void *array, glui32 len, char *typecode);
void my_vm_unreg_array(void *array, glui32 len, char *typecode, gidispatch_rock_t objrock);

Whenever a Glk function retains an array, it will call my_vm_reg_array(). This occurs only if you pass an array to an argument with the "#!" prefix. [But not in every such case. Wait for the my_vm_reg_array() call to confirm it.] The library passes the array and its length, exactly as you put them in the gluniversal_t array. It also passes the string which describes the argument.

[Currently, the only calls that retain arrays are glk_request_line_event(), glk_stream_open_memory(), glk_request_line_event_uni(), and glk_stream_open_memory_uni(). The first two of these use arrays of characters, so the string is "&+#!Cn". The latter two use arrays of glui32, so the string is "&+#!Iu".]

You can return any value in the gidispatch_rock_t object; the library will stash this away with the array.

When a Glk function releases a retained array, it will call my_vm_unreg_array(). It passes back the same array, len, and typecode parameters, as well as the gidispatch_rock_t you returned from my_vm_reg_array().

With these callbacks, you can maintain a collection of retained arrays. You can use this to copy data from C arrays to your own data structures, or keep relocatable memory locked, or prevent a garbage-collection system from deallocating an array while Glk is writing to it.

12.1.6: Table of Selectors

These values, and the values used for future Glk calls, are integers in the range 0x0001 to 0xFFFF (1 to 65535). The values are not sequential; they are divided into groups, roughly by category. Zero is not the selector of any Glk call, so it may be used for a null value.

Note that glk_main() does not have a selector, because it's provided by your program, not the library.

There is no way to use these selectors directly in the Glk API. [An earlier version of Glk had gestalt selectors gestalt_FunctionNameToID and gestalt_FunctionIDToName, but these have been withdrawn.] They are defined and used only by the dispatch layer.

Call selectors 0x1100 to 0x11FF (and the same range of gestalt selectors) are reserved for extension projects by Dannii Willis. Call selectors 0x1200 to 0x12FF (and gestalt selector 0x1200) are reserved for extension projects by Carlos Sanchez. Call selectors 0x2200 to 0x22FF (and the same range of gestalt selectors) are reserved for iOS extension features by Andrew Plotkin. These are not documented here.

12.2: The Blorb Layer

The material described in this section is not part of the Glk API per se. It is an external layer which allows the library to load resources (images and sounds) from a file specified by your program. The Blorb file format is a standard IF resource archive.

The Glk spec does not require that resources be stored in a Blorb file. It says only that the library knows how to load them and use them, when you so request. However, Blorb is the recommended way to supply portable resources. Most Glk libraries will support Blorb, using the interface defined in this section.

The quick summary: resources are identified by type (image, sound, etc) and by an index number. [But not by name. This is for historical reasons; Infocom's Z-machine architecture used this scheme.]

For the complete Blorb specification and tools for Blorb file manipulation, see:


12.2.1: How This Works

The Blorb layer is implemented in a C source file, gi_blorb.c, and its header, gi_blorb.h. This code is (mostly) platform-independent -- it is identical in every library, just as the glk.h header file is identical in every library. Each library author who wants to support Blorb should download the gi_blorb.c and gi_blorb.h files from the Glk web site, and compile them unchanged into the library.

Most of the functions defined in gi_blorb.h are intended for the library. If you are writing a Glk program, you can ignore them all, except for giblorb_set_resource_map(); see section 12.2.2, "What the Program Does". If you are implementing a Glk library, you can use this API to find and load resource data.

12.2.2: What the Program Does

If you wish your program to load its resources from a Blorb file, you need to find and open that file in your startup code. (See section 11.1, "Startup Options".) Each platform will have appropriate functions available for finding startup data. Be sure to open the file in binary mode, not text mode. Once you have opened the file as a Glk stream, pass it to giblorb_set_resource_map().

giblorb_err_t giblorb_set_resource_map(strid_t file);

This function tells the library that the file is indeed the Blorby source of all resource goodness. Whenever your program calls an image or sound function, such as glk_image_draw(), the library will search this file for the resource you request.

Do not close the stream after calling this function. The library is responsible for closing the stream at shutdown time.

If you do not call giblorb_set_resource_map() in your startup code, or if it fails, the library is left to its own devices for finding resources. Some libraries may try to load resources from individual files -- PIC1, PIC2, PIC3, and so on. (See the Blorb specification for more on this approach.) Other libraries will not have any other loading mechanism at all; no resources will be available.

12.2.3: What the Library Does

Each library must implement giblorb_set_resource_map(), if it wishes to support Blorb at all. Generally, this function should create a Blorb map and stash it away somewhere. It may also want to stash the stream itself, so that the library can read data directly from it.

giblorb_set_resource_map() should return giblorb_err_None (0) if it succeeded, or the appropriate Blorb error code if not. See section 12.2.5, "Blorb Errors".

The library must also link in the gi_blorb.c file. Most of this should compile without difficulty on any platform. However, it does need to allocate memory. As supplied, gi_blorb.c calls the ANSI functions malloc(), realloc(), and free(). If this is not appropriate on your OS, feel free to change these calls. They are isolated at the end of the file.

12.2.4: What the Blorb Layer Does

These are the functions which are implemented in gi_blorb.c. They will be compiled into the library, but they are the same on every platform. In general, only the library needs to call these functions. The Glk program should allow the library to do all the resource handling.

giblorb_err_t giblorb_create_map(strid_t file, giblorb_map_t **newmap);

This reads Blorb data out of a Glk stream. It does not load every resource at once; instead, it creates an map in memory which make it easy to find resources. A pointer to the map is stored in newmap. This is an opaque object; you pass it to the other Blorb-layer functions.

giblorb_err_t giblorb_destroy_map(giblorb_map_t *map);

Deallocate the map and all associated memory. This does not close the original stream.

giblorb_err_t giblorb_load_chunk_by_type(giblorb_map_t *map, glui32 method, giblorb_result_t *res, glui32 chunktype, glui32 count);

This loads a chunk of a given type. The count parameter distinguishes between chunks of the same type. If count is zero, the first chunk of that type is loaded, and so on.

To load a chunk of an IFF FORM type (such as AIFF), you should pass in the form type, rather than FORM. [This introduces a slight ambiguity -- you cannot distiguish between a FORM AIFF chunk and a non-FORM chunk of type AIFF. However, the latter is almost certainly a mistake.]

The returned data is written into res, according to method:

#define giblorb_method_DontLoad (0)
#define giblorb_method_Memory (1)
#define giblorb_method_FilePos (2)
typedef struct giblorb_result_struct {
    glui32 chunknum;
    union {
        void *ptr;
        glui32 startpos;
    } data;
    glui32 length;
    glui32 chunktype;
} giblorb_result_t;

The chunknum field is filled in with the number of the chunk. (This value can then be passed to giblorb_load_chunk_by_number() or giblorb_unload_chunk().) The length field is filled in with the length of the chunk in bytes. The chunktype field is the chunk's type, which of course will be the type you asked for.

If you specify giblorb_method_DontLoad, no data is actually loaded in. You can use this if you are only interested in whether a chunk exists, or in the chunknum and length parameters.

If you specify giblorb_method_FilePos, data.startpos is filled in with the file position of the chunk data. You can use glk_stream_set_position() to read the data from the stream.

If you specify giblorb_method_Memory, data.ptr is filled with a pointer to allocated memory containing the chunk data. This memory is owned by the map, not you. If you load the chunk more than once with giblorb_method_Memory, the Blorb layer is smart enough to keep just one copy in memory. You should not deallocate this memory yourself; call giblorb_unload_chunk() instead.

giblorb_err_t giblorb_load_chunk_by_number(giblorb_map_t *map, glui32 method, giblorb_result_t *res, glui32 chunknum);

This is similar to giblorb_load_chunk_by_type(), but it loads a chunk with a given chunk number. The type of the chunk can be found in the chunktype field of giblorb_result_t. You can get the chunk number from the chunknum field, after calling one of the other load functions.

giblorb_err_t giblorb_unload_chunk(giblorb_map_t *map, glui32 chunknum);

This frees the chunk data allocated by giblorb_method_Memory. If the given chunk has never been loaded into memory, this has no effect.

giblorb_err_t giblorb_load_resource(giblorb_map_t *map, glui32 method, giblorb_result_t *res, glui32 usage, glui32 resnum);

This loads a resource, given its usage and resource number. Currently, the three usage values are giblorb_ID_Pict (images), giblorb_ID_Snd (sounds), and giblorb_ID_Exec (executable program). See the Blorb specification for more information about the types of data that can be stored for these usages.

Note that a resource number is not the same as a chunk number. The resource number is the sound or image number specified by a Glk program. Chunk number is arbitrary, since chunks in a Blorb file can be in any order. To find the chunk number of a given resource, call giblorb_load_resource() and look in res.chunknum.

giblorb_err_t giblorb_count_resources(giblorb_map_t *map, glui32 usage, glui32 *num, glui32 *min, glui32 *max);

This counts the number of chunks with a given usage (image, sound, or executable.) The total number of chunks of that usage is stored in num. The lowest and highest resource number of that usage are stored in min and max. You can leave any of the three pointers NULL if you don't care about that information.

12.2.5: Blorb Errors

All Blorb layer functions, including giblorb_set_resource_map(), return the following error codes.

Up to top Previous chapter Next chapter