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:

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) above 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.)

A note about zero-size windows: a window may wind up with a height or width of zero. (Because of a fixed height of zero, or a percentage allocation of zero, or simply because the application display area does not have enough space.) The library should support this by hiding the window and its contents entirely. Any border between the window and its sibling should also disappear.

[When a window has zero size, it is reasonable for the library to stop passing input events to it. The input request is not cancelled, but the UI may not be able to give keyboard focus or mouse events to a zero-size region of the screen. Therefore, a program which sets a window to zero size should not rely on input from it.]

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 specifies 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:

The method argument must be the logical-or of a direction constant (winmethod_Above, winmethod_Below, winmethod_Left, winmethod_Right) and a split-method constant (winmethod_Fixed, winmethod_Proportional).

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. (Without sufficient space to display a complete line of text, it counts as "zero size".)

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       |
|         |       |         |         |         |
|         |       |         |         |         |
+---------+       +---------+         +---------+

The initial window is A. After the first split, the new pair window (O1, which covers the whole screen) knows that its new child (B) is below A, and gets 50% of its own area. (B 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 B 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, and alter the Border/NoBorder flag. 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. The library may not support the Border/NoBorder hint, in which case every pair of windows will have a visible border -- or no border -- between them.

[The Border/NoBorder was introduced in Glk 0.7.1. Prior to that, all games used the Border hint, and this remains the default. However, as noted, not all implementations display window borders. Therefore, for existing implementations, "Border" may be understood as "your normal style of window display"; "NoBorder" may be understood as "suppress any interwindow borders you may have".]

There may be decorations within the windows 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.]

By default, 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.

However, this default behavior can be changed with the glk_set_echo_line_event() call. If the default echoing is disabled, the library will not display the input text (plus newline) after input is either completed or cancelled. The buffer will end with whatever prompt you displayed before requesting input. If you want the traditional input behavior, it is then your responsibility to print the text, using the Input text style, followed by a newline (in the original style).

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, or if a newline (0x0A) is printed, the cursor goes to the beginning of the next line. The library makes no attempt to wrap lines at word breaks. If the cursor reaches the end of the last line, further printing has no effect on the window until the cursor is moved.

[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.2, "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. The glk_set_echo_line_event() call has no effect in grid windows.

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 graphics 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 do not support text output, nor line input. They may support character input. [Character input for graphics windows was added in Glk spec 0.7.5. Older interpreters may not support this feature.]

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.

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. If win is NULL, it is equivalent to

glk_stream_set_current(NULL);

If win is not NULL, it is equivalent to

glk_stream_set_current(glk_window_get_stream(win));

See section 5, "Streams".


Up to top Previous chapter Next chapter