briefglk.html        Marnie Parker


(Edited/co-written by Roger Firth and edited by Adam J. Thornton)


8/11/01 version 1.31h doeadeer3@aol.com


A brief overview of Glk { for the Inform programmer }


( Note: All examples use functions/routines from unglklib.h.)

CONTENTS

1. BASICS
1.1 What is Glulx Inform?
1.2 What is Glk?
1.3 What is infglk?
1.4 What can Glk do?
1.5 The Glk gestalt
1.6 Glk's components

2. GLK OBJECTS
2.1 Dynamic Glk objects v.s. static Inform objects
      (UNDO/RESTART/RESTORE)
2.2 Window objects
2.3 Sound Channel objects
2.4 File Reference objects
2.5 Stream objects
2.6 Object variables and rock constants

3. GLK EVENTS
3.1 Redraw events
3.2 Sound notification events
3.3 Glk timer events
3.4 Input events
3.4.1 Line and character input events
3.4.2 Mouse and hyperlink input events

4. EXTERNAL RESOURCES

5. GLK ENTRY POINTS
5.1 InitGlkWindow()
5.2 IdentifyGlkObject()
5.3 HandleGlkEvent()

6. TEXT STYLES
6.1 Preset text styles
6.2 Suggesting text styles
6.3 Testing text styles

7 GLULX INFORM'S GLKY "TRICKS"
7.1 Returning multiple values
      (gg_arguments)
7.2 "Vile reference errors from hell"



1. BASICS

This is a brief overview of Glk, intended for Inform programmers. To
actually see it in use, play the accompanying demonstration game, "Just
a Dream." To familiarize yourself with the routines/functions in unglklib,
refer to it and the source code for "Dream."



1.1 What is Glulx Inform?

Infocom used and Inform uses a VM (virtual machine) called the z-machine,
so games could and can be portable from platform to platform. Inform
Glulx detaches the Inform programming language from the old z-machine and
reattaches it to a new VM, Glulx. This "g-machine" breaks the memory
limitations of the z-machine and invokes Glk for input/output.

If you want to write a big Glulx game, Glulx Inform programming remains
relatively unchanged from regular Inform. However, if you want to write
a multimedia Glulx game, because Glk handles I/O, you must learn
and use Glk, infglk, or, possibly, the unglklib library. For more about
Glulx Inform, consult "The Game Author's Guide to Glulx Inform" by Andrew
Plotkin. It covers the changes to Inform that the author needs to know to
write big games. However, it doesn't really describe Glk in any detail.



1.2 What is Glk?

Glk is a portable API (application programming interface). It was not
designed solely for Inform. Zarf (Andrew Plotkin) wrote it to standardize
interpreter input/output from platform to platform, and also to make available
I/O features that weren't currently available. All of Glulx Inform's I/O
is handled by Glk: screen/audio/file output, and player/file input.

Glk is literally portable. Written with the C programming language, it is a
separate file, the "Glk library." This library must be present when you play
a Glulx game. (Actually, there are several platform-specific Glk libraries.)
Fortunately, most interpreters come bundled with it or incorporate it.

The bi-platform Glulx Inform library comes with an include file that defines
in Informese the numerous functions in the Glk library. This is so the Glulx
Inform library can invoke them. Built into the Glulx Inform library is ONE
glk() function. Its first argument is an integer that determines which
external Glk library function will be called. glk()'s subsequent arguments
set flags for that Glk library function.

When you compile a game, you essentially make the Glulx Inform library
(just like the regular Inform library) part of your game code. Once your
game is loaded into the interpreter, the glk() calls in the Glulx Inform
library and any glk() calls you added to your own code can then be made.

The interpreter passes along these calls to the external (or internal) Glk
library. Essentially, it "fields" them. If the Glk library used by that
interpreter implements a particular function, then that function is called
and executed. If that Glk library does not implement that function, the
interpreter still makes the call, but it "bounces" and nothing happens.

Each Glulxe interpreter is responsible for implementing Glk.

