From f19a194bd066ea4320c1c6b6b8c5a375f7787af5 Mon Sep 17 00:00:00 2001 From: "P. F. Chimento" Date: Thu, 14 Apr 2011 23:32:27 +0200 Subject: [PATCH] Updated documentation to match API 0.7.2 The Glk API documentation in Chimara now matches the official documentation, although not everything considered necessary by Gtk-Doc is documented. Stubs added for new functions. --- docs/reference/Makefile.am | 2 + docs/reference/blorb.sgml | 7 + docs/reference/chimara-docs.sgml | 14 +- docs/reference/chimara-sections.txt | 71 ++++- docs/reference/dispatch-selectors.sgml | 7 +- docs/reference/glk-api-conventions.sgml | 11 +- docs/reference/glk-display-style.sgml | 10 +- docs/reference/glk-front-matter.sgml | 12 +- docs/reference/glk-introduction.sgml | 7 +- docs/reference/glk-normalization.sgml | 48 ++++ docs/reference/glk-porting.sgml | 2 +- libchimara/Makefile.am | 1 + libchimara/case.c | 98 ++++++- libchimara/datetime.c | 207 ++++++++++++++ libchimara/doc.c | 364 ++++++++++++++++++++---- libchimara/fileref.c | 19 +- libchimara/gestalt.c | 9 +- libchimara/glk.c | 6 +- libchimara/graphics.c | 10 +- libchimara/input.c | 101 ++++++- libchimara/stream.c | 22 +- libchimara/strio.c | 19 +- libchimara/style.c | 3 + libchimara/timer.c | 2 +- libchimara/window.c | 37 +-- 25 files changed, 959 insertions(+), 130 deletions(-) create mode 100644 docs/reference/glk-normalization.sgml create mode 100644 libchimara/datetime.c diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am index 1c393a6..ccb250b 100644 --- a/docs/reference/Makefile.am +++ b/docs/reference/Makefile.am @@ -86,6 +86,7 @@ content_files = version.xml \ glk-main-function.sgml \ glk-api-conventions.sgml \ glk-character-encoding.sgml \ + glk-normalization.sgml \ glk-output.sgml \ glk-line-input.sgml \ glk-windows.sgml \ @@ -103,6 +104,7 @@ content_files = version.xml \ expand_content_files = \ glk-main-function.sgml \ glk-api-conventions.sgml \ + glk-normalization.sgml \ glk-output.sgml \ glk-line-input.sgml \ glk-window-arrangement.sgml \ diff --git a/docs/reference/blorb.sgml b/docs/reference/blorb.sgml index eeafe8e..28659f8 100644 --- a/docs/reference/blorb.sgml +++ b/docs/reference/blorb.sgml @@ -21,6 +21,13 @@ The material described in this section is not part of the Glk API per se. It is 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://www.eblong.com/zarf/blorb/ diff --git a/docs/reference/chimara-docs.sgml b/docs/reference/chimara-docs.sgml index 2aeebf4..43dbffa 100644 --- a/docs/reference/chimara-docs.sgml +++ b/docs/reference/chimara-docs.sgml @@ -44,6 +44,8 @@ + + @@ -119,11 +121,19 @@ + + + + The System Clock + + + + - + - + The Dispatch Layer diff --git a/docs/reference/chimara-sections.txt b/docs/reference/chimara-sections.txt index 3cfa597..4a4e2af 100644 --- a/docs/reference/chimara-sections.txt +++ b/docs/reference/chimara-sections.txt @@ -7,6 +7,7 @@ CHIMARA_ERROR chimara_error_quark ChimaraResourceLoadFunc ChimaraResourceType +ChimaraGlkWindowType chimara_glk_new chimara_glk_set_interactive chimara_glk_get_interactive @@ -25,6 +26,10 @@ chimara_glk_feed_char_input chimara_glk_feed_line_input chimara_glk_is_char_input_pending chimara_glk_is_line_input_pending +chimara_glk_get_num_tag_names +chimara_glk_get_tag +chimara_glk_get_tag_names +chimara_glk_update_style chimara_glk_set_resource_load_callback CHIMARA_GLK @@ -104,11 +109,18 @@ glk_gestalt gestalt_Version gestalt_Unicode GLK_MODULE_UNICODE +gestalt_UnicodeNorm +GLK_MODULE_UNICODE_NORM gestalt_CharOutput gestalt_CharOutput_CannotPrint gestalt_CharOutput_ApproxPrint gestalt_CharOutput_ExactPrint gestalt_LineInput +gestalt_LineInputEcho +GLK_MODULE_LINE_ECHO +gestalt_LineTerminators +GLK_MODULE_LINE_TERMINATORS +gestalt_LineTerminatorKey gestalt_CharInput gestalt_MouseInput gestalt_Timer @@ -165,6 +177,13 @@ glk_buffer_to_upper_case_uni glk_buffer_to_title_case_uni +
+glk-normalize +Unicode String Normalization +glk_buffer_canon_decompose_uni +glk_buffer_canon_normalize_uni +
+
glk-window-opening Window Opening, Closing, and Constraints @@ -175,10 +194,13 @@ winmethod_Above winmethod_Below winmethod_Fixed winmethod_Proportional +winmethod_Border +winmethod_NoBorder glk_window_close winmethod_DirMask winmethod_DivisionMask +winmethod_BorderMask
@@ -254,6 +276,8 @@ glk_cancel_char_event glk_request_line_event glk_request_line_event_uni glk_cancel_line_event +glk_set_echo_line_event +glk_set_terminators_line_event
@@ -511,10 +535,40 @@ glk_cancel_hyperlink_event GLK_MODULE_HYPERLINKS
+
+glk-clock +The System Clock +glktimeval_t +glk_current_time +glk_current_simple_time +
+ +
+glk-clock-conversions +Time and Date Conversions +glkdate_t +glk_time_to_date_utc +glk_time_to_date_local +glk_simple_time_to_date_utc +glk_simple_time_to_date_local +glk_date_to_time_utc +glk_date_to_time_local +glk_date_to_simple_time_utc +glk_date_to_simple_time_local +
+ +
+glk-clock-testing +Testing for Clock Capabilities +gestalt_DateTime +GLK_MODULE_DATETIME +
+
dispatch-interrogating Interrogating the Interface gidispatch_count_classes +gidispatch_get_class gidispatch_count_intconst gidispatch_get_intconst gidispatch_intconst_t @@ -634,22 +688,17 @@ garglk_set_line_terminators garglk_unput_string garglk_unput_string_uni garglk_set_zcolors +garglk_set_zcolors_stream garglk_set_reversevideo +garglk_set_reversevideo_stream zcolor_Current zcolor_Default -zcolor_Black -zcolor_Red -zcolor_Green -zcolor_Yellow -zcolor_Blue -zcolor_Magenta -zcolor_Cyan -zcolor_White -zcolor_LightGrey -zcolor_MediumGrey -zcolor_DarkGrey +zcolor_Cursor +zcolor_Transparent keycode_Erase +keycode_MouseWheelUp +keycode_MouseWheelDown zcolor_NUMCOLORS
diff --git a/docs/reference/dispatch-selectors.sgml b/docs/reference/dispatch-selectors.sgml index 6489324..216fb06 100644 --- a/docs/reference/dispatch-selectors.sgml +++ b/docs/reference/dispatch-selectors.sgml @@ -28,7 +28,12 @@ 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 in the dispatch layer. +They are defined and used only by the dispatch layer. + + +Call selectors 0x1200 to 0x12FF are reserved for extension projects by Carlos Sanchez. +The same is true of gestalt selector 0x1200. +These are not documented here. diff --git a/docs/reference/glk-api-conventions.sgml b/docs/reference/glk-api-conventions.sgml index e796e52..b6f6871 100644 --- a/docs/reference/glk-api-conventions.sgml +++ b/docs/reference/glk-api-conventions.sgml @@ -18,19 +18,16 @@ 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, and all functions are actual function declarations (as opposed to macros.) +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. - -There are a few places where macros would be more efficient — glk_gestalt() and glk_gestalt_ext(), for example — but they are not likely to be CPU bottlenecks, and clarity seems more important. - -%FALSE is 0; %TRUE is 1. %NULL is also 0. +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. -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. +%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 a %NULL pointer to indicate that you do not care about that value. +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. diff --git a/docs/reference/glk-display-style.sgml b/docs/reference/glk-display-style.sgml index d119c24..110af53 100644 --- a/docs/reference/glk-display-style.sgml +++ b/docs/reference/glk-display-style.sgml @@ -15,13 +15,17 @@ Description -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 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. -This is an important possibility to keep in mind. +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 other decorations 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. +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 of this figure), you lose, of course. diff --git a/docs/reference/glk-front-matter.sgml b/docs/reference/glk-front-matter.sgml index 2abdbc2..59ef6b3 100644 --- a/docs/reference/glk-front-matter.sgml +++ b/docs/reference/glk-front-matter.sgml @@ -4,7 +4,7 @@ ]> Glk API Specification -API version 0.7.0 +API version 0.7.2 Andrew @@ -13,11 +13,17 @@ erkyrath@eblong.com - 1998–2004 + 1998–2011 Andrew Plotkin -You have permission to display, download, and print this document, provided that you do so for personal, non-commercial use only. You may not modify or distribute this document without the author's written permission. + +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. + The authors of the Chimara library have adapted this document to better fit the format of a GtkDoc reference manual. They have also added notes specific to Chimara's implementation of the Glk API. The original API specification and further Glk information can be found at: http://www.eblong.com/zarf/glk/ diff --git a/docs/reference/glk-introduction.sgml b/docs/reference/glk-introduction.sgml index d7c317b..ec28979 100644 --- a/docs/reference/glk-introduction.sgml +++ b/docs/reference/glk-introduction.sgml @@ -5,10 +5,11 @@ Introduction What Glk Is -Glk is an attempt to define a portable API (programming interface) for applications with text UIs (user interfaces.) +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 and Infocom's Z-machine have nearly identical interface capabilities; each allows a program to... +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 @@ -18,7 +19,7 @@ Rather than go into a detailed explanation of what that means, let me give examp 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, pen-controlled interpreters on Newtons and PalmOS PDAs, and so on — and (ultimately) why there Macintoshes and Palms and X-windows platforms in the first place. +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. diff --git a/docs/reference/glk-normalization.sgml b/docs/reference/glk-normalization.sgml new file mode 100644 index 0000000..e7f3111 --- /dev/null +++ b/docs/reference/glk-normalization.sgml @@ -0,0 +1,48 @@ + + + + +A Note on Unicode Case-Folding and Normalization +3 +CHIMARA Library + + +A Note on Unicode Case-Folding and Normalization +How to handle line input + + +Description + +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, ß 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. + + + \ No newline at end of file diff --git a/docs/reference/glk-porting.sgml b/docs/reference/glk-porting.sgml index b90f7c3..6931986 100644 --- a/docs/reference/glk-porting.sgml +++ b/docs/reference/glk-porting.sgml @@ -84,7 +84,7 @@ By the way, the next person I see who #defines memmove()< 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. PalmOS and Newton simply don't use stdio. The Mac has a stdio library built on top of its native file API, but it's a large extra library which porters may not wish to link in. +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. diff --git a/libchimara/Makefile.am b/libchimara/Makefile.am index f932d3b..54ee6b9 100644 --- a/libchimara/Makefile.am +++ b/libchimara/Makefile.am @@ -17,6 +17,7 @@ libchimara_la_SOURCES = \ chimara-glk.c chimara-glk.h chimara-glk-private.h \ chimara-if.c chimara-if.h \ chimara-marshallers.c chimara-marshallers.h \ + datetime.c \ dispatch.c \ event.c event.h \ fileref.c fileref.h \ diff --git a/libchimara/case.c b/libchimara/case.c index 47086e1..1b6eed7 100644 --- a/libchimara/case.c +++ b/libchimara/case.c @@ -79,7 +79,7 @@ glk_char_to_upper(unsigned char ch) * * Unicode has some strange case cases. For example, a combined character * that looks like ss might properly be upper-cased into - * two characters S. Title-casing is even + * 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 @@ -140,10 +140,10 @@ glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len, glui32 numchars) * otherwise. * * See glk_buffer_to_lower_case_uni(). The title_case function has - * an additional (boolean) flag. Its basic function is to change the first - * character of the buffer to upper-case, and leave the rest of the buffer - * unchanged. If @lowerrest is true, it changes all the non-first characters to - * lower-case (instead of leaving them alone.) + * 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. * * * Earlier drafts of this spec had a separate function which title-cased the @@ -170,3 +170,91 @@ glk_buffer_to_title_case_uni(glui32 *buf, glui32 len, glui32 numchars, glui32 lo return numchars; } +/** + * glk_buffer_canon_decompose_uni: + * @buf: A character array in UCS-4. + * @len: Available length of @buf. + * @numchars: Number of characters in @buf. + * + * 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 è (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. + * + * Returns: The number of characters in @buf after decomposition. + */ +glui32 +glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len, glui32 numchars) +{ + g_return_val_if_fail(buf != NULL && (len > 0 || numchars > 0), 0); + g_return_val_if_fail(numchars <= len, 0); + + /* TODO: Implement this */ + return numchars; +} + +/** + * glk_buffer_canon_normalize_uni: + * @buf: A character array in UCS-4. + * @len: Available length of @buf. + * @numchars: Number of characters in @buf. + * + * 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 è (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. + * + * + * Returns: the number of characters in @buf after normalization. + */ +glui32 +glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len, glui32 numchars) +{ + g_return_val_if_fail(buf != NULL && (len > 0 || numchars > 0), 0); + g_return_val_if_fail(numchars <= len, 0); + + /* TODO: Implement this */ + return numchars; +} diff --git a/libchimara/datetime.c b/libchimara/datetime.c new file mode 100644 index 0000000..e01ab10 --- /dev/null +++ b/libchimara/datetime.c @@ -0,0 +1,207 @@ +#include +#include "glk.h" + +/** + * glk_current_time: + * @time: pointer to a #glktimeval_t structure. + * + * The current Unix time is stored in the structure @time. (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. + */ +void +glk_current_time(glktimeval_t *time) +{ + g_return_if_fail(time != NULL); +} + +/** + * glk_current_simple_time: + * @factor: Factor by which to divide the time value. + * + * 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. + * + * Returns: Unix time divided by @factor, truncated to 32 bits. + */ +glsi32 +glk_current_simple_time(glui32 factor) +{ + g_return_val_if_fail(factor != 0, -1); + + return -1; +} + +/** + * glk_time_to_date_utc: + * @time: A #glktimeval_t structure as returned by glk_current_time(). + * @date: An empty #glkdate_t structure to fill in. + * + * Convert the given timestamp (as returned by glk_current_time()) to a + * broken-out structure. This function returns a date and time in universal time + * (GMT). + * + * + * The seconds value may be 60 because of a leap second. + * + */ +void +glk_time_to_date_utc(glktimeval_t *time, glkdate_t *date) +{ + g_return_if_fail(time != NULL); + g_return_if_fail(date != NULL); +} + +/** + * glk_time_to_date_local: + * @time: A #glktimeval_t structure as returned by glk_current_time(). + * @date: An empty #glkdate_t structure to fill in. + * + * Does the same thing as glk_time_to_date_utc(), but this function returns + * local time. + */ +void +glk_time_to_date_local(glktimeval_t *time, glkdate_t *date) +{ + g_return_if_fail(time != NULL); + g_return_if_fail(date != NULL); +} + +/** + * glk_simple_time_to_date_utc: + * @time: Timestamp as returned by glk_current_simple_time(). + * @factor: Factor by which to multiply @time in order to get seconds. + * @date: An empty #glkdate_t structure to fill in. + * + * Convert the given timestamp (as returned by glk_current_simple_time()) to a + * broken-out structure in universal time. The @time argument is multiplied by + * @factor to produce a Unix timestamp. + * + * Since the resolution of glk_simple_time_to_date_utc() and + * glk_simple_time_to_date_local() is no better than seconds, they will return + * zero for the microseconds value. + */ +void +glk_simple_time_to_date_utc(glsi32 time, glui32 factor, glkdate_t *date) +{ + g_return_if_fail(factor != 0); + g_return_if_fail(date != NULL); +} + +/** + * glk_simple_time_to_date_local: + * @time: Timestamp as returned by glk_current_simple_time(). + * @factor: Factor by which to multiply @time in order to get seconds. + * @date: An empty #glkdate_t structure to fill in. + * + * Does the same thing as glk_simple_time_to_date_utc(), but fills in the @date + * structure in local time. + */ +void +glk_simple_time_to_date_local(glsi32 time, glui32 factor, glkdate_t *date) +{ + g_return_if_fail(factor != 0); + g_return_if_fail(date != NULL); +} + +/** + * glk_date_to_time_utc: + * @date: A date in the form of a #glkdate_t structure. + * @time: An empty #glktimeval_t structure to fill in. + * + * Convert the broken-out structure (interpreted as universal time) to a + * timestamp. The weekday value in @date 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.) + */ +void +glk_date_to_time_utc(glkdate_t *date, glktimeval_t *time) +{ + g_return_if_fail(date != NULL); + g_return_if_fail(time != NULL); +} + +/** + * glk_date_to_time_local: + * @date: A date in the form of a #glkdate_t structure. + * @time: An empty #glktimeval_t structure to fill in. + * + * Does the same thing as glk_date_to_time_utc(), but interprets the broken-out + * structure as local time. + * + * 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. + * + */ +void +glk_date_to_time_local(glkdate_t *date, glktimeval_t *time) +{ + g_return_if_fail(date != NULL); + g_return_if_fail(time != NULL); +} + +/** + * glk_date_to_simple_time_utc: + * @date: A date in the form of a #glkdate_t structure. + * @factor: Factor by which to divide the time value. + * + * Convert the broken-out structure (interpreted as universal time) to a + * timestamp divided by @factor. The weekday value in @date 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. + * + * Returns: a timestamp divided by @factor, and truncated to 32 bits, or -1 on + * error. + */ +glsi32 +glk_date_to_simple_time_utc(glkdate_t *date, glui32 factor) +{ + g_return_val_if_fail(date != NULL, -1); + g_return_val_if_fail(factor != 0, -1); + + return -1; +} + +/** + * glk_date_to_simple_time_local: + * @date: A date in the form of a #glkdate_t structure. + * @factor: Factor by which to divide the time value. + * + * Does the same thing as glk_date_to_simple_time_utc(), but interprets the + * broken-out structure as local time. + * + * Returns: a timestamp divided by @factor, and truncated to 32 bits, or -1 on + * error. + */ +glsi32 +glk_date_to_simple_time_local(glkdate_t *date, glui32 factor) +{ + g_return_val_if_fail(date != NULL, -1); + g_return_val_if_fail(factor != 0, -1); + + return -1; +} diff --git a/libchimara/doc.c b/libchimara/doc.c index df84c6f..3c04ce3 100644 --- a/libchimara/doc.c +++ b/libchimara/doc.c @@ -138,7 +138,7 @@ * * 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 (0) instead of a valid pointer. You + * 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, @@ -375,6 +375,25 @@ * functions act on whole strings, since the length of the string may change. */ +/** + * SECTION:glk-normalize + * @short_description: Combining characters + * @include: libchimara/glk.h + * + * 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 è (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. + */ + /** * SECTION:glk-window-opening * @short_description: Creating new windows and closing them @@ -793,7 +812,7 @@ * * 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(). + * 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(). @@ -883,10 +902,11 @@ * 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 is a - * mask of constants (see below) to indicate the file type and the mode (text or - * binary.) 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. + * 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. * * 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 @@ -1100,6 +1120,36 @@ * Before calling Glk hyperlink functions, you should use the gestalt selectors * %gestalt_Hyperlinks and %gestalt_HyperlinkInput. */ + +/** + * SECTION:glk-clock + * @short_description: Getting the current time from the system clock + * @include: libchimara/glk.h + * + * 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 Timer Events). Timer events may be + * delivered late according to the system clock. + */ + +/** + * SECTION:glk-clock-conversions + * @short_description: Converting from timestamps to date structures and back + * @include: libchimara/glk.h + * + */ + +/** + * SECTION:glk-clock-testing + * @short_description: Checking whether the library supports the clock functions + * @include: libchimara/glk.h + * + * Before calling Glk date and time functions, you should use the following + * gestalt selector. + */ /** * SECTION:dispatch-interrogating @@ -1278,7 +1328,7 @@ /** * GLK_MODULE_UNICODE: * - * If this preprocessor symbol is defined, so are all the Unicode functions and + * If this preprocessor symbol is defined, so are the core Unicode functions and * constants (see %gestalt_Unicode). If not, not. */ @@ -1334,6 +1384,21 @@ * functions and constants described in this section. If not, not. */ +/** + * GLK_MODULE_UNICODE_NORM: + * + * If this preprocessor symbol is defined, so are the Unicode normalization + * functions (see %gestalt_UnicodeNorm). If not, not. + */ + +/** + * GLK_MODULE_DATETIME: + * + * 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. + */ + /** * winid_t: * @@ -1380,8 +1445,8 @@ * So the version number 78.2.11 would be encoded as 0x004E020B. * * - * The current Glk specification version is 0.7.0, so this selector will return - * 0x00000700. + * The current Glk specification version is 0.7.2, so this selector will return + * 0x00000702. * * |[ * glui32 res; @@ -1452,11 +1517,13 @@ * * * Make sure you do not get confused by signed byte values. If you set a - * char variable ch to 0xFE, the - * small-thorn character (þ), and then call + * 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 + * 0xFFFFFFFE, which is not a legitimate character, even in Unicode. You * should write * |[ res = glk_gestalt(gestalt_CharOutput, (unsigned char)ch); ]| * instead. @@ -1517,7 +1584,8 @@ * * You can test whether the library supports timer events: * |[ res = glk_gestalt(gestalt_Timer, 0); ]| - * This returns 1 if timer events are supported, and 0 if they are not. + * This returns %TRUE (1) if timer events are supported, and %FALSE (0) if they + * are not. */ /** @@ -1544,7 +1612,7 @@ * gestalt_DrawImage: * * This selector returns 1 if images can be drawn in windows of the given type. - * If it returns 0, glk_image_draw() will fail and return %FALSE. You should + * 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. */ @@ -1653,14 +1721,14 @@ * * 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 selector: + * the %gestalt_Unicode and %gestalt_UnicodeNorm gestalt selectors. + * * |[ * glui32 res; * res = glk_gestalt(gestalt_Unicode, 0); * ]| - * - * This returns 1 if the Unicode functions are available. If it returns 0, you - * should not try to call them. They may print nothing, print gibberish, or + * 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(), @@ -1679,9 +1747,81 @@ * errors. * * To avoid this, you can perform a preprocessor test for the existence of - * #GLK_MODULE_UNICODE. + * %GLK_MODULE_UNICODE. */ - + +/** + * gestalt_UnicodeNorm: + * + * |[ + * glui32 res; + * res = glk_gestalt(gestalt_UnicodeNorm, 0); + * ]| + * This code returns 1 if the Unicode normalization functions are available. If + * it returns 0, you should not try to call them. The Unicode normalization + * functions include glk_buffer_canon_decompose_uni() and + * glk_buffer_canon_normalize_uni(). + * + * The equivalent preprocessor test for these functions is + * %GLK_MODULE_UNICODE_NORM. + */ + +/** + * gestalt_LineInputEcho: + * + * |[ + * res = glk_gestalt(gestalt_LineInputEcho, 0); + * ]| + * + * This returns 1 if glk_set_echo_line_event() is supported, and 0 if it is not. + * + * Remember that if it is not supported, the behavior is always the default, + * which is line echoing enabled. + * + */ + +/** + * gestalt_LineTerminators: + * + * |[ + * res = glk_gestalt(gestalt_LineTerminators, 0); + * ]| + * + * This returns 1 if glk_set_terminators_line_event() is supported, and 0 if it + * is not. + */ + +/** + * gestalt_LineTerminatorKey: + * + * |[ + * res = glk_gestalt(gestalt_LineTerminatorKey, ch); + * ]| + * + * This returns 1 if the keycode @ch can be passed to + * glk_set_terminators_line_event(). If it returns 0, that keycode will be + * ignored as a line terminator. Printable characters and %keycode_Return will + * always return 0. + */ + +/** + * gestalt_DateTime: + * + * |[ + * res = glk_gestalt(gestalt_DateTime, 0); + * ]| + * + * This returns 1 if the overall suite of system clock functions, as described + * in this chapter, is available. + * + * If this selector returns 0, you should not try to call these functions. They + * may have no effect, or they may cause a run-time error. + * + * + * Glk timer events are covered by a different selector. See %gestalt_Timer. + * + */ + /** * evtype_None: * @@ -1720,19 +1860,26 @@ * A full line of input completed in a window. See Line Input Events. * - * If a window has a pending request for line input, and the player hits - * enter in that window (or whatever action is appropriate to - * enter his input), 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 + * 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. + * + * 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. 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 stored in the buffer. + * 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. * @@ -2224,14 +2371,22 @@ * result. * * - * 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. + * 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). */ /** @@ -2248,8 +2403,10 @@ * * 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, it goes to the beginning of the next line. The library makes no attempt - * to wrap lines at word breaks. + * 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 @@ -2301,7 +2458,8 @@ * 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. + * same thing happens. The glk_set_echo_line_event() call has no effect in grid + * windows. */ /** @@ -2315,7 +2473,7 @@ * linkend="chimara-Graphics-in-Graphics-Windows">Graphics in Graphics * Windows. * - * When a text grid window is resized smaller, the bottom or right area is + * 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. * @@ -2406,7 +2564,24 @@ * When calling glk_window_open() with this @method, the new window will be * a given proportion of the old window's size. (See glk_window_open()). */ - + +/** + * winmethod_Border: + * + * When calling glk_window_open() with this @method, it specifies that there + * should be a visible window border between the new window and its sibling. + * (This is a hint to the library.) + */ + +/** + * winmethod_NoBorder: + * + * When calling glk_window_open() with this @method, it specifies that there + * 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.) + */ + /** * fileusage_Data: * @@ -2507,7 +2682,15 @@ * existed in the destination, instead of replacing it. * * - * Corresponds to mode "a" in the stdio library, using fopen(). + * 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. * */ @@ -2688,6 +2871,11 @@ * * Margin images are not implemented yet. */ + +/** + * glktimeval_t: + * + */ /*---------- TYPES, FUNCTIONS AND CONSTANTS FROM GI_DISPA.H ------------------*/ @@ -2735,6 +2923,13 @@ * This 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. + * */ /** @@ -2754,6 +2949,11 @@ * 1, where N is the value returned by * gidispatch_count_functions(). * + * + * 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. + * + * * Returns: A #gidispatch_function_t structure describing the function. */ @@ -2787,6 +2987,11 @@ * 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. + * + * * Returns: a #gidispatch_function_t structure, or %NULL. */ @@ -2801,7 +3006,8 @@ * the list of arguments, and @numargs is the length of the list. * * The arguments are all stored as #gluniversal_t objects. - * Basic Types + * Basic Dispatch + * 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 @@ -2818,10 +3024,11 @@ * #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 if the reference is %NULL, and %TRUE - * 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. + * 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: @@ -3073,8 +3280,7 @@ * * * - * Any type code can be prefixed with one or more of the following characters - * (order does not matter): + * Any type code can be prefixed with one or more of the following characters: * * * & @@ -3160,6 +3366,61 @@ * * * + * 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 + * + * * Returns: A string which encodes the prototype of the specified Glk function. */ @@ -3327,14 +3588,13 @@ /** * giblorb_result_t: * @chunknum: The chunk number (for use in giblorb_unload_chunk(), etc.) - * @data: A union containing a pointer to the data @ptr (if you used - * %giblorb_method_Memory) and the position in the file @startpos (if you used - * %giblorb_method_FilePos) * @length: The length of the data * @chunktype: The type of the chunk. * * Holds information about a chunk loaded from a Blorb file, and the method of - * accessing the chunk data. See giblorb_load_chunk_by_type() and + * accessing the chunk data. @data is a union of @ptr, a pointer to the data (if + * you used %giblorb_method_Memory) and @startpos, the position in the file (if + * you used %giblorb_method_FilePos). See giblorb_load_chunk_by_type() and * giblorb_load_chunk_by_number(). */ diff --git a/libchimara/fileref.c b/libchimara/fileref.c index a3042ed..24e96eb 100644 --- a/libchimara/fileref.c +++ b/libchimara/fileref.c @@ -1,8 +1,10 @@ +#include #include #include #include #include #include +#include #include "fileref.h" #include "magic.h" #include "chimara-glk-private.h" @@ -196,10 +198,10 @@ glk_fileref_create_temp(glui32 usage, glui32 rock) * open the file. * * - * It is possible 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. + * 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. * * * Returns: A new fileref, or #NULL if the fileref creation failed or the @@ -411,14 +413,23 @@ glk_fileref_destroy(frefid_t fref) * @fref: A refrence to the file to delete. * * Deletes the file referred to by @fref. It does not destroy @fref itself. + * + * You should only call this with a fileref that refers to an existing file. */ void glk_fileref_delete_file(frefid_t fref) { VALID_FILEREF(fref, return); if( glk_fileref_does_file_exist(fref) ) + { if(g_unlink(fref->filename) == -1) IO_WARNING( "Error deleting file", fref->filename, g_strerror(errno) ); + } + else + { + ILLEGAL(_("Tried to delete a fileref that does not refer to an existing file.")); + } + } /** diff --git a/libchimara/gestalt.c b/libchimara/gestalt.c index ebeb765..54fc700 100644 --- a/libchimara/gestalt.c +++ b/libchimara/gestalt.c @@ -5,7 +5,7 @@ /* Version of the Glk specification implemented by this library */ #define MAJOR_VERSION 0 #define MINOR_VERSION 7 -#define SUB_VERSION 0 +#define SUB_VERSION 2 /** * glk_gestalt: @@ -130,6 +130,7 @@ glk_gestalt_ext(glui32 sel, glui32 val, glui32 *arr, glui32 arrlen) case gestalt_Sound: case gestalt_SoundVolume: case gestalt_SoundNotify: + case gestalt_SoundMusic: #ifdef GSTREAMER_SOUND return 1; #else @@ -137,7 +138,11 @@ glk_gestalt_ext(glui32 sel, glui32 val, glui32 *arr, glui32 arrlen) #endif /* Unsupported capabilities */ - case gestalt_SoundMusic: + case gestalt_DateTime: + case gestalt_LineInputEcho: + case gestalt_LineTerminatorKey: + case gestalt_LineTerminators: + case gestalt_UnicodeNorm: /* Selector not supported */ default: return 0; diff --git a/libchimara/glk.c b/libchimara/glk.c index 81d3603..f5df12e 100644 --- a/libchimara/glk.c +++ b/libchimara/glk.c @@ -100,8 +100,10 @@ glk_exit(void) * all. So you can call it often. * * - * In a virtual machine interpreter, once per opcode is appropriate. In a - * program with lots of computation, pick a comparable rate. + * 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 diff --git a/libchimara/graphics.c b/libchimara/graphics.c index 6842000..afe3e76 100644 --- a/libchimara/graphics.c +++ b/libchimara/graphics.c @@ -229,11 +229,11 @@ image_cache_find(struct image_info* to_find) * @height: Pointer to a location at which to store the image's height. * * This gets information about the image resource with the given identifier. It - * returns %TRUE if there is such an image, and %FALSE 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.) + * 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 diff --git a/libchimara/input.c b/libchimara/input.c index 16673c9..7f8038c 100644 --- a/libchimara/input.c +++ b/libchimara/input.c @@ -273,11 +273,28 @@ glk_request_line_event(winid_t win, char *buf, glui32 maxlen, glui32 initlen) * glui32 values instead of an array of characters, and the values * may be any valid Unicode code points. * - * The result will be in Unicode Normalization Form C. This basically means that - * composite characters will be single characters where possible, instead of - * sequences of base and combining marks. See - * Unicode Standard Annex - * #15 for the details. + * 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 + * Unicode String + * Normalization. + * */ void glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initlen) @@ -953,3 +970,77 @@ cancel_old_input_request(winid_t win) WARNING("Could not cancel pending input request: unknown input request"); } } + +/** + * glk_set_echo_line_event: + * @win: The window in which to change the echoing behavior. + * @val: Zero to turn off echoing, nonzero for normal echoing. + * + * 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. + * + * + * 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. + * + * + * Not all libraries support this feature. You can test for it with + * %gestalt_LineInputEcho. + */ +void +glk_set_echo_line_event(winid_t win, glui32 val) +{ + VALID_WINDOW(win, return); +} + +/** + * glk_set_terminators_line_event: + * @win: The window for which to set the line input terminator keys. + * @keycodes: An array of keycode_ constants, of length @count. + * @count: The array length of @keycodes. + * + * 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 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.) You can test for this with %gestalt_LineTerminators and + * %gestalt_LineTerminatorKey. + */ +void +glk_set_terminators_line_event(winid_t win, glui32 *keycodes, glui32 count) +{ + VALID_WINDOW(win, return); +} \ No newline at end of file diff --git a/libchimara/stream.c b/libchimara/stream.c index 3c15127..a161a4b 100644 --- a/libchimara/stream.c +++ b/libchimara/stream.c @@ -103,7 +103,8 @@ glk_stream_iterate(strid_t str, glui32 *rockptr) * @str: A stream. * * Retrieves the stream @str's rock value. See Rocks. + * linkend="chimara-Rocks">Rocks. Window streams always have rock 0; all + * other streams return whatever rock you created them with. * * Returns: A rock value. */ @@ -421,9 +422,22 @@ file_stream_new(frefid_t fileref, glui32 fmode, glui32 rock, gboolean unicode) * Opens a stream which reads to or writes from a disk file. 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). If - * @fmode is %filemode_WriteAppend, the file mark is set to the end of 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 + * the file mode constants. + * + * + * 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 File References. * * When writing in binary mode, Unicode values (characters greater than 255) * cannot be written to the file. If you try, they will be stored as 0x3F diff --git a/libchimara/strio.c b/libchimara/strio.c index ab8443f..c3ea0c7 100644 --- a/libchimara/strio.c +++ b/libchimara/strio.c @@ -714,8 +714,8 @@ glk_get_char_stream(strid_t str) * glk_get_char_stream_uni: * @str: An input stream. * - * Reads one character from the stream @str. The result will be between 0 and - * 0x7FFFFFFF. If the end of the stream has been reached, the result will be -1. + * Reads one character from the stream @str. If the end of the stream has been + * reached, the result will be -1. * * Returns: A value between 0 and 0x7FFFFFFF, or -1 on end of stream. */ @@ -1236,6 +1236,14 @@ glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len) * good for much anyhow. * * + * 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. + * + * * Returns: position of the read/write mark in @str. */ glui32 @@ -1249,6 +1257,8 @@ glk_stream_get_position(strid_t str) return str->mark; case STREAM_TYPE_FILE: return ftell(str->file_pointer); + case STREAM_TYPE_WINDOW: + return 0; default: ILLEGAL_PARAM("Seeking illegal on stream type: %u", str->type); return 0; @@ -1280,6 +1290,9 @@ glk_stream_get_position(strid_t str) * * 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. */ void glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode) @@ -1317,6 +1330,8 @@ glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode) WARNING("Seek failed on file stream"); break; } + case STREAM_TYPE_WINDOW: + break; /* Quietly do nothing */ default: ILLEGAL_PARAM("Seeking illegal on stream type: %u", str->type); return; diff --git a/libchimara/style.c b/libchimara/style.c index 0df3bbd..9b82a66 100644 --- a/libchimara/style.c +++ b/libchimara/style.c @@ -1075,6 +1075,9 @@ glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2) * 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. * * Returns: TRUE upon successul retrieval, otherwise FALSE. */ diff --git a/libchimara/timer.c b/libchimara/timer.c index 400f506..ddaa4a8 100644 --- a/libchimara/timer.c +++ b/libchimara/timer.c @@ -35,7 +35,7 @@ extern GPrivate *glk_data_key; * 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. If you want a real-time operating system, talk to Wind River. + * Sorry. * * * diff --git a/libchimara/window.c b/libchimara/window.c index 6272c67..c0b0a0c 100644 --- a/libchimara/window.c +++ b/libchimara/window.c @@ -223,7 +223,8 @@ glk_window_get_root() * @method: Position of the new window and method of size computation. One of * %winmethod_Above, %winmethod_Below, %winmethod_Left, or %winmethod_Right * OR'ed with %winmethod_Fixed or %winmethod_Proportional. If @wintype is - * %wintype_Blank, then %winmethod_Fixed is not allowed. + * %wintype_Blank, then %winmethod_Fixed is not allowed. May also be OR'ed with + * %winmethod_Border or %winmethod_NoBorder. * @size: Size of the new window, in percentage points if @method is * %winmethod_Proportional, otherwise in characters if @wintype is * %wintype_TextBuffer or %wintype_TextGrid, or pixels if @wintype is @@ -239,9 +240,13 @@ glk_window_get_root() * * 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 is a mask of constants to specify 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. + * 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 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. @@ -890,8 +895,9 @@ glk_window_close(winid_t win, stream_result_t *result) * * Text grid * - * This will clear the window, filling all positions with blanks. The window - * cursor is moved to the top left corner (position 0,0). + * 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). * * * @@ -1014,15 +1020,12 @@ glk_window_clear(winid_t win) * glk_set_window: * @win: A window, or %NULL. * - * Sets the current stream to @win's window stream. It is exactly equivalent to - * |[ glk_stream_set_current(glk_window_get_stream(win)) ]| + * Sets the current stream to @win's window 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 Streams. - * - * Chimara - * - * Although this is not mentioned in the specification, @win may also be - * %NULL, in which case the current stream is also set to %NULL. - * */ void glk_set_window(winid_t win) @@ -1247,9 +1250,9 @@ glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr) * Whatever constraint you set, glk_window_get_size() will tell you the actual * window size you got. * - * Note that you can resize windows, 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. + * 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(). -- 2.30.2