Skip to content

Glk Multi Display Support

Andrew Plotkin edited this page Mar 12, 2015 · 6 revisions

The Glk API assumes that the application is managing exactly one Glk game display at a time. This fits poorly with the modern desktop GUI, in which a game interpreter might want to display several windows with a different game in each.

Interpreter applications typically work around this by launching sub-processes, each of which manages one game in one window. This is adequate, but we would like a better solution.

This is a proposal for a Glk interface update which supports multiple displays within a single application, without breaking backwards compatibility.

(This deals only with the C header glk.h and C library implementations. The Javascript GlkOte.js implementation already supports multiple Glk displays in separate browser windows -- each browser window is a separate Javascript context, so we get that for free. It could be extended to support multiple Glk displays in a single browser window, but this is not very important.)

Changes to the glk.h header

The glk.h header should serve both old (single-display) libraries and new (multi-display) libraries. Therefore, we need some preprocessor abuse!

Function definitions will now look like:

/* extern void glk_put_char(unsigned char ch); */
GLK_DECLARE_FN1(void, put_char, unsigned char ch);

(GLK_DECLARE_FN1 means "declare Glk function with one argument". GLK_DECLARE_FN0 to GLK_DECLARE_FN6 will be available.)

If the symbol GLK_LIBRARY_MULTI is not defined, this will compile to the old-style declaration, as shown in the comment. If GLK_LIBRARY_MULTI is defined, it compiles to two declarations:

extern void glk_put_char(unsigned char ch);
extern void glkmd_put_char(glk_context *gcontext, unsigned char ch);

The library will maintain a global variable:

extern glk_context *glk_context_singleton;

A call to the old-style function will invoke the new-style function using this context. That is, glk_put_char(ch) will be implemented as a call to glkmd_put_char(glk_context_singleton, ch). This allows old-style applications to compile with minimal change.

(These boilerplate old-style functions will live in a new file gi_multi.c, which will be distributed along with gi_dispa.c and gi_blorb.c.)

Note that some functions, such as glk_char_to_lower(), don't interact with the display state. For consistency, these will also get glkmd_XXX() implementations, but they can ignore the context argument.

Changes to gi_dispa.c

When GLK_LIBRARY_MULTI is set, gidispatch_function_t will contain pointers to both the glk_XXX() and glkmd_XXX() versions of each function. (This will entail more preprocessor abuse.)

The gidispatch_call() entry point will remain, calling glk_XXX() functions. Under GLK_LIBRARY_MULTI, there will also be gidispatch_call_multi(), with an additional context argument, which calls the glkmd_XXX() functions. (I haven't decided what kind of evil I will use to accomplish this, but nobody else ever has to read gi_dispa.c, so don't sweat it.)

Changes to libraries

An old-style library can import the new glk.h header file, and not define GLK_LIBRARY_MULTI. It will build the way it's always built, no changes needed.

To fully support multi-display Glk, a library will have to be refactored so that all Glk display state is contained in a glk_context struct. (The library must define this struct type.) It will have to rename all its glk_XXX() functions to glkmd_XXX(), adding a glk_context * as the first argument.

(CheapGlk and GlkTerm will remain as old-style, single-display libraries. RemGlk will become a multi-display library, although this is more of a proof of concept than a useful feature. A RemGlk interpreter can be launched with multiple games; input and output for each game will be tagged with a distinct session ID.)

More to write

XXX startup code XXX how to update an application