The Z-Machine Quote Box Problem

Glk uses a simple text-window model, which is easily implemented using the text display toolkits of most operating systems. It assumes that a text window is a single block of styled text. A story window is line-wrapped (with line breaks delimiting paragraphs); a status window is a given number of lines.

This model works well for the Z-machine -- until you run into the Z-machine quote-box trick.

Quote boxes were first used in Trinity. The technique was then imitated in Inform 5 (later 6) as the box statement. The trick roughly looks like this:

In Infocom's (V4) interpreter, the effect was to print the reverse-video text overlaid on the top of the story window, below the (single-line) status bar. The text would then scroll away as part of the story window's natural scrolling, over the next few command inputs.

In a naive Glk implementation -- or any simple implementation using text windows -- this trick fails. The interpreter will display the quote text for a tiny fraction of a second, or (if the display system has built-in buffering) not at all.

To work around this, our Z-machine interpreter must use a counter-trick. The strategy is to not decrease the window size when the Z-machine requests it. Instead, we leave the window open (ten lines, in the example) while accepting line input. At the following input event, we assume that the player has read the text, and collapse the window back to its "correct" size.

The Algorithm

To do this, we must maintain some extra state variables. Here is the appropriate algorithm, in pseudocode:

    var curheight = 0   # What the VM thinks the height is
    var maxheight = 0   # Height including possible quote box
    var seenheight = 0  # Last height the user saw

    # Set the window height. Called by the @split_window opcode.
    # Also called on @erase_window -1 (with an argument of zero).
    # May also be called by @set_cursor; see notes below.
    #
    function split_window(num_of_lines):
        var oldheight = curheight
        curheight = num_of_lines

        # We do not decrease the height at this time -- it can only
        # increase.
        if (curheight > maxheight)
            maxheight = curheight

        # However, if the VM thinks it's increasing the height, we must be
        # careful to clear the "newly created" space.
        if (curheight > oldheight)
            blank out all lines from oldheight to the bottom of the window
    
        set the true window height to maxheight
        # (If this adds new lines, they should start out blank)

    # If the status height is too large because of last turn's quote box,
    # shrink it down now.
    # This must be called immediately before any input event. (That is,
    # the beginning of the @read and @read_char opcodes.)
    #
    function resolve_status_height():
        # If the player has seen the entire window, we can shrink it.
        if (seenheight == maxheight)
            maxheight = curheight
    
        set the true window height to maxheight
        seenheight = maxheight
        maxheight = curheight

Test Cases

It is wise to test one's interpreter with the following games. In all cases, the quote should disappear after one player input. (Whether this is a typed command or a "hit a key to continue" keystroke.)

Curses: Title screens / I / LOOK / HELP / Q

(The game begins with a quote; the first "inventory" command displays another. The "help" command brings up a traditional Inform menu, which allows you check that you haven't broken that feature.)

Anchorhead: Title screens / HELP / Q

(The game begins with two Lovecraft quotes.)

Trinity: NE / EXAMINE SUNDIAL / UNSCREW GNOMON / SW

(Examining the sundial displays a quote.)

Commentary and Notes

The above code is taken from the Glk interface layer in the Fizmo interpreter. See glk_screen_if.c.

I have roughly equivalent code in my fork of Parchment ("zarfsite" branch; see textgrid.js). It differs in one way: the resolve_status_height functionality appears at the end of the TextGrid.stream method, which is called by the interpreter before input only if the status window was updated.

This is an insignificant change for Inform games, which update the status window every turn. However, Trinity is tidy and only updates the status window when necessary -- typically when you change locations. Therefore, when running Trinity in my Parchment interpreter, the sundial quote box sticks around until you leave the Wabe.

Both Fizmo and Parchment are careful about writing beyond the current window size. (That is, invoking @set_cursor to move the cursor below the status window height.) In this case, they increase the window size (call split_window, passing the cursor's line coordinate). I suspect this hack is necessary to support existing Z-code games. However, I can't name specific games that require it.

Inform 6 supports the box statement when compiling to Glulx, but it does not use anything like the Z-machine's trick. Instead, it opens a third text window and keeps it open until the following input. This is all done in VM code, and does not require any interpreter support.


Last updated February 16, 2014.

Glk home page

Zarfhome (map) (down)