The Gestalt System

The Gestalt System — Testing Glk's capabilities

Functions

Types and Values

Includes

#include <libchimara/glk.h>

Description

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

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

Functions

glk_gestalt_ext ()

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

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

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

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

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

Parameters

sel

A selector, representing which capability to request information about.

 

val

Extra information, depending on the value of sel .

 

arr

Location of an array to store extra information in, or NULL.

 

arrlen

Length of arr , or 0 if arr is NULL.

 

Returns

an integer, depending on what selector was called.


glk_gestalt ()

glui32
glk_gestalt (glui32 sel,
             glui32 val);

Calls the gestalt system to request information about selector sel , without passing an array to store extra information in (see glk_gestalt_ext()).

Parameters

sel

A selector, representing which capability to request information about.

 

val

Extra information, depending on the value of sel .

 

Returns

an integer, depending on what selector was called.

Types and Values

gestalt_Version

#define gestalt_Version (0)

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

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

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

So the version number 78.2.11 would be encoded as 0x004E020B.

The current Glk specification version is 0.7.4, so this selector will return 0x00000704.

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

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


gestalt_Unicode

#define gestalt_Unicode (15)

The basic text functions will be available in every Glk library. The Unicode functions may or may not be available. Before calling them, you should use the gestalt_Unicode and gestalt_UnicodeNorm gestalt selectors.

1
2
glui32 res;
res = glk_gestalt(gestalt_Unicode, 0);

This returns 1 if the core Unicode functions are available. If it returns 0, you should not try to call them. They may print nothing, print gibberish, or cause a run-time error. The Unicode functions include glk_buffer_to_lower_case_uni(), glk_buffer_to_upper_case_uni(), glk_buffer_to_title_case_uni(), glk_put_char_uni(), glk_put_string_uni(), glk_put_buffer_uni(), glk_put_char_stream_uni(), glk_put_string_stream_uni(), glk_put_buffer_stream_uni(), glk_get_char_stream_uni(), glk_get_buffer_stream_uni(), glk_get_line_stream_uni(), glk_request_char_event_uni(), glk_request_line_event_uni(), glk_stream_open_file_uni(), glk_stream_open_memory_uni().

If you are writing a C program, there is an additional complication. A library which does not support Unicode may not implement the Unicode functions at all. Even if you put gestalt tests around your Unicode calls, you may get link-time errors. If the glk.h file is so old that it does not declare the Unicode functions and constants, you may even get compile-time errors.

To avoid this, you can perform a preprocessor test for the existence of GLK_MODULE_UNICODE.


GLK_MODULE_UNICODE

#define GLK_MODULE_UNICODE

If this preprocessor symbol is defined, so are the core Unicode functions and constants (see gestalt_Unicode). If not, not.


gestalt_UnicodeNorm

#define gestalt_UnicodeNorm (16)
1
2
glui32 res;
res = glk_gestalt(gestalt_UnicodeNorm, 0);

This code returns 1 if the Unicode normalization functions are available. If it returns 0, you should not try to call them. The Unicode normalization functions include glk_buffer_canon_decompose_uni() and glk_buffer_canon_normalize_uni().

The equivalent preprocessor test for these functions is GLK_MODULE_UNICODE_NORM.


GLK_MODULE_UNICODE_NORM

#define GLK_MODULE_UNICODE_NORM

If this preprocessor symbol is defined, so are the Unicode normalization functions (see gestalt_UnicodeNorm). If not, not.


gestalt_CharOutput

#define gestalt_CharOutput (3)

If you set ch to a character code (Latin-1 or higher), and call

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

then res will be one of gestalt_CharOutput_CannotPrint, gestalt_CharOutput_ExactPrint, or gestalt_CharOutput_ApproxPrint (see below.)

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

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

This selector will always return gestalt_CharOutput_CannotPrint if ch is an unprintable eight-bit character (0 to 9, 11 to 31, 127 to 159.)

Make sure you do not get confused by signed byte values. If you set a “signed char” variable ch to 0xFE, the small-thorn character (þ), it will wind up as -2. (The same is true of a “char” variable, if your compiler treats “char” as signed!) If you then call

1
res = glk_gestalt(gestalt_CharOutput, ch);

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

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

