| Glk API Specification | |
| API version 0.7.5 | |
| Andrew Plotkin <erkyrath@eblong.com> | |
| Copyright 1998-2017 by Andrew Plotkin. This specification is licensed under | |
| a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported | |
| License: <http://creativecommons.org/licenses/by-nc-sa/3.0> | |
| The API *described* by this document is an idea, not an expression of an | |
| idea, and is therefore not copyrightable. Anyone is free to write programs | |
| that use the Glk API or libraries that implement it. | |
| This document and further Glk information can be found at: | |
| <http://eblong.com/zarf/glk/> | |
| 0 Introduction | |
| 0.1 What Glk Is | |
| 0.2 What About the Virtual Machine? | |
| 0.3 What Does Glk Not Do? | |
| 0.4 Conventions of This Document | |
| 0.5 Credits | |
| 1 Overall Structure | |
| 1.1 Your Program's Main Function | |
| 1.2 Exiting Your Program | |
| 1.3 The Interrupt Handler | |
| 1.4 The Tick Thing | |
| 1.5 Basic Types | |
| 1.6 Opaque Objects | |
| 1.6.1 Rocks | |
| 1.6.2 Iterating Through Opaque Objects | |
| 1.7 The Gestalt System | |
| 1.8 The Version Number | |
| 1.9 Other API Conventions | |
| 2 Character Encoding | |
| 2.1 Testing for Unicode Capabilities | |
| 2.2 Output | |
| 2.3 Line Input | |
| 2.4 Character Input | |
| 2.5 Upper and Lower Case | |
| 2.6 Unicode String Normalization | |
| 2.6.1 A Note on Unicode Case-Folding and Normalization | |
| 3 Windows | |
| 3.1 Window Arrangement | |
| 3.2 Window Opening, Closing, and Constraints | |
| 3.3 Changing Window Constraints | |
| 3.4 A Note on Display Style | |
| 3.5 The Types of Windows | |
| 3.5.1 Blank Windows | |
| 3.5.2 Pair Windows | |
| 3.5.3 Text Buffer Windows | |
| 3.5.4 Text Grid Windows | |
| 3.5.5 Graphics Windows | |
| 3.6 Echo Streams | |
| 3.7 Other Window Functions | |
| 4 Events | |
| 4.1 Character Input Events | |
| 4.2 Line Input Events | |
| 4.3 Mouse Input Events | |
| 4.4 Timer Events | |
| 4.5 Window Arrangement Events | |
| 4.6 Window Redrawing Events | |
| 4.7 Sound Notification Events | |
| 4.8 Hyperlink Events | |
| 4.9 Other Events | |
| 5 Streams | |
| 5.1 How To Print | |
| 5.2 How To Read | |
| 5.3 Closing Streams | |
| 5.4 Stream Positions | |
| 5.5 Styles | |
| 5.5.1 Suggesting the Appearance of Styles | |
| 5.5.2 Testing the Appearance of Styles | |
| 5.6 The Types of Streams | |
| 5.6.1 Window Streams | |
| 5.6.2 Memory Streams | |
| 5.6.3 File Streams | |
| 5.6.4 Resource Streams | |
| 5.7 Other Stream Functions | |
| 6 File References | |
| 6.1 The Types of File References | |
| 6.2 Other File Reference Functions | |
| 7 Graphics | |
| 7.1 Image Resources | |
| 7.2 Graphics in Graphics Windows | |
| 7.3 Graphics in Text Buffer Windows | |
| 7.4 Testing for Graphics Capabilities | |
| 8 Sound | |
| 8.1 Sound Resources | |
| 8.2 Creating and Destroying Sound Channels | |
| 8.3 Playing Sounds | |
| 8.4 Other Sound Channel Functions | |
| 8.5 Testing for Sound Capabilities | |
| 9 Hyperlinks | |
| 9.1 Creating Hyperlinks | |
| 9.2 Accepting Hyperlink Events | |
| 9.3 Testing for Hyperlink Capabilities | |
| 10 The System Clock | |
| 10.1 Time and Date Conversions | |
| 10.2 Testing for Clock Capabilities | |
| 11 Porting, Adapting, and Other Messy Bits | |
| 11.1 Startup Options | |
| 11.2 Going Outside the Glk API | |
| 11.2.1 Memory Management | |
| 11.2.2 String Manipulation | |
| 11.2.3 File Handling | |
| 11.2.4 Private Extensions to Glk | |
| 11.3 Glk and the Virtual Machine | |
| 11.3.1 Implementing a Higher Layer Over Glk | |
| 11.3.2 Glk as a VM's Native API | |
| 12 Appendices | |
| 12.1 The Dispatch Layer | |
| 12.1.1 How This Works | |
| 12.1.2 Interrogating the Interface | |
| 12.1.3 Dispatching | |
| 12.1.3.1 Basic Types | |
| 12.1.3.2 References | |
| 12.1.3.3 Structures | |
| 12.1.3.4 Arrays | |
| 12.1.3.5 Return Values | |
| 12.1.4 Getting Argument Prototypes | |
| 12.1.5 Functions the Library Must Provide | |
| 12.1.5.1 Opaque Object Registry | |
| 12.1.5.2 Retained Array Registry | |
| 12.1.6 Table of Selectors | |
| 12.2 The Blorb Layer | |
| 12.2.1 How This Works | |
| 12.2.2 What the Program Does | |
| 12.2.3 What the Library Does | |
| 12.2.4 What the Blorb Layer Does | |
| 12.2.5 Blorb Errors | |
| 0: Introduction | |
| 0.1: What Glk Is | |
| Glk defines a portable API (programming interface) for applications with | |
| text UIs (user interfaces.) It was primarily designed for interactive | |
| fiction, but it should be suitable for many interactive text utilities, | |
| particularly those based on a command line. | |
| Rather than go into a detailed explanation of what that means, let me give | |
| examples from the world of text adventures. TADS, Glulx, and Infocom's | |
| Z-machine have nearly identical interface capabilities; each allows a | |
| program to... | |
| * print an indefinite stream of text into an output buffer, with some | |
| style control | |
| * input a line of text | |
| * display a few lines of text in a small separate window | |
| * store information in a file, or read it in | |
| and so on. However, the implementation of these capabilities vary widely | |
| between platforms and operating systems. Furthermore, this variance is | |
| transparent to the program (the adventure game.) The game does not care | |
| whether output is displayed via a character terminal emulator or a GUI | |
| window; nor whether input uses Mac-style mouse editing or EMACS-style | |
| control key editing. | |
| On the third hand, the user is likely to care deeply about these interface | |
| decisions. This is why there are Mac-native interpreters on Macintoshes, | |
| stylus and touch-screen interpreters on mobile devices, and so on -- and | |
| (ultimately) why there are Macintoshes and iPads and terminal window apps | |
| in the first place. | |
| On the *fourth* hand, TADS and Inform are not alone; there is historically | |
| a large number of text adventure systems. Most are obsolete or effectively | |
| dead; but it is inevitable that more will appear. Users want each living | |
| system ported to all the platforms in use. Users also prefer these ports to | |
| use the same interface, as much as possible. | |
| This all adds up to a pain in the ass. | |
| Glk tries to draw a line between the parts of the text adventure world | |
| which are identical on all IF systems, and different on different operating | |
| systems, from the parts which are unique to each IF system but identical in | |
| all OSs. The border between these two worlds is the Glk API. | |
| My hope is that a new IF system, or existing ones which are less-supported | |
| (Hugo, AGT, etc) can be written using Glk for all input and output | |
| function. The IF system would then be in *truly* portable C. On the other | |
| side of the line, there would be a Glk library for each operating system | |
| and interface (Macintosh, X-windows, curses-terminal, etc.) Porting the IF | |
| system to every platform would be trivial; compile the system, and link in | |
| the library. | |
| Glk can also serve as a nice interface for applications other than games -- | |
| data manglers, quick hacks, or anything else which would normally lack | |
| niceties such as editable input, macros, scrolling, or whatever is native | |
| to your machine's interface idiom. | |
| 0.2: What About the Virtual Machine? | |
| You can think of Glk as an IF virtual machine, without the virtual machine | |
| part. The "machine" is just portable C code. | |
| An IF virtual machine has been designed specifically to go along with Glk. | |
| This VM, called Glulx, uses Glk as its interface; each Glk call corresponds | |
| to an input/output opcode of the VM. | |
| For more discussion of this approach, see section 11.3, "Glk and the | |
| Virtual Machine". Glulx is documented at <http://eblong.com/zarf/glulx/>. | |
| Of course, Glk can be used with other IF systems. The advantage of Glulx is | |
| that it provides the game author with direct and complete access to the Glk | |
| API. Other IF systems typically have an built-in abstract I/O API, which | |
| maps only partially onto Glk. For these systems, Glk tends to be a | |
| least-common-denominator interface: highly portable, but not necessarily | |
| featureful. (Even if Glk has a feature, it may not be available through the | |
| layers of abstraction.) | |
| 0.3: What Does Glk Not Do? | |
| Glk does not handle the things which should be handled by the program (or | |
| the IF system, or the virtual machine) which is linked to Glk. This means | |
| that Glk does not address | |
| * parsing | |
| * game object storage | |
| * computation | |
| * text compression | |
| 0.4: Conventions of This Document | |
| This document defines the Glk API. I have tried to specify exactly what | |
| everything does, what is legal, what is illegal, and why. | |
| Sections in square brackets [[like this]] are notes. They do not define | |
| anything; they clarify or explain what has already been defined. If there | |
| seems to be a conflict, ignore the note and follow the definition. | |
| [[Notes with the label "WORK" are things which I have not yet fully | |
| resolved. Your comments requested and welcome.]] | |
| This document is written for the point of view of the game programmer -- | |
| the person who wants to use the Glk library to print text, input text, and | |
| so on. By saying what the Glk library does, of course, this document also | |
| defines the task of the Glk programmer -- the person who wants to port the | |
| Glk library to a new platform or operating system. If the Glk library | |
| guarantees something, the game programmer can rely on it, and the Glk | |
| programmer is required to support it. Contrariwise, if the library does not | |
| guarantee something, the Glk programmer may handle it however he likes, and | |
| the game programmer must not rely on it. If something is illegal, the game | |
| programmer must not do it, and the Glk programmer is not required to worry | |
| about it. [[It is preferable, but not required, that the Glk library detect | |
| illegal requests and display error messages. The Glk library may simply | |
| crash when the game program does something illegal. This is why the game | |
| programmer must not do it. Right?]] | |
| Hereafter, "Glk" or "the library" refers to the Glk library, and "the | |
| program" is the game program (or whatever) which is using the Glk library | |
| to print text, input text, or whatever. "You" are the person writing the | |
| program. "The player" is the person who will use the program/Glk library | |
| combination to actually play a game. Or whatever. | |
| The Glk API is declared in a C header file called "glk.h". Please refer to | |
| that file when reading this one. | |
| 0.5: Credits | |
| Glk has been a work of many years and many people. If I tried to list | |
| everyone who has offered comments and suggestions, I would immediately go | |
| blank, forget everyone's name, and become a mute hermit-like creature | |
| living in a train tunnel under Oakland. But I must thank those people who | |
| have written Glk libraries and linking systems: Matt Kimball, Ross | |
| Raszewski, David Kinder, John Elliott, Joe Mason, Stark Springs, and, er, | |
| anyone I missed. Look! A train! | |
| Evin Robertson wrote the original proposal for the Glk Unicode functions, | |
| which I imported nearly verbatim into this document. Thank you. | |
| 1: Overall Structure | |
| 1.1: Your Program's Main Function | |
| The top level of the program -- the main() function in C, for example -- | |
| belongs to Glk. [[This means that Glk isn't really a library. In a sense, | |
| you are writing a library, which is linked into Glk. This is bizarre to | |
| think about, so forget it.]] | |
| You define a function called glk_main(), which the library calls to begin | |
| running your program. glk_main() should run until your program is finished, | |
| and then return. | |
| Glk does all its user-interface work in a function called glk_select(). | |
| This function waits for an event -- typically the player's input -- and | |
| returns an structure representing that event. This means that your program | |
| must have an event loop. In the very simplest case, you could write | |
| void glk_main() | |
| { | |
| event_t ev; | |
| while (1) { | |
| glk_select(&ev); | |
| switch (ev.type) { | |
| default: | |
| /* do nothing */ | |
| break; | |
| } | |
| } | |
| } | |
| This is a legal Glk-compatible program. As you might expect, it doesn't do | |
| anything. The player will see an empty window, which he can only stare at, | |
| or destroy in a platform-defined standard manner. [[Command-period on the | |
| Macintosh; a kill-window menu option in an X window manager; control-C in a | |
| curses terminal window.]] | |
| [[However, this program does not spin wildly and burn CPU time. The | |
| glk_select() function waits for an event it can return. Since it only | |
| returns events which you have requested, it will wait forever, and grant | |
| CPU time to other processes if that's meaningful on the player's machine.]] | |
| [[Actually, there are some events which are always reported. More may be | |
| defined in future versions of the Glk API. This is why the default response | |
| to an event is to do nothing. If you don't recognize the event, ignore it.]] | |
| 1.2: Exiting Your Program | |
| If you want to shut down your program in the middle of your glk_main() | |
| function, you can call glk_exit(). | |
| void glk_exit(void); | |
| This function does not return. | |
| If you print some text to a window and then shut down your program, you can | |
| assume that the player will be able to read it. Most likely the Glk library | |
| will give a "Hit any key to exit" prompt. (There are other possibilities, | |
| however. A terminal-window version of Glk might simply exit and leave the | |
| last screen state visible in the terminal window.) | |
| [[You should *only* shut down your program with glk_exit() or by returning | |
| from your glk_main() function. If you call the ANSI exit() function, or | |
| other platform-native functions, bad things may happen. Some versions of | |
| the Glk library may be designed for multiple sessions, for example, and you | |
| would be cutting off all the sessions instead of just yours. You would | |
| probably also prevent final text from being visible to the player.]] | |
| 1.3: The Interrupt Handler | |
| Most platforms have some provision for interrupting a program -- | |
| command-period on the Macintosh, control-C in Unix, possibly a window | |
| manager menu item, or other possibilities. This can happen at any time, | |
| including while execution is nested inside one of your own functions, or | |
| inside a Glk library function. | |
| If you need to clean up critical resources, you can specify an interrupt | |
| handler function. | |
| void glk_set_interrupt_handler(void (*func)(void)); | |
| The argument you pass to glk_set_interrupt_handler() should be a pointer to | |
| a function which takes no argument and returns no result. If Glk receives | |
| an interrupt, and you have set an interrupt handler, your handler will be | |
| called, before the process is shut down. | |
| Initially there is no interrupt handler. You can reset to not having any by | |
| calling glk_set_interrupt_handler(NULL). | |
| If you call glk_set_interrupt_handler() with a new handler function while | |
| an older one is set, the new one replaces the old one. Glk does not try to | |
| queue interrupt handlers. | |
| You should not try to interact with the player in your interrupt handler. | |
| Do not call glk_select() or glk_select_poll(). Anything you print to a | |
| window may not be visible to the player. | |
| 1.4: The Tick Thing | |
| Many platforms have some annoying thing that has to be done every so often, | |
| or the gnurrs come from the voodvork out and eat your computer. | |
| Well, not really. But you should call glk_tick() every so often, just in | |
| case. It may be necessary to yield time to other applications in a | |
| cooperative-multitasking OS, or to check for player interrupts in an | |
| infinite loop. | |
| void glk_tick(void); | |
| This call is fast; in fact, on average, it does nothing at all. So you can | |
| call it often. [[In a virtual machine interpreter, once per opcode is | |
| appropriate. A more parsimonious approach would be once per branch and | |
| function call opcode; this guarantees it will be called inside loops. In a | |
| program with lots of computation, pick a comparable rate.]] | |
| glk_tick() does not try to update the screen, or check for player input, or | |
| any other interface task. For that, you should call glk_select() or | |
| glk_select_poll(). See section 4, "Events". | |
| [[Captious critics have pointed out that in the sample program model.c, I | |
| do not call glk_tick() at all. This is because model.c has no heavy loops. | |
| It does a bit of work for each command, and then cycles back to the top of | |
| the event loop. The glk_select() call, of course, blocks waiting for input, | |
| so it does all the yielding and interrupt-checking one could imagine.]] | |
| [[Basically, you must ensure there's some fixed upper bound on the amount | |
| of computation that can occur before a glk_tick() (or glk_select()) occurs. | |
| In a VM interpreter, where the VM code might contain an infinite loop, this | |
| is critical. In a C program, you can often eyeball it.]] | |
| [[But the next version of model.c will have a glk_tick() in the ornate | |
| printing loop of verb_yada(). Just to make the point.]] | |
| 1.5: Basic Types | |
| For simplicity, all the arguments used in Glk calls are of a very few types. | |
| * 32-bit unsigned integer. Unsigned integers are used wherever | |
| possible, which is nearly everywhere. This type is called "glui32". | |
| * 32-bit signed integer. This type is called "glsi32". Rarely used. | |
| * References to library objects. These are pointers to opaque C | |
| structures; each library will use different structures, so you can not and | |
| should not try to manipulate their contents. See section 1.6, "Opaque | |
| Objects". | |
| * Pointer to one of the above types. | |
| * Pointer to a structure which consists entirely of the above types. | |
| * Unsigned char. This is used only for Latin-1 text characters; see | |
| section 2, "Character Encoding". | |
| * Pointer to char. Sometimes this means a null-terminated string; | |
| sometimes an unterminated buffer, with length as a separate gui32 argument. | |
| The documentation says which. | |
| * Pointer to void. When nothing else will do. | |
| 1.6: Opaque Objects | |
| Glk keeps track of a few classes of special objects. These are opaque to | |
| your program; you always refer to them using pointers to opaque C | |
| structures. | |
| Currently, these classes are: | |
| * Windows: Screen panels, used to input or output information. | |
| * Streams: Data streams, to which you can input or output text. [[There | |
| are file streams and window streams, since you can output data to windows | |
| or files.]] | |
| * File references: Pointers to files in permanent storage. [[In Unix a | |
| file reference is a pathname; on the Mac, an FSSpec. Actually there's a | |
| little more information included, such as file type and whether it is a | |
| text or binary file.]] | |
| * Sound channels: Audio output channels. [[Not all Glk libraries | |
| support sound.]] | |
| [[Note that there may be more object classes in future versions of the Glk | |
| API.]] | |
| When you create one of these objects, it is always possible that the | |
| creation will fail (due to lack of memory, or some other OS error.) When | |
| this happens, the allocation function will return NULL instead of a valid | |
| pointer. You should always test for this possibility. | |
| NULL is never the identifier of any object (window, stream, file reference, | |
| or sound channel). The value NULL is often used to indicate "no object" or | |
| "nothing", but it is not a valid reference. If a Glk function takes an | |
| object reference as an argument, it is illegal to pass in NULL unless the | |
| function definition says otherwise. | |
| The glk.h file defines types "winid_t", "strid_t", "frefid_t", "schanid_t" | |
| to store references. These are pointers to struct glk_window_struct, | |
| glk_stream_struct, glk_fileref_struct, and glk_schannel_struct | |
| respectively. It is, of course, illegal to pass one kind of pointer to a | |
| function which expects another. | |
| [[This is how you deal with opaque objects from a C program. If you are | |
| using Glk through a virtual machine, matters will probably be different. | |
| Opaque objects may be represented as integers, or as VM objects of some | |
| sort.]] | |
| 1.6.1: Rocks | |
| Every one of these objects (window, stream, file reference, or sound | |
| channel) has a "rock" value. This is simply a 32-bit integer value which | |
| you provide, for your own purposes, when you create the object. [[The | |
| library -- so to speak -- stuffs this value under a rock for safe-keeping, | |
| and gives it back to you when you ask for it.]] | |
| [[If you don't know what to use the rocks for, provide 0 and forget about | |
| it.]] | |
| 1.6.2: Iterating Through Opaque Objects | |
| For each class of opaque objects, there is an iterate function, which you | |
| can use to obtain a list of all existing objects of that class. It takes | |
| the form | |
| CLASSid_t glk_CLASS_iterate(CLASSid_t obj, glui32 *rockptr); | |
| ...where CLASS represents one of the opaque object classes. [[So, at the | |
| current time, these are the functions glk_window_iterate(), | |
| glk_stream_iterate(), and glk_fileref_iterate(). There may be more classes | |
| in future versions of the spec; they all behave the same.]] | |
| Calling glk_CLASS_iterate(NULL, r) returns the first object; calling | |
| glk_CLASS_iterate(obj, r) returns the next object, until there aren't any | |
| more, at which time it returns NULL. | |
| The rockptr argument is a pointer to a location; whenever | |
| glk_CLASS_iterate() returns an object, the object's rock is stored in the | |
| location (*rockptr). If you don't want the rocks to be returned, you may | |
| set rockptr to NULL. | |
| You usually use this as follows: | |
| obj = glk_CLASS_iterate(NULL, NULL); | |
| while (obj) { | |
| /* ...do something with obj... */ | |
| obj = glk_CLASS_iterate(obj, NULL); | |
| } | |
| If you create or destroy objects inside this loop, obviously, the results | |
| are unpredictable. However it is always legal to call | |
| glk_CLASS_iterate(obj, r) as long as obj is a valid object id, or NULL. | |
| The order in which objects are returned is entirely arbitrary. The library | |
| may even rearrange the order every time you create or destroy an object of | |
| the given class. As long as you do not create or destroy any object, the | |
| rule is that glk_CLASS_iterate(obj, r) has a fixed result, and iterating | |
| through the results as above will list every object exactly once. | |
| 1.7: The Gestalt System | |
| The "gestalt" mechanism (cheerfully stolen from the Mac OS) is a system by | |
| which the Glk API can be upgraded without making your life impossible. New | |
| capabilities (graphics, sound, or so on) can be added without changing the | |
| basic specification. The system also allows for "optional" capabilities -- | |
| those which not all Glk library implementations will support -- and allows | |
| you to check for their presence without trying to infer them from a version | |
| number. | |
| The basic idea is that you can request information about the capabilities | |
| of the API, by calling the gestalt functions: | |
| glui32 glk_gestalt(glui32 sel, glui32 val); | |
| glui32 glk_gestalt_ext(glui32 sel, glui32 val, glui32 *arr, glui32 | |
| arrlen); | |
| The selector (the "sel" argument) tells which capability you are requesting | |
| information about; the other three arguments are additional information, | |
| which may or may not be meaningful. The arr and arrlen arguments of | |
| glk_gestalt_ext() are always optional; you may always pass NULL and 0, if | |
| you do not want whatever information they represent. glk_gestalt() is | |
| simply a shortcut for this; glk_gestalt(x, y) is exactly the same as | |
| glk_gestalt_ext(x, y, NULL, 0). | |
| The critical point is that if the Glk library has never heard of the | |
| selector sel, it will return 0. It is *always* safe to call glk_gestalt(x, | |
| y) (or glk_gestalt_ext(x, y, NULL, 0)). Even if you are using an old | |
| library, which was compiled before the given capability was imagined, you | |
| can test for the capability by calling glk_gestalt(); the library will | |
| correctly indicate that it does not support it, by returning 0. | |
| (It is also safe to call glk_gestalt_ext(x, y, z, zlen) for an unknown | |
| selector x, where z is not NULL, as long as z points at an array of at | |
| least zlen elements. The selector will be careful not to write beyond that | |
| point in the array, if it writes to the array at all.) | |
| (If a selector does not use the second argument, you should always pass 0; | |
| do not assume that the second argument is simply ignored. This is because | |
| the selector may be extended in the future. You will continue to get the | |
| current behavior if you pass 0 as the second argument, but other values may | |
| produce other behavior.) | |
| 1.8: The Version Number | |
| For an example of the gestalt mechanism, consider the selector | |
| gestalt_Version. If you do | |
| glui32 res; | |
| res = glk_gestalt(gestalt_Version, 0); | |
| res will be set to a 32-bit number which encodes the version of the Glk | |
| spec which the library implements. The upper 16 bits stores the major | |
| version number; the next 8 bits stores the minor version number; the low 8 | |
| bits stores an even more minor version number, if any. [[So the version | |
| number 78.2.11 would be encoded as 0x004E020B.]] | |
| The current Glk specification version is 0.7.5, so this selector will | |
| return 0x00000705. | |
| glui32 res; | |
| res = glk_gestalt_ext(gestalt_Version, 0, NULL, 0); | |
| does exactly the same thing. Note that, in either case, the second argument | |
| is not used; so you should always pass 0 to avoid future surprises. | |
| 1.9: Other API Conventions | |
| The glk.h header file is the same on all platforms, with the sole exception | |
| of the typedef of glui32 and glsi32. These will always be defined as 32-bit | |
| unsigned and signed integer types, which may be "long" or "int" or some | |
| other C definition. | |
| Note that all constants are #defines. All functions are currently actual | |
| function declarations (as opposed to macros), but this may change in future | |
| Glk revisions. As in the standard C library, if Glk function is defined by | |
| a macro, an actual function of the same name will also be available. | |
| Functions that return or generate boolean values will produce only 0 | |
| (FALSE) or 1 (TRUE). Functions that accept boolean arguments will accept | |
| any value, with zero indicating FALSE and nonzero indicating TRUE. | |
| NULL (when used in this document) refers to the C null pointer. As stated | |
| above, it is illegal to pass NULL to a function which is expecting a valid | |
| object reference, unless the function definition says otherwise. | |
| Some functions have pointer arguments, acting as "variable" or "reference" | |
| arguments; the function's intent is to return some value in the space | |
| pointed to by the argument. Unless the function says otherwise, it is legal | |
| to pass NULL to indicate that you do not care about that value. | |
| 2: Character Encoding | |
| Glk has two separate, but parallel, APIs for managing text input and | |
| output. The basic functions deals entirely in 8-bit characters; their | |
| arguments are arrays of bytes (octets). These functions all assume the | |
| Latin-1 character encoding. Equivalently, they may be said to use code | |
| points U+00..U+FF of <Unicode>. | |
| Latin-1 is an 8-bit character encoding; it maps numeric codes in the range | |
| 0 to 255 into printed characters. The values from 32 to 126 are the | |
| standard printable ASCII characters (' ' to '~'). Values 0 to 31 and 127 to | |
| 159 are reserved for control characters, and have no printed equivalent. | |
| [[Note that the basic Glk text API does *not* use UTF-8, or any other | |
| Unicode character form. Each character is represented by a single byte -- | |
| even characters in the 128..255 range.]] | |
| The extended, or "Unicode", Glk functions deal entirely in 32-bit words. | |
| They take arrays of words, not bytes, as arguments. They can therefore cope | |
| with any Unicode code point. The extended functions have names ending in | |
| "_uni". | |
| [[Since these functions deal in arrays of 32-bit words, they can be said to | |
| use the UTF-32 character encoding form. (But *not* the UTF-32 character | |
| encoding *scheme* -- that's a stream of bytes which must be interpreted in | |
| big-endian or little-endian mode. Glk Unicode functions operate on long | |
| integers, not bytes.) UTF-32 is also known as UCS-4, according to the | |
| Unicode spec (appendix C.2), modulo some semantic requirements which we | |
| will not deal with here. For practical purposes, we can ignore the whole | |
| encoding issue, and assume that we are dealing with sequences of numeric | |
| code points.]] | |
| [[Why not UTF-8? It is a reasonable bare-bones compression algorithm for | |
| Unicode character streams; but IF systems typically have their own | |
| compression models for text. Compositing the two ideas causes more problems | |
| than it solves. The other advantage of UTF-8 is that 7-bit ASCII is | |
| automatically valid UTF-8; but this is not compelling for IF systems, in | |
| which the compiler can be tasked with generating consistent textual data. | |
| And UTF-8 is a variable-width encoding. Nobody ever wept at the prospect of | |
| avoiding that kettle of eels.]] | |
| [[What about bi-directional text? It's a good idea, and may show up in | |
| future versions of this document. It is not in this version because we want | |
| to get something simple implemented soon. For the moment, print out all | |
| text in reading order (not necessarily left-to-right) and hope for the | |
| best. Current suggestions include a stylehint_Direction, which the game can | |
| set to indicate that text in the given style should be laid out | |
| right-to-left. Top-to-bottom (or bottom-to-top) may be desirable too. The | |
| direction stylehints might only apply to full paragraphs (like | |
| justification stylehints); or they might apply to any text, thus requiring | |
| the library to lay out "zig-zag" blocks. The possibilities remain to be | |
| explored. Page layout is hard.]] | |
| [[Another possibility is to let the library determine the directionality of | |
| text from the character set. This is not impossible -- MacOSX text widgets | |
| do it. It may be too difficult.]] | |
| [[In the meantime, it is worth noting that the Windows Glk library does | |
| *not* autodetect directionality, but the CheapGlk library running on MacOSX | |
| does. Therefore, there is no platform-independent way to handle | |
| right-to-left fonts at present.]] | |
| 2.1: Testing for Unicode Capabilities | |
| 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 following gestalt selectors: | |
| 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. If this is defined, so are all the Unicode functions | |
| and constants. If not, not. | |
| glui32 res; | |
| res = glk_gestalt(gestalt_UnicodeNorm, 0); | |
| This 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. | |
| 2.2: Output | |
| When you are sending text to a window, or to a file open in text mode, you | |
| can print any of the printable Latin-1 characters: 32 to 126, 160 to 255. | |
| You can also print the newline character (linefeed, control-J, decimal 10, | |
| hex 0x0A.) | |
| It is *not* legal to print any other control characters (0 to 9, 11 to 31, | |
| 127 to 159). You may not print even common formatting characters such as | |
| tab (control-I), carriage return (control-M), or page break (control-L). | |
| [[As usual, the behavior of the library when you print an illegal character | |
| is undefined. It is preferable that the library display a numeric code, | |
| such as "\177" or "0x7F", to warn the user that something illegal has | |
| occurred. The library may skip illegal characters entirely; but you should | |
| not rely on this.]] | |
| Printing Unicode characters above 255 is a more complicated matter -- too | |
| complicated to be covered precisely by this specification. Refer to the | |
| Unicode specification, and good luck to you. | |
| [[Unicode combining characters are a particular nuisance. Printing a | |
| combining character may alter the appearance of the previous character | |
| printed. The library should be prepared to cope with this -- even if the | |
| characters are printed by two separate glk_put_char_uni() calls.]] | |
| Note that when you are sending data to a file open in binary mode, you can | |
| print any byte value, without restriction. See section 5.6.3, "File | |
| Streams". | |
| A particular implementation of Glk may not be able to display all the | |
| printable characters. It is guaranteed to be able to display the ASCII | |
| characters (32 to 126, and the newline 10.) Other characters may be printed | |
| correctly, printed as multi-character combinations (such as "ae" for the | |
| one-character "ae" ligature), or printed as some placeholder character | |
| (such as a bullet or question mark, or even an octal code.) | |
| You can test for this by using the gestalt_CharOutput selector. If you set | |
| ch to a character code (Latin-1 or higher), and call | |
| glui32 res, len; | |
| res = glk_gestalt_ext(gestalt_CharOutput, ch, &len, 1); | |
| then res will be one of the following values: | |
| * gestalt_CharOutput_CannotPrint: The character cannot be meaningfully | |
| printed. If you try, the player may see nothing, or may see a placeholder. | |
| * gestalt_CharOutput_ExactPrint: The character will be printed exactly | |
| as defined. | |
| * gestalt_CharOutput_ApproxPrint: The library will print some | |
| approximation of the character. It will be more or less right, but it may | |
| not be precise, and it may not be distinguishable from other, similar | |
| characters. (Examples: "ae" for the one-character "ae" ligature, "e" for an | |
| accented "e", "|" for a broken vertical bar.) | |
| In all cases, len (the glui32 value pointed at by the third argument) will | |
| be the number of actual glyphs which will be used to represent the | |
| character. In the case of gestalt_CharOutput_ExactPrint, this will always | |
| be 1; for gestalt_CharOutput_CannotPrint, it may be 0 (nothing printed) or | |
| higher; for gestalt_CharOutput_ApproxPrint, it may be 1 or higher. This | |
| information may be useful when printing text in a fixed-width font. | |
| [[As described in section 1.9, "Other API Conventions", you may skip this | |
| information by passing NULL as the third argument in glk_gestalt_ext(), or | |
| by calling glk_gestalt() instead.]] | |
| 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 | |
| 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 | |
| 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".]] | |
| 2.3: Line Input | |
| You can request that the player enter a line of text. See section 4.2, | |
| "Line Input Events". | |
| This text will be placed in a buffer of your choice. There is no length | |
| field or null terminator in the buffer. (The length of the text is returned | |
| as part of the line-input event.) | |
| If you use the basic text API, the buffer will contain only printable | |
| Latin-1 characters (32 to 126, 160 to 255). | |
| A particular implementation of Glk may not be able to accept all Latin-1 | |
| printable characters as input. It is guaranteed to be able to accept the | |
| ASCII characters (32 to 126.) | |
| You can test for this by using the gestalt_LineInput selector. If you set | |
| ch to a character code, and call | |
| glui32 res; | |
| res = glk_gestalt(gestalt_LineInput, ch); | |
| then res will be TRUE (1) if that character can be typed by the player in | |
| line input, and FALSE (0) if not. Note that if ch is a nonprintable Latin-1 | |
| character (0 to 31, 127 to 159), then this is guaranteed to return FALSE. | |
| 2.4: Character Input | |
| You can request that the player hit a single key. See section 4.1, | |
| "Character Input Events". | |
| If you use the basic text API, the character code which is returned can be | |
| any value from 0 to 255. The printable character codes have already been | |
| described. The remaining codes are typically control codes: control-A to | |
| control-Z and a few others. | |
| There are also a number of special codes, representing special keyboard | |
| keys, which can be returned from a char-input event. These are represented | |
| as 32-bit integers, starting with 4294967295 (0xFFFFFFFF) and working down. | |
| The special key codes are defined in the glk.h file. They include: | |
| * keycode_Left, keycode_Right, keycode_Up, keycode_Down (arrow keys) | |
| * keycode_Return (return or enter) | |
| * keycode_Delete (delete or backspace) | |
| * keycode_Escape | |
| * keycode_Tab | |
| * keycode_PageUp | |
| * keycode_PageDown | |
| * keycode_Home | |
| * keycode_End | |
| * keycode_Func1, keycode_Func2, keycode_Func3, ... keycode_Func12 | |
| (twelve function keys) | |
| * keycode_Unknown (any key which has no Latin-1 or special code) | |
| Various implementations of Glk will vary widely in which characters the | |
| player can enter. The most obvious limitation is that some characters are | |
| mapped to others. For example, most keyboards return a control-I code when | |
| the tab key is pressed. The Glk library, if it can recognize this at all, | |
| will generate a keycode_Tab event (value 0xFFFFFFF7) when this occurs. | |
| Therefore, for these keyboards, *no* keyboard key will generate a control-I | |
| event (value 9.) The Glk library will probably map many of the control | |
| codes to the other special keycodes. | |
| [[On the other hand, the library may be very clever and discriminate | |
| between tab and control-I. This is legal. The idea is, however, that if | |
| your program asks the player to "press the tab key", you should check for a | |
| keycode_Tab event as opposed to a control-I event.]] | |
| Some characters may not be enterable simply because they do not exist. | |
| [[Not all keyboards have a home or end key. A pen-based platform may not | |
| recognize any control characters at all.]] | |
| Some characters may not be enterable because they are reserved for the | |
| purposes of the interface. For example, the Mac Glk library reserves the | |
| tab key for switching between different Glk windows. Therefore, on the Mac, | |
| the library will never generate a keycode_Tab event *or* a control-I event. | |
| [[Note that the linefeed or control-J character, which is the only | |
| *printable* control character, is probably not *typable*. This is because, | |
| in most libraries, it will be converted to keycode_Return. Again, you | |
| should check for keycode_Return if your program asks the player to "press | |
| the return key".]] | |
| [[The delete and backspace keys are merged into a single keycode because | |
| they have such an astonishing history of being confused in the first | |
| place... this spec formally waives any desire to define the difference. Of | |
| course, a library is free to distinguish delete and backspace during line | |
| input. This is when it matters most; conflating the two during character | |
| input should not be a large problem.]] | |
| You can test for this by using the gestalt_CharInput selector. If you set | |
| ch to a character code, or a special code (from 0xFFFFFFFF down), and call | |
| glui32 res; | |
| res = glk_gestalt(gestalt_CharInput, ch); | |
| then res will be TRUE (1) if that character can be typed by the player in | |
| character input, and FALSE (0) if not. | |
| [[Glk porters take note: it is not a goal to be able to generate every | |
| single possible key event. If the library says that it can generate a | |
| particular keycode, then game programmers will assume that it is available, | |
| *and ask players to use it.* If a keycode_Home event can only be generated | |
| by typing escape-control-A, and the player does not know this, the player | |
| will be lost when the game says "Press the home key to see the next hint." | |
| It is better for the library to say that it *cannot* generate a | |
| keycode_Home event; that way the game can detect the situation and ask the | |
| user to type H instead.]] | |
| [[Of course, it is better not to rely on obscure keys in any case. The | |
| arrow keys and return are nearly certain to be available; the others are of | |
| gradually decreasing reliability, and you (the game programmer) should not | |
| depend on them. You *must* be certain to check for the ones you want to | |
| use, *including* the arrow keys and return, and be prepared to use | |
| different keys in your interface if gestalt_CharInput says they are not | |
| available.]] | |
| 2.5: Upper and Lower Case | |
| You can convert Latin-1 characters between upper and lower case with two | |
| Glk utility functions: | |
| unsigned char glk_char_to_lower(unsigned char ch); | |
| unsigned char glk_char_to_upper(unsigned char ch); | |
| These have a few advantages over the standard ANSI tolower() and toupper() | |
| macros. They work for the entire Latin-1 character set, including accented | |
| letters; they behave consistently on all platforms, since they're part of | |
| the Glk library; and they are safe for all characters. That is, if you call | |
| glk_char_to_lower() on a lower-case character, or a character which is not | |
| a letter, you'll get the argument back unchanged. | |
| The case-sensitive characters in Latin-1 are the ranges 0x41..0x5A, | |
| 0xC0..0xD6, 0xD8..0xDE (upper case) and the ranges 0x61..0x7A, 0xE0..0xF6, | |
| 0xF8..0xFE (lower case). These are arranged in parallel; so | |
| glk_char_to_lower() will add 0x20 to values in the upper-case ranges, and | |
| glk_char_to_upper() will subtract 0x20 from values in the lower-case ranges. | |
| Unicode character conversion is trickier, and must be applied to character | |
| arrays, not single characters. | |
| glui32 glk_buffer_to_lower_case_uni(glui32 *buf, glui32 len, glui32 | |
| numchars); | |
| glui32 glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len, glui32 | |
| numchars); | |
| glui32 glk_buffer_to_title_case_uni(glui32 *buf, glui32 len, glui32 | |
| numchars, glui32 lowerrest); | |
| These functions provide two length arguments because a string of Unicode | |
| characters may expand when its case changes. The len argument is the | |
| available length of the buffer; numchars is the number of characters in the | |
| buffer initially. (So numchars must be less than or equal to len. The | |
| contents of the buffer after numchars do not affect the operation.) | |
| The functions return the number of characters after conversion. If this is | |
| greater than len, the characters in the array will be safely truncated at | |
| len, but the true count will be returned. (The contents of the buffer after | |
| the returned count are undefined.) | |
| The lower_case and upper_case functions do what you'd expect: they convert | |
| every character in the buffer (the first numchars of them) to its upper or | |
| lower-case equivalent, if there is such a thing. | |
| The title_case function has an additional (boolean) flag. If the flag is | |
| zero, the function changes the first character of the buffer to upper-case, | |
| and leaves the rest of the buffer unchanged. If the flag is nonzero, it | |
| changes the first character to upper-case and the rest to lower-case. | |
| See the Unicode spec (chapter 3.13, chapter 4.2, etc) for the exact | |
| definitions of upper, lower, and title-case mapping. | |
| [[Unicode has some strange case cases. For example, a combined character | |
| that looks like "ss" might properly be upper-cased into *two* "S" | |
| characters. Title-casing is even stranger; "ss" (at the beginning of a | |
| word) might be title-cased into a different combined character that looks | |
| like "Ss". The glk_buffer_to_title_case_uni() function is actually | |
| title-casing the first character of the buffer. If it makes a difference.]] | |
| [[Earlier drafts of this spec had a separate function which title-cased the | |
| first character of every *word* in the buffer. I took this out after | |
| reading Unicode Standard Annex #29, which explains how to divide a string | |
| into words. If you want it, feel free to implement it.]] | |
| 2.6: Unicode String Normalization | |
| Comparing Unicode strings is difficult, because there can be several ways | |
| to represent a piece of text as a Unicode string. For example, the | |
| one-character string "e" (an accented "e") will be displayed the same as | |
| the two-character string containing "e" followed by Unicode character | |
| 0x0300 (COMBINING GRAVE ACCENT). These strings should be considered equal. | |
| Therefore, a Glk program that accepts line input should convert its text to | |
| a normalized form before parsing it. These functions offer those | |
| conversions. The algorithms are defined by the Unicode spec (chapter 3.7) | |
| and <Unicode Standard Annex #15>. | |
| glui32 glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len, glui32 | |
| numchars); | |
| This transforms a string into its canonical decomposition ("Normalization | |
| Form D"). Effectively, this takes apart multipart characters into their | |
| individual parts. For example, it would convert "e" (character 0xE8, an | |
| accented "e") into the two-character string containing "e" followed by | |
| Unicode character 0x0300 (COMBINING GRAVE ACCENT). If a single character | |
| has multiple accent marks, they are also rearranged into a standard order. | |
| glui32 glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len, glui32 | |
| numchars); | |
| This transforms a string into its canonical decomposition and recomposition | |
| ("Normalization Form C"). Effectively, this takes apart multipart | |
| characters, and then puts them back together in a standard way. For | |
| example, this would convert the two-character string containing "e" | |
| followed by Unicode character 0x0300 (COMBINING GRAVE ACCENT) into the | |
| one-character string "e" (character 0xE8, an accented "e"). | |
| The canon_normalize function includes decomposition as part of its | |
| implementation. You never have to call both functions on the same string. | |
| Both of these functions are idempotent. | |
| These functions provide two length arguments because a string of Unicode | |
| characters may expand when it is transformed. The len argument is the | |
| available length of the buffer; numchars is the number of characters in the | |
| buffer initially. (So numchars must be less than or equal to len. The | |
| contents of the buffer after numchars do not affect the operation.) | |
| The functions return the number of characters after transformation. If this | |
| is greater than len, the characters in the array will be safely truncated | |
| at len, but the true count will be returned. (The contents of the buffer | |
| after the returned count are undefined.) | |
| [[The Unicode spec also defines stronger forms of these functions, called | |
| "compatibility decomposition and recomposition" ("Normalization Form KD" | |
| and "Normalization Form KC".) These do all of the accent-mangling described | |
| above, but they also transform many other obscure Unicode characters into | |
| more familiar forms. For example, they split ligatures apart into separate | |
| letters. They also convert Unicode display variations such as script | |
| letters, circled letters, and half-width letters into their common forms.]] | |
| [[The Glk spec does not currently provide these stronger transformations. | |
| Glk's expected use of Unicode normalization is for line input, and an OS | |
| facility for line input will generally not produce these alternate | |
| character forms (unless the user goes out of his way to type them). | |
| Therefore, the need for these transformations does not seem to be worth the | |
| extra data table space.]] | |
| 2.6.1: A Note on Unicode Case-Folding and Normalization | |
| With all of these Unicode transformations hovering about, an author might | |
| reasonably ask about the right way to handle line input. Our recommendation | |
| is: call glk_buffer_to_lower_case_uni(), followed by | |
| glk_buffer_canon_normalize_uni(), and then parse the result. The parsing | |
| process should of course match against strings that have been put through | |
| the same process. | |
| The Unicode spec (chapter 3.13) gives a different, three-step process: | |
| decomposition, case-folding, and decomposition again. Our recommendation | |
| comes through a series of practical compromises: | |
| * The initial decomposition is only necessary because of a historical | |
| error in the Unicode spec: character 0x0345 (COMBINING GREEK YPOGEGRAMMENI) | |
| behaves inconsistently. We ignore this case, and skip this step. | |
| * Case-folding is a slightly different operation from lower-casing. | |
| (Case-folding splits some combined characters, so that, for example, "ss" | |
| can match both "ss" and "SS".) However, Glk does not currently offer a | |
| case-folding function. We substitute glk_buffer_to_lower_case_uni(). | |
| * I'm not sure why the spec recommends decomposition | |
| (glk_buffer_canon_decompose_uni()) rather than | |
| glk_buffer_canon_normalize_uni(). However, composed characters are the norm | |
| in source code, and therefore in compiled Inform game files. If we | |
| specified decomposition, the compiler would have to do extra work; also, | |
| the standard Inform dictionary table (with its fixed word length) would | |
| store fewer useful characters. Therefore, we substitute | |
| glk_buffer_canon_normalize_uni(). | |
| [[We may revisit these recommendations in future versions of the spec.]] | |
| 3: Windows | |
| On most platforms, the program/library combination will appear to the | |
| player in a window -- either a window which covers the entire screen, or | |
| one which shares screen space with other windows in a multi-programming | |
| environment. Obviously your program does not have worry about the details | |
| of this. The Glk screen space is a rectangle, which you can divide into | |
| panels for various purposes. It is these panels which I will refer to as | |
| "windows" hereafter. | |
| You refer to a window using an opaque C structure pointer. See section 1.6, | |
| "Opaque Objects". | |
| A window has a type. Currently there are four window types: | |
| * Text buffer windows: A stream of text. [[The "story window" of an | |
| Infocom game.]] You can only print at the end of the stream, and input a | |
| line of text at the end of the stream. | |
| * Text grid windows: A grid of characters in a fixed-width font. [[The | |
| "status window" of an Infocom game.]] You can print anywhere in the grid. | |
| * Graphics windows: A grid of colored pixels. Graphics windows do not | |
| support text output, but there are image commands to draw in them. Graphics | |
| windows can accept character (keystroke) input, but not line input. [[This | |
| is an optional capability; not all Glk libraries support graphics. See | |
| section 7.4, "Testing for Graphics Capabilities".]] | |
| * Blank windows: A blank window. Blank windows support neither input | |
| nor output. [[They exist mostly to be an example of a "generic" window. You | |
| are unlikely to want to use them.]] | |
| As Glk is an expanding system, more window types may be added in the | |
| future. Therefore, it is important to remember that not all window types | |
| will necessarily be available under all Glk libraries. | |
| There is one other special type of window, the pair window. Pair windows | |
| are created by Glk as part of the system of window arrangement. You cannot | |
| create them yourself. See section 3.5.2, "Pair Windows". | |
| Every window has a rock. This is a value you provide when the window is | |
| created; you can use it however you want. See section 1.6.1, "Rocks". | |
| When Glk starts up, there are no windows. | |
| [[When I say there are no windows, I mean there are no Glk windows. In a | |
| multiprogramming environment, such as X or MacOS, there may be an | |
| application window visible; this is the screen space that will contain all | |
| the Glk windows that you create. But at first, this screen space is empty | |
| and unused.]] | |
| Without a window, you cannot do any kind of input or output; so the first | |
| thing you'll want to do is create one. See section 3.2, "Window Opening, | |
| Closing, and Constraints". | |
| You can create as many windows as you want, of any types. You control their | |
| arrangement and sizes through a fairly flexible system of calls. See | |
| section 3.1, "Window Arrangement". | |
| You can close any windows you want. You can even close all the windows, | |
| which returns you to the original startup state. | |
| You can request input from any or all windows. Input can be mouse input (on | |
| platforms which support a mouse), single-character input, or input of an | |
| entire line of text. It is legal to request input from several windows at | |
| the same time. The library will have some interface mechanism for the | |
| player to control which window he is typing in. | |
| 3.1: Window Arrangement | |
| The Way of Window Arrangement is fairly complicated. I'll try to explain it | |
| coherently. [[If you are reading this document to get an overview of Glk, | |
| by all means skip forward to section 3.5, "The Types of Windows". Come back | |
| here later.]] | |
| Originally, there are no windows. You can create a window, which will take | |
| up the entire available screen area. You can then split this window in two. | |
| One of the halves is the original window; the other half is new, and can be | |
| of any type you want. You can control whether the new window is left, | |
| right, above, or below the original one. You can also control how the split | |
| occurs. It can be 50-50, or 70-30, or any other percentage split. Or, you | |
| can give a fixed width to the new window, and allow the old one to take up | |
| the rest of the available space. Or you can give a fixed width to the *old* | |
| window, and let the *new* one take up the rest of the space. | |
| Now you have two windows. In exactly the same way, you can split either of | |
| them -- the original window, or the one you just created. Whichever one you | |
| split becomes two, which together take up the same space that the one did | |
| before. | |
| You can repeat this as often as you want. Every time you split a window, | |
| one new window is created. Therefore, the call that does this is called | |
| glk_window_open(). [[It might have been less confusing to call it | |
| "glk_split_window" -- or it might have been more confusing. I picked one.]] | |
| It is important to remember that the order of splitting matters. If you | |
| split twice times, you don't have a trio of windows; you have a pair with | |
| another pair on one side. Mathematically, the window structure is a binary | |
| tree. | |
| Example time. Say you do two splits, each a 50-50 percentage split. You | |
| start with the original window A, and split that into A and B; then you | |
| split B into B and C. | |
| +---------+ | |
| | | O | |
| | A | / \ | |
| | | A O | |
| +---------+ / \ | |
| | B | B C | |
| +---------+ | |
| | C | | |
| +---------+ | |
| Or, you could split A into A and B, and then split A again into A and C. | |
| +---------+ | |
| | A | O | |
| +---------+ / \ | |
| | C | O B | |
| +---------+ / \ | |
| | | A C | |
| | B | | |
| | | | |
| +---------+ | |
| I'm using the simplest possible splits in the examples above. Every split | |
| is 50-50, and the new window of the pair is always *below* the original one | |
| (the one that gets split.) You can get fancier than that. Here are three | |
| more ways to perform the first example; all of them have the *same* tree | |
| structure, but look different on the screen. | |
| +---------+ +---------+ +---------+ | |
| | | | A | | | O | |
| | A | +---------+ | A | / \ | |
| | | | B | | | A O | |
| +---------+ +---------+ +----+----+ / \ | |
| | C | | | | | | B C | |
| +---------+ | C | | C | B | | |
| | B | | | | | | | |
| +---------+ +---------+ +----+----+ | |
| On the left, we turn the second split (B into B/C) upside down; we put the | |
| new window (C) 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: | |
| * winmethod_Above, winmethod_Below, winmethod_Left, winmethod_Right: | |
| The new window will be above, below, to the left, or to the right of the | |
| old one which was split. | |
| * winmethod_Fixed, winmethod_Proportional: The new window is a fixed | |
| size, or a given proportion of the old window's size. (See below.) | |
| * winmethod_Border, winmethod_NoBorder: There should or should not be a | |
| visible window border between the new window and its sibling. (This is a | |
| hint to the library; you might specify NoBorder between two graphics | |
| windows that should form a single image.) | |
| 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. | |
| * Text buffer: This may do any number of things, such as delete all | |
| text in the window, or print enough blank lines to scroll all text beyond | |
| visibility, or insert a page-break marker which is treated specially by the | |
| display part of the library. | |
| * Text grid: This will clear the window, filling all positions with | |
| blanks (in the normal style). The window cursor is moved to the top left | |
| corner (position 0,0). | |
| * Graphics: Clears the entire window to its current background color. | |
| See section 3.5.5, "Graphics Windows". | |
| * Other window types: No effect. | |
| It is illegal to erase a window which has line input pending. | |
| strid_t glk_window_get_stream(winid_t win); | |
| This returns the stream which is associated with the window. (See section | |
| 5.6.1, "Window Streams".) Every window has a stream which can be printed | |
| to, but this may not be useful, depending on the window type. [[For | |
| example, printing to a blank window's stream has no effect.]] | |
| void glk_set_window(winid_t win); | |
| This sets the current stream to the window's stream. 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". | |
| 4: Events | |
| As described above, all player input is handed to your program by the | |
| glk_select() call, in the form of events. You should write at least one | |
| event loop to retrieve these events. | |
| void glk_select(event_t *event); | |
| typedef struct event_struct { | |
| glui32 type; | |
| winid_t win; | |
| glui32 val1, val2; | |
| } event_t; | |
| This causes the program to wait for an event, and then store it in the | |
| structure pointed to by the argument. Unlike most Glk functions that take | |
| pointers, the argument of glk_select() may not be NULL. | |
| Most of the time, you only get the events that you request. However, there | |
| are some events which can arrive at any time. This is why you must always | |
| call glk_select() in a loop, and continue the loop until you get the event | |
| you really want. | |
| The event structure is self-explanatory. type is the event type. The window | |
| that spawned the event, if relevant, is in win. The remaining fields | |
| contain more information specific to the event. | |
| The event types are: | |
| * evtype_None: No event. This is a placeholder, and glk_select() never | |
| returns it. | |
| * evtype_Timer: An event that repeats at fixed intervals. | |
| * evtype_CharInput: A keystroke event in a window. | |
| * evtype_LineInput: A full line of input completed in a window. | |
| * evtype_MouseInput: A mouse click in a window. | |
| * evtype_Arrange: An event signalling that the sizes of some windows | |
| have changed. | |
| * evtype_Redraw: An event signalling that graphics windows must be | |
| redrawn. | |
| * evtype_SoundNotify: The completion of a sound being played in a sound | |
| channel. | |
| * evtype_Hyperlink: The selection of a hyperlink in a window. | |
| * evtype_VolumeNotify: The completion of a gradual volume change in a | |
| sound channel. | |
| Note that evtype_None is zero, and the other values are positive. Negative | |
| event types (0x80000000 to 0xFFFFFFFF) are reserved for | |
| implementation-defined events. | |
| You can also inquire if an event is available, without stopping to wait for | |
| one to occur. | |
| void glk_select_poll(event_t *event); | |
| This checks if an internally-spawned event is available. If so, it stores | |
| it in the structure pointed to by event. If not, it sets event->type to | |
| evtype_None. Either way, it returns almost immediately. | |
| The first question you now ask is, what is an internally-spawned event? | |
| glk_select_poll() does *not* check for or return evtype_CharInput, | |
| evtype_LineInput, or evtype_MouseInput events. It is intended for you to | |
| test conditions which may have occurred while you are computing, and not | |
| interfacing with the player. For example, time may pass during slow | |
| computations; you can use glk_select_poll() to see if a evtype_Timer event | |
| has occured. (See section 4.4, "Timer Events".) | |
| At the moment, glk_select_poll() checks for evtype_Timer, and possibly | |
| evtype_Arrange and evtype_SoundNotify events. But see section 4.9, "Other | |
| Events". | |
| The second question is, what does it mean that glk_select_poll() returns | |
| "almost immediately"? In some Glk libraries, text that you send to a window | |
| is buffered; it does not actually appear until you request player input | |
| with glk_select(). glk_select_poll() attends to this buffer-flushing task | |
| in the same way. (Although it does not do the "Hit any key to scroll down" | |
| waiting which may be done in glk_select(); that's a player-input task.) | |
| Similarly, on multitasking platforms, glk_select() may yield time to other | |
| processes; and glk_select_poll() does this as well. | |
| The upshot of this is that you should not call glk_select_poll() very | |
| often. If you are not doing much work between player inputs, you should not | |
| need to call it at all. [[For example, in a virtual machine interpreter, | |
| you should not call glk_select_poll() after every opcode.]] However, if you | |
| are doing intense computation, you may wish to call glk_select_poll() every | |
| so often to yield time to other processes. And if you are printing | |
| intermediate results during this computation, you should glk_select_poll() | |
| every so often, so that you can be certain your output will be displayed | |
| before the next glk_select(). | |
| [[However, you should call glk_tick() often -- once per opcode in a VM | |
| interpreter. See section 1.4, "The Tick Thing".]] | |
| 4.1: Character Input Events | |
| You can request character input from text buffer, text grid, and graphics | |
| windows. There are separate functions for requesting the availability of | |
| particular Latin-1 and Unicode characters; see section 2.1, "Testing for | |
| Unicode Capabilities". To test whether graphics windows support character | |
| input, use the gestalt_GraphicsCharInput selector. | |
| void glk_request_char_event(winid_t win); | |
| Request input of a Latin-1 character or special key. A window cannot have | |
| requests for both character and line input at the same time. Nor can it | |
| have requests for character input of both types (Latin-1 and Unicode). It | |
| is illegal to call glk_request_char_event() if the window already has a | |
| pending request for either character or line input. | |
| void glk_request_char_event_uni(winid_t win); | |
| Request input of a Unicode character or special key. | |
| void glk_cancel_char_event(winid_t win); | |
| This cancels a pending request for character input. (Either Latin-1 or | |
| Unicode.) For convenience, it is legal to call glk_cancel_char_event() even | |
| if there is no character input request on that window. Glk will ignore the | |
| call in this case. | |
| If a window has a pending request for character input, and the player hits | |
| a key in that window, glk_select() will return an event whose type is | |
| evtype_CharInput. Once this happens, the request is complete; it is no | |
| longer pending. You must call glk_request_char_event() or | |
| glk_request_char_event_uni() if you want another character from that window. | |
| In the event structure, win tells what window the event came from. val1 | |
| tells what character was entered; this will be a character code, or a | |
| special keycode. (See section 2.4, "Character Input".) If you called | |
| glk_request_char_event(), val1 will be in 0..255, or else a special | |
| keycode. In any case, val2 will be 0. | |
| 4.2: Line Input Events | |
| You can request line input from text buffer and text grid windows. There | |
| are separate functions for requesting the availability of particular | |
| Latin-1 and Unicode characters; see section 2.1, "Testing for Unicode | |
| Capabilities". | |
| void glk_request_line_event(winid_t win, char *buf, glui32 maxlen, | |
| glui32 initlen); | |
| Request input of a line of Latin-1 characters. A window cannot have | |
| requests for both character and line input at the same time. Nor can it | |
| have requests for line input of both types (Latin-1 and Unicode). It is | |
| illegal to call glk_request_line_event() if the window already has a | |
| pending request for either character or line input. | |
| The buf argument is a pointer to space where the line input will be stored. | |
| (This may not be NULL.) maxlen is the length of this space, in bytes; the | |
| library will not accept more characters than this. If initlen is nonzero, | |
| then the first initlen bytes of buf will be entered as pre-existing input | |
| -- just as if the player had typed them himself. [[The player can continue | |
| composing after this pre-entered input, or delete it or edit as usual.]] | |
| The contents of the buffer are undefined until the input is completed | |
| (either by a line input event, or glk_cancel_line_event(). The library may | |
| or may not fill in the buffer as the player composes, while the input is | |
| still pending; it is illegal to change the contents of the buffer yourself. | |
| void glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 | |
| maxlen, glui32 initlen); | |
| Request input of a line of Unicode characters. This works the same as | |
| glk_request_line_event(), except the result is stored in an array of glui32 | |
| values instead of an array of characters, and the values may be any valid | |
| Unicode code points. | |
| If possible, the library should return fully composed Unicode characters, | |
| rather than strings of base and composition characters. | |
| [[Fully-composed characters are the norm for Unicode text, so an | |
| implementation that ignores this issue will probably produce the right | |
| result. However, the game may not want to rely on that. Another factor is | |
| that case-folding can (occasionally) produce non-normalized text. | |
| Therefore, to cover all its bases, a game should call | |
| glk_buffer_to_lower_case_uni(), followed by | |
| glk_buffer_canon_normalize_uni(), before parsing.]] | |
| [[Earlier versions of this spec said that line input must always be in | |
| Unicode Normalization Form C. However, this has not been universally | |
| implemented. It is also somewhat redundant, for the results noted above. | |
| Therefore, we now merely recommend that line input be fully composed. The | |
| game is ultimately responsible for all case-folding and normalization. See | |
| section 2.6, "Unicode String Normalization".]] | |
| void glk_cancel_line_event(winid_t win, event_t *event); | |
| This cancels a pending request for line input. (Either Latin-1 or Unicode.) | |
| The event pointed to by the event argument will be filled in as if the | |
| player had hit enter, and the input composed so far will be stored in the | |
| buffer; see below. If you do not care about this information, pass NULL as | |
| the event argument. (The buffer will still be filled.) | |
| For convenience, it is legal to call glk_cancel_line_event() even if there | |
| is no line input request on that window. The event type will be set to | |
| evtype_None in this case. | |
| void glk_set_echo_line_event(winid_t win, glui32 val); | |
| Normally, after line input is completed or cancelled in a buffer window, | |
| the library ensures that the complete input line (or its latest state, | |
| after cancelling) is displayed at the end of the buffer, followed by a | |
| newline. This call allows you to suppress this behavior. If the val | |
| argument is zero, all *subsequent* line input requests in the given window | |
| will leave the buffer unchanged after the input is completed or cancelled; | |
| the player's input will not be printed. If val is nonzero, subsequent input | |
| requests will have the normal printing behavior. | |
| [[Note that this feature is unrelated to the window's echo stream.]] | |
| res = glk_gestalt(gestalt_LineInputEcho, 0); | |
| Not all libraries support this feature. 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*.]] | |
| If you turn off line input echoing, you can reproduce the standard input | |
| behavior by following each line input event (or line input cancellation) by | |
| printing the input line, in the Input style, followed by a newline in the | |
| original style. | |
| The glk_set_echo_line_event() does not affect a pending line input request. | |
| It also has no effect in non-buffer windows. [[In a grid window, the game | |
| can overwrite the input area at will, so there is no need for this | |
| distinction.]] | |
| void glk_set_terminators_line_event(winid_t win, glui32 *keycodes, | |
| glui32 count); | |
| If a window has a pending request for line input, the player can generally | |
| hit the enter key (in that window) to complete line input. The details will | |
| depend on the platform's native user interface. | |
| It is possible to request that other keystrokes complete line input as | |
| well. (This allows a game to intercept function keys or other special keys | |
| during line input.) To do this, call glk_set_terminators_line_event(), and | |
| pass an array of count keycodes. These must all be special keycodes (see | |
| section 2.4, "Character Input"). Do not include regular printable | |
| characters in the array, nor keycode_Return (which represents the default | |
| enter key and will always be recognized). To return to the default | |
| behavior, pass a NULL or empty array. | |
| The glk_set_terminators_line_event() affects *subsequent* line input | |
| requests in the given window. It does not affect a pending line input | |
| request. [[This distinction makes life easier for interpreters that set up | |
| UI callbacks only at the start of input.]] | |
| A library may not support this feature; if it does, it may not support all | |
| special keys as terminators. (Some keystrokes are reserved for OS or | |
| interpreter control.) | |
| res = glk_gestalt(gestalt_LineTerminators, 0); | |
| This returns 1 if glk_set_terminators_line_event() is supported, and 0 if | |
| it is not. | |
| 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. | |
| When line input is completed, glk_select() will return an event whose type | |
| is evtype_LineInput. Once this happens, the request is complete; it is no | |
| longer pending. You must call glk_request_line_event() if you want another | |
| line of text from that window. | |
| In the event structure, win tells what window the event came from. val1 | |
| tells how many characters were entered. val2 will be 0 unless input was | |
| ended by a special terminator key, in which case val2 will be the keycode | |
| (one of the values passed to glk_set_terminators_line_event()). | |
| The characters themselves are stored in the buffer specified in the | |
| original glk_request_line_event() or glk_request_line_event_uni() call. | |
| [[There is no null terminator or newline stored in the buffer.]] | |
| It is illegal to print anything to a window which has line input pending. | |
| [[This is because the window may be displaying and editing the player's | |
| input, and printing anything would make life unnecessarily complicated for | |
| the library.]] | |
| 4.3: Mouse Input Events | |
| On some platforms, Glk can recognize when the mouse (or other pointer) is | |
| used to select a spot in a window. You can request mouse input only in text | |
| grid windows and graphics windows. | |
| void glk_request_mouse_event(winid_t win); | |
| void glk_cancel_mouse_event(winid_t win); | |
| A window can have mouse input and character/line input pending at the same | |
| time. | |
| If the player clicks in a window which has a mouse input event pending, | |
| glk_select() will return an event whose type is evtype_MouseInput. Again, | |
| once this happens, the request is complete, and you must request another if | |
| you want further mouse input. | |
| In the event structure, win tells what window the event came from. | |
| In a text grid window, the val1 and val2 fields are the x and y coordinates | |
| of the character that was clicked on. [[So val1 is the column, and val2 is | |
| the row.]] The top leftmost character is considered to be (0,0). | |
| In a graphics window, they are the x and y coordinates of the pixel that | |
| was clicked on. Again, the top left corner of the window is (0,0). | |
| You can test whether mouse input is supported with the gestalt_MouseInput | |
| selector. | |
| res = glk_gestalt(gestalt_MouseInput, windowtype); | |
| This will return TRUE (1) if windows of the given type support mouse input. | |
| If this returns FALSE (0), it is still legal to call | |
| glk_request_mouse_event(), but it will have no effect, and you will never | |
| get mouse events. | |
| [[Most mouse-based idioms define standard functions for mouse hits in text | |
| windows -- typically selecting or copying text. It is up to the library to | |
| separate this from Glk mouse input. The library may choose to select text | |
| when it is clicked normally, and cause Glk mouse events when text is | |
| control-clicked. Or the other way around. Or it may be the difference | |
| between clicking and double-clicking. Or the library may reserve a | |
| particular mouse button, on a multi-button mouse. It may even specify a | |
| keyboard key to be the "mouse button", referring to wherever the mouse | |
| cursor is when the key is hit. Or some even more esoteric positioning | |
| system. You need only know that the user can do it, or not.]] | |
| [[However, since different platforms will handle this issue differently, | |
| you should be careful how you instruct the player in your program. Do not | |
| tell the player to "double-click", "right-click", or "control-click" in a | |
| window. The preferred term is "to touch the window", or a spot in the | |
| window.]] [[Goofy, but preferred.]] | |
| 4.4: Timer Events | |
| You can request that an event be sent at fixed intervals, regardless of | |
| what the player does. Unlike input events, timer events can be tested for | |
| with glk_select_poll() as well as glk_select(). | |
| void glk_request_timer_events(glui32 millisecs); | |
| It is possible that the library does not support timer events. You can | |
| check this with the gestalt_Timer selector. | |
| res = glk_gestalt(gestalt_Timer, 0); | |
| This returns TRUE (1) if timer events are supported, and FALSE (0) if they | |
| are not. | |
| Initially, there is no timer and you get no timer events. If you call | |
| glk_request_timer_events(N), with N not 0, you will get timer events about | |
| every N milliseconds thereafter. (Assuming that they are supported -- if | |
| not, glk_request_timer_events() has no effect.) Unlike keyboard and mouse | |
| events, timer events will continue until you shut them off. You do not have | |
| to re-request them every time you get one. Call glk_request_timer_events(0) | |
| to stop getting timer events. | |
| The rule is that when you call glk_select() or glk_select_poll(), if it has | |
| been more than N milliseconds since the last timer event, and (for | |
| glk_select()) if there is no player input, you will receive an event whose | |
| type is evtype_Timer. (win, val1, and val2 will all be 0.) | |
| Timer events do not stack up. If you spend 10N milliseconds doing | |
| computation, and then call glk_select(), you will not get ten timer events | |
| in a row. The library will simply note that it has been more than N | |
| milliseconds, and return a timer event right away. If you call glk_select() | |
| again immediately, it will be N milliseconds before the next timer event. | |
| This means that the timing of timer events is approximate, and the library | |
| will err on the side of being late. If there is a conflict between player | |
| input events and timer events, the player input takes precedence. [[This | |
| prevents the user from being locked out by overly enthusiastic timer | |
| events. Unfortunately, it also means that your timer can be locked out on | |
| slower machines, if the player pounds too enthusiastically on the keyboard. | |
| Sorry.]] | |
| [[I don't have to tell you that a millisecond is one thousandth of a | |
| second, do I?]] | |
| 4.5: Window Arrangement Events | |
| Some platforms allow the player to resize the Glk window during play. This | |
| will naturally change the sizes of your windows. If this occurs, then | |
| immediately *after* all the rearrangement, glk_select() will return an | |
| event whose type is evtype_Arrange. You can use this notification to | |
| redisplay the contents of a graphics or text grid window whose size has | |
| changed. [[The display of a text buffer window is entirely up to the | |
| library, so you don't need to worry about those.]] | |
| In the event structure, win will be NULL if all windows are affected. If | |
| only some windows are affected, win will refer to a window which contains | |
| all the affected windows. [[You can always play it safe, ignore win, and | |
| redraw every graphics and text grid window.]] val1 and val2 will be 0. | |
| An arrangement event is guaranteed to occur whenever the player causes any | |
| window to change size, as measured by its own metric. [[Size changes caused | |
| by you -- for example, if you open, close, or resize a window -- do not | |
| trigger arrangement events. You must be aware of the effects of your window | |
| management, and redraw the windows that you affect.]] | |
| [[It is possible that several different player actions can cause windows to | |
| change size. For example, if the player changes the screen resolution, an | |
| arrangement event might be triggered. This might also happen if the player | |
| changes his display font to a different size; the windows would then be | |
| different "sizes" in the metric of rows and columns, which is the important | |
| metric and the only one you have access to.]] | |
| Arrangement events, like timer events, can be returned by | |
| glk_select_poll(). But this will not occur on all platforms. You must be | |
| ready to receive an arrangement event when you call glk_select_poll(), but | |
| it is possible that it will not arrive until the next time you call | |
| glk_select(). [[This is because on some platforms, window resizing is | |
| handled as part of player input; on others, it can be triggered by an | |
| external process such as a window manager.]] | |
| 4.6: Window Redrawing Events | |
| On platforms that support graphics, it is possible that the contents of a | |
| graphics window will be lost, and have to be redrawn from scratch. If this | |
| occurs, then glk_select() will return an event whose type is evtype_Redraw. | |
| In the event structure, win will be NULL if all windows are affected. If | |
| only some windows are affected, win will refer to a window which contains | |
| all the affected windows. [[You can always play it safe, ignore win, and | |
| redraw every graphics window.]] val1 and val2 will be 0. | |
| Affected windows are already cleared to their background color when you | |
| receive the redraw event. | |
| Redraw events can be returned by glk_select_poll(). But, like arrangement | |
| events, this is platform-dependent. See section 4.5, "Window Arrangement | |
| Events". | |
| For more about redraw events and how they affect graphics windows, see | |
| section 3.5.5, "Graphics Windows". | |
| 4.7: Sound Notification Events | |
| On platforms that support sound, you can request to receive an | |
| evtype_SoundNotify event when a sound finishes playing. You can also | |
| request to receive an evtype_VolumeNotify event when a gradual volume | |
| change completes. See section 8.3, "Playing Sounds". | |
| 4.8: Hyperlink Events | |
| On platforms that support hyperlinks, you can request to receive an | |
| evtype_Hyperlink event when the player selects a link. See section 9.2, | |
| "Accepting Hyperlink Events". | |
| 4.9: Other Events | |
| There are currently no other event types defined by Glk. (The "evtype_None" | |
| constant is a placeholder, and is never returned by glk_select().) | |
| It is possible that new event types will be defined in the future. [[For | |
| example, if video or animation capabilities are added to Glk, there would | |
| probably be some sort of completion event for them.]] | |
| [[This is also why you must put calls to glk_select() in loops. If you | |
| tried to read a character by simply writing | |
| glk_request_char_event(win); | |
| glk_select(&ev); | |
| you might not get a CharInput event back. You could get some | |
| not-yet-defined event which happened to occur before the player hit a key. | |
| Or, for that matter, a window arrangement event.]] | |
| [[It is not generally necessary to put a call to glk_select_poll() in a | |
| loop. You usually call glk_select_poll() to update the display or test if | |
| an event is available, not to wait for a particular event. However, if you | |
| are using sound notification events, and several sounds are playing, it | |
| might be important to make sure you knew about all sounds completed at any | |
| particular time. You would do this with | |
| glk_select_poll(&ev); | |
| while (ev.type != evtype_None) { | |
| /* handle event */ | |
| glk_select_poll(&ev); | |
| } | |
| Once glk_select_poll() returns evtype_None, you should *not* call it again | |
| immediately. Do some work first. If you want to wait for an event, use | |
| glk_select(), not a loop of glk_select_poll().]] | |
| 5: Streams | |
| All character output in Glk is done through streams. Every window has an | |
| output stream associated with it. You can also write to files on disk; | |
| every open file is represented by an output stream as well. | |
| There are also input streams; these are used for reading from files on | |
| disk. It is possible for a stream to be both an input and an output stream. | |
| [[Player input is done through line and character input events, not | |
| streams. This is a small inelegance in theory. In practice, player input is | |
| slow and things can interrupt it, whereas file input is immediate. If a | |
| network extension to Glk were proposed, it would probably use events and | |
| not streams, since network communication is not immediate.]] | |
| It is also possible to create a stream that reads or writes to a buffer in | |
| memory. | |
| Finally, there may be platform-specific types of streams, which are created | |
| before your program starts running. [[For example, a program running under | |
| Unix may have access to standard input as a stream, even though there is no | |
| Glk call to explicitly open standard input. On the Mac, data in a Mac | |
| resource may be available through a resource-reading stream.]] You do not | |
| need to worry about the origin of such streams; just read or write them as | |
| usual. For information about how platform-specific streams come to be, see | |
| section 11.1, "Startup Options". | |
| A stream is opened with a particular file mode: | |
| * filemode_Write: An output stream. | |
| * filemode_Read: An input stream. | |
| * filemode_ReadWrite: Both an input and an output stream. | |
| * filemode_WriteAppend: An output stream, but the data will added to | |
| the end of whatever already existed in the destination, instead of | |
| replacing it. | |
| [[In the stdio library, using fopen(), filemode_Write would be mode "w"; | |
| filemode_Read would be mode "r"; filemode_ReadWrite would be mode "r+". | |
| Confusingly, filemode_WriteAppend cannot be mode "a", because the stdio | |
| spec says that when you open a file with mode "a", then fseek() doesn't | |
| work. So we have to use mode "r+" for appending. Then we run into the | |
| *other* stdio problem, which is that "r+" never creates a new file. So | |
| filemode_WriteAppend has to *first* open the file with "a", close it, | |
| reopen with "r+", and then fseek() to the end of the file. For | |
| filemode_ReadWrite, the process is the same, except without the fseek() -- | |
| we begin at the beginning of the file.]] | |
| [[We must also obey an obscure geas of ANSI C "r+" files: you can't switch | |
| from reading to writing without doing an fseek() in between. Switching from | |
| writing to reading has the same restriction, except that an fflush() also | |
| works.]] | |
| For information on opening streams, see the discussion of each specific | |
| type of stream in section 5.6, "The Types of Streams". Remember that it is | |
| always possible that opening a stream will fail, in which case the creation | |
| function will return NULL. | |
| Each stream remembers two character counts, the number of characters | |
| printed to and read from that stream. The write-count is exactly one per | |
| glk_put_char() call; it is figured before any platform-dependent character | |
| cookery. [[For example, if a newline character is converted to | |
| linefeed-plus-carriage-return, the stream's count still only goes up by | |
| one; similarly if an accented character is displayed as two characters.]] | |
| The read-count is exactly one per glk_get_char_stream() call, as long as | |
| the call returns an actual character (as opposed to an end-of-file token.) | |
| Glk has a notion of the "current (output) stream". If you print text | |
| without specifying a stream, it goes to the current output stream. The | |
| current output stream may be NULL, meaning that there isn't one. It is | |
| illegal to print text to stream NULL, or to print to the current stream | |
| when there isn't one. | |
| If the stream which is the current stream is closed, the current stream | |
| becomes NULL. | |
| void glk_stream_set_current(strid_t str); | |
| This sets the current stream to str, which must be an output stream. You | |
| may set the current stream to NULL, which means the current stream is not | |
| set to anything. | |
| strid_t glk_stream_get_current(void); | |
| Returns the current stream, or NULL if there is none. | |
| 5.1: How To Print | |
| void glk_put_char(unsigned char ch); | |
| This prints one character to the current stream. As with all basic | |
| functions, the character is assumed to be in the Latin-1 character | |
| encoding. See section 2, "Character Encoding". | |
| void glk_put_string(char *s); | |
| This prints a null-terminated string to the current stream. It is exactly | |
| equivalent to | |
| for (ptr = s; *ptr; ptr++) | |
| glk_put_char(*ptr); | |
| However, it may be more efficient. | |
| void glk_put_buffer(char *buf, glui32 len); | |
| This prints a block of characters to the current stream. It is exactly | |
| equivalent to | |
| for (i = 0; i < len; i++) | |
| glk_put_char(buf[i]); | |
| However, it may be more efficient. | |
| void glk_put_char_stream(strid_t str, unsigned char ch); | |
| void glk_put_string_stream(strid_t str, char *s); | |
| void glk_put_buffer_stream(strid_t str, char *buf, glui32 len); | |
| These are the same functions, except that you specify a stream to print to, | |
| instead of using the current stream. Again, it is illegal for str to be | |
| NULL, or the reference of an input-only stream. | |
| void glk_put_char_uni(glui32 ch); | |
| This prints one character to the current stream. The character is assumed | |
| to be a Unicode code point. See section 2, "Character Encoding". | |
| void glk_put_string_uni(glui32 *s); | |
| This prints a string of Unicode characters to the current stream. It is | |
| equivalent to a series of glk_put_char_uni() calls. A string ends on a | |
| glui32 whose value is 0. | |
| void glk_put_buffer_uni(glui32 *buf, glui32 len); | |
| This prints a block of Unicode characters to the current stream. It is | |
| equivalent to a series of glk_put_char_uni() calls. | |
| void glk_put_char_stream_uni(strid_t str, glui32 ch); | |
| void glk_put_string_stream_uni(strid_t str, glui32 *s); | |
| void glk_put_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len); | |
| 5.2: How To Read | |
| glsi32 glk_get_char_stream(strid_t str); | |
| This reads one character from the given stream. (There is no notion of a | |
| "current input stream.") It is illegal for str to be NULL, or an | |
| output-only stream. | |
| The result will be between 0 and 255. As with all basic text functions, Glk | |
| assumes the Latin-1 encoding. See section 2, "Character Encoding". If the | |
| end of the stream has been reached, the result will be -1. [[Note that | |
| high-bit characters (128..255) are *not* returned as negative numbers.]] | |
| If the stream contains Unicode data -- for example, if it was created with | |
| glk_stream_open_file_uni() or glk_stream_open_memory_uni() -- then | |
| characters beyond 255 will be returned as 0x3F ("?"). | |
| glui32 glk_get_buffer_stream(strid_t str, char *buf, glui32 len); | |
| This reads len characters from the given stream, unless the end of stream | |
| is reached first. No terminal null is placed in the buffer. It returns the | |
| number of characters actually read. | |
| glui32 glk_get_line_stream(strid_t str, char *buf, glui32 len); | |
| This reads characters from the given stream, until either len-1 characters | |
| have been read or a newline has been read. It then puts a terminal null | |
| ('\0') character on the end. It returns the number of characters actually | |
| read, including the newline (if there is one) but not including the | |
| terminal null. | |
| It is usually more efficient to read several characters at once with | |
| glk_get_buffer_stream() or glk_get_line_stream(), as opposed to calling | |
| glk_get_char_stream() several times. | |
| glsi32 glk_get_char_stream_uni(strid_t str); | |
| Reads one character from the given stream. If the end of the stream has | |
| been reached, the result will be -1. | |
| glui32 glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len); | |
| This reads len Unicode characters from the given stream, unless the end of | |
| the stream is reached first. No terminal null is placed in the buffer. It | |
| returns the number of Unicode characters actually read. | |
| glui32 glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len); | |
| This reads Unicode characters from the given stream, until either len-1 | |
| Unicode characters have been read or a newline has been read. It then puts | |
| a terminal null (a zero value) on the end. It returns the number of Unicode | |
| characters actually read, including the newline (if there is one) but not | |
| including the terminal null. | |
| 5.3: Closing Streams | |
| void glk_stream_close(strid_t str, stream_result_t *result); | |
| typedef struct stream_result_struct { | |
| glui32 readcount; | |
| glui32 writecount; | |
| } stream_result_t; | |
| This closes the stream str. The result argument points to a structure which | |
| is filled in with the final character counts of the stream. If you do not | |
| care about these, you may pass NULL as the result argument. | |
| If str is the current output stream, the current output stream is set to | |
| NULL. | |
| You cannot close window streams; use glk_window_close() instead. See | |
| section 3.2, "Window Opening, Closing, and Constraints". | |
| 5.4: Stream Positions | |
| You can set the position of the read/write mark in a stream. [[Which makes | |
| one wonder why they're called "streams" in the first place. Oh well.]] | |
| glui32 glk_stream_get_position(strid_t str); | |
| This returns the position of the mark. For memory streams and binary file | |
| streams, this is exactly the number of characters read or written from the | |
| beginning of the stream (unless you have moved the mark with | |
| glk_stream_set_position().) For text file streams, matters are more | |
| ambiguous, since (for example) writing one byte to a text file may store | |
| more than one character in the platform's native encoding. You can only be | |
| sure that the position increases as you read or write to the file. | |
| Additional complication: for Latin-1 memory and file streams, a character | |
| is a byte. For Unicode memory and file streams (those created by | |
| glk_stream_open_file_uni() and glk_stream_open_memory_uni()), a character | |
| is a 32-bit word. So in a binary Unicode file, positions are multiples of | |
| four bytes. | |
| [[If this bothers you, don't use binary Unicode files. I don't think | |
| they're good for much anyhow.]] | |
| void glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode); | |
| This sets the position of the mark. The position is controlled by pos, and | |
| the meaning of pos is controlled by seekmode: | |
| * seekmode_Start: pos characters after the beginning of the file. | |
| * seekmode_Current: pos characters after the current position (moving | |
| backwards if pos is negative.) | |
| * seekmode_End: pos characters after the end of the file. (pos should | |
| always be zero or negative, so that this will move backwards to a position | |
| within the file.) | |
| It is illegal to specify a position before the beginning or after the end | |
| of the file. | |
| In binary files, the mark position is exact -- it corresponds with the | |
| number of characters you have read or written. In text files, this mapping | |
| can vary, because of linefeed conversions or other character-set | |
| approximations. (See section 5, "Streams".) glk_stream_set_position() and | |
| glk_stream_get_position() measure positions in the platform's native | |
| encoding -- after character cookery. Therefore, in a text stream, it is | |
| safest to use glk_stream_set_position() only to move to the beginning or | |
| end of a file, or to a position determined by glk_stream_get_position(). | |
| Again, in Latin-1 streams, characters are bytes. In Unicode streams, | |
| characters are 32-bit words, or four bytes each. | |
| A window stream doesn't have a movable mark, so calling | |
| glk_stream_set_position() has no effect. glk_stream_get_position() on a | |
| window stream will always return zero. [[It might make more sense to return | |
| the number of characters written to the window, but existing libraries do | |
| not support this and it's not really worth adding the feature.]] | |
| 5.5: Styles | |
| You can send style-changing commands to an output stream. After a style | |
| change, new text which is printed to that stream will be given the new | |
| style, whatever that means for the stream in question. For a window stream, | |
| the text will appear in that style. For a memory stream, style changes have | |
| no effect. For a file stream, if the machine supports styled text files, | |
| the styles may be written to the file; more likely the style changes will | |
| have no effect. | |
| Styles are exclusive. A character is shown with exactly one style, not a | |
| subset of the possible styles. | |
| [[Note that every stream and window has its own idea of the "current | |
| style." Sending a style command to one window or stream does not affect any | |
| others.]] [[Except for a window's echo stream; see section 3.6, "Echo | |
| Streams".]] | |
| The styles are intended to distinguish meaning and use, not formatting. | |
| There is *no* standard definition of what each style will look like. That | |
| is left up to the Glk library, which will choose an appearance appropriate | |
| for the platform's interface and the player's preferences. | |
| There are currently eleven styles defined. More may be defined in the | |
| future. | |
| * style_Normal: The style of normal or body text. A new window or | |
| stream always starts with style_Normal as the current style. | |
| * style_Emphasized: Text which is emphasized. | |
| * style_Preformatted: Text which has a particular arrangement of | |
| characters. [[This style, unlike the others, *does* have a standard | |
| appearance; it will always be a fixed-width font. This is a concession to | |
| practicality. Games often want to display maps or diagrams using character | |
| graphics, and this is the style for that.]] | |
| * style_Header: Text which introduces a large section. This is suitable | |
| for the title of an entire game, or a major division such as a chapter. | |
| * style_Subheader: Text which introduces a smaller section within a | |
| large section. [[In a Colossal-Cave-style game, this is suitable for the | |
| name of a room (when the player looks around.)]] | |
| * style_Alert: Text which warns of a dangerous condition, or one which | |
| the player should pay attention to. | |
| * style_Note: Text which notifies of an interesting condition. [[This | |
| is suitable for noting that the player's score has changed.]] | |
| * style_BlockQuote: Text which forms a quotation or otherwise | |
| abstracted text. | |
| * style_Input: Text which the player has entered. You should generally | |
| not use this style at all; the library uses it for text which is typed | |
| during a line-input request. One case when it *is* appropriate for you to | |
| use style_Input is when you are simulating player input by reading commands | |
| from a text file. | |
| * style_User1: This style has no particular semantic meaning. You may | |
| define a meaning relevant to your own work, and use it as you see fit. | |
| * style_User2: Another style available for your use. | |
| Styles may be distinguished on screen by font, size, color, indentation, | |
| justification, and other attributes. Note that some attributes (notably | |
| justification and indentation) apply to entire paragraphs. If possible and | |
| relevant, you should apply a style to an entire paragraph -- call | |
| glk_set_style() immediately after printing the newline at the beginning of | |
| the text, and do the same at the end. | |
| [[For example, style_Header may well be centered text. If you print | |
| "Welcome to Victim (a short interactive mystery)", and only the word | |
| "Victim" is in the style_Header, the center-justification attribute will be | |
| lost. Similarly, a block quote is usually indented on both sides, but | |
| indentation is only meaningful when applied to an entire line or paragraph, | |
| so block quotes should take up an entire paragraph. Contrariwise, | |
| style_Emphasized need not be used on an entire paragraph. It is often used | |
| for single emphasized words in normal text, so you can expect that it will | |
| appear properly that way; it will be displayed in italics or underlining, | |
| not center-justified or indented.]] | |
| [[Yes, this is all a matter of mutual agreement between game authors and | |
| game players. It's not fixed by this specification. That's natural language | |
| for you.]] | |
| void glk_set_style(glui32 val); | |
| This changes the style of the current output stream. val should be one of | |
| the values listed above. However, any value is actually legal; if the | |
| interpreter does not recognize the style value, it will treat it as | |
| style_Normal. [[This policy allows for the future definition of styles | |
| without breaking old Glk libraries.]] | |
| void glk_set_style_stream(strid_t str, glui32 val); | |
| This changes the style of the stream str. | |
| 5.5.1: Suggesting the Appearance of Styles | |
| There are no guarantees of how styles will look, but you can make | |
| suggestions. | |
| void glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint, glsi32 | |
| val); | |
| void glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint); | |
| These functions set and clear hints about the appearance of one style for a | |
| particular type of window. You can also set wintype to wintype_AllTypes, | |
| which sets (or clears) a hint for all types of window. [[There is no | |
| equivalent constant to set a hint for all styles of a single window type.]] | |
| Initially, no hints are set for any window type or style. Note that having | |
| no hint set is not the same as setting a hint with value 0. | |
| These functions do *not* affect *existing* windows. They affect the windows | |
| which you create subsequently. If you want to set hints for all your game | |
| windows, call glk_stylehint_set() before you start creating windows. If you | |
| want different hints for different windows, change the hints before | |
| creating each window. | |
| [[This policy makes life easier for the interpreter. It knows everything | |
| about a particular window's appearance when the window is created, and it | |
| doesn't have to change it while the window exists.]] | |
| Hints are hints. The interpreter may ignore them, or give the player a | |
| choice about whether to accept them. Also, it is never necessary to set | |
| hints. You don't have to suggest that style_Preformatted be fixed-width, or | |
| style_Emphasized be boldface or italic; they will have appropriate | |
| defaults. Hints are for situations when you want to *change* the appearance | |
| of a style from what it would ordinarily be. The most common case when this | |
| is appropriate is for the styles style_User1 and style_User2. | |
| There are currently nine style hints defined. More may be defined in the | |
| future. | |
| * stylehint_Indentation: How much to indent lines of text in the given | |
| style. May be a negative number, to shift the text out (left) instead of in | |
| (right). The exact metric isn't precisely specified; you can assume that +1 | |
| is the smallest indentation possible which is clearly visible to the player. | |
| * stylehint_ParaIndentation: How much to indent the first line of each | |
| paragraph. This is in addition to the indentation specified by | |
| stylehint_Indentation. This too may be negative, and is measured in the | |
| same units as stylehint_Indentation. | |
| * stylehint_Justification: The value of this hint must be one of the | |
| constants stylehint_just_LeftFlush, stylehint_just_LeftRight (full | |
| justification), stylehint_just_Centered, or stylehint_just_RightFlush. | |
| * stylehint_Size: How much to increase or decrease the font size. This | |
| is relative; 0 means the interpreter's default font size will be used, | |
| positive numbers increase it, and negative numbers decrease it. Again, +1 | |
| is the smallest size increase which is easily visible. [[The amount of this | |
| increase may not be constant. +1 might increase an 8-point font to 9-point, | |
| but a 16-point font to 18-point.]] | |
| * stylehint_Weight: The value of this hint must be 1 for heavy-weight | |
| fonts (boldface), 0 for normal weight, and -1 for light-weight fonts. | |
| * stylehint_Oblique: The value of this hint must be 1 for oblique fonts | |
| (italic), or 0 for normal angle. | |
| * stylehint_Proportional: The value of this hint must be 1 for | |
| proportional-width fonts, or 0 for fixed-width. | |
| * stylehint_TextColor: The foreground color of the text. This is | |
| encoded in the 32-bit hint value: the top 8 bits must be zero, the next 8 | |
| bits are the red value, the next 8 bits are the green value, and the bottom | |
| 8 bits are the blue value. Color values range from 0 to 255. [[So | |
| 0x00000000 is black, 0x00FFFFFF is white, and 0x00FF0000 is bright red.]] | |
| * stylehint_BackColor: The background color behind the text. This is | |
| encoded the same way as stylehint_TextColor. | |
| * stylehint_ReverseColor: The value of this hint must be 0 for normal | |
| printing (TextColor on BackColor), or 1 for reverse printing (BackColor on | |
| TextColor). [[Some libraries may support this hint but not the TextColor | |
| and BackColor hints. Other libraries may take the opposite tack; others may | |
| support both, or neither.]] | |
| Again, when passing a style hint to a Glk function, any value is actually | |
| legal. If the interpreter does not recognize the stylehint value, it will | |
| ignore it. [[This policy allows for the future definition of style hints | |
| without breaking old Glk libraries.]] | |
| 5.5.2: Testing the Appearance of Styles | |
| You can suggest the appearance of a window's style before the window is | |
| created; after the window is created, you can test the style's actual | |
| appearance. These functions do not test the style hints; they test the | |
| attribute of the style as it appears to the player. | |
| Note that although you cannot change the appearance of a window's styles | |
| after the window is created, the library can. A platform may support | |
| dynamic preferences, which allow the player to change text formatting while | |
| your program is running. [[Changes that affect window size (such as font | |
| size changes) will be signalled by an evtype_Arrange event. However, more | |
| subtle changes (such as text color differences) are not signalled. If you | |
| test the appearance of styles at the beginning of your program, you must | |
| keep in mind the possibility that the player will change them later.]] | |
| glui32 glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2); | |
| This returns TRUE (1) if the two styles are visually distinguishable in the | |
| given window. If they are not, it returns FALSE (0). The exact meaning of | |
| this is left to the library to determine. | |
| glui32 glk_style_measure(winid_t win, glui32 styl, glui32 hint, glui32 | |
| *result); | |
| This tries to test an attribute of one style in the given window. The | |
| library may not be able to determine the attribute; if not, this returns | |
| FALSE (0). If it can, it returns TRUE (1) and stores the value in the | |
| location pointed at by result. [[As usual, it is legal for result to be | |
| NULL, although fairly pointless.]] | |
| The meaning of the value depends on the hint which was tested: | |
| * stylehint_Indentation, stylehint_ParaIndentation: The indentation and | |
| paragraph indentation. These are in a metric which is platform-dependent. | |
| [[Most likely either characters or pixels.]] | |
| * stylehint_Justification: One of the constants | |
| stylehint_just_LeftFlush, stylehint_just_LeftRight, | |
| stylehint_just_Centered, or stylehint_just_RightFlush. | |
| * stylehint_Size: The font size. Again, this is in a platform-dependent | |
| metric. [[Pixels, points, or simply 1 if the library does not support | |
| varying font sizes.]] | |
| * stylehint_Weight: 1 for heavy-weight fonts (boldface), 0 for normal | |
| weight, and -1 for light-weight fonts. | |
| * stylehint_Oblique: 1 for oblique fonts (italic), or 0 for normal | |
| angle. | |
| * stylehint_Proportional: 1 for proportional-width fonts, or 0 for | |
| fixed-width. | |
| * stylehint_TextColor, stylehint_BackColor: These are values from | |
| 0x00000000 to 0x00FFFFFF, encoded as described in section 5.5.1, | |
| "Suggesting the Appearance of Styles". | |
| * stylehint_ReverseColor: 0 for normal printing, 1 if the foreground | |
| and background colors are reversed. | |
| Signed values, such as the stylehint_Weight value, are cast to glui32. They | |
| may be cast to glsi32 to be dealt with in a more natural context. | |
| 5.6: The Types of Streams | |
| 5.6.1: Window Streams | |
| Every window has an output stream associated with it. This is created | |
| automatically, with filemode_Write, when you open the window. You get it | |
| with glk_window_get_stream(). Window streams always have rock value 0. | |
| A window stream cannot be closed with glk_stream_close(). It is closed | |
| automatically when you close its window with glk_window_close(). | |
| Only printable characters (including newline) may be printed to a window | |
| stream. See section 2, "Character Encoding". | |
| 5.6.2: Memory Streams | |
| You can open a stream which reads from or writes into a space in memory. | |
| strid_t glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode, | |
| glui32 rock); | |
| fmode must be filemode_Read, filemode_Write, or filemode_ReadWrite. | |
| buf points to the buffer where output will be read from or written to. | |
| buflen is the length of the buffer. | |
| When outputting, if more than buflen characters are written to the stream, | |
| all of them beyond the buffer length will be thrown away, so as not to | |
| overwrite the buffer. (The character count of the stream will still be | |
| maintained correctly. That is, it will count the number of characters | |
| written into the stream, not the number that fit in the buffer.) | |
| If buf is NULL, or for that matter if buflen is zero, then *everything* | |
| written to the stream is thrown away. This may be useful if you are | |
| interested in the character count. | |
| When inputting, if more than buflen characters are read from the stream, | |
| the stream will start returning -1 (signalling end-of-file.) If buf is | |
| NULL, the stream will always return end-of-file. | |
| The data is written to the buffer exactly as it was passed to the printing | |
| functions (glk_put_char(), etc); input functions will read the data exactly | |
| as it exists in memory. No platform-dependent cookery will be done on it. | |
| [[You can write a disk file in text mode, but a memory stream is | |
| effectively always in binary mode.]] | |
| Unicode values (characters greater than 255) cannot be written to the | |
| buffer. If you try, they will be stored as 0x3F ("?") characters. | |
| Whether reading or writing, the contents of the buffer are undefined until | |
| the stream is closed. The library may store the data there as it is | |
| written, or deposit it all in a lump when the stream is closed. It is | |
| illegal to change the contents of the buffer while the stream is open. | |
| strid_t glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, glui32 | |
| fmode, glui32 rock); | |
| This works just like glk_stream_open_memory(), except that the buffer is an | |
| array of 32-bit words, instead of bytes. This allows you to write and read | |
| any Unicode character. The buflen is the number of words, not the number of | |
| bytes. | |
| [[If the buffer contains the value 0xFFFFFFFF, and is opened for reading, | |
| the reader cannot distinguish that value from -1 (end-of-file). Fortunately | |
| 0xFFFFFFFF is not a valid Unicode character.]] | |
| 5.6.3: File Streams | |
| You can open a stream which reads from or writes to a disk file. | |
| strid_t glk_stream_open_file(frefid_t fileref, glui32 fmode, glui32 | |
| rock); | |
| fileref indicates the file which will be opened. fmode can be any of | |
| filemode_Read, filemode_Write, filemode_WriteAppend, or filemode_ReadWrite. | |
| If fmode is filemode_Read, the file must already exist; for the other | |
| modes, an empty file is created if none exists. If fmode is filemode_Write, | |
| and the file already exists, it is truncated down to zero length (an empty | |
| file); the other modes do not truncate. If fmode is filemode_WriteAppend, | |
| the file mark is set to the end of the file. | |
| [[Note, again, that this doesn't match stdio's fopen() call very well. See | |
| section 5, "Streams"]] | |
| If the filemode requires the file to exist, but the file does not exist, | |
| glk_stream_open_file() returns NULL. | |
| The file may be read or written in text or binary mode; this is determined | |
| by the fileref argument. Similarly, platform-dependent attributes such as | |
| file type are determined by fileref. See section 6, "File References". | |
| When writing in binary mode, byte values are written directly to the file. | |
| (Writing calls such as glk_put_char_stream() are defined in terms of | |
| Latin-1 characters, so the binary file can be presumed to use Latin-1. | |
| Newlines will remain as 0x0A bytes.) Unicode values (characters greater | |
| than 255) cannot be written to the file. If you try, they will be stored as | |
| 0x3F ("?") characters. | |
| When writing in text mode, character data is written in an encoding | |
| appropriate to the platform; this may be Latin-1 or some other format. | |
| Newlines may be converted to other line break sequences. Unicode values may | |
| be stored exactly, approximated, or abbreviated, depending on what the | |
| platform's text files support. | |
| strid_t glk_stream_open_file_uni(frefid_t fileref, glui32 fmode, glui32 | |
| rock); | |
| This works just like glk_stream_open_file(), except that in binary mode, | |
| characters are written and read as four-byte (big-endian) values. This | |
| allows you to write and read any Unicode character. | |
| In text mode, the file is written and read using the UTF-8 Unicode | |
| encoding. Files should be written without a byte-ordering mark. This | |
| ensures that text-mode files created by glk_stream_open_file() and | |
| glk_stream_open_file_uni() will be identical if only ASCII characters | |
| (32-127) are written. | |
| [[Previous versions of this spec said, of glk_stream_open_file_uni(): "In | |
| text mode, the file is written and read in a platform-dependent way, which | |
| may or may not handle all Unicode characters." This left open the | |
| possibility of other native text-file formats, as well as richer formats | |
| such as RTF or HTML. Richer formats do not seem to have ever been used; and | |
| at this point, UTF-8 is widespread enough for us to mandate it.]] | |
| To summarize: | |
| * glk_stream_open_file (byte stream), text mode: platform native text | |
| * glk_stream_open_file (byte stream), binary mode: Latin-1 | |
| * glk_stream_open_file_uni (word stream), text mode: UTF-8 | |
| * glk_stream_open_file_uni (word stream), binary mode: four-byte | |
| (big-endian) integers | |
| However, if the fileref was created via a prompt | |
| (glk_fileref_create_by_prompt), the player may have selected format options | |
| that override these rules. | |
| [[See also the comments about text and binary mode, section 6, "File | |
| References".]] | |
| 5.6.4: Resource Streams | |
| You can open a stream which reads from (but not writes to) a resource file. | |
| [[Typically this is embedded in a Blorb file, as Blorb is the official | |
| resource-storage format of Glk. A Blorb file can contain images and sounds, | |
| but it can also contain raw data files, which are accessed by the following | |
| functions. A data file is identified by number, not by a filename. The | |
| Blorb usage field will be 'Data'. The chunk type will be 'TEXT' for text | |
| resources, 'BINA' or 'FORM' for binary resources.]] | |
| [[For a 'FORM' Blorb chunk, the stream should start reading at the | |
| beginning of the chunk header -- that is, it should read the 'FORM' and | |
| length words before the chunk content. For 'TEXT' and 'BINA' chunks, the | |
| stream should skip the header and begin with the chunk content. This | |
| distinction is important when embedding AIFF sounds or Quetzal saved games, | |
| for example.]] | |
| [[Note that this FORM distinction was added to the Glk 0.7.4 spec in July | |
| 2012, several months after the spec went out. This is bad form, no pun | |
| intended, but I don't think it'll cause headaches. No games use the | |
| resource stream feature yet, as far as I know. A Glk library written in the | |
| interregnum of early 2012 will fail to recognize FORM chunks, and if a game | |
| tries to use one, glk_stream_open_resource will return NULL.]] | |
| [[If the running program is not associated with a Blorb file, the library | |
| may look for data files as actual files instead. These would be named | |
| "DATA1", "DATA2", etc, with a suffix distinguishing text and binary files. | |
| See "Other Resource Arrangements" in the Blorb spec: | |
| <http://eblong.com/zarf/blorb/>. The stream should always begin at the | |
| beginning of the file, in this case; there is no BINA/FORM distinction to | |
| worry about.]] | |
| strid_t glk_stream_open_resource(glui32 filenum, glui32 rock); | |
| strid_t glk_stream_open_resource_uni(glui32 filenum, glui32 rock); | |
| Open the given data resource for reading (only), as a normal or Unicode | |
| stream. [[Note that there is no notion of file usage -- the resource does | |
| not have to be specified as "saved game" or whatever.]] | |
| If no resource chunk of the given number exists, the open function returns | |
| NULL. | |
| As with file streams, a binary resource stream reads the resource as bytes | |
| (for a normal stream) or as four-byte (big-endian) words (for a Unicode | |
| stream). A text resource stream reads characters encoded as Latin-1 (for | |
| normal) or UTF-8 (for Unicode). [[Thus, the difference between text and | |
| binary resources is only important when opened as a Unicode stream.]] | |
| When reading from a resource stream, newlines are not remapped, even if | |
| they normally would be when reading from a text file on the host OS. If you | |
| read a line (glk_get_line_stream or glk_get_line_stream_uni), a Unix | |
| newline (0x0A) terminates the line. | |
| 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. | |
| 5.7: Other Stream Functions | |
| strid_t glk_stream_iterate(strid_t str, glui32 *rockptr); | |
| This iterates through all the existing streams. See section 1.6.2, | |
| "Iterating Through Opaque Objects". | |
| glui32 glk_stream_get_rock(strid_t str); | |
| This retrieves the stream's rock value. See section 1.6.1, "Rocks". Window | |
| streams always have rock 0; all other streams return whatever rock you | |
| created them with. | |
| 6: File References | |
| You deal with disk files using file references. Each fileref is an opaque C | |
| structure pointer; see section 1.6, "Opaque Objects". | |
| A file reference contains platform-specific information about the name and | |
| location of the file, and possibly its type, if the platform has a notion | |
| of file type. It also includes a flag indication whether the file is a text | |
| file or binary file. [[Note that this is different from the standard C I/O | |
| library, in which you specify text or binary mode when the file is opened.]] | |
| A fileref does not have to refer to a file which actually exists. You can | |
| create a fileref for a nonexistent file, and then open it in write mode to | |
| create a new file. | |
| You always provide a usage argument when you create a fileref. The usage | |
| indicates the file type and the mode (text or binary.) It must be the | |
| logical-or of a file-type constant and a mode constant. These values are | |
| used when you create a new file, and also to filter file lists when the | |
| player is selecting a file to load. The constants are as follows: | |
| * fileusage_SavedGame: A file which stores game state. | |
| * fileusage_Transcript: A file which contains a stream of text from the | |
| game (often an echo stream from a window.) | |
| * fileusage_InputRecord: A file which records player input. | |
| * fileusage_Data: Any other kind of file (preferences, statistics, | |
| arbitrary data.) | |
| * fileusage_BinaryMode: The file contents will be stored exactly as | |
| they are written, and read back in the same way. The resulting file may not | |
| be viewable on platform-native text file viewers. | |
| * fileusage_TextMode: The file contents will be transformed to a | |
| platform-native text file as they are written out. Newlines may be | |
| converted to linefeeds or linefeed-plus-carriage-return combinations; | |
| Latin-1 characters may be converted to native character codes. When reading | |
| a file in text mode, native line breaks will be converted back to newline | |
| (0x0A) characters, and native character codes may be converted to Latin-1 | |
| or UTF-8. [[Line breaks will always be converted; other conversions are | |
| more questionable. If you write out a file in text mode, and then read it | |
| back in text mode, high-bit characters (128 to 255) may be transformed or | |
| lost.]] | |
| In general, you should use text mode if the player expects to read the file | |
| with a platform-native text editor; you should use binary mode if the file | |
| is to be read back by your program, or if the data must be stored exactly. | |
| Text mode is appropriate for fileusage_Transcript; binary mode is | |
| appropriate for fileusage_SavedGame and probably for fileusage_InputRecord. | |
| fileusage_Data files may be text or binary, depending on what you use them | |
| for. | |
| When a fileref is created via a user prompt (glk_fileref_create_by_prompt), | |
| it may include extra file type information. [[For example, a prompt to | |
| write a transcript file might include a choice of text encodings, or even | |
| alternate formats such as RTF or HTML.]] This player-selected information | |
| will override the default encoding rules noted above. When a fileref is | |
| created non-interactively (glk_fileref_create_by_name, | |
| glk_fileref_create_temp) the default rules always apply. | |
| [[See also the comments about encoding, section 5.6.3, "File Streams".]] | |
| 6.1: The Types of File References | |
| There are four different functions for creating a fileref, depending on how | |
| you wish to specify it. Remember that it is always possible that a fileref | |
| creation will fail and return NULL. | |
| frefid_t glk_fileref_create_temp(glui32 usage, glui32 rock); | |
| This creates a reference to a temporary file. It is always a new file (one | |
| which does not yet exist). The file (once created) will be somewhere out of | |
| the player's way. [[This is why no name is specified; the player will never | |
| need to know it.]] | |
| A temporary file should not be used for long-term storage. It may be | |
| deleted automatically when the program exits, or at some later time, say | |
| when the machine is turned off or rebooted. You do not have to worry about | |
| deleting it yourself. | |
| frefid_t glk_fileref_create_by_prompt(glui32 usage, glui32 fmode, | |
| glui32 rock); | |
| This creates a reference to a file by asking the player to locate it. The | |
| library may simply prompt the player to type a name, or may use a | |
| platform-native file navigation tool. (The prompt, if any, is inferred from | |
| the usage argument.) | |
| fmode must be one of these values: | |
| * filemode_Read: The file must already exist; the player will be asked | |
| to select from existing files which match the usage. | |
| * filemode_Write: The file should not exist; if the player selects an | |
| existing file, he will be warned that it will be replaced. | |
| * filemode_ReadWrite: The file may or may not exist; if it already | |
| exists, the player will be warned that it will be modified. | |
| * filemode_WriteAppend: Same behavior as filemode_ReadWrite. | |
| The fmode argument should generally match the fmode which will be used to | |
| open the file. | |
| [[It is likely that the prompt or file tool will have a "cancel" option. If | |
| the player chooses this, glk_fileref_create_by_prompt() will return NULL. | |
| This is a major reason why you should make sure the return value is valid | |
| before you use it.]] | |
| The recommended file suffixes for files are ".glkdata" for fileusage_Data, | |
| ".glksave" for fileusage_SavedGame, ".txt" for fileusage_Transcript and | |
| fileusage_InputRecord. | |
| The prompt may also include a choice of file formats. This will affect the | |
| format of the file (written or parsed), as noted earlier. | |
| frefid_t glk_fileref_create_by_name(glui32 usage, char *name, glui32 | |
| rock); | |
| This creates a reference to a file with a specific name. The file will be | |
| in a fixed location relevant to your program, and visible to the player. | |
| [[This usually means "in the same directory as your program."]] | |
| Earlier versions of the Glk spec specified that the library may have to | |
| extend, truncate, or change your name argument in order to produce a legal | |
| native filename. This remains true. However, since Glk was originally | |
| proposed, the world has largely reached concensus about what a filename | |
| looks like. Therefore, it is worth including some recommended library | |
| behavior here. Libraries that share this behavior will more easily be able | |
| to exchange files, which may be valuable both to authors (distributing data | |
| files for games) and for players (moving data between different computers | |
| or different applications). | |
| The library should take the given filename argument, and delete any | |
| characters illegal for a filename. This will include all of the following | |
| characters (and more, if the OS requires it): slash, backslash, angle | |
| brackets (less-than and greater-than), colon, double-quote, pipe (vertical | |
| bar), question-mark, asterisk. The library should also truncate the | |
| argument at the first period (delete the first period and any following | |
| characters). If the result is the empty string, change it to the string | |
| "null". | |
| It should then append an appropriate suffix, depending on the usage: | |
| ".glkdata" for fileusage_Data, ".glksave" for fileusage_SavedGame, ".txt" | |
| for fileusage_Transcript and fileusage_InputRecord. | |
| The above behavior is *not* a requirement of the Glk spec. Older | |
| implementations can continue doing what they do. Some programs (e.g. | |
| web-based interpreters) may not have access to a traditional filesystem at | |
| all, and to them these recommendations will be meaningless. | |
| On the other side of the coin, the game file should not press these | |
| limitations. Best practice is for the game to pass a filename containing | |
| only letters and digits, beginning with a letters, and not mixing upper and | |
| lower case. Avoid overly-long filenames. | |
| [[The earlier Glk spec gave more stringent recommendations: "No more than 8 | |
| characters, consisting entirely of upper-case letters and numbers, starting | |
| with a letter". The DOS era is safely contained, if not over, so this has | |
| been relaxed. The I7 manual recommends "23 characters or fewer".]] | |
| [[To address other complications:]] | |
| [[Some filesystems are case-insensitive. If you create two filerefs with | |
| the names "File" and "FILE", they may wind up pointing to the same file, or | |
| they may not. Avoid doing this.]] | |
| [[Some programs will look for all files in the same directory as the | |
| program itself (or, for interpreted games, in the same directory as the | |
| game file). Others may keep files in a data-specific directory appropriate | |
| for the user (e.g., ~/Library on MacOS).]] | |
| [[If a game interpreter uses a data-specific directory, there is a question | |
| of whether to use a common location, or divide it into game-specific | |
| subdirectories. (Or to put it another way: should the namespace of named | |
| files be per-game or app-wide?) Since data files may be exchanged between | |
| games, they should be given an app-wide namespace. In contrast, saved games | |
| should be per-game, as they can never be exchanged. Transcripts and input | |
| records can go either way.]] | |
| [[When updating an older library to follow these recommendations, consider | |
| backwards compatibility for games already installed. When opening an | |
| existing file (that is, not in a write-only mode) it may be worth looking | |
| under the older name (suffix) if the newer one does not already exist.]] | |
| [[Game-save files are already stored with a variety of file suffixes, since | |
| that usage goes back to the oldest IF interpreters, long predating Glk. It | |
| is reasonable to treat them in some special way, while hewing closer to | |
| these recommendations for data files.]] | |
| [[In case anyone cares, fileusage_Data (".glkdata") can be associated with | |
| the MIME type application/x-glkdata; fileusage_SavedGame (".glksave") with | |
| application/x-glksave. ".glksave" files are commonly Quetzal save data (but | |
| might be other formats for IF systems other than Z-code and Glulx). | |
| ".glkdata" can contain any data, obviously. So knowing the MIME type | |
| doesn't get you much, but we offer them anyway.]] | |
| frefid_t glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, | |
| glui32 rock); | |
| This copies an existing file reference, but changes the usage. (The | |
| original fileref is not modified.) | |
| The use of this function can be tricky. If you change the type of the | |
| fileref (fileusage_Data, fileusage_SavedGame, etc), the new reference may | |
| or may not point to the same actual disk file. [[Most platforms use | |
| suffixes to indicate file type, so it typically will not. See the earlier | |
| comments about recommended file suffixes.]] If you do this, and open both | |
| file references for writing, the results are unpredictable. It is safest to | |
| change the type of a fileref only if it refers to a nonexistent file. | |
| If you change the mode of a fileref (fileusage_TextMode, | |
| fileusage_BinaryMode), but leave the rest of the type unchanged, the new | |
| fileref will definitely point to the same disk file as the old one. | |
| Obviously, if you write to a file in text mode and then read from it in | |
| binary mode, the results are platform-dependent. | |
| 6.2: Other File Reference Functions | |
| void glk_fileref_destroy(frefid_t fref); | |
| Destroys a fileref which you have created. This does *not* affect the disk | |
| file; it just reclaims the resources allocated by the glk_fileref_create... | |
| function. | |
| It is legal to destroy a fileref after opening a file with it (while the | |
| file is still open.) The fileref is only used for the opening operation, | |
| not for accessing the file stream. | |
| frefid_t glk_fileref_iterate(frefid_t fref, glui32 *rockptr); | |
| This iterates through all the existing filerefs. See section 1.6.2, | |
| "Iterating Through Opaque Objects". | |
| glui32 glk_fileref_get_rock(frefid_t fref); | |
| This retrieves the fileref's rock value. See section 1.6.1, "Rocks". | |
| void glk_fileref_delete_file(frefid_t fref); | |
| This deletes the file referred to by fref. It does not destroy the fileref | |
| itself. | |
| You should only call this with a fileref that refers to an existing file. | |
| glui32 glk_fileref_does_file_exist(frefid_t fref); | |
| This returns TRUE (1) if the fileref refers to an existing file, and FALSE | |
| (0) if not. | |
| 7: Graphics | |
| In accordance with this modern age, Glk provides for a modicum of graphical | |
| flair. It does *not* attempt to be a complete graphical toolkit. Those | |
| already exist. Glk strikes the usual uncomfortable balance between power, | |
| portability, and ease of implementation: commands for arranging | |
| pre-supplied images on the screen and intermixed with text. | |
| Graphics is an optional capability in Glk; not all libraries support | |
| graphics. This should not be a surprise. | |
| 7.1: Image Resources | |
| Most of the graphics commands in Glk deal with image resources. Your | |
| program does not have to worry about how images are stored. Everything is a | |
| resource, and a resource is referred to by an integer identifier. You may, | |
| for example, call a function to display image number 17. The format, | |
| loading, and displaying of that image is entirely up to the Glk library for | |
| the platform in question. | |
| Of course, it is also desirable to have a platform-independent way to store | |
| sounds and images. Blorb is the official resource-storage format of Glk. A | |
| Glk library does not have to understand Blorb, but it is more likely to | |
| understand Blorb than any other format. | |
| [[Glk does not specify the exact format of images, but Blorb does. Images | |
| in a Blorb archive must be PNG or JPEG files. More formats may be added if | |
| real-world experience shows it to be desirable. However, that is in the | |
| domain of the Blorb specification. The Glk spec, and Glk programming, will | |
| not change.]] | |
| At present, images can only be drawn in graphics windows and text buffer | |
| windows. In fact, a library may not implement both of these possibilities. | |
| You should test each with the gestalt_DrawImage selector if you plan to use | |
| it. See section 7.4, "Testing for Graphics Capabilities". | |
| glui32 glk_image_get_info(glui32 image, glui32 *width, glui32 *height); | |
| This gets information about the image resource with the given identifier. | |
| It returns TRUE (1) if there is such an image, and FALSE (0) if not. You | |
| can also pass pointers to width and height variables; if the image exists, | |
| the variables will be filled in with the width and height of the image, in | |
| pixels. (You can pass NULL for either width or height if you don't care | |
| about that information.) | |
| [[You should always use this function to measure the size of images when | |
| you are creating your display. Do this even if you created the images, and | |
| you know how big they "should" be. This is because images may be scaled in | |
| translating from one platform to another, or even from one machine to | |
| another. A Glk library might display all images larger than their original | |
| size, because of screen resolution or player preference. Images will be | |
| scaled proportionally, but you still need to call glk_image_get_info() to | |
| determine their absolute size.]] | |
| glui32 glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 | |
| val2); | |
| This draws the given image resource in the given window. The position of | |
| the image is given by val1 and val2, but their meaning varies depending on | |
| what kind of window you are drawing in. See section 7.2, "Graphics in | |
| Graphics Windows" and section 7.3, "Graphics in Text Buffer Windows". | |
| This function returns a flag indicating whether the drawing operation | |
| succeeded. [[A FALSE result can occur for many reasons. The image data | |
| might be corrupted; the library may not have enough memory to operate; | |
| there may be no image with the given identifier; the window might not | |
| support image display; and so on.]] | |
| glui32 glk_image_draw_scaled(winid_t win, glui32 image, glsi32 val1, | |
| glsi32 val2, glui32 width, glui32 height); | |
| This is similar to glk_image_draw(), but it scales the image to the given | |
| width and height, instead of using the image's standard size. (You can | |
| measure the standard size with glk_image_get_info().) | |
| If width or height is zero, nothing is drawn. Since those arguments are | |
| unsigned integers, they cannot be negative. If you pass in a negative | |
| number, it will be interpreted as a very large positive number, which is | |
| almost certain to end badly. | |
| 7.2: Graphics in Graphics Windows | |
| A graphics window is a rectangular canvas of pixels, upon which you can | |
| draw images. The contents are entirely under your control. You can draw as | |
| many images as you like, at any positions -- overlapping if you like. If | |
| the window is resized, you are responsible for redrawing everything. See | |
| section 3.5.5, "Graphics Windows". | |
| If you call glk_image_draw() or glk_image_draw_scaled() in a graphics | |
| window, val1 and val2 are interpreted as X and Y coordinates. The image | |
| will be drawn with its upper left corner at this position. | |
| It is legitimate for part of the image to fall outside the window; the | |
| excess is not drawn. Note that these are signed arguments, so you can draw | |
| an image which falls outside the left or top edge of the window, as well as | |
| the right or bottom. | |
| There are a few other commands which apply to graphics windows: | |
| void glk_window_set_background_color(winid_t win, glui32 color); | |
| This sets the window's background color. It does *not* change what is | |
| currently displayed; it only affects subsequent clears and resizes. The | |
| initial background color of each window is white. | |
| Colors are encoded in a 32-bit value: the top 8 bits must be zero, the next | |
| 8 bits are the red value, the next 8 bits are the green value, and the | |
| bottom 8 bits are the blue value. Color values range from 0 to 255. [[So | |
| 0x00000000 is black, 0x00FFFFFF is white, and 0x00FF0000 is bright red.]] | |
| [[This function may only be used with graphics windows. To set background | |
| colors in a text window, use text styles with color hints; see section 5.5, | |
| "Styles".]] | |
| void glk_window_fill_rect(winid_t win, glui32 color, glsi32 left, | |
| glsi32 top, glui32 width, glui32 height); | |
| This fills the given rectangle with the given color. It is legitimate for | |
| part of the rectangle to fall outside the window. If width or height is | |
| zero, nothing is drawn. | |
| void glk_window_erase_rect(winid_t win, glsi32 left, glsi32 top, glui32 | |
| width, glui32 height); | |
| This fills the given rectangle with the window's background color. | |
| You can also fill an entire graphics window with its background color by | |
| calling glk_window_clear(). | |
| [[Note that graphics windows do not support a full set of object-drawing | |
| commands, nor can you draw text in them. That may be available in a future | |
| Glk extension. For now, it seems reasonable to limit the task to a single | |
| primitive, the drawing of a raster image. And then there's the ability to | |
| fill a rectangle with a solid color -- a small extension, and hopefully no | |
| additional work for the library, since it can already clear with arbitrary | |
| background colors. In fact, if glk_window_fill_rect() did not exist, an | |
| author could invent it -- by briefly setting the background color, erasing | |
| a rectangle, and restoring.]] | |
| 7.3: Graphics in Text Buffer Windows | |
| A text buffer is a linear text stream. You can draw images in-line with | |
| this text. If you are familiar with HTML, you already understand this | |
| model. You draw images with flags indicating alignment. The library takes | |
| care of scrolling, resizing, and reformatting text buffer windows. | |
| If you call glk_image_draw() or glk_image_draw_scaled() in a text buffer | |
| window, val1 gives the image alignment. The val2 argument is currently | |
| unused, and should always be zero. | |
| * imagealign_InlineUp: The image appears at the current point in the | |
| text, sticking up. That is, the bottom edge of the image is aligned with | |
| the baseline of the line of text. | |
| * imagealign_InlineDown: The image appears at the current point, and | |
| the top edge is aligned with the top of the line of text. | |
| * imagealign_InlineCenter: The image appears at the current point, and | |
| it is centered between the top and baseline of the line of text. If the | |
| image is taller than the line of text, it will stick up and down equally. | |
| * imagealign_MarginLeft: The image appears in the left margin. | |
| Subsequent text will be displayed to the right of the image, and will flow | |
| around it -- that is, it will be left-indented for as many lines as it | |
| takes to pass the image. | |
| * imagealign_MarginRight: The image appears in the right margin, and | |
| subsequent text will flow around it on the left. | |
| The two "margin" alignments require some care. To allow proper positioning, | |
| images using imagealign_MarginLeft and imagealign_MarginRight *must* be | |
| placed at the beginning of a line. That is, you may only call | |
| glk_image_draw() (with these two alignments) in a window, if you have just | |
| printed a newline to the window's stream, or if the window is entirely | |
| empty. If you margin-align an image in a line where text has already | |
| appeared, no image will appear at all. | |
| Inline-aligned images count as "text" for the purpose of this rule. | |
| You may have images in both margins at the same time. | |
| It is also legal to have more than one image in the *same* margin (left or | |
| right.) However, this is not recommended. It is difficult to predict how | |
| text will wrap in that situation, and libraries may err on the side of | |
| conservatism. | |
| You may wish to "break" the stream of text down below the current margin | |
| image. Since lines of text can be in any font and size, you cannot do this | |
| by counting newlines. Instead, use this function: | |
| void glk_window_flow_break(winid_t win); | |
| If the current point in the text is indented around a margin-aligned image, | |
| this acts like the correct number of newlines to start a new line below the | |
| image. (If there are several margin-aligned images, it goes below all of | |
| them.) If the current point is *not* beside a margin-aligned image, this | |
| call has no effect. | |
| When a text buffer window is resized, a flow-break behaves cleverly; it may | |
| become active or inactive as necessary. You can consider this function to | |
| insert an invisible mark in the text stream. The mark works out how many | |
| newlines it needs to be whenever the text is formatted for display. | |
| An example of the use of glk_window_flow_break(): If you display a | |
| left-margin image at the start of every line, they can stack up in a | |
| strange diagonal way that eventually squeezes all the text off the screen. | |
| [[If you can't picture this, draw some diagrams. Make the margin images | |
| more than one line tall, so that each line starts already indented around | |
| the last image.]] To avoid this problem, call glk_window_flow_break() | |
| immediately before glk_image_draw() for every margin-aligned image. | |
| In all windows other than text buffers, glk_window_flow_break() has no | |
| effect. | |
| 7.4: Testing for Graphics Capabilities | |
| Before calling Glk graphics functions, you should use the following gestalt | |
| selectors. | |
| 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. | |
| If you are writing a C program, there is an additional complication. A | |
| library which does not support graphics may not implement the graphics | |
| functions at all. Even if you put gestalt tests around your graphics calls, | |
| you may get link-time errors. If the glk.h file is so old that it does not | |
| declare the graphics 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_IMAGE. If this is defined, so are all the functions and | |
| constants described in this section. If not, not. | |
| [[To be extremely specific, there are two ways this can happen. If the | |
| glk.h file that comes with the library is too old to have the graphics | |
| declarations in it, it will of course lack GLK_MODULE_IMAGE as well. If the | |
| glk.h file is recent, but the library is old, the definition of | |
| GLK_MODULE_IMAGE should be removed from glk.h, to avoid link errors. This | |
| is not a great solution. A better one is for the library to implement the | |
| graphics functions as stubs that do nothing (or cause run-time errors). | |
| Since no program will call the stubs without testing gestalt_Graphics, this | |
| is sufficient.]] | |
| res = glk_gestalt(gestalt_DrawImage, windowtype); | |
| This 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. | |
| res = glk_gestalt(gestalt_GraphicsTransparency, 0); | |
| 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.]] | |
| res = glk_gestalt(gestalt_GraphicsCharInput, 0); | |
| This returns 1 if graphics windows can accept character input requests. If | |
| it returns zero, do not call glk_request_char_event() or | |
| glk_request_char_event_uni() on a graphics window. | |
| 8: Sound | |
| As with graphics, so with sound. Sounds, however, obviously don't appear in | |
| windows. To play a sound in Glk, you must first create a sound channel to | |
| hold it. This is an entirely new class of opaque objects; there are create | |
| and destroy and iterate and get_rock functions for channels, just as there | |
| are for windows and streams and filerefs. | |
| A channel can be playing exactly one sound at a time. If you want to play | |
| more than one sound simultaneously, you need more than one sound channel. | |
| On the other hand, a single sound can be played on several channels at the | |
| same time, or overlapping itself. | |
| Sound is an optional capability in Glk. | |
| 8.1: Sound Resources | |
| As with images, sounds are kept in resources, and your program does not | |
| have to worry about the formatting or storage. A resource is referred to by | |
| an integer identifier. | |
| A resource can theoretically contain any kind of sound data, of any length. | |
| A resource can even be infinitely long. [[This would be represented by some | |
| sound encoding with a built-in repeat-forever flag -- but that is among the | |
| details which are hidden from you.]] A resource can also contain two or | |
| more channels of sound (stereo data). Do not confuse such in-sound channels | |
| with Glk sound channels. A single Glk sound channel suffices to play any | |
| sound, even stereo sounds. | |
| [[Again, Blorb is the official resource-storage format of Glk. Sounds in | |
| Blorb files can be encoded as Ogg, AIFF, or MOD. See the Blorb | |
| specification for details.]] | |
| 8.2: Creating and Destroying Sound Channels | |
| schanid_t glk_schannel_create(glui32 rock); | |
| schanid_t glk_schannel_create_ext(glui32 rock, glui32 volume); | |
| This creates a sound channel, about as you'd expect. | |
| Remember that it is possible that the library will be unable to create a | |
| new channel, in which case glk_schannel_create() will return NULL. | |
| When you create a channel using glk_schannel_create(), it has full volume, | |
| represented by the value 0x10000. Half volume would be 0x8000, | |
| three-quarters volume would be 0xC000, and so on. A volume of zero | |
| represents silence. The glk_schannel_create_ext() call lets you create a | |
| channel with the volume already set at a given level. | |
| You can overdrive the volume of a channel by setting a volume greater than | |
| 0x10000. However, this is not recommended; the library may be unable to | |
| increase the volume past full, or the sound may become distorted. You | |
| should always create sound resources with the maximum volume you will need, | |
| and then reduce the volume when appropriate using the channel-volume calls. | |
| [[Mathematically, these volume changes should be taken as linear | |
| multiplication of a waveform represented as linear samples. As I understand | |
| it, linear PCM encodes the sound pressure, and therefore a volume of 0x8000 | |
| should represent a 6 dB drop.]] | |
| Not all libraries support glk_schannel_create_ext(). You should test the | |
| gestalt_Sound2 selector before you rely on it; see section 8.5, "Testing | |
| for Sound Capabilities". | |
| void glk_schannel_destroy(schanid_t chan); | |
| Destroy the channel. If the channel is playing a sound, the sound stops | |
| immediately (with no notification event). | |
| 8.3: Playing Sounds | |
| glui32 glk_schannel_play(schanid_t chan, glui32 snd) | |
| Begin playing the given sound on the channel. If the channel was already | |
| playing a sound (even the same one), the old sound is stopped (with no | |
| notification event). | |
| This returns 1 if the sound actually started playing, and 0 if there was | |
| any problem. [[The most obvious problem is if there is no sound resource | |
| with the given identifier. But other problems can occur. For example, the | |
| MOD-playing facility in a library might be unable to handle two MODs at the | |
| same time, in which case playing a MOD resource would fail if one was | |
| already playing.]] | |
| glui32 glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 | |
| repeats, glui32 notify); | |
| This works the same as glk_schannel_play(), but lets you specify additional | |
| options. glk_schannel_play(chan, snd) is exactly equivalent to | |
| glk_schannel_play_ext(chan, snd, 1, 0). | |
| The repeats value is the number of times the sound should be repeated. A | |
| repeat value of -1 (or rather 0xFFFFFFFF) means that the sound should | |
| repeat forever. A repeat value of 0 means that the sound will not be played | |
| at all; nothing happens. (Although a previous sound on the channel will be | |
| stopped, and the function will return 1.) | |
| The notify value should be nonzero in order to request a sound notification | |
| event. If you do this, when the sound is completed, you will get an event | |
| with type evtype_SoundNotify. The window will be NULL, val1 will be the | |
| sound's resource id, and val2 will be the nonzero value you passed as | |
| notify. | |
| If you request sound notification, and the repeat value is greater than | |
| one, you will get the event only after the *last* repetition. If the repeat | |
| value is 0 or -1, you will never get a notification event at all. | |
| Similarly, if the sound is stopped or interrupted, or if the channel is | |
| destroyed while the sound is playing, there will be no notification event. | |
| Not all libraries support sound notification. You should test the | |
| gestalt_Sound2 selector before you rely on it; see section 8.5, "Testing | |
| for Sound Capabilities". | |
| Note that you can play a sound on a channel whose volume is zero. This has | |
| no audible result, unless you later change the volume; but it produces | |
| notifications as usual. You can also play a sound on a paused channel; the | |
| sound is paused immediately, and does not progress. | |
| glui32 glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount, | |
| glui32 *sndarray, glui32 soundcount, glui32 notify); | |
| This works the same as glk_schannel_play_ext(), except that you can specify | |
| more than one sound. The channel references and sound resource numbers are | |
| given as two arrays, which must be the same length. The notify argument | |
| applies to all the sounds; the repeats value for all the sounds is 1. | |
| All the sounds will begin at exactly the same time. | |
| This returns the number of sounds that began playing correctly. (This will | |
| be a number from 0 to soundcount.) | |
| [[If the notify argument is nonzero, you will get a separate sound | |
| notification event as each sound finishes. They will all have the same val2 | |
| value.]] | |
| [[Note that you have to supply chancount and soundcount as separate | |
| arguments, even though they are required to be the same. This is an awkward | |
| consequence of the way array arguments are dispatched in Glulx.]] | |
| void glk_schannel_stop(schanid_t chan); | |
| Stops any sound playing in the channel. No notification event is generated, | |
| even if you requested one. If no sound is playing, this has no effect. | |
| void glk_schannel_pause(schanid_t chan); | |
| Pause any sound playing in the channel. This does not generate any | |
| notification events. If the channel is already paused, this does nothing. | |
| New sounds started in a paused channel are paused immediately. | |
| A volume change in progress is *not* paused, and may proceed to completion, | |
| generating a notification if appropriate. | |
| void glk_schannel_unpause(schanid_t chan); | |
| Unpause the channel. Any paused sounds begin playing where they left off. | |
| If the channel is not already paused, this does nothing. | |
| [[This means, for example, that you can pause a channel that is currently | |
| not playing any sounds. If you then add a sound to the channel, it will not | |
| start playing; it will be paused at its beginning. If you later unpause the | |
| channel, the sound will commence.]] | |
| void glk_schannel_set_volume(schanid_t chan, glui32 vol); | |
| void glk_schannel_set_volume_ext(schanid_t chan, glui32 vol, glui32 | |
| duration, glui32 notify); | |
| Sets the volume in the channel, from 0 (silence) to 0x10000 (full volume). | |
| Again, you can overdrive the volume by setting a value greater than | |
| 0x10000, but this is not recommended. | |
| If the duration is zero, the change is immediate. Otherwise, the change | |
| begins immediately, and occurs smoothly over the next duration milliseconds. | |
| The notify value should be nonzero in order to request a volume | |
| notification event. If you do this, when the volume change is completed, | |
| you will get an event with type evtype_VolumeNotify. The window will be | |
| NULL, val1 will be zero, and val2 will be the nonzero value you passed as | |
| notify. | |
| The glk_schannel_set_volume() does not include duration and notify values. | |
| Both are assumed to be zero: immediate change, no notification. | |
| You can call these functions between sounds, or while a sound is playing. | |
| However, a zero-duration change while a sound is playing may produce | |
| unpleasant clicks. | |
| At most one volume change can be occurring on a sound channel at any time. | |
| If you call one of these functions while a previous volume change is in | |
| progress, the previous change is interrupted. The beginning point of the | |
| new volume change should be wherever the previous volume change was | |
| interrupted (rather than the previous change's beginning or ending point). | |
| Not all libraries support thse functions. You should test the appropriate | |
| gestalt selectors before you rely on them; see section 8.5, "Testing for | |
| Sound Capabilities". | |
| void glk_sound_load_hint(glui32 snd, glui32 flag); | |
| This gives the library a hint about whether the given sound should be | |
| loaded or not. If the flag is nonzero, the library may preload the sound or | |
| do other initialization, so that glk_schannel_play() will be faster. If the | |
| flag is zero, the library may release memory or other resources associated | |
| with the sound. Calling this function is always optional, and it has no | |
| effect on what the library actually plays. | |
| 8.4: Other Sound Channel Functions | |
| schanid_t glk_schannel_iterate(schanid_t chan, glui32 *rockptr); | |
| This function can be used to iterate through the list of all open channels. | |
| See section 1.6.2, "Iterating Through Opaque Objects". | |
| As that section describes, the order in which channels are returned is | |
| arbitrary. | |
| glui32 glk_schannel_get_rock(schanid_t chan); | |
| This retrieves the channel's rock value. See section 1.6.1, "Rocks". | |
| 8.5: Testing for Sound Capabilities | |
| Before calling Glk sound functions, you should use the following gestalt | |
| selectors. | |
| 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. | |
| If you are writing a C program, there is an additional complication. A | |
| library which does not support sound may not implement the sound functions | |
| at all. Even if you put gestalt tests around your sound calls, you may get | |
| link-time errors. If the glk.h file is so old that it does not declare the | |
| sound 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_SOUND2. If this is defined, so are all the functions and | |
| constants described in this section. If not, not. | |
| Earlier versions of the Glk spec defined separate selectors for various | |
| optional capabilities. This has proven to be an unnecessarily confusing | |
| strategy, and is no longer used. The following selectors still exist, but | |
| you should not need to test them; the gestalt_Sound2 selector covers all of | |
| them. | |
| res = glk_gestalt(gestalt_Sound, 0); | |
| This returns 1 if the older (pre-0.7.3) 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. | |
| You can perform a preprocessor test for the existence of GLK_MODULE_SOUND. | |
| If this is defined, so are the functions listed above. | |
| 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. | |
| 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. | |
| 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. | |
| 9: Hyperlinks | |
| Some games may wish to mark up the text in their windows with hyperlinks, | |
| which can be selected by the player -- most likely by mouse click. Glk | |
| allows this in a manner similar to the way text styles are set. | |
| Hyperlinks are an optional capability in Glk. | |
| 9.1: Creating Hyperlinks | |
| void glk_set_hyperlink(glui32 linkval); | |
| void glk_set_hyperlink_stream(strid_t str, glui32 linkval); | |
| These calls set the current link value in the current output stream, or the | |
| specified output stream, respectively. A link value is any non-zero | |
| integer; zero indicates no link. Subsequent text output is considered to | |
| make up the body of the link, which continues until the link value is | |
| changed (or set to zero). | |
| Note that it is almost certainly useless to change the link value of a | |
| stream twice with no intervening text. The result will be a zero-length | |
| link, which the player probably cannot see or select; the library may | |
| optimize it out entirely. | |
| Setting the link value of a stream to the value it already has, has no | |
| effect. | |
| If the library supports images, they take on the current link value as they | |
| are output, just as text does. The player can select an image in a link | |
| just as he does text. (This includes margin-aligned images, which can lead | |
| to some peculiar situations, since a right-margin image may not appear | |
| directly adjacent to the text it was output with.) | |
| The library will attempt to display links in some distinctive way (and it | |
| will do this whether or not hyperlink input has actually been requested for | |
| the window). Naturally, blue underlined text is most likely. Link images | |
| may not be distinguished from non-link images, so it is best not to use a | |
| particular image both ways. | |
| 9.2: Accepting Hyperlink Events | |
| void glk_request_hyperlink_event(winid_t win); | |
| void glk_cancel_hyperlink_event(winid_t win); | |
| These calls works like the other event request calls. A pending request on | |
| a window remains pending until the player selects a link, or the request is | |
| cancelled. | |
| A window can have hyperlink input and mouse, character, or line input | |
| pending at the same time. However, if hyperlink and mouse input are | |
| requested at the same time, the library may not provide an intuitive way | |
| for the player to distingish which a mouse click represents. Therefore, | |
| this combination should be avoided. | |
| When a link is selected in a window with a pending request, glk_select() | |
| will return an event of type evtype_Hyperlink. In the event structure, win | |
| tells what window the event came from, and val1 gives the (non-zero) link | |
| value. | |
| If no hyperlink request is pending in a window, the library will ignore | |
| attempts to select a link. No evtype_Hyperlink event will be generated | |
| unless it has been requested. | |
| 9.3: Testing for Hyperlink Capabilities | |
| Before calling Glk hyperlink functions, you should use the following | |
| gestalt selectors. | |
| 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. | |
| You can test whether hyperlinks are supported with the | |
| gestalt_HyperlinkInput selector. | |
| 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. | |
| If you are writing a C program, you can perform a preprocessor test for the | |
| existence of GLK_MODULE_HYPERLINKS. If this is defined, so are all the | |
| functions and constants described in this section. If not, not. | |
| 10: The System Clock | |
| You can get the current time, either as a Unix timestamp (seconds since | |
| 1970) or as a broken-out structure of time elements (year, month, day, | |
| hour, minute, second). | |
| The system clock is not guaranteed to line up with timer events (see | |
| section 4.4, "Timer Events"). Timer events may be delivered late according | |
| to the system clock. | |
| void glk_current_time(glktimeval_t *time); | |
| typedef struct glktimeval_struct { | |
| glsi32 high_sec; | |
| glui32 low_sec; | |
| glsi32 microsec; | |
| } glktimeval_t; | |
| The current Unix time is stored in the structure. (The argument may not be | |
| NULL.) This is the number of seconds since the beginning of 1970 (UTC). | |
| The first two values in the structure should be considered a single | |
| *signed* 64-bit number. This allows the glktimeval_t to store a reasonable | |
| range of values in the future and past. The high_sec value will remain zero | |
| until sometime in 2106. If your computer is running in 1969, perhaps due to | |
| an unexpected solar flare, then high_sec will be negative. | |
| The third value in the structure represents a fraction of a second, in | |
| microseconds (from 0 to 999999). The resolution of the glk_current_time() | |
| call is platform-dependent; the microsec value may not be updated | |
| continuously. | |
| glsi32 glk_current_simple_time(glui32 factor); | |
| If dealing with 64-bit values is awkward, you can also get the current time | |
| as a lower-resolution 32-bit value. This is simply the Unix time divided by | |
| the factor argument (which must not be zero). For example, if factor is 60, | |
| the result will be the number of minutes since 1970 (rounded towards | |
| negative infinity). If factor is 1, you will get the Unix time directly, | |
| but the value will be truncated starting some time in 2038. | |
| 10.1: Time and Date Conversions | |
| void glk_time_to_date_utc(glktimeval_t *time, glkdate_t *date); | |
| void glk_time_to_date_local(glktimeval_t *time, glkdate_t *date); | |
| typedef struct glkdate_struct { | |
| glsi32 year; /* full (four-digit) year */ | |
| glsi32 month; /* 1-12, 1 is January */ | |
| glsi32 day; /* 1-31 */ | |
| glsi32 weekday; /* 0-6, 0 is Sunday */ | |
| glsi32 hour; /* 0-23 */ | |
| glsi32 minute; /* 0-59 */ | |
| glsi32 second; /* 0-59, maybe 60 during a leap second */ | |
| glsi32 microsec; /* 0-999999 */ | |
| } glkdate_t; | |
| Convert the given timestamp (as returned by glk_current_time()) to a | |
| broken-out structure. The "utc" function returns a date and time in | |
| universal time (GMT); the "local" function returns local time. | |
| [[The seconds value may be 60 because of a leap second.]] | |
| void glk_simple_time_to_date_utc(glsi32 time, glui32 factor, glkdate_t | |
| *date); | |
| void glk_simple_time_to_date_local(glsi32 time, glui32 factor, | |
| glkdate_t *date); | |
| Convert the given timestamp (as returned by glk_current_simple_time()) to a | |
| broken-out structure in universal or local time. The time argument is | |
| multiplied by factor to produce a Unix timestamp. | |
| Since the resolution of these functions is no better than seconds, they | |
| will return zero for the microseconds value. | |
| void glk_date_to_time_utc(glkdate_t *date, glktimeval_t *time); | |
| void glk_date_to_time_local(glkdate_t *date, glktimeval_t *time); | |
| Convert the broken-out structure (interpreted as universal or local time) | |
| to a timestamp. The weekday value in glkdate_t is ignored. The other values | |
| need not be in their normal ranges; they will be normalized. | |
| If the time cannot be represented by the platform's time library, this may | |
| return -1 for the seconds value. (I.e., the high_sec and low_sec fields | |
| both $FFFFFFFF. The microseconds field is undefined in this case.) | |
| The glk_date_to_time_local() function may not be smart about Daylight | |
| Saving Time conversions. [[If implemented with the mktime() libc function, | |
| it should use the negative tm_isdst flag to "attempt to divine whether | |
| summer time is in effect".]] | |
| glsi32 glk_date_to_simple_time_utc(glkdate_t *date, glui32 factor); | |
| glsi32 glk_date_to_simple_time_local(glkdate_t *date, glui32 factor); | |
| Convert the broken-out structure (interpreted as universal or local time) | |
| to a timestamp divided by factor. The weekday value in glkdate_t is | |
| ignored. The other values need not be in their normal ranges; they will be | |
| normalized. | |
| If the time cannot be represented by the platform's time library, this may | |
| return -1. | |
| 10.2: Testing for Clock Capabilities | |
| Before calling Glk date and time functions, you should use the following | |
| gestalt selector. | |
| 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 section 4.4, | |
| "Timer Events").]] | |
| If you are writing a C program, you can perform a preprocessor test for the | |
| existence of GLK_MODULE_DATETIME. If this is defined, so are all the | |
| functions and data types described in this section. | |
| 11: Porting, Adapting, and Other Messy Bits | |
| Life is not perfect, and neither are our toys. In a world of perfect toys, | |
| a Glk program could compile with any Glk library and run without human | |
| intervention. Guess what. | |
| 11.1: Startup Options | |
| One large grey area is starting up, startup files, and other program | |
| options. It is easy to assume that all C programs run with the (argc, argv) | |
| model -- that all the information they need comes as an array of strings at | |
| startup time. This is sometimes true. But in a GUI system, files are often | |
| opened by clicking, options are specified in dialog boxes, and so on; and | |
| this does not necessarily happen at the beginning of main(). | |
| Therefore, Glk does not try to pass an (argc, argv) list to your | |
| glk_main(). Nor does it provide a portable API for startup files and | |
| options. [[Doing that well would require API calls to parse command-line | |
| arguments of various types, and then also design and handle dialog boxes. | |
| It would go far beyond the level of complexity which Glk aspires to.]] | |
| Instead, startup files and options are handled in an *entirely | |
| platform-dependent* manner. You, as the author of a Glk program, must | |
| describe how your program should behave. As your program is ported to | |
| various Glk libraries, each porter must decide how to implement that | |
| behavior on the platform in question. The library should store the options | |
| and files in global variables, where your glk_main() routine can read them. | |
| It is reasonable to modularize this code, and call it the "startup code". | |
| But the startup code is not necessarily a single function, and it certainly | |
| does not have well-defined arguments such as an (argc, argv) list. You can | |
| consider that your startup behavior is divided into the messy part, which | |
| is nonportable and goes in the startup code, and the clean part, which is | |
| entirely Glk-portable and goes at the beginning of glk_main(). | |
| This is not as much of a mess as it sounds. Many programs, and almost all | |
| IF programs, follow one of a few simple models. | |
| * The simple model: There are no startup files. The program just starts | |
| running when invoked. | |
| * The game-file model: The program begins running when it is handed a | |
| single file of a particular type. On command-line systems, this comes as a | |
| filename in a command-line option. On GUI systems, it will usually be a | |
| platform-native event which contains a file reference. | |
| Any Glk library will be able to support these two models, probably through | |
| compile-time options. The details will vary. [[For one notable case, the | |
| Mac Glk library has two possible behaviors when compiled with the game-file | |
| model. If the player double-clicks a game file, the library calls | |
| glk_main() immediately. If the player double-clicks the application icon, | |
| the library allows the player to wait, perhaps adjusting preferences; it | |
| only calls glk_main() after the game file is selected through a file | |
| dialog.]] | |
| [[In fact, if life were this simple, it would be worth adding these models | |
| to the Glk API somehow. Unfortunately, it's not. Consider AGT: the "game | |
| file" is actually about ten separate files with related filenames, in the | |
| same directory. Glk does not contain API calls to do precise file and | |
| pathname manipulation; it is too complicated an area to support. So this | |
| situation must be handled non-portably.]] | |
| More complicated models are also possible. You might want to accept files | |
| through GUI events at any time, not just at startup. This could be handled | |
| by defining a new Glk event type, and having the library send such an event | |
| when a platform-native icon-click is detected. You would then have to | |
| decide how the situation should be handled in a command-line Glk library. | |
| But that is inherent in your task as a program author. | |
| Options and preferences are a separate problem. Most often, a command-line | |
| library will handle them with command-line arguments, and a GUI library | |
| will handle them with a dialog box. Ideally, you should describe how both | |
| cases should behave -- list the command-line arguments, and perhaps how | |
| they could be labelled in a dialog. [[This is unlikely to be very | |
| complicated. Although who knows.]] | |
| Remember that the Glk library is likely to have some options of its own -- | |
| matters of display styles and so on. A command-line library will probably | |
| have a simple API to extract its own options and pass the rest on to the | |
| startup code. | |
| 11.2: Going Outside the Glk API | |
| Nonportable problems are not limited to the start of execution. There is | |
| also the question of OS services which are not represented in Glk. The ANSI | |
| C libraries are so familiar that they seem universal, but they are actually | |
| not necessarily present. Palmtop platforms such as PalmOS are particularly | |
| good at leaving out ANSI libraries. | |
| 11.2.1: Memory Management | |
| Everyone uses malloc(), realloc(), and free(). However, some platforms have | |
| a native memory-management API which may be more suitable in porting your | |
| program. | |
| The malloc() system is simple; it can probably be implemented as a layer on | |
| top of whatever native API is available. So you don't absolutely have to | |
| worry about this. However, it can't hurt to group all your malloc() and | |
| free() calls in one part of your program, so that a porter can easily | |
| change them all if it turns out to be a good idea. | |
| 11.2.2: String Manipulation | |
| This is more of a nuisance, because the set of string functions varies | |
| quite a bit between platforms. Consider bcopy(), memcpy(), and memmove(); | |
| stricmp() and strcasecmp(); strchr() and index(); and so on. And again, on | |
| a palmtop machine, none of these may be available. The maximally safe | |
| course is to implement what you need yourself. [[See the model.c program | |
| for an example; it implements its own str_eq() and str_len().]] | |
| The maximally safe course is also a pain in the butt, and may well be | |
| inefficient (a platform may have a memcpy() which is highly optimized for | |
| large moves.) That's porting in the big city. | |
| [[By the way, the next person I see who #defines memmove() as memcpy() when | |
| a real memmove() isn't available, gets slapped in the face with a | |
| lead-lined rubber salmon.]] | |
| 11.2.3: File Handling | |
| This is the real nuisance, because Glk provides a limited set of stream and | |
| file functions. And yet there are all these beautiful ANSI stdio calls, | |
| which have all these clever tricks -- ungetc(), fast macro fgetc(), | |
| formatted fprintf(), not to mention the joys of direct pathname | |
| manipulation. Why bother with the Glk calls? | |
| The problem is, the stdio library really isn't always the best choice, | |
| particularly on mobile OSes. | |
| There's also the problem of hooking into the Glk API. Window output goes | |
| through Glk streams. [[It would have been lovely to use the stdio API for | |
| that, but it's not generally possible.]] | |
| As usual, it's a judgement call. If you have a large existing pile of | |
| source code which you're porting, and it uses a lot of icky stdio features | |
| like ungetc(), it may be better not to bother changing everything to the | |
| Glk file API. If you're starting from scratch, using the Glk calls will | |
| probably be cleaner. | |
| 11.2.4: Private Extensions to Glk | |
| Sometimes -- hopefully rarely -- there's stuff you just gotta do. | |
| Explicit pathname modification is one possible case. Creating or deleting | |
| directories. New Glk event types caused by interface events. Control over | |
| pull-down menus. | |
| Like startup code, you just have to decide what you want, and ask your | |
| porters to port it. These are the non-portable parts of your task. As I | |
| said, that's porting in the big city. | |
| If an extension or new function is so useful that everyone is implementing | |
| it, I'll consider adding it to the Glk API (as an optional capability, with | |
| a Gestalt selector and everything.) I'm flexible. In a morally correct | |
| manner, of course. | |
| 11.3: Glk and the Virtual Machine | |
| Most IF games are built on a virtual machine, such as the Z-machine or the | |
| TADS runtime structure. Building a virtual machine which uses Glk as its | |
| interface is somewhat more complicated than writing a single Glk program. | |
| The question to ask is: what API will be exported to the game author -- the | |
| person writing a program to run on the VM? | |
| 11.3.1: Implementing a Higher Layer Over Glk | |
| Thus far, each virtual machine has had its own built-in I/O API. Most of | |
| them have identical basic capabilities -- read lines of input, display a | |
| stream of output, show a status line of some sort, and so on. This | |
| commonality, of course, is the ground from which Glk sprouted in the first | |
| place. | |
| If the I/O API is a subset of the capabilities of Glk, it can be | |
| implemented as a layer on top of Glk. In this way, an existing VM can often | |
| be ported to Glk without any change visible to the author. Standard TADS | |
| can be ported in this way; the V5/8 Z-machine can as well (with the sole | |
| exception, as far as I know, of colored text.) | |
| 11.3.2: Glk as a VM's Native API | |
| The other approach is to use Glk as the virtual machine's own I/O API, and | |
| provide it directly to the game author. The Glulx virtual machine is built | |
| this way. This is inherently more powerful, since it allows access to all | |
| of Glk, instead of a subset. As Glk is designed to be easily expandable, | |
| and will gain new (optional) capabilities over time, this approach also | |
| allows a VM to gain capabilities over time without much upheaval. | |
| [[To a certain extent, Glk was designed for this use more than any other. | |
| For example, this is why all Glk function arguments are either pointers or | |
| 32-bit integers, and why all Glk API structures are effectively arrays of | |
| same. It is also why the iterator functions exist; a VM's entire memory | |
| space may be reset by an "undo" or "restore" command, and it would then | |
| have to, ah, take inventory of its streams and windows and filerefs.]] | |
| [[This is also another reason why Glk provides file API calls. A VM can | |
| provide Glk as the game author's entire access to the file system, as well | |
| as the author's entire access to the display system. The VM will then be | |
| simpler, more modular, not as tied into the native OS -- all that good | |
| stuff.]] | |
| [[The Society of C Pedants wishes me to point out that the structures in | |
| the Glk API aren't *really* arrays of 32-bit integers. A structure made up | |
| entirely of 32-bit integers can still be padded weirdly by a C compiler. | |
| This problem is solved cleanly by the dispatch layer; see below.]] | |
| The mechanics of this are tricky, because Glk has many API calls, and more | |
| will be added over time. | |
| In a VM with a limited number of opcodes, it may be best to allocate a | |
| single "Glk" opcode, with a variable number of arguments, the first of | |
| which is a function selector. (Glulx does this.) Allow at least 16 bits for | |
| this selector; there may be more than 256 Glk calls someday. (For a list of | |
| standard selectors for Glk calls, see section 12.1.6, "Table of Selectors".) | |
| In a VM with a large opcode space, you could reserve a 16-bit range of | |
| opcodes for Glk. | |
| It may also be feasible to extend the function-call mechanism in some way, | |
| to include the range of Glk functions. | |
| In any case, the API still has to be exported to the game author in | |
| whatever language is compiled to the VM. Ideally, this can be done as a set | |
| of function calls. [[But it doesn't have to be. The Inform compiler, for | |
| example, can accept assembly opcodes in-line with Inform source code. It's | |
| nearly as convenient to let the author type in opcodes as function calls.]] | |
| There is a further complication when new calls are added to Glk. This | |
| should not be a major problem. The compiler is mapping Glk calls one-to-one | |
| to its own functions or opcodes, so this should be a matter of adding to a | |
| fixed list somewhere in the compiler and releasing an upgrade. | |
| Alternatively, if the compiler has some way to define new opcodes, even | |
| this much effort is not necessary. [[The Inform compiler is designed this | |
| way; the game author can define new opcodes and use them. So if a new call | |
| has been added to Glk, and it has been implemented in the interpreter with | |
| a known selector, it can be used in Inform immediately, without a compiler | |
| upgrade.]] | |
| Or, you can provide a completely dynamic interface to the Glk API. This is | |
| the province of the Glk dispatch layer, which is not part of Glk proper; it | |
| rests on top. See section 12.1, "The Dispatch Layer". | |
| 12: Appendices | |
| 12.1: The Dispatch Layer | |
| The material described in this section is not part of the Glk API per se. | |
| It is an external layer, lying on top of Glk, which allows a program to | |
| invoke Glk dynamically -- determining the capabilities and interfaces of | |
| Glk at run-time. | |
| This is most useful for virtual machines and other run-time systems, which | |
| want to use Glk without being bound to a particular version of the Glk API. | |
| In other words, a VM can export Glk to VM programs, without hard-wiring a | |
| list of Glk functions within itself. If a new Glk library is released, with | |
| new functions, the VM can simply link in the library; the new functions | |
| will be available to VM programs without further work. | |
| If you are writing a C program which uses the Glk API, you can ignore this | |
| section entirely. If you are writing a VM which uses Glk, you need to read | |
| it. If you are implementing a Glk library, you should also read it. (There | |
| are some additional interfaces which your library must support for the | |
| dispatch layer to work right.) | |
| 12.1.1: How This Works | |
| The dispatch layer is implemented in a C source file, gi_dispa.c, and its | |
| header, gi_dispa.h. This code is platform-independent -- it is identical in | |
| every library, just as the glk.h header file is identical in every library. | |
| Each library author should download the gi_dispa.c and gi_dispa.h files | |
| from the Glk web site, and compile them unchanged into the library. | |
| This code is mostly external to Glk; it operates by calling the documented | |
| Glk API, not library internals. This is how gi_dispa.c can be | |
| platform-independent. However, the dividing line is not perfect. There are | |
| a few extra functions, not part of the Glk API, which the library must | |
| implement; gi_dispa.c (and no one else) calls these functions. These | |
| functions are simple and should not make life much harder for library | |
| implementors. | |
| The dispatch layer then provides a dispatch API. The heart of this is the | |
| gidispatch_call() function, which allows you to call *any* Glk function | |
| (specified by number) and pass in a list of arguments in a standardized | |
| way. You may also make use of gidispatch_prototype(), which gives you the | |
| proper format of that list for each function. Ancilliary functions let you | |
| enumerate the functions and constants in the Glk API. | |
| 12.1.2: Interrogating the Interface | |
| These are the ancilliary functions that let you enumerate. | |
| glui32 gidispatch_count_classes(void); | |
| This returns the number of opaque object classes used by the library. You | |
| will need to know this if you want to keep track of opaque objects as they | |
| are created; see section 12.1.5.1, "Opaque Object Registry". | |
| As of Glk API 0.6.0, there are four classes: windows, streams, filerefs, | |
| and sound channels (numbered 0, 1, 2, and 3 respectively.) | |
| glui32 gidispatch_count_intconst(void); | |
| This returns the number of integer constants exported by the library. | |
| gidispatch_intconst_t *gidispatch_get_intconst(glui32 index); | |
| typedef struct gidispatch_intconst_struct { | |
| char *name; | |
| glui32 val; | |
| } gidispatch_intconst_t; | |
| This returns a structure describing an integer constant which the library | |
| exports. These are, roughly, all the constants defined in the glk.h file. | |
| index can range from 0 to N-1, where N is the value returned by | |
| gidispatch_count_intconst(). | |
| The structure simply contains a string and a value. The string is a | |
| symbolic name of the value, and can be re-exported to anyone interested in | |
| using Glk constants. | |
| [[In the current gi_dispa.c library, these structures are static and | |
| immutable, and will never be deallocated. However, it is safer to assume | |
| that the structure may be reused in future gidispatch_get_intconst() | |
| calls.]] | |
| glui32 gidispatch_count_functions(void); | |
| This returns the number of functions exported by the library. | |
| gidispatch_function_t *gidispatch_get_function(glui32 index); | |
| typedef struct gidispatch_function_struct { | |
| glui32 id; | |
| void *fnptr; | |
| char *name; | |
| } gidispatch_function_t; | |
| This returns a structure describing a Glk function. index can range from 0 | |
| to N-1, where N is the value returned by gidispatch_count_functions(). | |
| The id field is a selector -- a numeric constant used to refer to the | |
| function in question. name is the function name, as it is given in the | |
| glk.h file, but without the "glk_" prefix. And fnptr is the address of the | |
| function itself. [[This is included because it might be useful, but it is | |
| *not* recommended. To call an arbitrary Glk function, you should use | |
| gidispatch_call().]] See section 12.1.6, "Table of Selectors" for the | |
| selector definitions. See section 12.1.3, "Dispatching" for more about | |
| calling Glk functions by selector. | |
| gidispatch_function_t *gidispatch_get_function_by_id(glui32 id); | |
| This returns a structure describing the Glk function with selector id. If | |
| there is no such function in the library, this returns NULL. | |
| [[Again, it is safest to assume that the structure is only valid until the | |
| next gidispatch_get_function() or gidispatch_get_function_by_id() call.]] | |
| 12.1.3: Dispatching | |
| void gidispatch_call(glui32 funcnum, glui32 numargs, gluniversal_t | |
| *arglist); | |
| funcnum is the function number to invoke; see section 12.1.6, "Table of | |
| Selectors". arglist is the list of arguments, and numargs is the length of | |
| the list. | |
| The arguments are all stored as gluniversal_t objects. This is a union, | |
| encompassing all the types that can be passed to Glk functions. | |
| typedef union gluniversal_union { | |
| glui32 uint; | |
| glsi32 sint; | |
| void *opaqueref; | |
| unsigned char uch; | |
| signed char sch; | |
| char ch; | |
| char *charstr; | |
| void *array; | |
| glui32 ptrflag; | |
| } gluniversal_t; | |
| 12.1.3.1: Basic Types | |
| Numeric arguments are passed in the obvious way -- one argument per | |
| gluniversal_t, with the uint or sint field set to the numeric value. | |
| Characters and strings are also passed in this way -- chars in the uch, | |
| sch, or ch fields (depending on whether the char is signed) and strings in | |
| the charstr field. Opaque objects (windows, streams, etc) are passed in the | |
| opaqueref field (which is void*, in order to handle all opaque pointer | |
| types.) | |
| However, pointers (other than C strings), arrays, and structures complicate | |
| life. So do return values. | |
| 12.1.3.2: References | |
| A reference to a numeric type or object reference -- that is, glui32*, | |
| winid_t*, and so on -- takes *one or two* gluniversal_t objects. The first | |
| is a flag indicating whether the reference argument is NULL or not. The | |
| ptrflag field of this gluniversal_t should be FALSE (0) if the reference is | |
| NULL, and TRUE (1) otherwise. If FALSE, that is the end of the argument; | |
| you should not use a gluniversal_t to explicitly store the NULL reference. | |
| If the flag is TRUE, you must then put a gluniversal_t storing the base | |
| type of the reference. | |
| For example, consider a hypothetical function, with selector 0xABCD: | |
| void glk_glomp(glui32 num, winid_t win, glui32 *numref, strid_t | |
| *strref); | |
| ...and the calls: | |
| glui32 value; | |
| winid_t mainwin; | |
| strid_t gamefile; | |
| glk_glomp(5, mainwin, &value, &gamefile); | |
| To perform this through gidispatch_call(), you would do the following: | |
| gluniversal_t arglist[6]; | |
| arglist[0].uint = 5; | |
| arglist[1].opaqueref = mainwin; | |
| arglist[2].ptrflag = TRUE; | |
| arglist[3].uint = value; | |
| arglist[4].ptrflag = TRUE; | |
| arglist[5].opaqueref = gamefile; | |
| gidispatch_call(0xABCD, 6, arglist); | |
| value = arglist[3].uint; | |
| gamefile = arglist[5].opaqueref; | |
| Note that you copy the value of the reference arguments into and out of | |
| arglist. Of course, it may be that glk_glomp() only uses these as pass-out | |
| references or pass-in references; if so, you could skip copying in or out. | |
| For further examples: | |
| glk_glomp(7, mainwin, NULL, NULL); | |
| ...or... | |
| gluniversal_t arglist[4]; | |
| arglist[0].uint = 7; | |
| arglist[1].opaqueref = mainwin; | |
| arglist[2].ptrflag = FALSE; | |
| arglist[3].ptrflag = FALSE; | |
| gidispatch_call(0xABCD, 4, arglist); | |
| glk_glomp(13, NULL, NULL, &gamefile); | |
| ...or... | |
| gluniversal_t arglist[5]; | |
| arglist[0].uint = 13; | |
| arglist[1].opaqueref = NULL; | |
| arglist[2].ptrflag = FALSE; | |
| arglist[3].ptrflag = TRUE; | |
| arglist[4].opaqueref = gamefile; | |
| gidispatch_call(0xABCD, 5, arglist); | |
| gamefile = arglist[4].opaqueref; | |
| glk_glomp(17, NULL, &value, NULL); | |
| ...or... | |
| gluniversal_t arglist[5]; | |
| arglist[0].uint = 17; | |
| arglist[1].opaqueref = NULL; | |
| arglist[2].ptrflag = TRUE; | |
| arglist[3].uint = value; | |
| arglist[4].ptrflag = FALSE; | |
| gidispatch_call(0xABCD, 5, arglist); | |
| value = arglist[3].uint; | |
| As you see, the length of arglist depends on how many of the reference | |
| arguments are NULL. | |
| 12.1.3.3: Structures | |
| A structure pointer is represented by a single ptrflag, possibly followed | |
| by a sequence of gluniversal_t objects (one for each field of the | |
| structure.) Again, if the structure pointer is non-NULL, the ptrflag should | |
| be TRUE and be followed by values; if not, the ptrflag should be NULL and | |
| stands alone. | |
| For example, the function glk_select() can be invoked as follows: | |
| event_t ev; | |
| gluniversal_t arglist[5]; | |
| arglist[0].ptrflag = TRUE; | |
| gidispatch_call(0x00C0, 5, arglist); | |
| ev.type = arglist[1].uint; | |
| ev.win = arglist[2].opaqueref; | |
| ev.val1 = arglist[3].uint; | |
| ev.val2 = arglist[4].uint; | |
| Since the structure passed to glk_select() is a pass-out reference (the | |
| entry values are ignored), you don't need to fill in arglist[1..4] before | |
| calling gidispatch_call(). | |
| [[Theoretically, you would invoke glk_select(NULL) by setting | |
| arglist[0].ptrflag to FALSE, and using a one-element arglist instead of | |
| five-element. But it's illegal to pass NULL to glk_select(). So you cannot | |
| actually do this.]] | |
| 12.1.3.4: Arrays | |
| In the Glk API, an array argument is always followed by a numeric argument | |
| giving the array's length. These two C arguments are a single logical | |
| argument, which is represented by *one or three* gluniversal_t objects. The | |
| first is a ptrflag, indicating whether the argument is NULL or not. The | |
| second is a pointer, stored in the array field. The third is the array | |
| length, stored in the uint field. And again, if the ptrflag is NULL, the | |
| following two are omitted. | |
| For example, the function glk_put_buffer() can be invoked as follows: | |
| char buf[64]; | |
| glui32 len = 64; | |
| glk_put_buffer(buf, len); | |
| ...or... | |
| gluniversal_t arglist[3]; | |
| arglist[0].ptrflag = TRUE; | |
| arglist[1].array = buf; | |
| arglist[2].uint = len; | |
| gidispatch_call(0x0084, 3, arglist); | |
| Since you are passing a C char array to gidispatch_call(), the contents | |
| will be read directly from that. There is no need to copy data into | |
| arglist, as you would for a basic type. | |
| If you are implementing a VM whose native representation of char arrays is | |
| more complex, you will have to do more work. You should allocate a C char | |
| array, copy your characters into it, make the call, and then free the | |
| array. [[glk_put_buffer() does not modify the array passed to it, so there | |
| is no need to copy the characters out.]] | |
| 12.1.3.5: Return Values | |
| The return value of a function is not treated specially. It is simply | |
| considered to be a pass-out reference argument which may not be NULL. It | |
| comes after all the other arguments of the function. | |
| For example, the function glk_window_get_rock() can be invoked as follows: | |
| glui32 rock; | |
| winid_t win; | |
| rock = glk_window_get_rock(win); | |
| ...or... | |
| gluniversal_t arglist[3]; | |
| arglist[0].opaqueref = win; | |
| arglist[1].ptrflag = TRUE; | |
| gidispatch_call(0x0021, 3, arglist); | |
| rock = arglist[2].uint; | |
| 12.1.4: Getting Argument Prototypes | |
| There are many possible ways to set up a gluniversal_t array, and it's | |
| illegal to call gidispatch_call() with an array which doesn't match the | |
| function. Furthermore, some references are passed in, some passed out, and | |
| some both. How do you know how to handle the argument list? | |
| One possibility is to recognize each function selector, and set up the | |
| arguments appropriately. However, this entails writing special code for | |
| each Glk function; which is exactly what we don't want to do. | |
| Instead, you can call gidispatch_prototype(). | |
| char *gidispatch_prototype(glui32 funcnum); | |
| This returns a string which encodes the proper argument list for the given | |
| function. If there is no such function in the library, this returns NULL. | |
| The prototype string for the glk_glomp() function described above would be: | |
| "4IuQa&Iu&Qb:". The "4" is the number of arguments (including the return | |
| value, if there is one, which in this case there isn't.) "Iu" denotes an | |
| unsigned integer; "Qa" is an opaque object of class 0 (window). "&Iu" is a | |
| *reference* to an unsigned integer, and "&Qb" is a reference to a stream. | |
| The colon at the end terminates the argument list; the return value would | |
| follow it, if there was one. | |
| Note that the initial number ("4" in this case) is the number of logical | |
| arguments, not the number of gluniversal_t objects which will be passed to | |
| gidispatch_call(). The glk_glomp() call uses anywhere from four to six | |
| gluniversal_t objects, as demonstrated above. | |
| The basic type codes: | |
| * Iu, Is: Unsigned and signed 32-bit integer. | |
| * Cn, Cu, Cs: Character, unsigned char, and signed char. [[Of course Cn | |
| will be the same as either Cu or Cs, depending on the platform. For this | |
| reason, Glk avoids using it, but it is included here for completeness.]] | |
| * S: A C-style string (null-terminated array of char). In Glk, strings | |
| are always treated as read-only and used immediately; the library does not | |
| retain a reference to a string between Glk calls. A Glk call that wants to | |
| use writable char arrays will use an array type ("#C"), not string ("S"). | |
| * U: A zero-terminated array of 32-bit integers. This is primarily | |
| intended as a Unicode equivalent of "S". Like "S" strings, "U" strings are | |
| read-only and used immediately. A Glk call that wants to use writable | |
| Unicode arrays will use an array type ("#Iu") instead of "U". | |
| * F: A floating-point value. Glk does not currently use floating-point | |
| values, but we might as well define a code for them. | |
| * Qa, Qb, Qc...: A reference to an opaque object. The second letter | |
| determines which class is involved. (The number of classes can be gleaned | |
| from gidispatch_count_classes(); see section 12.1.2, "Interrogating the | |
| Interface"). [[If Glk expands to have more than 26 classes, we'll think of | |
| something.]] | |
| Any type code can be prefixed with one or more of the following characters: | |
| * &: A reference to the type; or, if you like, a variable passed by | |
| reference. The reference is passed both in and out, so you must copy the | |
| value in before calling gidispatch_call() and copy it out afterward. | |
| * <: A reference which is pass-out only. The initial value is ignored, | |
| so you only need copy out the value after the call. | |
| * >: A reference which is pass-in only. [[This is not generally used | |
| for simple types, but is useful for structures and arrays.]] | |
| * +: Combined with "&", "<", or ">", indicates that a valid reference | |
| is mandatory; NULL cannot be passed. [[Note that even though the ptrflag | |
| gluniversal_t for a "+" reference is always TRUE, it cannot be omitted.]] | |
| * :: The colon separates the arguments from the return value, or | |
| terminates the string if there is no return value. Since return values are | |
| always non-NULL pass-out references, you may treat ":" as equivalent to | |
| "<+". The colon is never combined with any other prefix character. | |
| * [...]: Combined with "&", "<", or ">", indicates a structure | |
| reference. Between the brackets is a complete argument list encoding | |
| string, including the number of arguments. [[For example, the prototype | |
| string for glk_select() is "1<+[4IuQaIuIu]:" -- one argument, which is a | |
| pass-out non-NULL reference to a structure, which contains four | |
| arguments.]] Currently, structures in Glk contain only basic types. | |
| * #: Combined with "&", "<", or ">", indicates an array reference. As | |
| described above, this encompasses up to three gluniversal_t objects -- | |
| ptrflag, pointer, and integer length. [[Depending on the design of your | |
| program, you may wish to pass a pointer directly to your program's memory, | |
| or allocate an array and copy the contents in and out. See section | |
| 12.1.3.4, "Arrays".]] | |
| * !: Combined with "#", indicates that the array is *retained* by the | |
| library. The library will keep a reference to the array; the contents are | |
| undefined until further notice. You should not use or copy the contents of | |
| the array out after the call, even for "&#!" or "<#!" arrays. Instead, do | |
| it when the library releases the array. [[For example, | |
| glk_stream_open_memory() retains the array that you pass it, and releases | |
| it when the stream is closed. The library can notify you automatically when | |
| arrays are retained and released; see section 12.1.5.2, "Retained Array | |
| Registry".]] | |
| The order of these characters and prefixes is not completely arbitrary. | |
| Here is a formal grammar for the prototype strings. [[Thanks to Neil | |
| Cerutti for working this out.]] | |
| <prototype> -> ArgCount [ <arg_list> ] ':' [ <arg> ] EOL | |
| <arg_list> -> <arg> { <arg> } | |
| <arg> -> TypeName | <ref_type> | |
| <ref_type> -> RefType [ '+' ] <target_type> | |
| <target_type> -> TypeName | <array> | <struct> | |
| <array> -> '#' [ '!' ] TypeName | |
| <struct> -> '[' ArgCount [ <arg_list> ] ']' | |
| TypeName is "I[us]|C[nus]|S|U|F|Q[a-z]" | |
| ArgCount is '\d+' | |
| RefType is '&|<|>' | |
| EOL is end of input | |
| 12.1.5: Functions the Library Must Provide | |
| Ideally, the three layers -- program, dispatch layer, Glk library -- would | |
| be completely modular; each would refer only to the layers beneath it. | |
| Sadly, there are a few places where the library must notify the program | |
| that something has happened. Worse, these situations are only relevant to | |
| programs which use the dispatch layer, and then only some of those. | |
| Since C is uncomfortable with the concept of calling functions which may | |
| not exist, Glk handles this with call-back function pointers. The program | |
| can pass callbacks in to the library; if it does, the library will call | |
| them, and if not, the library doesn't try. | |
| These callbacks are optional, in the sense that the program may or may not | |
| set them. However, any library which wants to interoperate with the | |
| dispatch layer must *allow* the program to set them; it is the program's | |
| choice. The library does this by implementing set_registry functions -- the | |
| functions to which the program passes its callbacks. | |
| [[Even though these callbacks and the functions to set them are declared in | |
| gi_dispa.h, they are not defined in gi_dispa.c. The dispatch layer merely | |
| coordinates them. The program defines the callback functions; the library | |
| calls them.]] | |
| 12.1.5.1: Opaque Object Registry | |
| The Glk API refers to opaque objects by pointer; but a VM probably cannot | |
| store pointers to native memory. Therefore, a VM program will want to keep | |
| a VM-accessible collection of opaque objects. [[For example, it might keep | |
| a hash table for each opaque object class, mapping integer identifiers to | |
| object pointers.]] | |
| To make this possible, a Glk library must implement | |
| gidispatch_set_object_registry(). | |
| void gidispatch_set_object_registry(gidispatch_rock_t (*reg)(void *obj, | |
| glui32 objclass), void (*unreg)(void *obj, glui32 objclass, | |
| gidispatch_rock_t objrock)); | |
| Your program calls this early (before it begins actually executing VM | |
| code.) You pass in two function pointers, matching the following prototypes: | |
| gidispatch_rock_t my_vm_reg_object(void *obj, glui32 objclass); | |
| void my_vm_unreg_object(void *obj, glui32 objclass, gidispatch_rock_t | |
| objrock); | |
| Whenever the Glk library creates an object, it will call | |
| my_vm_reg_object(). It will pass the object pointer and the class number | |
| (from 0 to N-1, where N is the value returned by | |
| gidispatch_count_classes().) | |
| You can return any value in the gidispatch_rock_t object; the library will | |
| stash this away inside the object. [[Note that this is entirely separate | |
| from the regular Glk rock, which is always a glui32 and can be set | |
| independently.]] | |
| typedef union glk_objrock_union { | |
| glui32 num; | |
| void *ptr; | |
| } gidispatch_rock_t; | |
| Whenever the Glk library destroys an object, it will call | |
| my_vm_unreg_object(). It passes you the object pointer, class number, and | |
| the object rock. | |
| You can, at any time, get the object rock of an object. The library | |
| implements this function: | |
| gidispatch_rock_t gidispatch_get_objrock(void *obj, glui32 objclass); | |
| With this and your two callbacks, you can maintain (say) a hash table for | |
| each object class, and easily convert back and forth between hash table | |
| keys and Glk object pointers. A more sophisticated run-time system (such as | |
| Java) could create a typed VM object for every Glk object, thus allowing VM | |
| code to manipulate Glk objects intelligently. | |
| One significant detail: It is possible that some Glk objects will already | |
| exist when your glk_main() function is called. [[For example, MacGlk can | |
| open a stream when the user double-clicks a file; this occurs before | |
| glk_main().]] So when you call gidispatch_set_object_registry(), it may | |
| *immediately* call your my_vm_reg_object() callback, notifying you of the | |
| existing objects. You must be prepared for this possibility. [[If you are | |
| keeping hash tables, for example, create them *before* you call | |
| gidispatch_set_object_registry().]] | |
| 12.1.5.2: Retained Array Registry | |
| A few Glk functions take an array and hold onto it. The memory is "owned" | |
| by the library until some future Glk call releases it. While the library | |
| retains the array, your program should not read, write, move, or deallocate | |
| it. When the library releases it, the contents are in their final form, and | |
| you can copy them out (if appropriate) and dispose of the memory as you | |
| wish. | |
| To allow this, the library implements gidispatch_set_retained_registry(). | |
| void gidispatch_set_retained_registry(gidispatch_rock_t (*reg)(void | |
| *array, glui32 len, char *typecode), void (*unreg)(void *array, glui32 len, | |
| char *typecode, gidispatch_rock_t objrock)); | |
| Again, you pass in two function pointers: | |
| gidispatch_rock_t my_vm_reg_array(void *array, glui32 len, char | |
| *typecode); | |
| void my_vm_unreg_array(void *array, glui32 len, char *typecode, | |
| gidispatch_rock_t objrock); | |
| Whenever a Glk function retains an array, it will call my_vm_reg_array(). | |
| This occurs only if you pass an array to an argument with the "#!" prefix. | |
| [[But not in every such case. Wait for the my_vm_reg_array() call to | |
| confirm it.]] The library passes the array and its length, exactly as you | |
| put them in the gluniversal_t array. It also passes the string which | |
| describes the argument. | |
| [[Currently, the only calls that retain arrays are | |
| glk_request_line_event(), glk_stream_open_memory(), | |
| glk_request_line_event_uni(), and glk_stream_open_memory_uni(). The first | |
| two of these use arrays of characters, so the string is "&+#!Cn". The | |
| latter two use arrays of glui32, so the string is "&+#!Iu".]] | |
| You can return any value in the gidispatch_rock_t object; the library will | |
| stash this away with the array. | |
| When a Glk function releases a retained array, it will call | |
| my_vm_unreg_array(). It passes back the same array, len, and typecode | |
| parameters, as well as the gidispatch_rock_t you returned from | |
| my_vm_reg_array(). | |
| With these callbacks, you can maintain a collection of retained arrays. You | |
| can use this to copy data from C arrays to your own data structures, or | |
| keep relocatable memory locked, or prevent a garbage-collection system from | |
| deallocating an array while Glk is writing to it. | |
| 12.1.6: Table of Selectors | |
| These values, and the values used for future Glk calls, are integers in the | |
| range 0x0001 to 0xFFFF (1 to 65535). The values are not sequential; they | |
| are divided into groups, roughly by category. Zero is not the selector of | |
| any Glk call, so it may be used for a null value. | |
| * 0x0001: glk_exit | |
| * 0x0002: glk_set_interrupt_handler | |
| * 0x0003: glk_tick | |
| * 0x0004: glk_gestalt | |
| * 0x0005: glk_gestalt_ext | |
| * 0x0020: glk_window_iterate | |
| * 0x0021: glk_window_get_rock | |
| * 0x0022: glk_window_get_root | |
| * 0x0023: glk_window_open | |
| * 0x0024: glk_window_close | |
| * 0x0025: glk_window_get_size | |
| * 0x0026: glk_window_set_arrangement | |
| * 0x0027: glk_window_get_arrangement | |
| * 0x0028: glk_window_get_type | |
| * 0x0029: glk_window_get_parent | |
| * 0x002A: glk_window_clear | |
| * 0x002B: glk_window_move_cursor | |
| * 0x002C: glk_window_get_stream | |
| * 0x002D: glk_window_set_echo_stream | |
| * 0x002E: glk_window_get_echo_stream | |
| * 0x002F: glk_set_window | |
| * 0x0030: glk_window_get_sibling | |
| * 0x0040: glk_stream_iterate | |
| * 0x0041: glk_stream_get_rock | |
| * 0x0042: glk_stream_open_file | |
| * 0x0043: glk_stream_open_memory | |
| * 0x0044: glk_stream_close | |
| * 0x0045: glk_stream_set_position | |
| * 0x0046: glk_stream_get_position | |
| * 0x0047: glk_stream_set_current | |
| * 0x0048: glk_stream_get_current | |
| * 0x0049: glk_stream_open_resource | |
| * 0x0060: glk_fileref_create_temp | |
| * 0x0061: glk_fileref_create_by_name | |
| * 0x0062: glk_fileref_create_by_prompt | |
| * 0x0063: glk_fileref_destroy | |
| * 0x0064: glk_fileref_iterate | |
| * 0x0065: glk_fileref_get_rock | |
| * 0x0066: glk_fileref_delete_file | |
| * 0x0067: glk_fileref_does_file_exist | |
| * 0x0068: glk_fileref_create_from_fileref | |
| * 0x0080: glk_put_char | |
| * 0x0081: glk_put_char_stream | |
| * 0x0082: glk_put_string | |
| * 0x0083: glk_put_string_stream | |
| * 0x0084: glk_put_buffer | |
| * 0x0085: glk_put_buffer_stream | |
| * 0x0086: glk_set_style | |
| * 0x0087: glk_set_style_stream | |
| * 0x0090: glk_get_char_stream | |
| * 0x0091: glk_get_line_stream | |
| * 0x0092: glk_get_buffer_stream | |
| * 0x00A0: glk_char_to_lower | |
| * 0x00A1: glk_char_to_upper | |
| * 0x00B0: glk_stylehint_set | |
| * 0x00B1: glk_stylehint_clear | |
| * 0x00B2: glk_style_distinguish | |
| * 0x00B3: glk_style_measure | |
| * 0x00C0: glk_select | |
| * 0x00C1: glk_select_poll | |
| * 0x00D0: glk_request_line_event | |
| * 0x00D1: glk_cancel_line_event | |
| * 0x00D2: glk_request_char_event | |
| * 0x00D3: glk_cancel_char_event | |
| * 0x00D4: glk_request_mouse_event | |
| * 0x00D5: glk_cancel_mouse_event | |
| * 0x00D6: glk_request_timer_events | |
| * 0x00E0: glk_image_get_info | |
| * 0x00E1: glk_image_draw | |
| * 0x00E2: glk_image_draw_scaled | |
| * 0x00E8: glk_window_flow_break | |
| * 0x00E9: glk_window_erase_rect | |
| * 0x00EA: glk_window_fill_rect | |
| * 0x00EB: glk_window_set_background_color | |
| * 0x00F0: glk_schannel_iterate | |
| * 0x00F1: glk_schannel_get_rock | |
| * 0x00F2: glk_schannel_create | |
| * 0x00F3: glk_schannel_destroy | |
| * 0x00F4: glk_schannel_create_ext | |
| * 0x00F7: glk_schannel_play_multi | |
| * 0x00F8: glk_schannel_play | |
| * 0x00F9: glk_schannel_play_ext | |
| * 0x00FA: glk_schannel_stop | |
| * 0x00FB: glk_schannel_set_volume | |
| * 0x00FC: glk_sound_load_hint | |
| * 0x00FD: glk_schannel_set_volume_ext | |
| * 0x00FE: glk_schannel_pause | |
| * 0x00FF: glk_schannel_unpause | |
| * 0x0100: glk_set_hyperlink | |
| * 0x0101: glk_set_hyperlink_stream | |
| * 0x0102: glk_request_hyperlink_event | |
| * 0x0103: glk_cancel_hyperlink_event | |
| * 0x0120: glk_buffer_to_lower_case_uni | |
| * 0x0121: glk_buffer_to_upper_case_uni | |
| * 0x0122: glk_buffer_to_title_case_uni | |
| * 0x0123: glk_buffer_canon_decompose_uni | |
| * 0x0124: glk_buffer_canon_normalize_uni | |
| * 0x0128: glk_put_char_uni | |
| * 0x0129: glk_put_string_uni | |
| * 0x012A: glk_put_buffer_uni | |
| * 0x012B: glk_put_char_stream_uni | |
| * 0x012C: glk_put_string_stream_uni | |
| * 0x012D: glk_put_buffer_stream_uni | |
| * 0x0130: glk_get_char_stream_uni | |
| * 0x0131: glk_get_buffer_stream_uni | |
| * 0x0132: glk_get_line_stream_uni | |
| * 0x0138: glk_stream_open_file_uni | |
| * 0x0139: glk_stream_open_memory_uni | |
| * 0x013A: glk_stream_open_resource_uni | |
| * 0x0140: glk_request_char_event_uni | |
| * 0x0141: glk_request_line_event_uni | |
| * 0x0150: glk_set_echo_line_event | |
| * 0x0151: glk_set_terminators_line_event | |
| * 0x0160: glk_current_time | |
| * 0x0161: glk_current_simple_time | |
| * 0x0168: glk_time_to_date_utc | |
| * 0x0169: glk_time_to_date_local | |
| * 0x016A: glk_simple_time_to_date_utc | |
| * 0x016B: glk_simple_time_to_date_local | |
| * 0x016C: glk_date_to_time_utc | |
| * 0x016D: glk_date_to_time_local | |
| * 0x016E: glk_date_to_simple_time_utc | |
| * 0x016F: glk_date_to_simple_time_local | |
| Note that glk_main() does not have a selector, because it's provided by | |
| your program, not the library. | |
| There is no way to use these selectors directly in the Glk API. [[An | |
| earlier version of Glk had gestalt selectors gestalt_FunctionNameToID and | |
| gestalt_FunctionIDToName, but these have been withdrawn.]] They are defined | |
| and used only by the dispatch layer. | |
| Call selectors 0x1100 to 0x11FF (and the same range of gestalt selectors) | |
| are reserved for extension projects by Dannii Willis. Call selectors 0x1200 | |
| to 0x12FF (and gestalt selector 0x1200) are reserved for extension projects | |
| by Carlos Sanchez. Call selectors 0x2200 to 0x22FF (and the same range of | |
| gestalt selectors) are reserved for iOS extension features by Andrew | |
| Plotkin. These are not documented here. | |
| 12.2: The Blorb Layer | |
| The material described in this section is not part of the Glk API per se. | |
| It is an external layer which allows the library to load resources (images | |
| and sounds) from a file specified by your program. The Blorb file format is | |
| a standard IF resource archive. | |
| The Glk spec does not require that resources be stored in a Blorb file. It | |
| says only that the library knows how to load them and use them, when you so | |
| request. However, Blorb is the recommended way to supply portable | |
| resources. Most Glk libraries will support Blorb, using the interface | |
| defined in this section. | |
| The quick summary: resources are identified by type (image, sound, etc) and | |
| by an index number. [[But not by name. This is for historical reasons; | |
| Infocom's Z-machine architecture used this scheme.]] | |
| For the complete Blorb specification and tools for Blorb file manipulation, | |
| see: | |
| <http://eblong.com/zarf/blorb/> | |
| 12.2.1: How This Works | |
| The Blorb layer is implemented in a C source file, gi_blorb.c, and its | |
| header, gi_blorb.h. This code is (mostly) platform-independent -- it is | |
| identical in every library, just as the glk.h header file is identical in | |
| every library. Each library author who wants to support Blorb should | |
| download the gi_blorb.c and gi_blorb.h files from the Glk web site, and | |
| compile them unchanged into the library. | |
| Most of the functions defined in gi_blorb.h are intended for the library. | |
| If you are writing a Glk program, you can ignore them all, except for | |
| giblorb_set_resource_map(); see section 12.2.2, "What the Program Does". If | |
| you are implementing a Glk library, you can use this API to find and load | |
| resource data. | |
| 12.2.2: What the Program Does | |
| If you wish your program to load its resources from a Blorb file, you need | |
| to find and open that file in your startup code. (See section 11.1, | |
| "Startup Options".) Each platform will have appropriate functions available | |
| for finding startup data. Be sure to open the file in binary mode, not text | |
| mode. Once you have opened the file as a Glk stream, pass it to | |
| giblorb_set_resource_map(). | |
| giblorb_err_t giblorb_set_resource_map(strid_t file); | |
| This function tells the library that the file is indeed the Blorby source | |
| of all resource goodness. Whenever your program calls an image or sound | |
| function, such as glk_image_draw(), the library will search this file for | |
| the resource you request. | |
| Do *not* close the stream after calling this function. The library is | |
| responsible for closing the stream at shutdown time. | |
| If you do not call giblorb_set_resource_map() in your startup code, or if | |
| it fails, the library is left to its own devices for finding resources. | |
| Some libraries may try to load resources from individual files -- PIC1, | |
| PIC2, PIC3, and so on. (See the Blorb specification for more on this | |
| approach.) Other libraries will not have any other loading mechanism at | |
| all; no resources will be available. | |
| 12.2.3: What the Library Does | |
| Each library must implement giblorb_set_resource_map(), if it wishes to | |
| support Blorb at all. Generally, this function should create a Blorb map | |
| and stash it away somewhere. It may also want to stash the stream itself, | |
| so that the library can read data directly from it. | |
| giblorb_set_resource_map() should return giblorb_err_None (0) if it | |
| succeeded, or the appropriate Blorb error code if not. See section 12.2.5, | |
| "Blorb Errors". | |
| The library must also link in the gi_blorb.c file. Most of this should | |
| compile without difficulty on any platform. However, it does need to | |
| allocate memory. As supplied, gi_blorb.c calls the ANSI functions malloc(), | |
| realloc(), and free(). If this is not appropriate on your OS, feel free to | |
| change these calls. They are isolated at the end of the file. | |
| 12.2.4: What the Blorb Layer Does | |
| These are the functions which are implemented in gi_blorb.c. They will be | |
| compiled into the library, but they are the same on every platform. In | |
| general, only the library needs to call these functions. The Glk program | |
| should allow the library to do all the resource handling. | |
| giblorb_err_t giblorb_create_map(strid_t file, giblorb_map_t **newmap); | |
| This reads Blorb data out of a Glk stream. It does not load every resource | |
| at once; instead, it creates an map in memory which make it easy to find | |
| resources. A pointer to the map is stored in newmap. This is an opaque | |
| object; you pass it to the other Blorb-layer functions. | |
| giblorb_err_t giblorb_destroy_map(giblorb_map_t *map); | |
| Deallocate the map and all associated memory. This does *not* close the | |
| original stream. | |
| giblorb_err_t giblorb_load_chunk_by_type(giblorb_map_t *map, glui32 | |
| method, giblorb_result_t *res, glui32 chunktype, glui32 count); | |
| This loads a chunk of a given type. The count parameter distinguishes | |
| between chunks of the same type. If count is zero, the first chunk of that | |
| type is loaded, and so on. | |
| To load a chunk of an IFF FORM type (such as AIFF), you should pass in the | |
| form type, rather than FORM. [[This introduces a slight ambiguity -- you | |
| cannot distiguish between a FORM AIFF chunk and a non-FORM chunk of type | |
| AIFF. However, the latter is almost certainly a mistake.]] | |
| The returned data is written into res, according to method: | |
| #define giblorb_method_DontLoad (0) | |
| #define giblorb_method_Memory (1) | |
| #define giblorb_method_FilePos (2) | |
| typedef struct giblorb_result_struct { | |
| glui32 chunknum; | |
| union { | |
| void *ptr; | |
| glui32 startpos; | |
| } data; | |
| glui32 length; | |
| glui32 chunktype; | |
| } giblorb_result_t; | |
| The chunknum field is filled in with the number of the chunk. (This value | |
| can then be passed to giblorb_load_chunk_by_number() or | |
| giblorb_unload_chunk().) The length field is filled in with the length of | |
| the chunk in bytes. The chunktype field is the chunk's type, which of | |
| course will be the type you asked for. | |
| If you specify giblorb_method_DontLoad, no data is actually loaded in. You | |
| can use this if you are only interested in whether a chunk exists, or in | |
| the chunknum and length parameters. | |
| If you specify giblorb_method_FilePos, data.startpos is filled in with the | |
| file position of the chunk data. You can use glk_stream_set_position() to | |
| read the data from the stream. | |
| If you specify giblorb_method_Memory, data.ptr is filled with a pointer to | |
| allocated memory containing the chunk data. This memory is owned by the | |
| map, not you. If you load the chunk more than once with | |
| giblorb_method_Memory, the Blorb layer is smart enough to keep just one | |
| copy in memory. You should *not* deallocate this memory yourself; call | |
| giblorb_unload_chunk() instead. | |
| giblorb_err_t giblorb_load_chunk_by_number(giblorb_map_t *map, glui32 | |
| method, giblorb_result_t *res, glui32 chunknum); | |
| This is similar to giblorb_load_chunk_by_type(), but it loads a chunk with | |
| a given chunk number. The type of the chunk can be found in the chunktype | |
| field of giblorb_result_t. You can get the chunk number from the chunknum | |
| field, after calling one of the other load functions. | |
| giblorb_err_t giblorb_unload_chunk(giblorb_map_t *map, glui32 chunknum); | |
| This frees the chunk data allocated by giblorb_method_Memory. If the given | |
| chunk has never been loaded into memory, this has no effect. | |
| giblorb_err_t giblorb_load_resource(giblorb_map_t *map, glui32 method, | |
| giblorb_result_t *res, glui32 usage, glui32 resnum); | |
| This loads a resource, given its usage and resource number. Currently, the | |
| three usage values are giblorb_ID_Pict (images), giblorb_ID_Snd (sounds), | |
| and giblorb_ID_Exec (executable program). See the Blorb specification for | |
| more information about the types of data that can be stored for these | |
| usages. | |
| Note that a resource number is not the same as a chunk number. The resource | |
| number is the sound or image number specified by a Glk program. Chunk | |
| number is arbitrary, since chunks in a Blorb file can be in any order. To | |
| find the chunk number of a given resource, call giblorb_load_resource() and | |
| look in res.chunknum. | |
| giblorb_err_t giblorb_count_resources(giblorb_map_t *map, glui32 usage, | |
| glui32 *num, glui32 *min, glui32 *max); | |
| This counts the number of chunks with a given usage (image, sound, or | |
| executable.) The total number of chunks of that usage is stored in num. The | |
| lowest and highest resource number of that usage are stored in min and max. | |
| You can leave any of the three pointers NULL if you don't care about that | |
| information. | |
| 12.2.5: Blorb Errors | |
| All Blorb layer functions, including giblorb_set_resource_map(), return the | |
| following error codes. | |
| * giblorb_err_None, or zero: No error. | |
| * giblorb_err_CompileTime: Something is compiled wrong in the Blorb | |
| layer. | |
| * giblorb_err_Alloc: Memory could not be allocated. | |
| * giblorb_err_Read: Data could not be read from the file. | |
| * giblorb_err_NotAMap: The map parameter is invalid. | |
| * giblorb_err_Format: The Blorb file is corrupted or invalid. | |
| * giblorb_err_NotFound: The requested data could not be found. | |
Xet Storage Details
- Size:
- 249 kB
- Xet hash:
- 9112d51524eaa9c83a6c418ca8409464e4a435384e2234a81059bbb06c9937d6
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.