( Note: Or, in other words... "each interpreter is responsible for linking
to a conforming Glk implementation." A.J.T. )



1.3 What is infglk?

infglk.h is an Inform include file by John Catre. It was created by a Perl
script that: read the C-source code of the external Glk library, lifted out
the Glk functions, and formated them into Informese. Because they come from
the C-source, they have C-language-style names and formats. However, infglk
functions are easier to use than trying to decode the first integer in that
one glk() function built into Glulx Inform. For example, in Glulx Inform,
glk($000059, etc.) will play a sound. In infglk, the same function call is
glk_schannel_play_ext(). Also, infglk defines constants to be used with its
functions (again, easier to read and remember than numbers).

Why the unglklib library then? For understandability -- although infglk has
essentially broken down that one Glulx Inform glk() function into others,
the infglk functions are still fairly compressed and could be broken out
even more. Also, for legibility -- despite the Glk clarification that infglk
provides, some of us find infglk itself, with its C-language-style names
and formats, confusing.



1.4 What can Glk do?

This is the fun part. Glk's multimedia capabilities can: print fancy
screen text (very large, red-colored, proportional, etc.), create new
windows, draw graphics in the main text window or other windows, play
music and sounds, and use mouse input ("clicks") and hyperlink selection
to perform actions. For instance, the player could select a hyperlinked
"north" to go north, rather than enter "N" at the keyboard. Or use the
mouse to click a picture to open a door. The possibilities, while maybe
not endless, are definitely intriguing.

Unfortunately, Glk has a steep learning curve, so this file attempts to
cover the bare minimum the Inform programmer needs to know.



1.5 The Glk gestalt

Glk has this funny thing called its gestalt ("ghe-shtalt", a German word
meaning "the shape of the whole thing"). Glk's gestalt is its current
set of I/O capabilities -- which may change and be added onto. Since Glk
keeps evolving, in a sense, the gestalt is also its POTENTIAL configuration.

Since each Glulxe interpreter is responsible for implementing Glk, and
not all interpreters may support of all Glk's current capabilities, for
Informers the gestalt really means -- TESTING THE PLAYER'S
INTERPRETER. Before using a specific glk capability, you should test the
player's interpreter to see if it is supported. For instance, you write a game that
requires graphic windows, but the player's interpreter doesn't support them.
Your game code can test the interpreter, discover that graphic windows are
not supported, and then exit with an explanatory message. Or you might test
it for sound support, and if sound is not available, print a message
instead of playing a sound.

if (SupportsSounds())
   PlaySound(channel, babble_snd);
else "You hear a babbling sound.";
( Note: Testing the Glk gestalt actually tests the platform-specific Glk
library used by the player's interpreter. But the concept is the same, because
the interpreter won't support something if its Glk library doesn't. "Testing
the player's interpreter" is also easier to say, understand, and remember.)



1.6 Glk's components

Glk is built on a system of "objects" which also represent four "classes"
(these are quite separate from Inform's objects and classes):

Glk Object Usage
Windows independent screen areas
Sound channels audio output
File references pointers to external files
Streams text/data input/output

It recognizes "events" either triggered by something the player does, or
something you've initiated:

Glk Event Originator
Line input player
Character input player
Mouse input player
Hyperlink selection player
Resize window player
Redraw graphic window player/internal
Timed events author
Sound notification author

And, it can read/load external "resources":

External Resource Formats supported
Images JPEG/JPG, PNG
Sounds AIFF, MOD



2. GLK OBJECTS

Glk objects are multimedia objects for game input/output.

THEY ARE CREATED DYNAMICALLY AT RUN-TIME IN THE
INTERPRETER'S MEMORY.

Every Glk object is also a member of a class. For instance, a window is
a member of the Window class.



2.1 Dynamic Glk objects v.s. static Inform objects
(UNDO/RESTART/RESTORE)

There is really no connection between regular Inform's game objects and Glk's
multimedia objects, except for the word "object." But because you may only be
familiar game objects, this section covers the crucial difference between the
two: when/how they are created and what this means for UNDO/RESTART/
RESTORE.

Regular Inform - In regular Inform, when you write a game, you program
"game objects." They are part of the game's story and help tell that story:
locations, scenery, takeable objects, and NPCs (non-player characters).

Game objects are * static * data structures -- once coded they don't really
change during game play.

Glulx Inform - In Glulx Inform, the Glk objects you program are not part of
the game's story, but ways to read/see/hear/experience that story: text
windows (for reading the story), graphic windows (for viewing the story's
images), and sound channels (for listening to the story's music and sounds).
Multimedia objects are * dynamic * input/output structures -- once coded they
* do * change during game play: are created, destroyed, and/or change dimension.

Regular Inform - Regular Inform's game objects are coded as text or numerical
values and saved as text or numerical values. Their basic structure doesn't
really change from the time of coding through the time of game play. Although
the player may appear to move an object around in the game, what really happens
internally is that pointers in the object tree change so they point to different
parents, siblings, and children. For example, when the player takes a lantern
from a cave, the pointer to its parent changes from the cave to the player.

When a regular Inform game is saved, the object tree and other variables are
what is saved; when it is restored, they are what is restored. So, using the
above example, when the game with the lantern is restored, the variable
representing it and its pointers in the object tree are restored. To the player
it appears the lantern was moved from the cave to his/her possession. Thus the
game resumes, and the player blithely continues exploring the game world.

Glulx Inform - Although Glulx Inform's multimedia objects are also coded as
text or numerical values and saved as text or numerical values, their basic
structure does change from the time of coding through the time of game play.
Dynamic Glk objects don't really exist until the game is loaded into the
interpreter, then, and only then, are they actually created. When the game ends,
the interpreter is closed, or the computer is turned off, they simply disappear.

In other words, although the code for Glk objects exists before game play,
the actual multimedia objects themselves only exist * during * game play.

It makes sense that a window displaying a graphic, or a sound currently playing,
can't be saved as text or a numerical value to a disk file. What can be saved
are the variables (and constants) that point to Glk objects. If gg_mywin points
to a window that you created, when the game is saved, that variable is saved --
not the window itself. If a sound is currently playing, variables noting a sound
is playing and which sound it is, are what is saved -- not the sound itself.

When a Glulx game is restored, just like with regular Inform, the variables
(and constants) that point to Glk objects are what is restored. But a Glulx
game may not resume as blithely as a regular Inform game does. For example, the
player restores a game saved earlier, before gg_mywin was opened. But there it
is, still on the screen. Why? It has been opened in memory since, so it's still
in memory, even though its recovered variable says it hasn't been opened yet.

* Your Glk objects are not restored automatically after UNDO/RESTART/
RESTORE.
* Since they are created dynamically AFTER game start, obviously the
Glulx Inform library cannot know about their existence. It can only know about the
Glk objects * it * created (see reserved rock constants under "Object variables and
constants"). So it is up to you to restore your Glk objects to their previous
states, by: opening/closing windows, redrawing graphics, and playing/stopping
sounds. Fortunately, most of the necessary code will be routines you will
already have in your game to manage your Glk objects during game play.

In regular Inform, you don't really have to think about what happens during
UNDO/RESTART/RESTORE. In Glulx Inform, you do. Restoring your Glk objects
after UNDO/RESTART/RESTORE is the job of IdentifyGlkObject(). Your job is to
enable it to do so -- by putting code there to adjust your Glk objects in
memory until they match the state of their recovered variables.

( Note: Although Glk's file structures and streams use methods similar to the
ones regular Inform uses to write files and print text, they are also dynamic.)



2.2 Window objects

Window Types - Windows are screen areas that operate independently of
each other. There are three types of Glk windows: text buffer windows (like the
main text window, containing the prompt and room descriptions), text grid
windows (which are really character arrays with one character per grid square
-- like the Glulx status line), and graphic windows.

( Note: Windows are very important in Glulx. Text is printed to the current
window and line/character/mouse/hyperlink input are tied to specific windows.)

Text buffer windows can display text and graphics. Text grid windows (which,
by necessity, must use a fixed-width font) can only display text. Graphic
windows can only display graphics.

Window type Can display
Text Buffer text and graphics
Text Grid only text
Graphic only graphics

Default Windows - Three default windows are created by the Glulx Inform
library: gg_mainwin (the main text window), gg_statuswin (the status line),
and gg_quotewin (the quote box -- although actually only its potential is
created, as it is opened just before each use and closed afterward).

Default windows Type
gg_mainwin text buffer
gg_statuswin text grid
gg_quotewin text buffer

Creating Windows - At any time you may also create/open additional windows
of your own. When you open a new window, it sub-divides or "splits" an existing
window. The new window is the child of the split window; the split window is
the parent of the new window. Since Glk windows are created by splitting,
they "tile" not "cascade."

( Note: It is a good idea to open your main windows during Inform's Initialise,
so you can double-check that the interpreter is able to open them. Also, windows
opened in Initialise appear upon game start.)

A child window splits its parent window either horizontally (into two smaller
windows, one on top of the other), or vertically (into two smaller windows,
side-by-side). If the parent window is split horizontally, the size of the new
window will be determined by its height; if split vertically, it will be
determined by its width. The new window's size will also be either proportional
(30% of its parent's height or width) or fixed-width (a set height or width).

For instance, you open a graphic window, gg_mygraphwin by splitting gg_mainwin.
Then, later you open another text buffer window, gg_mytextwin, by splitting
either gg_mainwin or gg_mygraphwin. This window splitting process also creates
a window tree. When you close a window, the space it occupied is returned to
its parent. * It is wise to close a "child" window before closing its parent. *

( Note: Window trees are currently not covered by unglklib or this file).

    --------------------
    |                  |   A = gg_mainwin
    |                  |
    |                  |
    |         A        |
    |                  |
    |                  |
    |                  |
    --------------------
then
    --------------------
    |                  |   A = gg_mainwin    (parent of gg_graphwin)
    |         B        |   B = gg_mygraphwin
    |                  |
    --------------------
    |                  |
    |         A        |
    |                  |
    --------------------
then
    --------------------
    |                  |   A = gg_mainwin    (parent of gg_mygraphwin)
    |         B        |   B = gg_mygraphwin  and gg_mytextwin)
    |                  |   C = gg_mytextwin
    --------------------   (sized by width)
    |            |     |
    |      A     |  C  |
    |            |     |
    --------------------
or
    --------------------
    |         C        |   A = gg_mainwin    (parent of gg_mygraphwin)
    |------------------|   B = gg_myraphwin  (parent of gg_mytextwin)
    |         B        |   C = gg_mytextwin
    --------------------   (sized by height)
    |                  |
    |         A        |
    |                  |
    --------------------




2.3 Sound Channel objects

Sound Channels are just what they seem, channels for playing sounds, one
sound per channel. Sounds may be "mixed" by playing two sounds in two
channels simultaneously.

( Note: Since simultaneous sound playing uses up a lot of the computer's
and interpreter's resources, use AIF format for both rather than MOD format.)



2.4 File Reference objects

File References point to external files so they can be accessed. They
are a little like regular Inform objects -- almost static. Although
created dynamically in memory like all other Glk objects, they basically
operate as containers for several variables: the name of a disk file, its
location, and the file type, text or binary.

One default file reference, gg_scriptref, is created by the Glulx Inform
library for the game transcript.



2.5 Stream objects

Streams are essentially input/output "flows" of information, usually text.
All character output in Glulx Inform is handled by streams. There are:
window streams (each window has a default output stream associated with
it), memory streams, and file streams.

File streams are most used. They perform the actual writing/reading of disk
files. Opening a file stream opens a file; closing a file stream closes a file.

The Glulx Inform library handles normal file writing:  save game, transcript, and
command recording for debugging (gg_savestr, gg_scriptstr, and ggcommand_str
are created by the library). So you don't need to bother with streams unless you
want to write specialized files and/or do special effects with window streams.

( Note: Currently unglklib and this file do not cover the details of
file reading/writing -- file read/write markers and seek modes).



2.5 Glk Object variables and rock constants

Since Glk objects are created dynamically, they need global variables to
point to them. Because they can't be saved, they also need constants to
"anchor" them.

Zarf's naming convention for Glk objects is to give them a prefix of gg_
(ergo, gg_mainwin). In Inform code, in order to distinguish them from regular
Inform objects, following this naming convention is not a bad idea. That way
you can easily decide which objects should be included in IdentifyGlkObject().
(Although who knows if gg stands for Glulx Glk, or Glkly Glulx, or what.)

But variables are not enough, because their contents can become invalid after
UNDO/RESTART/RESTORE -- so constants are also used. Zarf calls these constants
rock numbers, rock ids, or rocks. And rocks are exactly what they are -- they
"weigh down" what otherwise could be ephemeral Glk objects floating free in
memory. You assign a rock, an unique number, to each Glk object, associating
it with a permanent identification tag. If/when the variable pointing to that
glk object becomes invalid, then its rock id can be used to re-identify
(re-tag) it so its variable can be corrected.

Therefore, every time that you create a new Glk object, you assign to it not
only a variable, but also a constant.

Variable Constant
gg_mywin = OpenTextWindow(gg_mainwin, 1, 0, 60, GG_MYWIN_ROCK);
gg_mychannel = CreateChannel(GG_MYCHANNEL_ROCK);
gg_myfileref = CreateFileRef("MYFILE", 0, 0, GG_MYFREF_ROCK);
gg_myfilestrm = OpenFileStream(gg_myfileref, 1, GG_MYFILESTR_ROCK);

The variable, such gg_mywin or gg_myfileref, is what is used in Inform code
to actually refer to a Glk object (0 = doesn't exist, # = exists). Essentially
a non-zero value means that the variable points to the Glk object's address
in memory (actually an entry in a hashing table, for those interested). For
instance, if gg_mywin is not zero, it would mean that that window is open; if
gg_myfref is not zero, then that file reference has been created. If zero,
then, that window is closed; that file reference doesn't exist.

A glk object's rock id is really only used twice to identify a Glk object:
when it is first created/open, and in IdentifyGlkObject(), when it is
re-identified/re-tagged after UNDO/RESTART/RESTORE.
Some rock constants can be considered "reserved" because they are already
used by the Glulx Inform library.

Constants Value Assigned to Object type
GG_MAINWIN_ROCK 201 main text window windows
GG_STATUSWIN_ROCK 202 status line
GG_QUOTEWIN_ROCK 203 quote box
GG_SAVESTR_ROCK 301 save file file streams
GG_SCRIPTSTR_ROCK 302 transcript
GG_COMMANDWSTR_ROCK 303 command file (debugging)
GG_COMMANDRSTR_ROCK 304 (ditto)
GG_SCRIPTFREF_ROCK 401 script file file reference

Following this numbering convention, Zarf recommends starting new Glk
objects at: 210 for windows, 310 for streams, and 410 for file references.

( Note: Just to be consistent, I start my sound channels at 510.)



3. GLK EVENTS

Glk events are usually associated with player input: the player enters
some text, selects a hyperlink, and/or clicks the mouse. However, there are
also other, more internal types of Glk events. Handling those and responding
to the player's input is the job of HandleGlkEvent(). Your job is to put
code there to deal with the player's individualized multimedia input.

infglk and the Glk specifications refer to "requesting events." In
unglklib and here, this is referred to as " activating" an event.



3.1 Redraw events

Redraw events are the simplest Glk events. Most of the code you will put
into HandleGlkEvent() to handle redraw events will be routines that
already exist in your game to open/close windows and draw graphics.

Arrange Window -- When the player resizes the application window (the
overall interpreter window), it will resize all of the windows within
it, so code to respond to that event must be provided by the game author.

Redraw Graphic Window -- A graphic in a graphic window may be disrupted
if the player resets their color resolution. Again, code must be provided.

Activation: Redraw events aren't activated by the game author. They
occur when the player performs the action (resizes, resets) that
triggers them.



3.2 Sound notification events

Sound Notification -- Sound notification means being notified
(HandleGlkEvent() is notified) when a sound * finishes * playing. This can
be used for special effects.

Activation: You ask for sound notification when calling PlaySound().
If the "notify" argument is true, sound notification (if the interpreter
supports it) is triggered. Then the Glk entry point HandleGlkEvent() will
wait for the sound to finish playing before executing your code.



3.3 Glk Timer events

Glk has a single global timer analogous to the system clock. So, unlike the
regular Inform timer, the Glk timer is based on "real time," not game time.
Also, the minimum "time slice" permissible by Glk is a single timer "tick".
Once the Glk timer is started at a certain rate, it keeps ticking at that rate
until stopped. As each tick occurs, author-provided code may be executed.
This can be used for special effects, such as animating graphics.

Activation:  The Glk timer can be started or stopped. However, there is no
way to check whether it is currently running or not. And since it can only clock
one speed at a time, global flags are a handy way to keep track of when it is
running and to turn it on/off. The Glk timer *can* clock multiple timed events
concurrently, but any simultaneous events will clock at the same rate. So if
you want multiple timed events clocked at different rates, they should be
spaced in your code so there's no possibility of overlap.



3.4 Input events

Input Events - Input events get the player's input, either: line, character
mouse, or hyperlink selection. This is how the player interacts with the game.

Activation: You must first activate all input events each time before they
are used, or they will not work. An input event is activated -- turned on --
for a specific window. When the player triggers the event, it is then
considered complete and arrives at HandleGlkEvent() for processing.

All input events are associated with specific windows. However, not all
windows types support all events:

Window type Input types supported Input types not supported
Text Buffer Line  
Character  
  Mouse
Hyperlink  
Text Grid Line  
Character  
Mouse  
Hyperlink  
Graphic   Line
  Character
Mouse  
    Hyperlink

Other window limitations - Line and character input events cannot be active
in the same window at the same time. And mouse input and hyperlinks selection
cannot be activate in the same window at the same time. However, line/character
and mouse input, or line/character and hyperlink selection can be activate in
the same window at the same time. And various combinations of these input
events can be active in * different * windows at the same time.

( Note: Since both mouse input and hyperlink selection use the mouse, having
both activate in the same window at the same time may confuse the interpreter,
leaving it unable to distinguish between the two)



3.4.1 Line and character input events

HALTING INPUT EVENTS - Both line and character input essentially "halt"
the game and wait until the player responds. This might not be immediately
evident, because while they wait, the player can respond with other input.

Line Input -- Gets a line of input from the player, such as a command
entered at the blinking prompt. The Glulx Inform library handles normal
line input; unless you become a Glk expert, you don't need to bother with
it. However, if you want to, KeyboardPrimitive() is the routine normally
used by the Glulx Inform library to get the player's line input.

Character Input -- Gets a character from the player. There is also a
Glulx Inform library function, KeyCharPrimitive(), that will get one
character from the player. It is preferable to use that instead of a
character input event. KeyCharPrimtive() can be used just to pause the
game until the player presses any key, or its return value may be "caught"
(key = KeyCharPrimitive()) and compared to the key code constants defined
in infglk.h. They include arrow and function keys, among others.

Activation: Line/character events will normally be activated by the Glulx
Inform library, or with KeyboardPrimitive() or KeyCharPrimitive(). Line
input remains pending until the player presses RETURN to terminate the line.
Character input remains pending until the player presses a key.

( Note: KeyboardPrimitive() and KeyCharPrimitive() require no additional code
from the author in HandleGkEvent(), as it is already called by both routines).



3.4.2 Mouse and hyperlink input events

Mouse input and hyperlink usually operate as "short cuts" to regular Inform
commands. You put code into HandleGlkEvent() to pass the commands they are
meant to represent back to the Inform parser, which processes them normally.

Example: "North" (hyperlinked) could pass along "n" to the Inform parser as
if the player typed at the prompt.

NON-HALTING INPUT EVENTS - Mouse and hyperlink input do not halt the game
and wait for the player to respond. If/when the player uses either, the
event is considered complete and arrives at HandleGlkEvent() for processing.
But if the player uses neither, they just remain pending, uncompleted.

Mouse Input -- Mouse input means clicking a button with the cursor in some
location. Different interpreters will interpret that to mean one or more
buttons clicked, left or right button clicked -- whatever is appropriate
to the machines that the interpreter runs on.

Hyperlink Selection -- Hyperlink selection is just what it says. Hyperlinked
text will usually be displayed in blue, underlined text.

Activation: As with all input events, both are activated for a specific window,
and both must be activated each time before they are used, or they won't work.
ActivateHyperlinks(gg_mywin);
    print "There is a doorway here, leading ";
    Hyperlink(1); print "north"; Hyperlink(2); ".";




4. EXTERNAL RESOURCES

Resources are the external files that are "loaded" into Glulx Inform
objects:  graphic files to be drawn in text buffer or graphic windows, and
sound files to be played in sound channels. Because they are external to
Glulx, they are usually packed into an archive for the player's convenience.

The archive format used by Glulx Inform is blorb. A blorb file, or .blb,
may contain either: compiled source code (the game), graphics, and sounds;
or just graphics and sounds. If the game is not included in the archive,
then there will be two files: a .blb and .ulx (the last containing the game
alone). Currently (as far as I know) only the Windows Glulxe interpreter
supports the use of separate .ulx and .blb files. If a blorb is used, the
other interpreters expect it to contain both the game and its resources.

A Glulx game can also use separate graphics and sounds instead of a blorb.
If so, each will be separate and may get "lost" (not be present at game time).

But if graphic and sound files are packed in a blorb archive (along with the
game code, or separate from it), it is very unlikely they will get lost. Even
if the game author forgets to pack a resource, the game will usually not
compile unless all the graphics and sounds referred to can be found.

If the blorb itself is missing (only the .ulx is present when a .blb is also
expected), the game will still play -- just without graphics and sounds.

Accessing Resources with a Blorb - If you use a blorb, iblorb (and
blorbtar, I think) will create an Inform blorb include file, .bli, that assigns
constants to blorb resources. Then to refer to a resource, just use a constant.

Example:
PlaySound(gg_mychannel, babble_snd);  babble_snd is a constant
Accessing Resources without a Blorb - If you use separate sound and
graphic files, you refer to them directly when you pass them to a routine. Glulx
Inform will assume, since no blorb is used, that this means the resource is on the
hard disk in the same directory as the .ulx file (game with no blorb).

Example:
PlaySound(gg_mychannel, 1);
Since a graphic may be a .jpg or .png, and a sound may be a .aif or .mod, the
Blorb spec recommends that images be named PIC1, PIC2, etc., and sounds
be named SND1, SND2, etc. So if you pass 1 to PlaySound(), the interpreter
will look for 1 as SND1.aif or SND1.mod on disk. (Note: Unfortunately,
separate graphics and sounds currently cannot be combined with a blorb.)

File Formats - Currently two graphic file formats are supported by Glulx:
JPG and PNG. Two sound file formats are also supported: AIF and MOD.

(Note: Right now there are only two blorb packing programs available for
Informers -- iblorb, Inform-Blorb for Dos, by L. Ross Raszewski and
blorbtar, a perl script by Evin Robertson.)



5. GLK ENTRY POINTS

The Glulx Inform library has three Glk entry points: InitGlkWindow(),
IdentifyGlkObject(), and HandleGlkEvent(). These are built into into the
library, like the one glk() function. If you provide none of them, the
default library ones will be used.

Because they are built-in, they can't be "wrapped" or simplified by any
add-on library.

( Note: Not yet, anyway.)



5.1 InitGlkWindow()

When Called - InitGlkWindow() is called by the Glulx Inform library
right after game start (before Initialise and after IdentifyGlkObject()). It
creates the three main windows: gg_mainwin, gg_statuswin, and
gg_quotewin. If you want to change those windows' defaults, you have
to initialize them yourself with InitGlkWindow().

However, if you just want to replace the status line with one you have
customized, then, as in regular Inform, put Replace DrawStatusLine; in
your game code after the parser is included and before verblib and grammar
are included. Then simply add your own DrawStatusLine() routine.

Glulx Inform also has a new routine, StatusLineHeight(), so you can change
the default height of the status line to something else. To call it, just
put StatusLineHeight(gg_statuswin, #) in Initialise.

( Note: Replacing InitGlkWindow() is not covered in this file or unglklib.
But an example replacement of the status line is included in "Just a Dream.")



5.2 IdentifyGlkObject()

If you create any of your own Glk objects, then you * must * include your
own IdentifyGlkObject() to re-identify (re-tag) them after UNDO/RESTART/
RESTORE. Since the default library routine cannot know about the glk
objects you've created, it won't identify/tag them.

When Called - IdentifyGlkObject() is called by the Glulx Inform library:
after game start (before Initialise and before InitGlkWindow()), and after
UNDO/RESTART/RESTORE.

It is called in three passes, called phases.
Here's an example:
[ IdentifyGlkObject phase type ref rock id;

! phase = which pass
! type  = what type of Glk object
! ref   = its reference the (variable that points to it)
! rock  = its rock constant
!---
! id    = local variable

! Zero out Glk object variables (references). Be sure to include 
! all author-created Glk objects.

    if (phase == 0)
    {   gg_mywin          = 0;
        gg_myforechannel  = 0;
        gg_mybackchannel  = 0;
        gg_myfileref      = 0;
        gg_myfilestrm     = 0;
        return;
    }

! Find what the actual state of those Glk objects is in memory. 
! They will be identified by their rock numbers 
! (0, GLK_NULL == don't exist # = do exist).

    if (phase == 1)
    {   switch (type)
        {   0: ! it's a window
                switch (rock)
                {   GG_MYWIN_ROCK : gg_mywin      = ref;
                }

            1: ! it's a stream
               switch (rock)
               {   GG_MYSTR_ROCK : gg_myfilestrm = ref;
               }

            2: ! it's a fileref
               switch (rock)
               {  GG_MYFREF_ROCK : gg_myfileref  = ref;
               }
        }
        return;
    }

! Now also find the state of sound channels, which must be
! handled in this phase two, not phase one.

   if (phase == 2)
   {   ! Loop through existing channels. 0 in first argument
       ! of NextChannel returns the first one.
       if (sound_on)
       {  id = NextChannel(0, gg_arguments);
          while (id)
          {  switch (gg_arguments-->0)
            {  GG_MYFORECHANNEL_ROCK : gg_myforechannel = id;
               GG_MYBACKCHANNEL_ROCK : gg_mybackchannel = id;
            }
            id = NextChannel(id, gg_arguments);
          }
       }

! Next restore everything to the recovered game state:  open 
! a window if it was open; close a window if it wasn't opened
! yet; play a sound; stop a sound.

      ArrangeMyWindows();
      DrawMyGraphics();
      PlayMySounds();

    }
]; ! end of phase 2


5.3 HandleGlkEvent()

If you activate any Glk events, then you * must * include your own
HandleGlkEvent() to handle them. Since the default library routine cannot
know about the events you've activated, it won't respond to them.

When Called - HandleGlkEvent() is called by the Glulx Inform library, when:
the player resizes the application window, the player changes their color settings,
a sound finishes playing (if sound notification has been requested), the Glk
timer emits a "tick" (if the timer has been started), the player enters a
line or a character (if either event has been activated), the player uses
mouse input (if activated), and the player selects a hyperlink (if activated).

How it Works - HandleGlkEvent() is basically in a "loop" with
KeyboardPrimitive() and KeyCharPrimitive() (the two routines normally used
by the Glulx Inform library to get player input). Both call it. When the player
enters a line or a character, HandleGlkEvent() is called before the line or
character is send onto the parser. Any other pending events are dealt with
at the same time (resize, reset, timer, sound notification, etc.).

Mouse and hyperlink input can both supersede line or character input events.
No text may have been entered by the player before they are used, and if it
was, they will still be handled by HandleGlkEvent() first. So, because they
are also input events, when the player uses either, it is a good idea to
cancel any possible pending line or character input before responding.
HandleGlkEvent()'s context argument tells whether KeyCharPrimitive() or
KeyboardPrimitive() was active when the mouse or a hyperlink was "clicked."

Here's an example:

[ HandleGlkEvent ev context abortres newcmd cmdlen;

! ev        = event array
!             ev-->0 = event type
!             ev-->1 = window event is associated with
!             ev-->2 = value, depends on usage
!             ev-->3 = value, depends on usage
! context   = when happened, 1 = char input was pending
!                                (KeyCharPrimitive())
!                            0 = line input was pending
!                                (KeyboardPrimitive())
! abortres   = input buffer
! ______
! newcmd   = new command
! cmdlen   = length of command

    switch (ev-->0){

! Windows are resized by the player, so update
! windows and graphics.

      evtype_Arrange :
        ArrangeMyWindows();
        DrawMyGraphics();

! Color/resolution settings are changed by the player,
! so fix graphics.

      evtype_Redraw :
        DrawMyGraphics();

! Sound has finished playing, so do something appropriate.

      evtype_SoundNotify:
        RespondToSndNofity();

! Mouse was clicked, so perform some action. Mouse input
! and hyperlinks are associated with specific windows.

! See notes under hyperlinks for an explanation.

      evtype_MouseInput :
        if (ev-->1 == gg_mygraphwin)
        {  ! canel line or character
           ! input depending on context
           if (context == 1)
              CancelCharInput(gg_mainwin);
           else
              CancelLineInput(gg_mainwin);
           newcmd = "open door";
           cmdlen = PrintAnyToArray(abortres+WORDSIZE,
                    INPUT_BUFFER_LEN-WORDSIZE, newcmd);
           abortres-->0 = cmdlen;
           return 2;
        }

! Return the command as if it had been typed in by the
! player, passing it through the parser back to the
! appropriate object or room location. A return value
! of 2 passes the command along as if player typed it.

! The command "open door" or "n" is put into the
! abortres array (the input buffer) to be passed along
! to and processed by the Inform parser. PrintAnyToArray
! actually copies the command to the input buffer, and
! also returns the exact byte length of the author-created
! command string (the Inform parser needs the exact length).
! Next, the length is put into the first word of the input
! buffer. Then 2 is returned to pass the command
! back to the parser as if the player typed it.

! ev--2> = value, in this case, the specific hyperlink
! number.

       evtype_HyperLink :
         if (ev-->1 == gg_mainwin)
         { ! cancel line or character
           ! input depending on context
           if (context == 1)
              CancelCharInput(gg_mainwin);  
            else
              CancelLineInput(gg_mainwin);
            switch(ev-->2)
            {  1 : newcmd = "open door";
               2 : newcmd = "n";
            }
            cmdlen = PrintAnyToArray(abortres+WORDSIZE,
                     INPUT_BUFFER_LEN-WORDSIZE, newcmd);
            abortres-->0 = cmdlen;
            return 2;
         }


      evtype_Timer :
        if (timer_counter == NULL) StopGlkTimer();
        else DoMyTimedRoutine();
    }
]; ! end of event switch


6. TEXT STYLES

Text styles aren't Glk objects or events. However, changing text styles
with Glk is still complicated. There are two types of styles: "styles" and
"style hints." Since this can become tongue-twisting and confusing, they
will be referred to here as style " categories " and style " attributes. "



6.1 Preset text styles

Glk currently comes with eleven style categories. Each category is "preset"
by each Glulxe interpreter with certain fonts and style attributes (bold,
color, size, etc.).
   Normal       - normal text
    Emphasized   - usually bold (may be italic)
    Preformatted - fixed-width font -- for maps/diagrams
    Header       - large text -- for titles like the game name
    Subheader    - smaller than Header -- subtitles like the room name
    Alert        - warns of dangerous condition
    Note         - an interesting condition like score notification
    BlockQuote   - may be indented -- quotations, etc. (like HTML)
    Input        - player's input
    User1        - defined by game author
    User2        - defined by game author

These style categories alone can be used to print "fancy" text. Or you
may change their style attributes.

( Note: Presets for categories may differ from interpreter to interpreter.)



6.2 Suggesting text styles

You may set specific attributes (bold, size, etc. -- but for obvious reasons,
no fonts) for each style category. However, setting style attributes for a
style category may, or may not work. Therefore, they are called style "hints"
in the Glk specs and infglk, and " suggestions " in unglklib and here.

Example:  SetTextStyleItalic(style_User1, 1);

Why suggesting text styles may not work:

  1. You can't know what fonts are on the player's computer. However, the
    interpreter, because it * is * on the player's computer, can find what
    fonts are installed, create a list, and choose default fonts and
    attributes for each category -- just how web browsers work with HTML.

  2. During game play, the player may change the fonts/styles for a category.
    (One of Glk's capabilities is to allow players to do just that.)
    Lists of categories and fonts are available for the player to select from.

  3. Some fonts don't support certain attributes. For instance, some fonts
    have no italic variant, and some may only be displayed in certain sizes.

So, if a style category is preset to a font that doesn't have an italic
variant, or if the player has changed that category to a font without one,
then suggesting that category be italic is pointless. And if it does have one,
and your suggestion * works *, the player may change it during game play anyway.

When/How to Suggest -- The other complicated thing is that TEXT STYLE
SUGGESTIONS MUST BE MADE BEFORE THE WINDOW THEY ARE
TO BE USED IN IS OPENED. Suggesting them after the window is open leaves
the style categories unaffected. So you must think ahead.

They can also be suggested only for a TYPE of window (text buffer, text grid),
not for individual windows. So, to set a category with different attributes
in different windows, new suggestions must be made before opening each window.

Accumulative - On the flip side, style attributes are accumulative -- one style
category can have more than one style attribute. Text style setting routines can
be called again to suggest additional attributes for the same category.

( Note: Since suggestions must be made before a window is opened, it doesn't
exist as an object yet, so naturally it can't be referenced when suggesting.)



6.3 Testing text styles

Since text style suggestions may not work, the only way you can find out
if they succeeded is to later test the style category itself to see what
attributes it currently has. Unlike setting style attributes, individual
window's style categories * can * be tested, because once windows are open,
attributes for the same category can differ from window to window.

Example:
is_italic = TestTextStyleItalic(gg_mytextwin, style_User1);



7. GLULX INFORM'S GLKY "TRICKS"

The next two sub-sections detail some tricky Glulx Inform Glk programming.
Not tricky because they are necessarily hard; tricky because they involve
"insider knowledge." So you should know about them, or you might get stumped.



7.1 Returning multiple values
(gg_arguments)

Several Glky functions either need to return more than one value and/or have
memory addresses passed to some of their arguments. In unglklib and infglk,
these arguments will have ptr (pointer) as a suffix.

GetWindowSize(win, widthptr, heightptr);

Since memory addresses in Inform are accessed by arrays, a multiple purpose
global array, gg_arguments, has been added to parserm.h to be used with such
Glky routines. When passed to an argument, the first element of the array is
accessed by the array name; the second element by the array name + WORDSIZE.

GetWindowSize(gg_mywin, gg_arguments, gg_arguments+WORDSIZE);

Then each element of the gg_arguments array can be read into local variables.

width =  gg_arguments-->0;
height = gg_arguments-->1;



This section was written by Roger Firth.

Any Inform routine is able to return exactly one value; in unglklib this
value is generally used either to identify a Glk component:

    gg_mywin = OpenTextWindow(...);

or to provide a true/false (or success/failure) report:
    if (SupportsGraphics())
 
...

    if (IsWinGraphic(gg_mywin))
Several infglk routines (but currently only one unglklib routine) need to
return *more than one* value, which means that a different approach is needed.
The way a routine can return several values is by giving it arguments which
are the addresses of entries in an array. If the routine stores values into
those entries and then returns, then the calling program can access the
returned values from the array. An example may make this clearer:

    ! Example routine which returns the square of its first argument
    ! in its second argument, and the cube of its first argument in
    ! its third argument.
    [ myRoutine x x_squared x_cubed;
        x_squared-->0 = x * x;
        x_cubed-->0   = x * x * x;
    ];

    ! Word array with two entries to receive the routine's replies.
    array myResults 2;

    myRoutine(5, myResults, myResults+4);
    print "The square is ", myResults-->0, "^";
    print "The cube is ",   myResults-->1, "^";

In the call to myRoutine, the second argument is the address of the first
entry in myResults. The statement "x_squared--> 0 = x * x;" stores a return
value into that array entry. Similarly, the third argument, myResults+4, is
the address of the second entry in myResults (since in Glulx there are four
bytes to a word). The statement "x_cubed--> 0 = x * x * x;" stores the other
return value into the second array entry.

In fact, you don't need to declare your own array in order to receive
return arguments from routines. A multi-purpose global array, gg_arguments,
is already available in the Glulx Inform library to be used with such Glky
functions. The first element of the array is referenced using gg_arguments;
the second element by gg_arguments+4. If you ever needed the third and
fourth elements, they would be gg_arguments+8 and gg_arguments+12.

To access array elements, it's actually better to use the constant WORDSIZE,
which the Glulx bi-platform library sets to 2 for regular Inform and to 4
for Glulx Inform. So you'd pass the arguments array entries using

    gg_arguments, gg_arguments+WORDSIZE, gg_arguments+WORDSIZE*2, ...

Here is the actual unglklib routine which does this; GetWindowSize()
returns the current width and height of the specified window:
  [ GetWindowSize win widthptr heightptr;
    ! win        = window to get size of
    ! widthptr   = returns width of window
    ! heightptr  = returns height of window

        if (win == 0) rfalse;
        return glk_window_get_size(win, widthptr, heightptr);
    ];

And here is how you might call GetWindowSize() in your game to find the
size of the main text window:
GetWindowSize(gg_mainwin, gg_arguments, gg_arguments+WORDSIZE);
    width  = gg_arguments-->0;
    height = gg_arguments-->1;

( Note: See Zarf's "Author's Guide to Glulx Inform" for more about WORDSIZE.)



7.2 "Vile references errors from hell"

Sound familiar? Basically this situation is the same as with "vile zero
errors from hell." If you use an invalid reference to perform an action
(just as if you use a unassigned Inform variable to perform an action), the
Glulxe interpreter may "burp" (just as a regular Inform interpreter may burp.)

Regular Inform - Most "vile zero errors from hell" involve using an unassigned
variable as a condition to perform an action. Something like the following is
a common cause of zero errors. If second was not previously assigned a value,
some interpreters will burp with an Inform error.

if (second == something) do something;    (burp)

Not all the Inform grammar routines assign a value to second. So the solution
is to first check that second is not zero, before using it as a condition.

if ((second ~= 0) &&
    (second == something)) do something;  (burpless)

Glulx Inform - A similar thing can happen with Glulx Inform. If you close or
destroy a Glk object, then later use its variable as a condition to perform an
action, some interpreters will burp with a Glk error (and stop the game).

CloseWindow(gg_mywin);                   (gg_mywin is now
                                          an invalid reference)

if (gg_mywin) do something;              (burp)

However, an invalid Glk object reference is NOT zero, it is a pointer to a
memory address (an entry in a hashing table) that no longer exists.

The solution is to immediately assign zero to the Glk object's variable after
it has been closed/destroyed, so the variable can then be used later as a
condition to perform an action.

CloseWindow(gg_mywin);
gg_mywin = 0;                            (make sure variable
                                          is now zero)

if (gg_mywin) do something;              (burpless)

if (gg_mywin == 0) do something;         (burpless)

( Note: This is slightly over-simplified, but that's basically the concept.)



One parting thought: Glk is actually a lot easier to use, than it is to
describe or understand.        Doe :-)



Version History:

1.31 Comments added to the File Reference, Stream, and External Files sections.
        Glk Timer section also clarified.
1.30 First released version.