instead.

Unicode includes the concept of non-spacing or combining characters, which do not represent glyphs; and double-width characters, whose glyphs take up two spaces in a fixed-width font. Future versions of this spec may recognize these concepts by returning a len of 0 or 2 when gestalt_CharOutput_ExactPrint is used. For the moment, we are adhering to a policy of “simple stuff first”.


gestalt_CharOutput_CannotPrint

#define   gestalt_CharOutput_CannotPrint (0)

When the gestalt_CharOutput selector returns this for a character, the character cannot be meaningfully printed. If you try, the player may see nothing, or may see a placeholder.


gestalt_CharOutput_ApproxPrint

#define   gestalt_CharOutput_ApproxPrint (1)

When the gestalt_CharOutput selector returns this for a character, the library will print some approximation of the character. It will be more or less right, but it may not be precise, and it may not be distinguishable from other, similar characters. (Examples: “ae” for the one-character “æ” ligature, “e” for “è”, “|” for a broken vertical bar (¦).)


gestalt_CharOutput_ExactPrint

#define   gestalt_CharOutput_ExactPrint (2)

When the gestalt_CharOutput selector returns this for a character, the character will be printed exactly as defined.


gestalt_LineInput

#define gestalt_LineInput (2)

If you set ch to a character code, and call

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

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


gestalt_LineInputEcho

#define gestalt_LineInputEcho (17)
1
res = glk_gestalt(gestalt_LineInputEcho, 0);

This returns 1 if glk_set_echo_line_event() is supported, and 0 if it is not.

Remember that if it is not supported, the behavior is always the default, which is line echoing enabled.


GLK_MODULE_LINE_ECHO

#define GLK_MODULE_LINE_ECHO

If this preprocessor symbol is defined, so is glk_set_echo_line_event(). If not, not.


gestalt_LineTerminators

#define gestalt_LineTerminators (18)
1
res = glk_gestalt(gestalt_LineTerminators, 0);

This returns 1 if glk_set_terminators_line_event() is supported, and 0 if it is not.


GLK_MODULE_LINE_TERMINATORS

#define GLK_MODULE_LINE_TERMINATORS

If this preprocessor symbol is defined, so is glk_set_terminators_line_event(). If not, not.


gestalt_LineTerminatorKey

#define gestalt_LineTerminatorKey (19)
1
res = glk_gestalt(gestalt_LineTerminatorKey, ch);

This returns 1 if the keycode ch can be passed to glk_set_terminators_line_event(). If it returns 0, that keycode will be ignored as a line terminator. Printable characters and keycode_Return will always return 0.


gestalt_CharInput

#define gestalt_CharInput (1)

If you set ch to a character code, or a special code (from 0xFFFFFFFF down), and call

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

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


gestalt_MouseInput

#define gestalt_MouseInput (4)

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

1
res = glk_gestalt(gestalt_MouseInput, windowtype);

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


gestalt_Timer

#define gestalt_Timer (5)

You can test whether the library supports timer events:

1
res = glk_gestalt(gestalt_Timer, 0);

This returns TRUE (1) if timer events are supported, and FALSE (0) if they are not.


gestalt_Graphics

#define gestalt_Graphics (6)

Before calling Glk graphics functions, you should use the following gestalt selector:

1
2
glui32 res;
res = glk_gestalt(gestalt_Graphics, 0);

This returns 1 if the overall suite of graphics functions is available. This includes glk_image_draw(), glk_image_draw_scaled(), glk_image_get_info(), glk_window_erase_rect(), glk_window_fill_rect(), glk_window_set_background_color(), and glk_window_flow_break(). It also includes the capability to create graphics windows.

If this selector returns 0, you should not try to call these functions. They may have no effect, or they may cause a run-time error. If you try to create a graphics window, you will get NULL.


gestalt_DrawImage

#define gestalt_DrawImage (7)

This selector returns 1 if images can be drawn in windows of the given type. If it returns 0, glk_image_draw() will fail and return FALSE (0). You should test wintype_Graphics and wintype_TextBuffer separately, since libraries may implement both, neither, or only one.


gestalt_Sound2

#define gestalt_Sound2 (21)

You can test whether the library supports sound:

1
2
glui32 res;
res = glk_gestalt(gestalt_Sound2, 0);

This returns 1 if the overall suite of sound functions is available. This includes all the functions defined in this chapter. It also includes the capabilities described below under gestalt_SoundMusic, gestalt_SoundVolume, and gestalt_SoundNotify.


gestalt_Sound

#define gestalt_Sound (8)
1
2
glui32 res;
res = glk_gestalt(gestalt_Sound, 0);

This returns 1 if the overall suite of sound functions is available. This includes glk_schannel_create(), glk_schannel_destroy(), glk_schannel_iterate(), glk_schannel_get_rock(), glk_schannel_play(), glk_schannel_play_ext(), glk_schannel_stop(), glk_schannel_set_volume(), and glk_sound_load_hint().

If this selector returns 0, you should not try to call these functions. They may have no effect, or they may cause a run-time error.

This selector is guaranteed to return 1 if gestalt_Sound2 does.


gestalt_SoundVolume

#define gestalt_SoundVolume (9)

You can test whether the library supports setting the volume of sound channels:

1
res = glk_gestalt(gestalt_SoundVolume, 0);

This selector returns 1 if the glk_schannel_set_volume() function works. If it returns zero, glk_schannel_set_volume() has no effect.

This selector is guaranteed to return 1 if gestalt_Sound2 does.


gestalt_SoundNotify

#define gestalt_SoundNotify (10)

You can test whether the library supports sound notification events:

1
res = glk_gestalt(gestalt_SoundNotify, 0);

This selector returns 1 if the library supports sound notification events. If it returns zero, you will never get such events.

This selector is guaranteed to return 1 if gestalt_Sound2 does.


gestalt_Hyperlinks

#define gestalt_Hyperlinks (11)

You can test whether the library supports hyperlinks:

1
2
glui32 res;
res = glk_gestalt(gestalt_Hyperlinks, 0);

This returns 1 if the overall suite of hyperlinks functions is available. This includes glk_set_hyperlink(), glk_set_hyperlink_stream(), glk_request_hyperlink_event(), glk_cancel_hyperlink_event().

If this selector returns 0, you should not try to call these functions. They may have no effect, or they may cause a run-time error.


gestalt_HyperlinkInput

#define gestalt_HyperlinkInput (12)

You can test whether hyperlinks are supported with the gestalt_HyperlinkInput selector:

1
res = glk_gestalt(gestalt_HyperlinkInput, windowtype);

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


gestalt_SoundMusic

#define gestalt_SoundMusic (13)

You can test whether music resources are supported:

1
res = glk_gestalt(gestalt_SoundMusic, 0);

This returns 1 if the library is capable of playing music sound resources. If it returns 0, only sampled sounds can be played.

“Music sound resources” means MOD songs — the only music format that Blorb currently supports. The presence of this selector is, of course, an ugly hack. It is a concession to the current state of the Glk libraries, some of which can handle AIFF but not MOD sounds.

This selector is guaranteed to return 1 if gestalt_Sound2 does.


gestalt_GraphicsTransparency

#define gestalt_GraphicsTransparency (14)

This returns 1 if images with alpha channels can actually be drawn with the appropriate degree of transparency. If it returns 0, the alpha channel is ignored; fully transparent areas will be drawn in an implementation-defined color.

The JPEG format does not support transparency or alpha channels; the PNG format does.


gestalt_DateTime

#define gestalt_DateTime (20)
1
res = glk_gestalt(gestalt_DateTime, 0);

This returns 1 if the overall suite of system clock functions, as described in this chapter, is available.

If this selector returns 0, you should not try to call these functions. They may have no effect, or they may cause a run-time error.

Glk timer events are covered by a different selector. See gestalt_Timer.


gestalt_ResourceStream

#define gestalt_ResourceStream (22)
1
res = glk_gestalt(gestalt_ResourceStream, 0);

This returns 1 if the glk_stream_open_resource() and glk_stream_open_resource_uni() functions are available. If it returns 0, you should not call them.


GLK_MODULE_RESOURCE_STREAM

#define GLK_MODULE_RESOURCE_STREAM

If this preprocessor symbol is defined, so are glk_stream_open_resource() and glk_stream_open_resource_uni(). If not, not.