Home Contents Search

GLKE GLK Enterprise

Premium 5
Premium 6
Premium Domains
Premium 2
Premium 3
Premium 4
Rare domains
LLLLL.com
LLLLL.com 2
LLLLL.com 3
cities_realestate
Similar   Websites
education_sites
entertainment_sites
games
misc_sites
LLLL.com Site
Acronym 2
Acronym 4
Acronym 5
Acronym 6
Acronym 7
Acronym 8
Acronym 9
Acronym 10
Acronym 3
Brandable sites
Pin Yin sites
service_sites
technology
Acronym sites
Payment Options
About Our Office

Acronym Definition
GLKE Glucokinase Equipment
GLKE Geo Seattle, WA, USA - Lake Union Sea Plane Base
GLKE Good Luck Kenya
GLKE Generic List Keyless Entry
GLKE Greenland Kinetic Energy
GLKE General Logic Knowledge Express
GLKE Good Looking Korean Escrow
GLKE Graphic Lab Key Employee
GLKE GLK Equipment
GLKE GLK Enterprise

Copyright 1998-2000 by Andrew Plotkin. You have permission to display, download, and print this document, provided that you do so for personal, non-commercial use only. You may not modify or distribute this document without the author's written permission.
This document and further Glk information can be found at: http://www.eblong.com/zarf/glk/
GLK is a portable API created by Andrew Plotkin for use by programs with a text interface; these programs mostly include interactive fiction interpreters for Z-machine, TADS, Glulx, and Hugo games.

The GLK API Specification describes facilities for input, output, text formatting, graphics, sounds, and file I/O.

Currently, the GLK API is available on the following platforms:

* Java
* Macintosh
* MS-DOS
* PocketPC
* Python
* Unix
* Microsoft Windows
* X Window System

* 0. Introduction
* 0.1. What Glk Is
* 0.2. What About Java?
* 0.3. What About the Virtual Machine?
* 0.4. What Does Glk Not Do?
* 0.5. Conventions of This Document
* 1. Overall Structure
* 1.1. Your Program's Main Function
* 1.2. Exiting Your Program
* 1.3. The Interrupt Handler
* 1.4. The Tick Thing
* 1.5. Basic Types
* 1.6. Opaque Objects
* 1.6.1. Rocks
* 1.6.2. Iterating Through Opaque Objects
* 1.7. The Gestalt System
* 1.8. The Version Number
* 1.9. Other API Conventions
* 2. Character Encoding
* 2.1. Output
* 2.2. Line Input
* 2.3. Character Input
* 2.4. Upper and Lower Case
* 3. Windows
* 3.1. Window Arrangement
* 3.2. Window Opening, Closing, and Constraints
* 3.3. Changing Window Constraints
* 3.4. A Note on Display Style
* 3.5. The Types of Windows
* 3.5.1. Blank Windows
* 3.5.2. Pair Windows
* 3.5.3. Text Buffer Windows
* 3.5.4. Text Grid Windows
* 3.5.5. Graphics Windows
* 3.6. Echo Streams
* 3.7. Other Window Functions
* 4. Events
* 4.1. Character Input Events
* 4.2. Line Input Events
* 4.3. Mouse Input Events
* 4.4. Timer Events
* 4.5. Window Arrangement Events
* 4.6. Window Redrawing Events
* 4.7. Sound Notification Events
* 4.8. Hyperlink Events
* 4.9. Other Events
* 5. Streams
* 5.1. How To Print
* 5.2. How To Read
* 5.3. Closing Streams
* 5.4. Stream Positions
* 5.5. Styles
* 5.5.1. Suggesting the Appearance of Styles
* 5.5.2. Testing the Appearance of Styles
* 5.6. The Types of Streams
* 5.6.1. Window Streams
* 5.6.2. Memory Streams
* 5.6.3. File Streams
* 5.7. Other Stream Functions
* 6. File References
* 6.1. The Types of File References
* 6.2. Other File Reference Functions
* 7. Graphics
* 7.1. Image Resources
* 7.2. Graphics in Graphics Windows
* 7.3. Graphics in Text Buffer Windows
* 7.4. Testing for Graphics Capabilities
* 8. Sound
* 8.1. Sound Resources
* 8.2. Creating and Destroying Sound Channels
* 8.3. Playing Sounds
* 8.4. Other Sound Channel Functions
* 8.5. Testing for Sound Capabilities
* 9. Hyperlinks
* 9.1. Creating Hyperlinks
* 9.2. Accepting Hyperlink Events
* 9.3. Testing for Hyperlink Capabilities
* 10. Porting, Adapting, and Other Messy Bits
* 10.1. Startup Options
* 10.2. Going Outside the Glk API
* 10.2.1. Memory Management
* 10.2.2. String Manipulation
* 10.2.3. File Handling
* 10.2.4. Private Extensions to Glk
* 10.3. Glk and the Virtual Machine
* 10.3.1. Implementing a Higher Layer Over Glk
* 10.3.2. Glk as a VM's Native API
* 11. Appendices
* 11.1. The Dispatch Layer
* 11.1.1. How This Works
* 11.1.2. Interrogating the Interface
* 11.1.3. Dispatching
* 11.1.3.1. Basic Types
* 11.1.3.2. References
* 11.1.3.3. Structures
* 11.1.3.4. Arrays
* 11.1.3.5. Return Values
* 11.1.4. Getting Argument Prototypes
* 11.1.5. Functions the Library Must Provide
* 11.1.5.1. Opaque Object Registry
* 11.1.5.2. Retained Array Registry
* 11.1.6. Table of Selectors
* 11.2. The Blorb Layer
* 11.2.1. How This Works
* 11.2.2. What the Program Does
* 11.2.3. What the Library Does
* 11.2.4. What the Blorb Layer Does
* 11.2.5. Blorb Errors

0: Introduction

0.1: What Glk Is

Glk is an attempt to define a portable API (programming interface) for applications with text UIs (user interfaces.)

Rather than go into a detailed explanation of what that means, let me give examples from the world of text adventures. TADS and Infocom's Z-machine have nearly identical interface capabilities; each allows a program to...

* print an indefinite stream of text into an output buffer, with some style control
* input a line of text
* display a few lines of text in a small separate window
* store information in a file, or read it in

and so on. However, the implementation of these capabilities vary widely between platforms and operating systems. Furthermore, this variance is transparent to the program (the adventure game.) The game does not care whether output is displayed via a character terminal emulator or a GUI window; nor whether input uses Mac-style mouse editing or EMACS-style control key editing.

On the third hand, the user is likely to care deeply about these interface decisions. This is why there are Mac-native interpreters on Macintoshes, pen-controlled interpreters on Newtons and Pilots, and so on -- and (ultimately) why there Macintoshes and Pilots and X-windows platforms in the first place.

On the fourth hand, TADS and Inform are not alone; there is historically a large number of text adventure systems. Most are obsolete or effectively dead; but it is inevitable that more will appear. Users want each living system ported to all the platforms in use. Users also prefer these ports to use the same interface, as much as possible.

This all adds up to a pain in the ass.

Glk tries to draw a line between the parts of the text adventure world which are identical on all IF systems, and different on different operating systems, from the parts which are unique to each IF system but identical in all OSs. The border between these two worlds is the Glk API.

My hope is that a new IF system, or existing ones which are less-supported (Hugo, AGT, etc) can be written using Glk for all input and output function. The IF system would then be in truly portable C. On the other side of the line, there would be a Glk library for each operating system and interface (Macintosh, X-windows, curses-terminal, etc.) Porting the IF system to every platform would be trivial; compile the system, and link in the library.

Glk can also serve as a nice interface for applications other than games -- data manglers, quick hacks, or anything else which would normally lack niceties such as editable input, macros, scrolling, or whatever is native to your machine's interface idiom.

0.2: What About Java?

Java is the ultimate tool for cross-platform development. You wanna buy a bridge? How about some shares of Sun stock? Onna stick?

Java has several disadvantages for my purpose. First, it is resource-intensive. It is unlikely that Java will ever run on a 386/68030 generation computer, which is perfectly adequate for text IF. Java may run on a palmtop, but the standard Java library will not. It is also impossible for a single person to port Java to a new platform -- both legally and practically, Java is in the control of large corporations.

Secondly, Java allows too fine-grained a control over the interface. It is not possible to create a text interface using the standard Java library; it is not even really possible to make a graphical interface in the Mac idiom. Java programs have their own Java-specific interface idiom. This trend is accelerating.

On the other hand, it is perfectly possible to implement a Glk library in Java and using the Java interface idiom. This would allow a Glk-based IF system to be easily ported to any machine that runs Java -- if the player likes the way Java looks, of course.

0.3: What About the Virtual Machine?

You can think of Glk as an IF virtual machine, without the virtual machine part. The "machine" is just portable C code.

It is not inconceivable that a new IF virtual machine might be designed to go along with Glk. This VM would use Glk as its interface; each Glk call would correspond to an input/output opcode of the VM.

For more discussion of this approach, see section 10.3, "Glk and the Virtual Machine".

0.4: What Does Glk Not Do?

Glk does not handle the things which should be handled by the program (or the IF system, or the virtual machine) which is linked to Glk. This means that Glk does not address

* parsing
* game object storage
* computation
* text compression

0.5: Conventions of This Document

This document defines the Glk API. I have tried to specify exactly what everything does, what is legal, what is illegal, and why.

Sections in square brackets [like this] are notes. They do not define anything; they clarify or explain what has already been defined. If there seems to be a conflict, ignore the note and follow the definition.

[Notes with the label "WORK" are things which I have not yet fully resolved. Your comments requested and welcome.]

This document is written for the point of view of the game programmer -- the person who wants to use the Glk library to print text, input text, and so on. By saying what the Glk library does, of course, this document also defines the task of the Glk programmer -- the person who wants to port the Glk library to a new platform or operating system. If the Glk library guarantees something, the game programmer can rely on it, and the Glk programmer is required to support it. Contrariwise, if the library does not guarantee something, the Glk programmer may handle it however he likes, and the game programmer must not rely on it. If something is illegal, the game programmer must not do it, and the Glk programmer is not required to worry about it. [It is preferable, but not required, that the Glk library detect illegal requests and display error messages. The Glk library may simply crash when the game program does something illegal. This is why the game programmer must not do it. Right?]

Hereafter, "Glk" or "the library" refers to the Glk library, and "the program" is the game program (or whatever) which is using the Glk library to print text, input text, or whatever. "You" are the person writing the program. "The player" is the person who will use the program/Glk library combination to actually play a game. Or whatever.

The Glk API is declared in a C header file called "glk.h". Please refer to that file when reading this one.
1: Overall Structure

1.1: Your Program's Main Function

The top level of the program -- the main() function in C, for example -- belongs to Glk. [This means that Glk isn't really a library. In a sense, you are writing a library, which is linked into Glk. This is bizarre to think about, so forget it.]

You define a function called glk_main(), which the library calls to begin running your program. glk_main() should run until your program is finished, and then return.

Glk does all its user-interface work in a function called glk_select(). This function waits for an event -- typically the player's input -- and returns an structure representing that event. This means that your program must have an event loop. In the very simplest case, you could write

void glk_main()
{
event_t ev;
while (1) {
glk_select(&ev);
switch (ev.type) {
default:
/* do nothing */
break;
}
}
}


This is a legal Glk-compatible program. As you might expect, it doesn't do anything. The player will see an empty window, which he can only stare at, or destroy in a platform-defined standard manner. [Command-period on the Macintosh; a kill-window menu option in an X window manager; control-C in a curses terminal window.]

[However, this program does not spin wildly and burn CPU time. The glk_select() function waits for an event it can return. Since it only returns events which you have requested, it will wait forever, and grant CPU time to other processes if that's meaningful on the player's machine.] [Actually, there are some events which are always reported. More may be defined in future versions of the Glk API. This is why the default response to an event is to do nothing. If you don't recognize the event, ignore it.]

1.2: Exiting Your Program

If you want to shut down your program in the middle of your glk_main() function, you can call glk_exit().

void glk_exit(void);

This function does not return.

If you print some text to a window and then shut down your program, you can assume that the player will be able to read it. Most likely the Glk library will give a "Hit any key to exit" prompt. (There are other possibilities, however. A terminal-window version of Glk might simply exit and leave the last screen state visible in the terminal window.)

[You should only shut down your program with glk_exit() or by returning from your glk_main() function. If you call the ANSI exit() function, or other platform-native functions, bad things may happen. Some versions of the Glk library may be designed for multiple sessions, for example, and you would be cutting off all the sessions instead of just yours. You would probably also prevent final text from being visible to the player.]

1.3: The Interrupt Handler

Most platforms have some provision for interrupting a program -- command-period on the Macintosh, control-C in Unix, possibly a window manager menu item, or other possibilities. This can happen at any time, including while execution is nested inside one of your own functions, or inside a Glk library function.

If you need to clean up critical resources, you can specify an interrupt handler function.

void glk_set_interrupt_handler(void (*func)(void));

The argument you pass to glk_set_interrupt_handler() should be a pointer to a function which takes no argument and returns no result. If Glk receives an interrupt, and you have set an interrupt handler, your handler will be called, before the process is shut down.

Initially there is no interrupt handler. You can reset to not having any by calling glk_set_interrupt_handler(NULL).

If you call glk_set_interrupt_handler() with a new handler function while an older one is set, the new one replaces the old one. Glk does not try to queue interrupt handlers.

You should not try to interact with the player in your interrupt handler. Do not call glk_select() or glk_select_poll(). Anything you print to a window may not be visible to the player.

1.4: The Tick Thing

Many platforms have some annoying thing that has to be done every so often, or the gnurrs come from the voodvork out and eat your computer.

Well, not really. But you should call glk_tick() every so often, just in case. It may be necessary to yield time to other applications in a cooperative-multitasking OS, or to check for player interrupts in an infinite loop.

void glk_tick(void);

This call is fast; in fact, on average, it does nothing at all. So you can call it often. [In a virtual machine interpreter, once per opcode is appropriate. In a program with lots of computation, pick a comparable rate.]

glk_tick() does not try to update the screen, or check for player input, or any other interface task. For that, you should call glk_select() or glk_select_poll(). See section 4, "Events".

[Captious critics have pointed out that in the sample program model.c, I do not call glk_tick() at all. This is because model.c has no heavy loops. It does a bit of work for each command, and then cycles back to the top of the event loop. The glk_select() call, of course, blocks waiting for input, so it does all the yielding and interrupt-checking one could imagine.]

[Basically, you must ensure there's some fixed upper bound on the amount of computation that can occur before a glk_tick() (or glk_select()) occurs. In a VM interpreter, where the VM code might contain an infinite loop, this is critical. In a C program, you can often eyeball it.]

[But the next version of model.c will have a glk_tick() in the ornate printing loop of verb_yada(). Just to make the point.]

1.5: Basic Types

For simplicity, all the arguments used in Glk calls are of a very few types.

* 32-bit unsigned integer. Unsigned integers are used wherever possible, which is nearly everywhere. This type is called "glui32".
* 32-bit signed integer. This type is called "glsi32". Rarely used.
* References to library objects. These are pointers to opaque C structures; each library will use different structures, so you can not and should not try to manipulate their contents. See section 1.6, "Opaque Objects".
* Pointer to one of the above types.
* Pointer to a structure which consists entirely of the above types.
* Unsigned char. This is used only for Latin-1 text characters; see section 2, "Character Encoding".
* Pointer to char. Sometimes this means a null-terminated string; sometimes an unterminated buffer, with length as a separate gui32 argument. The documentation says which.
* Pointer to void. When nothing else will do.

1.6: Opaque Objects

Glk keeps track of a few classes of special objects. These are opaque to your program; you always refer to them using pointers to opaque C structures.

Currently, these classes are:

* Windows: Screen panels, used to input or output information.
* Streams: Data streams, to which you can input or output text. [There are file streams and window streams, since you can output data to windows or files.]
* File references: Pointers to files in permanent storage. [In Unix a file reference is a pathname; on the Mac, an FSSpec. Actually there's a little more information included, such as file type and whether it is a text or binary file.]
* Sound channels: Audio output channels. [Not all Glk libraries support sound.]

[Note that there may be more object classes in future versions of the Glk API.]

When you create one of these objects, it is always possible that the creation will fail (due to lack of memory, or some other OS error.) When this happens, the allocation function will return NULL (0) instead of a valid pointer. You should always test for this possibility.

NULL is never the identifier of any object (window, stream, file reference, or sound channel). The value NULL is often used to indicate "no object" or "nothing", but it is not a valid reference. If a Glk function takes an object reference as an argument, it is illegal to pass in NULL unless the function definition says otherwise.

The glk.h file defines types "winid_t", "strid_t", "frefid_t", "schanid_t" to store references. These are pointers to struct glk_window_struct, glk_stream_struct, glk_fileref_struct, and glk_schannel_struct respectively. It is, of course, illegal to pass one kind of pointer to a function which expects another.

[This is how you deal with opaque objects from a C program. If you are using Glk through a virtual machine, matters will probably be different. Opaque objects may be represented as integers, or as VM objects of some sort.]

1.6.1: Rocks

Every one of these objects (window, stream, file reference, or sound channel) has a "rock" value. This is simply a 32-bit integer value which you provide, for your own purposes, when you create the object. [The library -- so to speak -- stuffs this value under a rock for safe-keeping, and gives it back to you when you ask for it.]

[If you don't know what to use the rocks for, provide 0 and forget about it.]

1.6.2: Iterating Through Opaque Objects

For each class of opaque objects, there is an iterate function, which you can use to obtain a list of all existing objects of that class. It takes the form

CLASSid_t glk_CLASS_iterate(CLASSid_t obj, glui32 *rockptr);

...where CLASS represents one of the opaque object classes. [So, at the current time, these are the functions glk_window_iterate(), glk_stream_iterate(), and glk_fileref_iterate(). There may be more classes in future versions of the spec; they all behave the same.]

Calling glk_CLASS_iterate(NULL, r) returns the first object; calling glk_CLASS_iterate(obj, r) returns the next object, until there aren't any more, at which time it returns NULL.

The rockptr argument is a pointer to a location; whenever glk_CLASS_iterate() returns an object, the object's rock is stored in the location (*rockptr). If you don't want the rocks to be returned, you may set rockptr to NULL.

You usually use this as follows:

obj = glk_CLASS_iterate(NULL, NULL);
while (obj) {
/* ...do something with obj... */
obj = glk_CLASS_iterate(obj, NULL);
}


If you create or destroy objects inside this loop, obviously, the results are unpredictable. However it is always legal to call glk_CLASS_iterate(obj, r) as long as obj is a valid object id, or NULL.

The order in which objects are returned is entirely arbitrary. The library may even rearrange the order every time you create or destroy an object of the given class. As long as you do not create or destroy any object, the rule is that glk_CLASS_iterate(obj, r) has a fixed result, and iterating through the results as above will list every object exactly once.

1.7: The Gestalt System

The "gestalt" mechanism (cheerfully stolen from the Mac OS) is a system by which the Glk API can be upgraded without making your life impossible. New capabilities (graphics, sound, or so on) can be added without changing the basic specification. The system also allows for "optional" capabilities -- those which not all Glk library implementations will support -- and allows you to check for their presence without trying to infer them from a version number.

The basic idea is that you can request information about the capabilities of the API, by calling the gestalt functions:

glui32 glk_gestalt(glui32 sel, glui32 val);
glui32 glk_gestalt_ext(glui32 sel, glui32 val, glui32 *arr, glui32 arrlen);

The selector (the "sel" argument) tells which capability you are requesting information about; the other three arguments are additional information, which may or may not be meaningful. The arr and arrlen arguments of glk_gestalt_ext() are always optional; you may always pass NULL and 0, if you do not want whatever information they represent. glk_gestalt() is simply a shortcut for this; glk_gestalt(x, y) is exactly the same as glk_gestalt_ext(x, y, NULL, 0).

The critical point is that if the Glk library has never heard of the selector sel, it will return 0. It is always safe to call glk_gestalt(x, y) (or glk_gestalt_ext(x, y, NULL, 0)). Even if you are using an old library, which was compiled before the given capability was imagined, you can test for the capability by calling glk_gestalt(); the library will correctly indicate that it does not support it, by returning 0.

(It is also safe to call glk_gestalt_ext(x, y, z, zlen) for an unknown selector x, where z is not NULL, as long as z points at an array of at least zlen elements. The selector will be careful not to write beyond that point in the array, if it writes to the array at all.)

(If a selector does not use the second argument, you should always pass 0; do not assume that the second argument is simply ignored. This is because the selector may be extended in the future. You will continue to get the current behavior if you pass 0 as the second argument, but other values may produce other behavior.)

1.8: The Version Number

For an example of the gestalt mechanism, consider the selector gestalt_Version. If you do

glui32 res;
res = glk_gestalt(gestalt_Version, 0);


res will be set to a 32-bit number which encodes the version of the Glk spec which the library implements. The upper 16 bits stores the major version number; the next 8 bits stores the minor version number; the low 8 bits stores an even more minor version number, if any. [So the version number 78.2.11 would be encoded as 0x004E020B.]

The current Glk specification version is 0.6.1, so this selector will return 0x00000601.

glui32 res;
res = glk_gestalt_ext(gestalt_Version, 0, NULL, 0);


does exactly the same thing. Note that, in either case, the second argument is not used; so you should always pass 0 to avoid future surprises.

1.9: Other API Conventions

The glk.h header file is the same on all platforms, with the sole exception of the typedef of glui32 and glsi32. These will always be defined as 32-bit unsigned and signed integer types, which may be "long" or "int" or some other C definition.

Note that all constants are #defines, and all functions are actual function declarations (as opposed to macros.) [There are a few places where macros would be more efficient -- glk_gestalt() and glk_gestalt_ext(), for example -- but they are not likely to be CPU bottlenecks, and clarity seems more important.]

FALSE is 0; TRUE is 1. NULL is also 0.

As stated above, it is illegal to pass NULL to a function which is expecting a valid object reference, unless the function definition says otherwise.

Some functions have pointer arguments, acting as "variable" or "reference" arguments; the function's intent is to return some value in the space pointed to by the argument. Unless the function says otherwise, it is legal to pass a NULL pointer to indicate that you do not care about that value.
2: Character Encoding

Glk uses the Latin-1 Unicode encoding, and keeps it holy.

Latin-1 is an 8-bit character encoding; it maps numeric codes in the range 0 to 255 into printed characters. The values from 32 to 126 are the standard printable ASCII characters (' ' to '~'). Values 0 to 31 and 127 to 159 are reserved for control characters, and have no printed equivalent.

Glk uses different parts of the Latin-1 encoding for different purposes.

2.1: Output

When you are sending text to a window, or to a file open in text mode, you can print any of the printable Latin-1 characters: 32 to 126, 160 to 255. You can also print the newline character (linefeed, control-J, decimal 10, hex 0x0A.)

It is not legal to print any other control characters (0 to 9, 11 to 31, 127 to 159). You may not print even common formatting characters such as tab (control-I), carriage return (control-M), or page break (control-L). [As usual, the behavior of the library when you print an illegal character is undefined. It is preferable that the library display a numeric code, such as "\177" or "0x7F", to warn the user that something illegal has occurred. The library may skip illegal characters entirely; but you should not rely on this.]

Note that when you are sending data to a file open in binary mode, you can print any byte value, without restriction. See section 5.6.3, "File Streams".

A particular implementation of Glk may not be able to display all the printable characters. It is guaranteed to be able to display the ASCII characters (32 to 126, and the newline 10.) Other characters may be printed correctly, printed as multi-character combinations (such as "ae" for the one-character "ae" ligature (æ)), or printed as some placeholder character (such as a bullet or question mark, or even an octal code.)

You can test for this by using the gestalt_CharOutput selector. If you set ch to a character code (from 0 to 255), and call

glui32 res, len;
res = glk_gestalt_ext(gestalt_CharOutput, ch, &len, 1);


then res will be one of the following values:

* gestalt_CharOutput_CannotPrint: The character cannot be meaningfully printed. If you try, the player may see nothing, or may see a placeholder.
* gestalt_CharOutput_ExactPrint: The character will be printed exactly as defined.
* gestalt_CharOutput_ApproxPrint: The library will print some approximation of the character. It will be more or less right, but it may not be precise, and it may not be distinguishable from other, similar characters. (Examples: "ae" for the one-character "ae" ligature (æ), "e" for an accented "e" (è), "|" for a broken vertical bar.)

In all cases, len (the glui32 value pointed at by the third argument) will be the number of actual glyphs which will be used to represent the character. In the case of gestalt_CharOutput_ExactPrint, this will always be 1; for gestalt_CharOutput_CannotPrint, it may be 0 (nothing printed) or higher; for gestalt_CharOutput_ApproxPrint, it may be 1 or higher. This information may be useful when printing text in a fixed-width font.

[As described in section 1.9, "Other API Conventions", you may skip this information by passing NULL as the third argument in glk_gestalt_ext(), or by calling glk_gestalt() instead.]

If ch is outside the range 0 to 255, this selector will always return gestalt_CharOutput_CannotPrint. It is also guaranteed to do this if ch is an unprintable character (0 to 9, 11 to 31, 127 to 159.)

[Make sure you do not get confused by signed byte values. If you set a "char" variable ch to 0xFE, the small-thorn character (þ), and then call

res = glk_gestalt(gestalt_CharOutput, ch);


then (by the definition of C/C++) ch will be sign-extended to 0xFFFFFFFE, which is not in the range 0 to 255. You should write

res = glk_gestalt(gestalt_CharOutput, (unsigned char)ch);


instead.]

2.2: Line Input

You can request that the player enter a line of text. See section 4.2, "Line Input Events".

This text will be placed in a buffer of your choice. There is no length field or null terminator in the buffer. (The length of the text is returned as part of the line-input event.)

The buffer will contain only printable Latin-1 characters (32 to 126, 160 to 255).

A particular implementation of Glk may not be able to accept all printable characters as input. It is guaranteed to be able to accept the ASCII characters (32 to 126.)

You can test for this by using the gestalt_LineInput selector. If you set ch to a character code (from 0 to 255), and call

glui32 res;
res = glk_gestalt(gestalt_LineInput, ch);


then res will be TRUE (1) if that character can be typed by the player in line input, and FALSE (0) if not. Note that if ch is a nonprintable character (0 to 31, 127 to 159), or if ch is outside the range 0 to 255, then this is guaranteed to return FALSE.

2.3: Character Input

You can request that the player hit a single key. See section 4.1, "Character Input Events".

The character code which is returned can be any value from 0 to 255. The printable character codes have already been described. The remaining codes are typically control codes, control-A to control-Z and a few others.

There are also a number of special codes, representing special keyboard keys, which can be returned from a char-input event. These are represented as 32-bit integers, starting with 4294967295 (0xFFFFFFFF) and working down. The special key codes are defined in the glk.h file. They include:

* keycode_Left, keycode_Right, keycode_Up, keycode_Down (arrow keys)
* keycode_Return (return or enter)
* keycode_Delete (delete or backspace)
* keycode_Escape
* keycode_Tab
* keycode_PageUp
* keycode_PageDown
* keycode_Home
* keycode_End
* keycode_Func1, keycode_Func2, keycode_Func3, ... keycode_Func12 (twelve function keys)
* keycode_Unknown (any key which has no Latin-1 or special code)

Various implementations of Glk will vary widely in which characters the player can enter. The most obvious limitation is that some characters are mapped to others. For example, most keyboards return a control-I code when the tab key is pressed. The Glk library, if it can recognize this at all, will generate a keycode_Tab event (value 0xFFFFFFF7) when this occurs. Therefore, for these keyboards, no keyboard key will generate a control-I event (value 9.) The Glk library will probably map many of the control codes to the other special keycodes.

[On the other hand, the library may be very clever and discriminate between tab and control-I. This is legal. The idea is, however, that if your program asks the player to "press the tab key", you should check for a keycode_Tab event as opposed to a control-I event.]

Some characters may not be enterable simply because they do not exist. [Not all keyboards have a home or end key. A pen-based platform may not recognize any control characters at all.]

Some characters may not be enterable because they are reserved for the purposes of the interface. For example, the Mac Glk library reserves the tab key for switching between different Glk windows. Therefore, on the Mac, the library will never generate a keycode_Tab event or a control-I event.

[Note that the linefeed or control-J character, which is the only printable control character, is probably not typable. This is because, in most libraries, it will be converted to keycode_Return. Again, you should check for keycode_Return if your program asks the player to "press the return key".]

[The delete and backspace keys are merged into a single keycode because they have such an astonishing history of being confused in the first place... this spec formally waives any desire to define the difference. Of course, a library is free to distinguish delete and backspace during line input. This is when it matters most; conflating the two during character input should not be a large problem.]

You can test for this by using the gestalt_CharInput selector. If you set ch to a character code (from 0 to 255) or a special code (from 0xFFFFFFFF down), and call

glui32 res;
res = glk_gestalt(gestalt_CharInput, ch);


then res will be TRUE (1) if that character can be typed by the player in character input, and FALSE (0) if not.

[Glk porters take note: it is not a goal to be able to generate every single possible key event. If the library says that it can generate a particular keycode, then game programmers will assume that it is available, and ask players to use it. If a keycode_Home event can only be generated by typing escape-control-A, and the player does not know this, the player will be lost when the game says "Press the home key to see the next hint." It is better for the library to say that it cannot generate a keycode_Home event; that way the game can detect the situation and ask the user to type H instead.]

[Of course, it is better not to rely on obscure keys in any case. The arrow keys and return are nearly certain to be available; the others are of gradually decreasing reliability, and you (the game programmer) should not depend on them. You must be certain to check for the ones you want to use, including the arrow keys and return, and be prepared to use different keys in your interface if gestalt_CharInput says they are not available.]

2.4: Upper and Lower Case

You can convert characters from upper to lower case with two Glk utility functions:

unsigned char glk_char_to_lower(unsigned char ch);
unsigned char glk_char_to_upper(unsigned char ch);

These have a few advantages over the standard ANSI tolower() and toupper() macros. They work for the entire Latin-1 character set, including accented letters; they behave consistently on all platforms, since they're part of the Glk library; and they are safe for all characters. That is, if you call glk_char_to_lower() on a lower-case character, or a character which is not a letter, you'll get the argument back unchanged.

The case-sensitive characters in Latin-1 are the ranges 0x41..0x5A, 0xC0..0xD6, 0xD8..0xDE (upper case) and the ranges 0x61..0x7A, 0xE0..0xF6, 0xF8..0xFE (lower case). These are arranged in parallel; so glk_char_to_lower() will add 0x20 to values in the upper-case ranges, and glk_char_to_upper() will subtract 0x20 from values in the lower-case ranges.
3: Windows

On most platforms, the program/library combination will appear to the player in a window -- either a window which covers the entire screen, or one which shares screen space with other windows in a multi-programming environment. Obviously your program does not have worry about the details of this. The Glk screen space is a rectangle, which you can divide into panels for various purposes. It is these panels which I will refer to as "windows" hereafter.

You refer to a window using an opaque C structure pointer. See section 1.6, "Opaque Objects".

A window has a type. Currently there are four window types:

* Text buffer windows: A stream of text. [The "story window" of an Infocom game.] You can only print at the end of the stream, and input a line of text at the end of the stream.
* Text grid windows: A grid of characters in a fixed-width font. [The "status window" of an Infocom game.] You can print anywhere in the grid.
* Graphics windows: A grid of colored pixels. Graphics windows do not support text input or output, but there are image commands to draw in them. [This is an optional capability; not all Glk libraries support graphics. See section 7.4, "Testing for Graphics Capabilities".]
* Blank windows: A blank window. Blank windows support neither input nor output. [They exist mostly to be an example of a "generic" window. You are unlikely to want to use them.]

As Glk is an expanding system, more window types may be added in the future. Therefore, it is important to remember that not all window types will necessarily be available under all Glk libraries.

There is one other special type of window, the pair window. Pair windows are created by Glk as part of the system of window arrangement. You cannot create them yourself. See section 3.5.2, "Pair Windows".

Every window has a rock. This is a value you provide when the window is created; you can use it however you want. See section 1.6.1, "Rocks".

When Glk starts up, there are no windows.

[When I say there are no windows, I mean there are no Glk windows. In a multiprogramming environment, such as X or MacOS, there may be an application window visible; this is the screen space that will contain all the Glk windows that you create. But at first, this screen space is empty and unused.]

Without a window, you cannot do any kind of input or output; so the first thing you'll want to do is create one. See section 3.2, "Window Opening, Closing, and Constraints".

You can create as many windows as you want, of any types. You control their arrangement and sizes through a fairly flexible system of calls. See section 3.1, "Window Arrangement".

You can close any windows you want. You can even close all the windows, which returns you to the original startup state.

You can request input from any or all windows. Input can be mouse input (on platforms which support a mouse), single-character input, or input of an entire line of text. It is legal to request input from several windows at the same time. The library will have some interface mechanism for the player to control which window he is typing in.

3.1: Window Arrangement

The Way of Window Arrangement is fairly complicated. I'll try to explain it coherently. [If you are reading this document to get an overview of Glk, by all means skip forward to section 3.5, "The Types of Windows". Come back here later.]

Originally, there are no windows. You can create a window, which will take up the entire available screen area. You can then split this window in two. One of the halves is the original window; the other half is new, and can be of any type you want. You can control whether the new window is left, right, above, or below the original one. You can also control how the split occurs. It can be 50-50, or 70-30, or any other percentage split. Or, you can give a fixed width to the new window, and allow the old one to take up the rest of the available space. Or you can give a fixed width to the old window, and let the new one take up the rest of the space.

Now you have two windows. In exactly the same way, you can split either of them -- the original window, or the one you just created. Whichever one you split becomes two, which together take up the same space that the one did before.

You can repeat this as often as you want. Every time you split a window, one new window is created. Therefore, the call that does this is called glk_window_open(). [It might have been less confusing to call it "glk_split_window" -- or it might have been more confusing. I picked one.]

It is important to remember that the order of splitting matters. If you split twice times, you don't have a trio of windows; you have a pair with another pair on one side. Mathematically, the window structure is a binary tree.

Example time. Say you do two splits, each a 50-50 percentage split. You start with the original window A, and split that into A and B; then you split B into B and C.

+---------+
| | O
| A | / \
| | A O
+---------+ / \
| B | B C
+---------+
| C |
+---------+


Or, you could split A into A and B, and then split A again into A and C.

+---------+
| A | O
+---------+ / \
| C | O B
+---------+ / \
| | A C
| B |
| |
+---------+


I'm using the simplest possible splits in the examples above. Every split is 50-50, and the new window of the pair is always below the original one (the one that gets split.) You can get fancier than that. Here are three more ways to perform the first example; all of them have the same tree structure, but look different on the screen.

+---------+ +---------+ +---------+
| | | A | | | O
| A | +---------+ | A | / \
| | | B | | | A O
+---------+ +---------+ +----+----+ / \
| C | | | | | | B C
+---------+ | C | | C | B |
| B | | | | | |
+---------+ +---------+ +----+----+


On the left, we turn the second split (B into B/C) upside down; we put the new window (C) below the old window (B).

In the center, we mess with the percentages. The first split (A into A/B) is a 25-75 split, which makes B three times the size of A. The second (B into B/C) is a 33-66 split, which makes C twice the size of B. This looks rather like the second example above, but has a different internal structure.

On the right, the second split (B into B/C) is vertical instead of horizontal, with the new window (C) on the left of the old one.

The visible windows on the Glk screen are "leaf nodes" of the binary tree; they hang off the ends of the branches in the diagram. There are also the "internal nodes", the ones at the forks, which are marked as "O". These are the mysterious pair windows.

You don't create pair windows directly; they are created as a consequence of window splits. Whenever you create a new window, a new pair window is also created automatically. In the following two-split process, you can see that when a window is split, it is replaced by a new pair window, and moves down to become one of that "O"'s two children.

+---+ A
| |
| A |
| |
+---+

+---+ O
| A | / \
+---+ A B
| B |
+---+

+---+ O
| A | / \
+-+-+ A O
|C|B| / \
+-+-+ B C


You can't draw into a pair window. It's completely filled up with the two windows it contains. They're what you should be drawing into.

Why have pair windows in the system at all? They're convenient for certain operations. For example, you can close any window at any time; but sometimes you want to close an entire nest of windows at once. In the third stage shown, if you close the lower pair window, it blows away all its descendents -- both B and C -- and leaves just a single window, A, which is what you started with.

I'm using some math terminology already, so I'll explain it briefly. The "root" of the tree is the top (math trees, like family trees, grow upside down.) If there's only one window, it's the root; otherwise the root is the topmost "O". Every pair window has exactly two "children". Other kinds of windows are leaves on the tree, and have no children. A window's "descendants", obviously, are its children and grandchildren and great-grandchildren and so on. The "parent" and "ancestors" of a window are exactly what you'd expect. So the root window is the ancestor of every other window.

There are Glk functions to determine the root window, and to determine the parent of any given window. Note that every window's parent is a pair window. (Except for the root window, which has no parent.)

3.2: Window Opening, Closing, and Constraints

winid_t glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype, glui32 rock);

If there are no windows, the first three arguments are meaningless. split must be zero, and method and size are ignored. wintype is the type of window you're creating, and rock is the rock (see section 1.6.1, "Rocks").

If any windows exist, new windows must be created by splitting existing ones. split is the window you want to split; this must not be zero. method is a mask of constants to specify the direction and the split method (see below). size is the size of the split. wintype is the type of window you're creating, and rock is the rock.

The winmethod constants:

* winmethod_Above, winmethod_Below, winmethod_Left, winmethod_Right: The new window will be above, below, to the left, or to the right of the old one which was split.
* winmethod_Fixed, winmethod_Proportional: The new window is a fixed size, or a given proportion of the old window's size. (See below.)

Remember that it is possible that the library will be unable to create a new window, in which case glk_window_open() will return NULL. [It is acceptable to gracefully exit, if the window you are creating is an important one -- such as your first window. But you should not try to perform any window operation on the id until you have tested to make sure it is non-zero.]

The examples we've seen so far have the simplest kind of size control. (Yes, this is "below".) Every pair is a percentage split, with X percent going to one side, and (100-X) percent going to the other side. If the player resizes the window, the whole mess expands, contracts, or stretches in a uniform way.

As I said above, you can also make fixed-size splits. This is a little more complicated, because you have to know how this fixed size is measured.

Sizes are measured in a way which is different for each window type. For example, a text grid window is measured by the size of its fixed-width font. You can make a text grid window which is fixed at a height of four rows, or ten columns. A text buffer window is measured by the size of its font. [Remember that different windows may use different size fonts. Even two text grid windows may use fixed-size fonts of different sizes.] Graphics windows are measured in pixels, not characters. Blank windows aren't measured at all; there's no meaningful way to measure them, and therefore you can't create a blank window of a fixed size, only of a proportional (percentage) size.

So to create a text buffer window which takes the top 40% of the original window's space, you would execute

newwin = glk_window_open(win, winmethod_Above | winmethod_Proportional, 40, wintype_TextBuffer, 0);


To create a text grid which is always five lines high, at the bottom of the original window, you would do

newwin = glk_window_open(win, winmethod_Below | winmethod_Fixed, 5, wintype_TextGrid, 0);


Note that the meaning of the size argument depends on the method argument. If the method is winmethod_Fixed, it also depends on the wintype argument. The new window is then called the "key window" of this split, because its window type determines how the split size is computed. [For winmethod_Proportional splits, you can still call the new window the "key window". But the key window is not important for proportional splits, because the size will always be computed as a simple ratio of the available space, not a fixed size of one child window.]

This system is more or less peachy as long as all the constraints work out. What happens when there is a conflict? The rules are simple. Size control always flows down the tree, and the player is at the top. Let's bring out an example:

+---------+
| C: 2 |
| rows | O
+---------+ / \
| A | O B
+---------+ / \
| | A C
| B: 50% |
| |
| |
+---------+


First we split A into A and B, with a 50% proportional split. Then we split A into A and C, with C above, C being a text grid window, and C gets a fixed size of two rows (as measured in its own font size). A gets whatever remains of the 50% it had before.

Now the player stretches the window vertically.

+---------+
| C: 2 |
| rows |
+---------+
| A |
| |
+---------+
| |
| |
| B: 50% |
| |
| |
+---------+


The library figures: the topmost split, the original A/B split, is 50-50. So B gets half the screen space, and the pair window next to it (the lower "O") gets the other half. Then it looks at the lower "O". C gets two rows; A gets the rest. All done.

Then the user maliciously starts squeezing the window down, in stages:

+---------+ +---------+ +---------+ +---------+ +---------+
| C: 2 | | C | | C | | C | +---------+
| rows | | | | | +---------+ +---------+
+---------+ +---------+ +---------+ +---------+ | B |
| A | | A | +---------+ | B | +---------+
| | +---------+ | | | |
+---------+ | | | B | +---------+
| | | B | | |
| | | | +---------+
| B: 50% | | |
| | +---------+
| |
+---------+


The logic remains the same. B always gets half the space. At stage 3, there's no room left for A, so it winds up with zero height. Nothing displayed in A will be visible. At stage 4, there isn't even room in the upper 50% to give C its two rows; so it only gets one. Finally, C is squashed out of existence as well.

When a window winds up undersized, it remembers what size it should be. In the example above, A remembers that it should be two rows; if the user expands the window to the original size, it would return to the original layout.

The downward flow of control is a bit harsh. After all, in stage 4, there's room for C to have its two rows if only B would give up some of its 50%. But this does not happen. [This makes life much easier for the Glk library. To determine the configuration of a window, it only needs to look at the window's ancestors, never at its descendants. So window layout is a simple recursive algorithm, no backtracking.]

What happens when you split a fixed-size window? The resulting pair window -- that is, the two new parts together -- retain the same size constraint as the original window that was split. The key window for the original split is still the key window for that split, even though it's now a grandchild instead of a child.

The easy, and correct, way to think about this is that the size constraint is stored by a window's parent, not the window itself; and a constraint consists of a pointer to a key window plus a size value.

+---------+ +---------+ +---------+
| | | | | C: 2 |
| | A | A: 50% | O1 | rows | O1
| | | | / \ +---------+ / \
| | | | A B | A | O2 B
| A | +---------+ +---------+ / \
| | | | | | A C
| | | B | | B |
| | | | | |
| | | | | |
+---------+ +---------+ +---------+


After the first split, the new pair window (O1, which covers the whole screen) knows that its first child (A) is above the second, and gets 50% of its own area. (A is the key window for this split, but a proportional split doesn't care about key windows.)

After the second split, all this remains true; O1 knows that its first child gets 50% of its space, and A is O1's key window. But now O1's first child is O2 instead of A. The newer pair window (O2) knows that its first child (C) is above the second, and gets a fixed size of two rows. (As measured in C's font, because C is O2's key window.)

If we split C, now, the resulting pair will still be two C-font rows high -- that is, tall enough for two lines of whatever font C displays. For the sake of example, we'll do this vertically.

+----+----+
| C | D |
| | | O1
+----+----+ / \
| A | O2 B
+---------+ / \
| | A O3
| B | / \
| | C D
| |
+---------+


O3 now knows that its children have a 50-50 left-right split. O2 is still committed to giving its upper child, O3, two C-font rows. Again, this is because C is O2's key window. [This turns out to be a good idea, because it means that C, the text grid window, is still two rows high. If O3 had been a upper-lower split, things wouldn't work out so neatly. But the rules would still apply. If you don't like this, don't do it.]

void glk_window_close(winid_t win, stream_result_t *result);

This closes a window, which is pretty much exactly the opposite of opening a window. It is legal to close all your windows, or to close the root window (which does the same thing.)

The result argument is filled with the output character count of the window stream. See section 5, "Streams" and section 5.3, "Closing Streams".

When you close a window (and it is not the root window), the other window in its pair takes over all the freed-up area. Let's close D, in the current example:

+---------+
| C |
| | O1
+---------+ / \
| A | O2 B
+---------+ / \
| | A C
| B |
| |
| |
+---------+


Notice what has happened. D is gone. O3 is gone, and its 50-50 left-right split has gone with it. The other size constraints are unchanged; O2 is still committed to giving its upper child two rows, as measured in the font of O2's key window, which is C. Conveniently, O2's upper child is C, just as it was before we created D. In fact, now that D is gone, everything is back to the way it was before we created D.

But what if we had closed C instead of D? We would have gotten this:

+---------+
+---------+
| | O1
| A | / \
| | O2 B
+---------+ / \
| | A D
| B |
| |
| |
+---------+


Again, O3 is gone. But D has collapsed to zero height. This is because its height is controlled by O2, and O2's key window was C, and C is now gone. O2 no longer has a key window at all, so it cannot compute a height for its upper child, so it defaults to zero.

[This may seem to be an inconvenient choice. That is deliberate. You should not leave a pair window with no key, and the zero-height default reminds you not to. You can use glk_window_set_arrangement() to set a new split measurement and key window. See section 3.3, "Changing Window Constraints".]

3.3: Changing Window Constraints

There are library functions to change and to measure the size of a window.

void glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr);
void glk_window_set_arrangement(winid_t win, glui32 method, glui32 size, winid_t keywin);
void glk_window_get_arrangement(winid_t win, glui32 *methodptr, glui32 *sizeptr, winid_t *keywinptr);

glk_window_get_size() simply returns the actual size of the window, in its measurement system. As described in section 1.9, "Other API Conventions", either widthptr or heightptr can be NULL, if you only want one measurement. [Or, in fact, both, if you want to waste time.]

glk_window_set_arrangement() changes the size of an existing split -- that is, it changes the constraint of a given pair window. glk_window_get_arrangement() returns the constraint of a given pair window.

Consider the example above, where D has collapsed to zero height. Say D was a text buffer window. You could make a more useful layout by doing

winid_t o2;
o2 = glk_window_get_parent(d);
glk_window_set_arrangement(o2, winmethod_Above | winmethod_Fixed, 3, d);


That would set the D (the upper child of O2) to be O2's key window, and give it a fixed size of 3 rows.

If you later wanted to expand D, you could do

glk_window_set_arrangement(o2, winmethod_Above | winmethod_Fixed, 5, NULL);


That expands D to five rows. Note that, since O2's key window is already set to D, it is not necessary to provide the keywin argument; you can pass NULL to mean "leave the key window unchanged."

If you do change the key window of a pair window, the new key window must be a descendant of that pair window. In the current example, you could change O2's key window to be A, but not B. The key window also cannot be a pair window itself.

glk_window_set_arrangement(o2, winmethod_Below | winmethod_Fixed, 3, NULL);


This changes the constraint to be on the lower child of O2, which is A. The key window is still D; so A would then be three rows high as measured in D's font, and D would get the rest of O2's space. That may not be what you want. To set A to be three rows high as measured in A's font, you would do

glk_window_set_arrangement(o2, winmethod_Below | winmethod_Fixed, 3, a);


Or you could change O2 to a proportional split:

glk_window_set_arrangement(o2, winmethod_Below | winmethod_Proportional, 30, NULL);


or

glk_window_set_arrangement(o2, winmethod_Above | winmethod_Proportional, 70, NULL);


These do exactly the same thing, since 30% above is the same as 70% below. You don't need to specify a key window with a proportional split, so the keywin argument is NULL. (You could actually specify either A or D as the key window, but it wouldn't affect the result.)

Whatever constraint you set, glk_window_get_size() will tell you the actual window size you got.

Note that you can resize windows, but you can't flip or rotate them. You can't move A above D, or change O2 to a vertical split where A is left or right of D. [To get this effect you could close one of the windows, and re-split the other one with glk_window_open().]

3.4: A Note on Display Style

The way windows are displayed is, of course, entirely up to the Glk library; it depends on what is natural for the player's machine. The borders between windows may be black lines, 3-D bars, rows of "#" characters; there may even be no borders at all. [This is an important possibility to keep in mind.]

There may be other decorations as well. A text buffer window will often have a scroll bar. The library (or player) may prefer wide margins around each text window. And so on.

The library is reponsible for handling these decorations, margins, spaces, and borders. You should never worry about them. You are guaranteed that if you request a fixed size of two rows, your text grid window will have room for two rows of characters -- if there is enough total space. Any margins or borders will be allowed for already. If there isn't enough total space (as in stages 4 and 5, above), you lose, of course.

How do you know when you're losing? You can call glk_window_get_size() to determine the window size you really got. Obviously, you should draw into your windows based on their real size, not the size you requested. If there's enough space, the requested size and the real size will be identical; but you should not rely on this. Call glk_window_get_size() and check.

3.5: The Types of Windows

This is a technical description of all the window types, and exactly how they behave.

3.5.1: Blank Windows

A blank window is always blank. It supports no input and no output. (You can call glk_window_get_stream() on it, as you can with any window, but printing to the resulting stream has no effect.) A blank window has no size; glk_window_get_size() will return (0,0), and it is illegal to set a window split with a fixed size in the measurement system of a blank window.

[A blank window is not the same as there being no windows. When Glk starts up, there are no windows at all, not even a window of the blank type.]

3.5.2: Pair Windows

A pair window is completely filled by the two windows it contains. It supports no input and no output, and it has no size.

You cannot directly create a pair window; one is automatically created every time you split a window with glk_window_open(). Pair windows are always created with a rock value of 0.

You can close a pair window with glk_window_close(); this also closes every window contained within the pair window.

It is legal to split a pair window when you call glk_window_open().

3.5.3: Text Buffer Windows

A text buffer window contains a linear stream of text. It supports output; when you print to it, the new text is added to the end. There is no way for you to affect text which has already been printed. There are no guarantees about how much text the window keeps; old text may be stored forever, so that the user can scroll back to it, or it may be thrown away as soon as it scrolls out of the window. [Therefore, there may or may not be a player-controllable scroll bar or other scrolling widget.]

The display of the text in a text buffer is up to the library. Lines will probably not be broken in the middles of words -- but if they are, the library is not doing anything illegal, only ugly. Text selection and copying to a clipboard, if available, are handled however is best on the player's machine. Paragraphs (as defined by newline characters in the output) may be indented. [You should not, in general, fake this by printing spaces before each paragraph of prose text. Let the library and player preferences handle that. Special cases (like indented lists) are of course up to you.]

When a text buffer is cleared (with glk_window_clear()), the library will do something appropriate; the details may vary. It may clear the window, with later text appearing at the top -- or the bottom. It may simply print enough blank lines to scroll the current text out of the window. It may display a distinctive page-break symbol or divider.

The size of a text buffer window is necessarily imprecise. Calling glk_window_get_size() will return the number of rows and columns that would be available if the window was filled with "0" (zero) characters in the "normal" font. However, the window may use a non-fixed-width font, so that number of characters in a line could vary. The window might even support variable-height text (say, if the player is using large text for emphasis); that would make the number of lines in the window vary as well.

Similarly, when you set a fixed-size split in the measurement system of a text buffer, you are setting a window which can handle a fixed number of rows (or columns) of "0" characters. The number of rows (or characters) that will actually be displayed depends on font variances.

A text buffer window supports both character and line input, but not mouse input.

In character input, there will be some visible signal that the window is waiting for a keystroke. (Typically, a cursor at the end of the text.) When the player hits a key in that window, an event is generated, but the key is not printed in the window.

In line input, again, there will be some visible signal. It is most common for the player to compose input in the window itself, at the end of the text. (This is how IF story input usually looks.) But it's not strictly required. An alternative approach is the way MUD clients usually work: there is a dedicated one-line input window, outside of Glk's window space, and the user composes input there. [If this approach is used, there will still be some way to handle input from two windows at once. It is the library's responsibility to make this available to the player. You only need request line input and wait for the result.]

When the player finishes his line of input, the library will display the input text at the end of the buffer text (if it wasn't there already.) It will be followed by a newline, so that the next text you print will start a new line (paragraph) after the input.

If you call glk_cancel_line_event(), the same thing happens; whatever text the user was composing is visible at the end of the buffer text, followed by a newline.

3.5.4: Text Grid Windows

A text grid contains a rectangular array of characters, in a fixed-width font. Its size is the number of columns and rows of the array.

A text grid window supports output. It maintains knowledge of an output cursor position. When the window is opened, it is filled with blanks (space characters), and the output cursor starts in the top left corner -- character (0,0). If the window is cleared with glk_window_clear(), the window is filled with blanks again, and the cursor returns to the top left corner.

When you print, the characters of the output are laid into the array in order, left to right and top to bottom. When the cursor reaches the end of a line, it goes to the beginning of the next line. The library makes no attempt to wrap lines at word breaks.

[Note that printing fancy characters may cause the cursor to advance more than one position per character. (For example, the "ae" ligature (æ) may print as two characters.) See section 2.1, "Output", for how to test this situation.]

You can set the cursor position with glk_window_move_cursor().

void glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos);

If you move the cursor right past the end of a line, it wraps; the next character which is printed will appear at the beginning of the next line.

If you move the cursor below the last line, or when the cursor reaches the end of the last line, it goes "off the screen" and further output has no effect. You must call glk_window_move_cursor() or glk_window_clear() to move the cursor back into the visible region.

[Note that the arguments of glk_window_move_cursor are unsigned ints. This is okay, since there are no negative positions. If you try to pass a negative value, Glk will interpret it as a huge positive value, and it will wrap or go off the last line.]

[Also note that the output cursor is not necessarily visible. In particular, when you are requesting line or character input in a grid window, you cannot rely on the cursor position to prompt the player where input is indicated. You should print some character prompt at that spot -- a ">" character, for example.]

When a text grid window is resized smaller, the bottom or right area is thrown away, but the remaining area stays unchanged. When it is resized larger, the new bottom or right area is filled with blanks. [You may wish to watch for evtype_Arrange events, and clear-and-redraw your text grid windows when you see them change size.]

Text grid window support character and line input, as well as mouse input (if a mouse is available.)

Mouse input returns the position of the character that was touched, from (0,0) to (width-1,height-1).

Character input is as described in the previous section.

Line input is slightly different; it is guaranteed to take place in the window, at the output cursor position. The player can compose input only to the right edge of the window; therefore, the maximum input length is (windowwidth - 1 - cursorposition). If the maxlen argument of glk_request_line_event() is smaller than this, the library will not allow the input cursor to go more than maxlen characters past its start point. [This allows you to enter text in a fixed-width field, without the player being able to overwrite other parts of the window.]

When the player finishes his line of input, it will remain visible in the window, and the output cursor will be positioned at the beginning of the next row. Again, if you glk_cancel_line_event(), the same thing happens.

3.5.5: Graphics Windows

A graphics window contains a rectangular array of pixels. Its size is the number of columns and rows of the array.

Each graphics window has a background color, which is initially white. You can change this; see section 7.2, "Graphics in Graphics Windows".

When a text grid window is resized smaller, the bottom or right area is thrown away, but the remaining area stays unchanged. When it is resized larger, the new bottom or right area is filled with the background color. [You may wish to watch for evtype_Arrange events, and clear-and-redraw your graphics windows when you see them change size.]

In some libraries, you can receive a graphics-redraw event (evtype_Redraw) at any time. This signifies that the window in question has been cleared to its background color, and must be redrawn. If you create any graphics windows, you must handle these events.

[Redraw events can be triggered when a Glk window is uncovered or made visible by the platform's window manager. On the other hand, some Glk libraries handle these problem automatically -- for example, with a backing store -- and do not send you redraw events. On the third hand, the backing store may be discarded if memory is low, or for other reasons -- perhaps the screen's color depth has changed. So redraw events are always a possibility, even in clever libraries. This is why you must be prepared to handle them.]

[However, you will not receive a redraw event when you create a graphics window. It is assumed that you will do the initial drawing of your own accord. You also do not get redraw events when a graphics window is enlarged. If you ordered the enlargement, you already know about it; if the player is responsible, you receive a window-arrangement event, which covers the situation.]

For a description of the drawing functions that apply to graphics windows, see section 7.2, "Graphics in Graphics Windows".

Graphics windows support no input or output.

Not all libraries support graphics windows. You can test whether Glk graphics are available using the gestalt system. In a C program, you can also test whether the graphics functions are defined at compile-time. See section 7.4, "Testing for Graphics Capabilities". [As with all windows, you should also test for NULL when you create a graphics window.]

3.6: Echo Streams

Every window has an associated window stream; you print to the window by printing to this stream. However, it is possible to attach a second stream to a window. Any text printed to the window is also echoed to this second stream, which is called the window's "echo stream."

Effectively, any call to glk_put_char() (or the other output commands) which is directed to the window's window stream, is replicated to the window's echo stream. This also goes for the style commands such as glk_set_style().

Note that the echoing is one-way. You can still print text directly to the echo stream, and it will go wherever the stream is bound, but it does not back up and appear in the window.

void glk_window_set_echo_stream(winid_t win, strid_t str);
strid_t glk_window_get_echo_stream(winid_t win);

Initially, a window has no echo stream, so glk_window_get_echo_stream(win) will return NULL. You can set a window's echo stream to be any valid output stream by calling glk_window_set_echo_stream(win, str). You can reset a window to stop echoing by calling glk_window_set_echo_stream(win, NULL).

An echo stream can be of any type, even another window's window stream. [This would be somewhat silly, since it would mean that any text printed to the window would be duplicated in another window. More commonly, you would set a window's echo stream to be a file stream, in order to create a transcript file from that window.]

A window can only have one echo stream. But a single stream can be the echo stream of any number of windows, sequentially or simultaneously.

If a window is closed, its echo stream remains open; it is not automatically closed. [Do not confuse the window's window stream with its echo stream. The window stream is "owned" by the window, and dies with it. The echo stream is merely temporarily associated with the window.]

If a stream is closed, and it is the echo stream of one or more windows, those windows are reset to not echo anymore. (So then calling glk_window_get_echo_stream() on them will return NULL.)

It is illegal to set a window's echo stream to be its own window stream. That would create an infinite loop, and is nearly certain to crash the Glk library. It is similarly illegal to create a longer loop (two or more windows echoing to each other.)

3.7: Other Window Functions

winid_t glk_window_iterate(winid_t win, glui32 *rockptr);

This function can be used to iterate through the list of all open windows (including pair windows.) See section 1.6.2, "Iterating Through Opaque Objects".

As that section describes, the order in which windows are returned is arbitrary. The root window is not necessarily first, nor is it necessarily last.

glui32 glk_window_get_rock(winid_t win);

This returns the window's rock value. Pair windows always have rock 0; all other windows return whatever rock you created them with.

glui32 glk_window_get_type(winid_t win);

This returns the window's type (wintype_...)

winid_t glk_window_get_parent(winid_t win);

This returns the window which is the parent of the given window. If win is the root window, this returns NULL, since the root window has no parent. Remember that the parent of every window is a pair window; other window types are always childless.

winid_t glk_window_get_sibling(winid_t win);

This returns the other child of the given window's parent. If win is the root window, this returns NULL.

winid_t glk_window_get_root(void);

This returns the root window. If there are no windows, this returns NULL.

void glk_window_clear(winid_t win);

Erase the window. The meaning of this depends on the window type.

* Text buffer: This may do any number of things, such as delete all text in the window, or print enough blank lines to scroll all text beyond visibility, or insert a page-break marker which is treated specially by the display part of the library.
* Text grid: This will clear the window, filling all positions with blanks. The window cursor is moved to the top left corner (position 0,0).
* Graphics: Clears the entire window to its current background color. See section 3.5.5, "Graphics Windows".
* Other window types: No effect.

It is illegal to erase a window which has line input pending.

strid_t glk_window_get_stream(winid_t win);

This returns the stream which is associated with the window. (See section 5.6.1, "Window Streams".) Every window has a stream which can be printed to, but this may not be useful, depending on the window type. [For example, printing to a blank window's stream has no effect.]

void glk_set_window(winid_t win);

This sets the current stream to the window's stream. It is exactly equivalent to

glk_stream_set_current(glk_window_get_stream(win)).

4: Events

As described above, all player input is handed to your program by the glk_select() call, in the form of events. You should write at least one event loop to retrieve these events.

void glk_select(event_t *event);

typedef struct event_struct {
glui32 type;
winid_t win;
glui32 val1, val2;
} event_t;


This causes the program to wait for an event, and then store it in the structure pointed to by the argument. Unlike most Glk functions that take pointers, the argument of glk_select() may not be NULL.

Most of the time, you only get the events that you request. However, there are some events which can arrive at any time. This is why you must always call glk_select() in a loop, and continue the loop until you get the event you really want.

The event structure is self-explanatory. type is the event type. The window that spawned the event, if relevant, is in win. The remaining fields contain more information specific to the event.

The event types are:

* evtype_None: No event. This is a placeholder, and glk_select() never returns it.
* evtype_Timer: An event that repeats at fixed intervals.
* evtype_CharInput: A keystroke event in a window.
* evtype_LineInput: A full line of input completed in a window.
* evtype_MouseInput: A mouse click in a window.
* evtype_Arrange: An event signalling that the sizes of some windows have changed.
* evtype_Redraw: An event signalling that graphics windows must be redrawn.
* evtype_Hyperlink: The selection of a hyperlink in a window.

Note that evtype_None is zero, and the other values are positive. Negative even types (0x80000000 to 0xFFFFFFFF) are reserved for implementation-defined events.

You can also inquire if an event is available, without stopping to wait for one to occur.

void glk_select_poll(event_t *event);

This checks if an internally-spawned event is available. If so, it stores it in the structure pointed to by event. If not, it sets event->type to evtype_None. Either way, it returns almost immediately.

The first question you now ask is, what is an internally-spawned event? glk_select_poll() does not check for or return evtype_CharInput, evtype_LineInput, or evtype_MouseInput events. It is intended for you to test conditions which may have occurred while you are computing, and not interfacing with the player. For example, time may pass during slow computations; you can use glk_select_poll() to see if a evtype_Timer event has occured. (See section 4.4, "Timer Events".)

At the moment, glk_select_poll() checks for evtype_Timer, and possibly evtype_Arrange and evtype_SoundNotify events. But see section 4.9, "Other Events".

The second question is, what does it mean that glk_select_poll() returns "almost immediately"? In some Glk libraries, text that you send to a window is buffered; it does not actually appear until you request player input with glk_select(). glk_select_poll() attends to this buffer-flushing task in the same way. (Although it does not do the "Hit any key to scroll down" waiting which may be done in glk_select(); that's a player-input task.)

Similarly, on multitasking platforms, glk_select() may yield time to other processes; and glk_select_poll() does this as well.

The upshot of this is that you should not call glk_select_poll() very often. If you are not doing much work between player inputs, you should not need to call it at all. [For example, in a virtual machine interpreter, you should not call glk_select_poll() after every opcode.] However, if you are doing intense computation, you may wish to call glk_select_poll() every so often to yield time to other processes. And if you are printing intermediate results during this computation, you should glk_select_poll() every so often, so that you can be certain your output will be displayed before the next glk_select().

[However, you should call glk_tick() often -- once per opcode in a VM interpreter. See section 1.4, "The Tick Thing".]

4.1: Character Input Events

You can request character input from text buffer and text grid windows.

void glk_request_char_event(winid_t win);

A window cannot have requests for both character and line input at the same time. It is illegal to call glk_request_char_event() if the window already has a pending request for either character or line input.

void glk_cancel_char_event(winid_t win);

This cancels a pending request for character input. For convenience, it is legal to call glk_cancel_char_event() even if there is no character input request on that window. Glk will ignore the call in this case.

If a window has a pending request for character input, and the player hits a key in that window, glk_select() will return an event whose type is evtype_CharInput. Once this happens, the request is complete; it is no longer pending. You must call glk_request_char_event() if you want another character from that window.

In the event structure, win tells what window the event came from. val1 tells what character was entered; this will be a code from 0 to 255, or a special keycode. (See section 2.3, "Character Input".) val2 will be 0.

4.2: Line Input Events

You can request line input from text buffer and text grid windows.

void glk_request_line_event(winid_t win, char *buf, glui32 maxlen, glui32 initlen);

A window cannot have requests for both character and line input at the same time. It is illegal to call glk_request_line_event() if the window already has a pending request for either character or line input.

The buf argument is a pointer to space where the line input will be stored. (This may not be NULL.) maxlen is the length of this space, in bytes; the library will not accept more characters than this. If initlen is nonzero, then the first initlen bytes of buf will be entered as pre-existing input -- just as if the player had typed them himself. [The player can continue composing after this pre-entered input, or delete it or edit as usual.]

The contents of the buffer are undefined until the input is completed (either by a line input event, or glk_cancel_line_event(). The library may or may not fill in the buffer as the player composes, while the input is still pending; it is illegal to change the contents of the buffer yourself.

void glk_cancel_line_event(winid_t win, event_t *event);

This cancels a pending request for line input. The event pointed to by the event argument will be filled in as if the player had hit enter, and the input composed so far will be stored in the buffer; see below. If you do not care about this information, pass NULL as the event argument. (The buffer will still be filled.)

For convenience, it is legal to call glk_cancel_line_event() even if there is no line input request on that window. The event type will be set to evtype_None in this case.

If a window has a pending request for line input, and the player hits enter in that window (or whatever action is appropriate to enter his input), glk_select() will return an event whose type is evtype_LineInput. Once this happens, the request is complete; it is no longer pending. You must call glk_request_line_event() if you want another line of text from that window.

In the event structure, win tells what window the event came from. val1 tells how many characters were entered. val2 will be 0. The characters themselves are stored in the buffer specified in the original glk_request_line_event() call. [There is no null terminator stored in the buffer.]

It is illegal to print anything to a window which has line input pending. [This is because the window may be displaying and editing the player's input, and printing anything would make life unnecessarily complicated for the library.]

4.3: Mouse Input Events

On some platforms, Glk can recognize when the mouse (or other pointer) is used to select a spot in a window. You can request mouse input only in text grid windows and graphics windows.

void glk_request_mouse_event(winid_t win);
void glk_cancel_mouse_event(winid_t win);

A window can have mouse input and character/line input pending at the same time.

If the player clicks in a window which has a mouse input event pending, glk_select() will return an event whose type is evtype_MouseInput. Again, once this happens, the request is complete, and you must request another if you want further mouse input.

In the event structure, win tells what window the event came from.

In a text grid window, the val1 and val2 fields are the x and y coordinates of the character that was clicked on. [So val1 is the column, and val2 is the row.] The top leftmost character is considered to be (0,0).

In a graphics window, they are the x and y coordinates of the pixel that was clicked on. Again, the top left corner of the window is (0,0).

You can test whether mouse input is supported with the gestalt_MouseInput selector.

res = glk_gestalt(gestalt_MouseInput, windowtype);


This will return TRUE (1) if windows of the given type support mouse input. If this returns FALSE (0), it is still legal to call glk_request_mouse_event(), but it will have no effect, and you will never get mouse events.

[Most mouse-based idioms define standard functions for mouse hits in text windows -- typically selecting or copying text. It is up to the library to separate this from Glk mouse input. The library may choose to select text when it is clicked normally, and cause Glk mouse events when text is control-clicked. Or the other way around. Or it may be the difference between clicking and double-clicking. Or the library may reserve a particular mouse button, on a multi-button mouse. It may even specify a keyboard key to be the "mouse button", referring to wherever the mouse cursor is when the key is hit. Or some even more esoteric positioning system. You need only know that the user can do it, or not.]

[However, since different platforms will handle this issue differently, you should be careful how you instruct the player in your program. Do not tell the player to "double-click", "right-click", or "control-click" in a window. The preferred term is "to touch the window", or a spot in the window.] [Goofy, but preferred.]

4.4: Timer Events

You can request that an event be sent at fixed intervals, regardless of what the player does. Unlike input events, timer events can be tested for with glk_select_poll() as well as glk_select().

void glk_request_timer_events(glui32 millisecs);

It is possible that the library does not support timer events. You can check this with the gestalt_Timer selector.

res = glk_gestalt(gestalt_Timer, 0);


This returns 1 if timer events are supported, and 0 if they are not.

Initially, there is no timer and you get no timer events. If you call glk_request_timer_events(N), with N not 0, you will get timer events about every N milliseconds thereafter. (Assuming that they are supported -- if not, glk_request_timer_events() has no effect.) Unlike keyboard and mouse events, timer events will continue until you shut them off. You do not have to re-request them every time you get one. Call glk_request_timer_events(0) to stop getting timer events.

The rule is that when you call glk_select() or glk_select_poll(), if it has been more than N milliseconds since the last timer event, and (for glk_select()) if there is no player input, you will receive an event whose type is evtype_Timer. (win, val1, and val2 will all be 0.)

Timer events do not stack up. If you spend 10N milliseconds doing computation, and then call glk_select(), you will not get ten timer events in a row. The library will simply note that it has been more than N milliseconds, and return a timer event right away. If you call glk_select() again immediately, it will be N milliseconds before the next timer event.

This means that the timing of timer events is approximate, and the library will err on the side of being late. If there is a conflict between player input events and timer events, the player input takes precedence. [This prevents the user from being locked out by overly enthusiastic timer events. Unfortunately, it also means that your timer can be locked out on slower machines, if the player pounds too enthusiastically on the keyboard. Sorry. If you want a real-time operating system, talk to Wind River.]

[I don't have to tell you that a millisecond is one thousandth of a second, do I?]

4.5: Window Arrangement Events

Some platforms allow the player to resize the Glk window during play. This will naturally change the sizes of your windows. If this occurs, then immediately after all the rearrangement, glk_select() will return an event whose type is evtype_Arrange. You can use this notification to redisplay the contents of a graphics or text grid window whose size has changed. [The display of a text buffer window is entirely up to the library, so you don't need to worry about those.]

In the event structure, win will be NULL if all windows are affected. If only some windows are affected, win will refer to a window which contains all the affected windows. [You can always play it safe, ignore win, and redraw every graphics and text grid window.] val1 and val2 will be 0.

An arrangement event is guaranteed to occur whenever the player causes any window to change size, as measured by its own metric. [Size changes caused by you -- for example, if you open, close, or resize a window -- do not trigger arrangement events. You must be aware of the effects of your window management, and redraw the windows that you affect.]

[It is possible that several different player actions can cause windows to change size. For example, if the player changes the screen resolution, an arrangement event might be triggered. This might also happen if the player changes his display font to a different size; the windows would then be different "sizes" in the metric of rows and columns, which is the important metric and the only one you have access to.]

Arrangement events, like timer events, can be returned by glk_select_poll(). But this will not occur on all platforms. You must be ready to receive an arrangement event when you call glk_select_poll(), but it is possible that it will not arrive until the next time you call glk_select(). [This is because on some platforms, window resizing is handled as part of player input; on others, it can be triggered by an external process such as a window manager.]

4.6: Window Redrawing Events

On platforms that support graphics, it is possible that the contents of a graphics window will be lost, and have to be redrawn from scratch. If this occurs, then glk_select() will return an event whose type is evtype_Redraw.

In the event structure, win will be NULL if all windows are affected. If only some windows are affected, win will refer to a window which contains all the affected windows. [You can always play it safe, ignore win, and redraw every graphics window.] val1 and val2 will be 0.

Affected windows are already cleared to their background color when you receive the redraw event.

Redraw events can be returned by glk_select_poll(). But, like arrangement events, this is platform-dependent. See section 4.5, "Window Arrangement Events".

For more about redraw events and how they affect graphics windows, see section 3.5.5, "Graphics Windows".

4.7: Sound Notification Events

On platforms that support sound, you can request to receive an evtype_SoundNotify event when a sound finishes playing. See section 8.3, "Playing Sounds".

4.8: Hyperlink Events

On platforms that support hyperlinks, you can request to receive an evtype_Hyperlink event when the player selects a link. See section 9.2, "Accepting Hyperlink Events".

4.9: Other Events

There are currently no other event types defined by Glk. (The "evtype_None" constant is a placeholder, and is never returned by glk_select().)

It is possible that new event types will be defined in the future. [For example, if video or animation capabilities are added to Glk, there would probably be some sort of completion event for them.]

[This is also why you must put calls to glk_select() in loops. If you tried to read a character by simply writing

glk_request_char_event(win);
glk_select(&ev);


you might not get a CharInput event back. You could get some not-yet-defined event which happened to occur before the player hit a key. Or, for that matter, a window arrangement event.]

[It is not generally necessary to put a call to glk_select_poll() in a loop. You usually call glk_select_poll() to update the display or test if an event is available, not to wait for a particular event. However, if you are using sound notification events, and several sounds are playing, it might be important to make sure you knew about all sounds completed at any particular time. You would do this with

glk_select_poll(&ev);
while (ev.type != evtype_None) {
/* handle event */
glk_select_poll(&ev);
}


Once glk_select_poll() returns evtype_None, you should not call it again immediately. Do some work first. If you want to wait for an event, use glk_select(), not a loop of glk_select_poll().]
5: Streams

All character output in Glk is done through streams. Every window has an output stream associated with it. You can also write to files on disk; every open file is represented by an output stream as well.

There are also input streams; these are used for reading from files on disk. It is possible for a stream to be both an input and an output stream. [Player input is done through line and character input events, not streams. This is a small inelegance in theory. In practice, player input is slow and things can interrupt it, whereas file input is immediate. If a network extension to Glk were proposed, it would probably use events and not streams, since network communication is not immediate.]

It is also possible to create a stream that reads or writes to a buffer in memory.

Finally, there may be platform-specific types of streams, which are created before your program starts running. [For example, a program running under Unix may have access to standard input as a stream, even though there is no Glk call to explicitly open standard input. On the Mac, data in a Mac resource may be available through a resource-reading stream.] You do not need to worry about the origin of such streams; just read or write them as usual. For information about how platform-specific streams come to be, see section 10.1, "Startup Options".

A stream is opened with a particular file mode:

* filemode_Write: An output stream.
* filemode_Read: An input stream.
* filemode_ReadWrite: Both an input and an output stream.
* filemode_WriteAppend: An output stream, but the data will added to the end of whatever already existed in the destination, instead of replacing it.

[In the stdio library, using fopen(), filemode_Write would be mode "w"; filemode_Read would be mode "r"; filemode_ReadWrite would be mode "r+; filemode_WriteAppend would be mode "a".]

For information on opening streams, see the discussion of each specific type of stream in section 5.6, "The Types of Streams". Remember that it is always possible that opening a stream will fail, in which case the creation function will return NULL.

Each stream remembers two character counts, the number of characters printed to and read from that stream. The write-count is exactly one per glk_put_char() call; it is figured before any platform-dependent character cookery. [For example, if a newline character is converted to linefeed-plus-carriage-return, the stream's count still only goes up by one; similarly if an accented character is displayed as two characters.] The read-count is exactly one per glk_get_char_stream() call, as long as the call returns an actual character (as opposed to an end-of-file token.)

Glk has a notion of the "current (output) stream". If you print text without specifying a stream, it goes to the current output stream. The current output stream may be NULL, meaning that there isn't one. It is illegal to print text to stream NULL, or to print to the current stream when there isn't one.

If the stream which is the current stream is closed, the current stream becomes NULL.

void glk_stream_set_current(strid_t str);

This sets the current stream to str, which must be an output stream. You may set the current stream to NULL, which means the current stream is not set to anything.

strid_t glk_stream_get_current(void);

Returns the current stream, or NULL if there is none.

5.1: How To Print

void glk_put_char(unsigned char ch);

This prints one character to the current stream. As always, the character is assumed to be in the Latin-1 character encoding. See section 2, "Character Encoding".

void glk_put_string(char *s);

This prints a null-terminated string to the current stream. It is exactly equivalent to

for (ptr = s; *ptr; ptr++)
glk_put_char(*ptr);


However, it may be more efficient.

void glk_put_buffer(char *buf, glui32 len);

This prints a block of characters to the current stream. It is exactly equivalent to

for (i = 0; i < len; i++)
glk_put_char(buf[i]);


However, it may be more efficient.

void glk_put_char_stream(strid_t str, unsigned char ch);
void glk_put_string_stream(strid_t str, char *s);
void glk_put_buffer_stream(strid_t str, char *buf, glui32 len);

These are the same functions, except that you specify a stream to print to, instead of using the current stream. Again, it is illegal for str to be NULL, or the reference of an input-only stream.

5.2: How To Read

glsi32 glk_get_char_stream(strid_t str);

This reads one character from the given stream. (There is no notion of a "current input stream.") It is illegal for str to be NULL, or an output-only stream.

The result will be between 0 and 255; as always, Glk assumes the Latin-1 encoding. See section 2, "Character Encoding". If the end of the stream has been reached, the result will be -1. [Note that high-bit characters (128..255) are not returned as negative numbers.]

glui32 glk_get_buffer_stream(strid_t str, char *buf, glui32 len);

This reads len characters from the given stream, unless the end of stream is reached first. No terminal null is placed in the buffer. It returns the number of characters actually read.

glui32 glk_get_line_stream(strid_t str, char *buf, glui32 len);

This reads characters from the given stream, until either len-1 characters have been read or a newline has been read. It then puts a terminal null ('\0') character on the end. It returns the number of characters actually read, including the newline (if there is one) but not including the terminal null.

It is usually more efficient to read several characters at once with glk_get_buffer_stream() or glk_get_line_stream(), as opposed to calling glk_get_char_stream() several times.

5.3: Closing Streams

void glk_stream_close(strid_t str, stream_result_t *result);

typedef struct stream_result_struct {
glui32 readcount;
glui32 writecount;
} stream_result_t;


This closes the stream str. The result argument points to a structure which is filled in with the final character counts of the stream. If you do not care about these, you may pass NULL as the result argument.

If str is the current output stream, the current output stream is set to NULL.

You cannot close window streams; use glk_window_close() instead. See section 3.2, "Window Opening, Closing, and Constraints".

5.4: Stream Positions

You can set the position of the read/write mark in a stream. [Which makes one wonder why they're called "streams" in the first place. Oh well.]

glui32 glk_stream_get_position(strid_t str);

This returns the position of the mark. For memory streams and binary file streams, this is exactly the number of bytes read or written from the beginning of the stream (unless you have moved the mark with glk_stream_set_position().) For text file streams, matters are more ambiguous, since (for example) writing one byte to a text file may store more than one character in the platform's native encoding. You can only be sure that the position increases as you read or write to the file.

void glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode);

This sets the position of the mark. The position is controlled by pos, and the meaning of pos is controlled by seekmode:

* seekmode_Start: pos characters after the beginning of the file.
* seekmode_Current: pos characters after the current position (moving backwards if pos is negative.)
* seekmode_End: pos characters after the end of the file. (pos should always be zero or negative, so that this will move backwards to a position within the file.)

It is illegal to specify a position before the beginning or after the end of the file.

In binary files, the mark position is exact -- it corresponds with the number of characters you have read or written. In text files, this mapping can vary, because of linefeed conversions or other character-set approximations. (See section 5, "Streams".) glk_stream_set_position() and glk_stream_get_position() measure positions in the platform's native encoding -- after character cookery. Therefore, in a text stream, it is safest to use glk_stream_set_position() only to move to the beginning or end of a file, or to a position determined by glk_stream_get_position().

5.5: Styles

You can send style-changing commands to an output stream. After a style change, new text which is printed to that stream will be given the new style, whatever that means for the stream in question. For a window stream, the text will appear in that style. For a memory stream, style changes have no effect. For a file stream, if the machine supports styled text files, the styles may be written to the file; more likely the style changes will have no effect.

Styles are exclusive. A character is shown with exactly one style, not a subset of the possible styles.

[Note that every stream and window has its own idea of the "current style." Sending a style command to one window or stream does not affect any others.] [Except for a window's echo stream; see section 3.6, "Echo Streams".]

The styles are intended to distinguish meaning and use, not formatting. There is no standard definition of what each style will look like. That is left up to the Glk library, which will choose an appearance appropriate for the platform's interface and the player's preferences.

There are currently eleven styles defined. More may be defined in the future.

* style_Normal: The style of normal or body text. A new window or stream always starts with style_Normal as the current style.
* style_Emphasized: Text which is emphasized.
* style_Preformatted: Text which has a particular arrangement of characters. [This style, unlike the others, does have a standard appearance; it will always be a fixed-width font. This is a concession to practicality. Games often want to display maps or diagrams using character graphics, and this is the style for that.]
* style_Header: Text which introduces a large section. This is suitable for the title of an entire game, or a major division such as a chapter.
* style_Subheader: Text which introduces a smaller section within a large section. [In a Colossal-Cave-style game, this is suitable for the name of a room (when the player looks around.)]
* style_Alert: Text which warns of a dangerous condition, or one which the player should pay attention to.
* style_Note: Text which notifies of an interesting condition. [This is suitable for noting that the player's score has changed.]
* style_BlockQuote: Text which forms a quotation or otherwise abstracted text.
* style_Input: Text which the player has entered. You should generally not use this style at all; the library uses it for text which is typed during a line-input request. One case when it is appropriate for you to use style_Input is when you are simulating player input by reading commands from a text file.
* style_User1: This style has no particular semantic meaning. You may define a meaning relevant to your own work, and use it as you see fit.
* style_User2: Another style available for your use.

Styles may be distinguished on screen by font, size, color, indentation, justification, and other attributes. Note that some attributes (notably justification and indentation) apply to entire paragraphs. If possible and relevant, you should apply a style to an entire paragraph -- call glk_set_style() immediately after printing the newline at the beginning of the text, and do the same at the end.

[For example, style_Header may well be centered text. If you print "Welcome to Victim (a short interactive mystery)", and only the word "Victim" is in the style_Header, the center-justification attribute will be lost. Similarly, a block quote is usually indented on both sides, but indentation is only meaningful when applied to an entire line or paragraph, so block quotes should take up an entire paragraph. Contrariwise, style_Emphasized need not be used on an entire paragraph. It is often used for single emphasized words in normal text, so you can expect that it will appear properly that way; it will be displayed in italics or underlining, not center-justified or indented.]

[Yes, this is all a matter of mutual agreement between game authors and game players. It's not fixed by this specification. That's natural language for you.]

void glk_set_style(glui32 val);

This changes the style of the current output stream. val should be one of the values listed above. However, any value is actually legal; if the interpreter does not recognize the style value, it will treat it as style_Normal. [This policy allows for the future definition of styles without breaking old Glk libraries.]

void glk_set_style_stream(strid_t str, glui32 val);

This changes the style of the stream str.

5.5.1: Suggesting the Appearance of Styles

There are no guarantees of how styles will look, but you can make suggestions.

void glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint, glsi32 val);
void glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint);

These functions set and clear hints about the appearance of one style for a particular type of window. You can also set wintype to wintype_AllTypes, which sets (or clears) a hint for all types of window. [There is no equivalent constant to set a hint for all styles of a single window type.]

Initially, no hints are set for any window type or style. Note that having no hint set is not the same as setting a hint with value 0.

These functions do not affect existing windows. They affect the windows which you create subsequently. If you want to set hints for all your game windows, call glk_stylehint_set() before you start creating windows. If you want different hints for different windows, change the hints before creating each window.

[This policy makes life easier for the interpreter. It knows everything about a particular window's appearance when the window is created, and it doesn't have to change it while the window exists.]

Hints are hints. The interpreter may ignore them, or give the player a choice about whether to accept them. Also, it is never necessary to set hints. You don't have to suggest that style_Preformatted be fixed-width, or style_Emphasized be boldface or italic; they will have appropriate defaults. Hints are for situations when you want to change the appearance of a style from what it would ordinarily be. The most common case when this is appropriate is for the styles style_User1 and style_User2.

There are currently nine style hints defined. More may be defined in the future.

* stylehint_Indentation: How much to indent lines of text in the given style. May be a negative number, to shift the text out (left) instead of in (right). The exact metric isn't precisely specified; you can assume that +1 is the smallest indentation possible which is clearly visible to the player.
* stylehint_ParaIndentation: How much to indent the first line of each paragraph. This is in addition to the indentation specified by stylehint_Indentation. This too may be negative, and is measured in the same units as stylehint_Indentation.
* stylehint_Justification: The value of this hint must be one of the constants stylehint_just_LeftFlush, stylehint_just_LeftRight (full justification), stylehint_just_Centered, or stylehint_just_RightFlush.
* stylehint_Size: How much to increase or decrease the font size. This is relative; 0 means the interpreter's default font size will be used, positive numbers increase it, and negative numbers decrease it. Again, +1 is the smallest size increase which is easily visible. [The amount of this increase may not be constant. +1 might increase an 8-point font to 9-point, but a 16-point font to 18-point.]
* stylehint_Weight: The value of this hint must be 1 for heavy-weight fonts (boldface), 0 for normal weight, and -1 for light-weight fonts.
* stylehint_Oblique: The value of this hint must be 1 for oblique fonts (italic), or 0 for normal angle.
* stylehint_Proportional: The value of this hint must be 1 for proportional-width fonts, or 0 for fixed-width.
* stylehint_TextColor: The foreground color of the text. This is encoded in the 32-bit hint value: the top 8 bits must be zero, the next 8 bits are the red value, the next 8 bits are the green value, and the bottom 8 bits are the blue value. Color values range from 0 to 255. [So 0x00000000 is black, 0x00FFFFFF is white, and 0x00FF0000 is bright red.]
* stylehint_BackColor: The background color behind the text. This is encoded the same way as stylehint_TextColor.
* stylehint_ReverseColor: The value of this hint must be 0 for normal printing (TextColor on BackColor), or 1 for reverse printing (BackColor on TextColor). [Some libraries may support this hint but not the TextColor and BackColor hints. Other libraries may take the opposite tack; others may support both, or neither.]

Again, when passing a style hint to a Glk function, any value is actually legal. If the interpreter does not recognize the stylehint value, it will ignore it. [This policy allows for the future definition of style hints without breaking old Glk libraries.]

5.5.2: Testing the Appearance of Styles

You can suggest the appearance of a window's style before the window is created; after the window is created, you can test the style's actual appearance. These functions do not test the style hints; they test the attribute of the style as it appears to the player.

Note that although you cannot change the appearance of a window's styles after the window is created, the library can. A platform may support dynamic preferences, which allow the player to change text formatting while your program is running. [Changes that affect window size (such as font size changes) will be signalled by an evtype_Arrange event. However, more subtle changes (such as text color differences) are not signalled. If you test the appearance of styles at the beginning of your program, you must keep in mind the possibility that the player will change them later.]

glui32 glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2);

This returns TRUE (1) if the two styles are visually distinguishable in the given window. If they are not, it returns FALSE (0). The exact meaning of this is left to the library to determine.

glui32 glk_style_measure(winid_t win, glui32 styl, glui32 hint, glui32 *result);

This tries to test an attribute of one style in the given window. The library may not be able to determine the attribute; if not, this returns FALSE (0). If it can, it returns TRUE (1) and stores the value in the location pointed at by result. [As usual, it is legal for result to be NULL, although fairly pointless.]

The meaning of the value depends on the hint which was tested:

* stylehint_Indentation, stylehint_ParaIndentation: The indentation and paragraph indentation. These are in a metric which is platform-dependent. [Most likely either characters or pixels.]
* stylehint_Justification: One of the constants stylehint_just_LeftFlush, stylehint_just_LeftRight, stylehint_just_Centered, or stylehint_just_RightFlush.
* stylehint_Size: The font size. Again, this is in a platform-dependent metric. [Pixels, points, or simply 1 if the library does not support varying font sizes.]
* stylehint_Weight: 1 for heavy-weight fonts (boldface), 0 for normal weight, and -1 for light-weight fonts.
* stylehint_Oblique: 1 for oblique fonts (italic), or 0 for normal angle.
* stylehint_Proportional: 1 for proportional-width fonts, or 0 for fixed-width.
* stylehint_TextColor, stylehint_BackColor: These are values from 0x00000000 to 0x00FFFFFF, encoded as described in section 5.5.1, "Suggesting the Appearance of Styles".
* stylehint_ReverseColor: 0 for normal printing, 1 if the foreground and background colors are reversed.

5.6: The Types of Streams

5.6.1: Window Streams

Every window has an output stream associated with it. This is created automatically, with filemode_Write, when you open the window. You get it with glk_window_get_stream().

A window stream cannot be closed with glk_stream_close(). It is closed automatically when you close its window with glk_window_close().

Only printable characters (including newline) may be printed to a window stream. See section 2, "Character Encoding".

5.6.2: Memory Streams

You can open a stream which reads from or writes into a space in memory.

strid_t glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode, glui32 rock);

fmode must be filemode_Read, filemode_Write, or filemode_ReadWrite.

buf points to the buffer where output will be read from or written to. buflen is the length of the buffer.

When outputting, if more than buflen characters are written to the stream, all of them beyond the buffer length will be thrown away, so as not to overwrite the buffer. (The character count of the stream will still be maintained correctly. That is, it will count the number of characters written into the stream, not the number that fit in the buffer.)

If buf is NULL, or for that matter if buflen is zero, then everything written to the stream is thrown away. This may be useful if you are interested in the character count.

When inputting, if more than buflen characters are read from the stream, the stream will start returning -1 (signalling end-of-file.) If buf is NULL, the stream will always return end-of-file.

The data is written to the buffer exactly as it was passed to the printing functions (glk_put_char(), etc); input functions will read the data exactly as it exists in memory. No platform-dependent cookery will be done on it. [You can write a disk file in text mode, but a memory stream is effectively always in binary mode.]

Whether reading or writing, the contents of the buffer are undefined until the stream is closed. The library may store the data there as it is written, or deposit it all in a lump when the stream is closed. It is illegal to change the contents of the buffer while the stream is open.

5.6.3: File Streams

You can open a stream which reads from or writes to a disk file.

strid_t glk_stream_open_file(frefid_t fileref, glui32 fmode, glui32 rock);

fileref indicates the file which will be opened. fmode can be any of filemode_Read, filemode_Write, filemode_WriteAppend, or filemode_ReadWrite. If fmode is filemode_Read, the file must already exist; for the other modes, an empty file is created if none exists. If fmode is filemode_Write, and the file already exists, it is truncated down to zero length (an empty file). If fmode is filemode_WriteAppend, the file mark is set to the end of the file.

The file may be read or written in text or binary mode; this is determined by the fileref argument. Similarly, platform-dependent attributes such as file type are determined by fileref. See section 6, "File References".

5.7: Other Stream Functions

strid_t glk_stream_iterate(strid_t str, glui32 *rockptr);

This iterates through all the existing streams. See section 1.6.2, "Iterating Through Opaque Objects".

glui32 glk_stream_get_rock(strid_t str);

This retrieves the stream's rock value. See section 1.6.1, "Rocks".
6: File References

You deal with disk files using file references. Each fileref is an opaque C structure pointer; see section 1.6, "Opaque Objects".

A file reference contains platform-specific information about the name and location of the file, and possibly its type, if the platform has a notion of file type. It also includes a flag indication whether the file is a text file or binary file. [Note that this is different from the standard C I/O library, in which you specify text or binary mode when the file is opened.]

A fileref does not have to refer to a file which actually exists. You can create a fileref for a nonexistent file, and then open it in write mode to create a new file.

You always provide a usage argument when you create a fileref. The usage is a mask of constants to indicate the file type and the mode (text or binary.) These values are used when you create a new file, and also to filter file lists when the player is selecting a file to load. The constants are as follows:

* fileusage_SavedGame: A file which stores game state.
* fileusage_Transcript: A file which contains a stream of text from the game (often an echo stream from a window.)
* fileusage_InputRecord: A file which records player input.
* fileusage_Data: Any other kind of file (preferences, statistics, arbitrary data.)

* fileusage_BinaryMode: The file contents will be stored exactly as they are written, and read back in the same way. The resulting file may not be viewable on platform-native text file viewers.
* fileusage_TextMode: The fil