/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "glk/tads/os_glk.h" #include "glk/tads/tads.h" #include "glk/tads/os_buffer.h" namespace Glk { namespace TADS { static void redraw_windows(void); static void os_status_redraw(void); extern void os_banners_redraw(void); static char lbuf[256], rbuf[256]; static int curwin = 0; static int curattr = 0; winid_t mainwin; winid_t statuswin; uint mainfg; uint mainbg; uint statusfg; uint statusbg; int G_os_pagelength; int G_os_linewidth; int G_os_moremode; char G_os_gamename[OSFNMAX]; /* ------------------------------------------------------------------------ */ /* * Initialize. This should be called during program startup to * initialize the OS layer and check OS-specific command-line arguments. * * If 'prompt' and 'buf' are non-null, and there are no arguments on the * given command line, the OS code can use the prompt to ask the user to * supply a filename, then store the filename in 'buf' and set up * argc/argv to give a one-argument command string. (This mechanism for * prompting for a filename is obsolescent, and is retained for * compatibility with a small number of existing implementations only; * new implementations should ignore this mechanism and leave the * argc/argv values unchanged.) */ int os_init(int *argc, char *argv[], const char *prompt, char *buf, int bufsiz) { mainwin = g_vm->glk_window_open(0, 0, 0, wintype_TextBuffer, 0); if (!mainwin) error("fatal: could not open window!\n"); /* get default colors for main window */ if (!g_vm->glk_style_measure(mainwin, style_Normal, stylehint_TextColor, &mainfg)) mainfg = 0; if (!g_vm->glk_style_measure(mainwin, style_Normal, stylehint_BackColor, &mainbg)) mainbg = 0; /* get default colors for status window */ statuswin = g_vm->glk_window_open(mainwin, winmethod_Above | winmethod_Fixed, 1, wintype_TextGrid, 0); if (!g_vm->glk_style_measure(statuswin, style_Normal, stylehint_TextColor, &statusfg)) statusfg = 0; if (!g_vm->glk_style_measure(statuswin, style_Normal, stylehint_BackColor, &statusbg)) statusbg = 0; /* close statuswin; reopened on request */ g_vm->glk_window_close(statuswin, 0); statuswin = nullptr; g_vm->glk_set_window(mainwin); strcpy(rbuf, ""); return 0; } /* * Uninitialize. This is called prior to progam termination to reverse * the effect of any changes made in os_init(). For example, if * os_init() put the terminal in raw mode, this should restore the * previous terminal mode. This routine should not terminate the * program (so don't call exit() here) - the caller might have more * processing to perform after this routine returns. */ void os_uninit(void) { } void os_term(int status) { g_vm->quitGame(); } void os_instbrk(int install) { // No implementation } bool os_break() { return false; } void os_sleep_ms(long delay_in_milliseconds) { g_system->delayMillis(delay_in_milliseconds); } /* ------------------------------------------------------------------------ */ /* * Get system information. 'code' is a SYSINFO_xxx code, which * specifies what type of information to get. The 'param' argument's * meaning depends on which code is selected. 'result' is a pointer to * an integer that is to be filled in with the result value. If the * code is not known, this function should return false. If the code is * known, the function should fill in *result and return true. */ int os_get_sysinfo(int code, void *param, long *result) { switch (code) { case SYSINFO_TEXT_HILITE: *result = 1; return true; case SYSINFO_BANNERS: *result = 1; return true; case SYSINFO_TEXT_COLORS: *result = SYSINFO_TXC_RGB; return true; #ifdef USE_HTML case SYSINFO_INTERP_CLASS: *result = SYSINFO_ICLASS_HTML; return true; case SYSINFO_HTML: *result = 1; return true; #else case SYSINFO_INTERP_CLASS: *result = SYSINFO_ICLASS_TEXTGUI; return true; case SYSINFO_HTML: *result = 0; return true; #endif case SYSINFO_JPEG: case SYSINFO_PNG: case SYSINFO_WAV: case SYSINFO_MIDI: case SYSINFO_WAV_MIDI_OVL: case SYSINFO_WAV_OVL: case SYSINFO_PREF_IMAGES: case SYSINFO_PREF_SOUNDS: case SYSINFO_PREF_MUSIC: case SYSINFO_PREF_LINKS: case SYSINFO_MPEG: case SYSINFO_MPEG1: case SYSINFO_MPEG2: case SYSINFO_MPEG3: case SYSINFO_LINKS_HTTP: case SYSINFO_LINKS_FTP: case SYSINFO_LINKS_NEWS: case SYSINFO_LINKS_MAILTO: case SYSINFO_LINKS_TELNET: case SYSINFO_PNG_TRANS: case SYSINFO_PNG_ALPHA: case SYSINFO_OGG: *result = 0; return true; default: return false; } } /* ------------------------------------------------------------------------ */ /* * Display routines. * * Our display model is a simple stdio-style character stream. * * In addition, we provide an optional "status line," which is a * non-scrolling area where a line of text can be displayed. If the status * line is supported, text should only be displayed in this area when * os_status() is used to enter status-line mode (mode 1); while in status * line mode, text is written to the status line area, otherwise (mode 0) * it's written to the normal main text area. The status line is normally * shown in a different color to set it off from the rest of the text. * * The OS layer can provide its own formatting (word wrapping in * particular) if it wants, in which case it should also provide pagination * using os_more_prompt(). */ /* * Print a string on the console. These routines come in two varieties: * * os_printz - write a NULL-TERMINATED string *. os_print - write a COUNTED-LENGTH string, which may not end with a null * * These two routines are identical except that os_printz() takes a string * which is terminated by a null byte, and os_print() instead takes an * explicit length, and a string that may not end with a null byte. * * os_printz(str) may be implemented as simply os_print(str, strlen(str)). * * The string is written in one of three ways, depending on the status mode * set by os_status(): * * status mode == 0 -> write to main text window *. status mode == 1 -> write to status line *. anything else -> do not display the text at all * * Implementations are free to omit any status line support, in which case * they should simply suppress all output when the status mode is anything * other than zero. * * The following special characters must be recognized in the displayed * text: * * '\n' - newline: end the current line and move the cursor to the start of * the next line. If the status line is supported, and the current status * mode is 1 (i.e., displaying in the status line), then two special rules * apply to newline handling: newlines preceding any other text should be * ignored, and a newline following any other text should set the status * mode to 2, so that all subsequent output is suppressed until the status * mode is changed with an explicit call by the client program to * os_status(). * * '\r' - carriage return: end the current line and move the cursor back to * the beginning of the current line. Subsequent output is expected to * overwrite the text previously on this same line. The implementation * may, if desired, IMMEDIATELY clear the previous text when the '\r' is * written, rather than waiting for subsequent text to be displayed. * * All other characters may be assumed to be ordinary printing characters. * The routine need not check for any other special characters. * */ void os_printz(const char *str) { os_print(str, strlen(str)); } void os_print(const char *str, size_t len) { if (curwin == 0 && str) os_put_buffer(str, len); if (curwin == 1) { const char *p; size_t rem, max; /* The string requires some fiddling for the status window */ for (p = str, rem = len ; rem != 0 && *p == '\n'; p++, --rem) ; if (rem != 0 && p[rem-1] == '\n') --rem; /* if that leaves anything, update the statusline */ if (rem != 0) { max = sizeof(lbuf) - strlen(lbuf) - 1; strncat(lbuf, p, rem > max ? max : rem); os_status_redraw(); } } } /* * Set the status line mode. There are three possible settings: * * 0 -> main text mode. In this mode, all subsequent text written with * os_print() and os_printz() is to be displayed to the main text area. * This is the normal mode that should be in effect initially. This mode * stays in effect until an explicit call to os_status(). * * 1 -> statusline mode. In this mode, text written with os_print() and * os_printz() is written to the status line, which is usually rendered as * a one-line area across the top of the terminal screen or application * window. In statusline mode, leading newlines ('\n' characters) are to * be ignored, and any newline following any other character must change * the mode to 2, as though os_status(2) had been called. * * 2 -> suppress mode. In this mode, all text written with os_print() and * os_printz() must simply be ignored, and not displayed at all. This mode * stays in effect until an explicit call to os_status(). */ void os_status(int stat) { curwin = stat; if (stat == 1) { if (statuswin == NULL) { g_vm->glk_stylehint_set(wintype_TextGrid, style_User1, stylehint_ReverseColor, 1); statuswin = g_vm->glk_window_open(mainwin, winmethod_Above | winmethod_Fixed, 1, wintype_TextGrid, 0); } strcpy(lbuf, ""); } } /* get the status line mode */ int os_get_status() { return curwin; } /* * Set the score value. This displays the given score and turn counts on * the status line. In most cases, these values are displayed at the right * edge of the status line, in the format "score/turns", but the format is * up to the implementation to determine. In most cases, this can simply * be implemented as follows: * */ void os_score(int score, int turncount) { char buf[40]; sprintf(buf, "%d/%d", score, turncount); os_strsc(buf); } /* display a string in the score area in the status line */ void os_strsc(const char *p) { snprintf(rbuf, sizeof rbuf, "%s", p); os_status_redraw(); } static void os_status_redraw(void) { char fmt[32]; char buf[256]; uint wid; uint div; if (!statuswin) return; g_vm->glk_window_get_size(statuswin, &wid, NULL); div = wid - strlen(rbuf) - 3; sprintf(fmt, " %%%ds %%s ", - (int)div); sprintf(buf, fmt, lbuf, rbuf); g_vm->glk_window_clear(statuswin); g_vm->glk_set_window(statuswin); g_vm->glk_set_style(style_User1); os_put_buffer(buf, strlen(buf)); g_vm->glk_set_window(mainwin); } static void redraw_windows(void) { os_status_redraw(); os_banners_redraw(); } /* clear the screen */ void oscls(void) { g_vm->glk_window_clear(mainwin); } /* ------------------------------------------------------------------------ */ /* * Set text attributes. Text subsequently displayed through os_print() and * os_printz() are to be displayed with the given attributes. * * 'attr' is a (bitwise-OR'd) combination of OS_ATTR_xxx values. A value * of zero indicates normal text, with no extra attributes. */ void os_set_text_attr(int attr) { curattr = attr; if (curattr & OS_ATTR_BOLD && curattr & OS_ATTR_ITALIC) g_vm->glk_set_style(style_Alert); else if (curattr & OS_ATTR_BOLD) g_vm->glk_set_style(style_Subheader); else if (curattr & OS_ATTR_ITALIC) g_vm->glk_set_style(style_Emphasized); else g_vm->glk_set_style(style_Normal); } /* * Set the text foreground and background colors. This sets the text * color for subsequent os_printf() and os_vprintf() calls. * * The background color can be OS_COLOR_TRANSPARENT, in which case the * background color is "inherited" from the current screen background. * Note that if the platform is capable of keeping old text for * "scrollback," then the transparency should be a permanent attribute of * the character - in other words, it should not be mapped to the current * screen color in the scrollback buffer, because doing so would keep the * current screen color even if the screen color changes in the future. * * Text color support is optional. If the platform doesn't support text * colors, this can simply do nothing. If the platform supports text * colors, but the requested color or attributes cannot be displayed, the * implementation should use the best available approximation. */ void os_set_text_color(os_color_t fg, os_color_t bg) { } /* * Set the screen background color. This sets the text color for the * background of the screen. If possible, this should immediately redraw * the main text area with this background color. The color is given as an * OS_COLOR_xxx value. * * If the platform is capable of redisplaying the existing text, then any * existing text that was originally displayed with 'transparent' * background color should be redisplayed with the new screen background * color. In other words, the 'transparent' background color of previously * drawn text should be a permanent attribute of the character - the color * should not be mapped on display to the then-current background color, * because doing so would lose the transparency and thus retain the old * screen color on a screen color change. */ void os_set_screen_color(os_color_t color) { } /* * Set the game title. The output layer calls this routine when a game * sets its title (via an HTML tag, for example). If it's * convenient to do so, the OS layer can use this string to set a window * caption, or whatever else makes sense on each system. Most * character-mode implementations will provide an empty implementation, * since there's not usually any standard way to show the current * application title on a character-mode display. */ void os_set_title(const char *title) { #ifdef GARGLK g_vm->garglk_set_story_title(title); #endif } /* * Show the system-specific MORE prompt, and wait for the user to respond. * Before returning, remove the MORE prompt from the screen. * * This routine is only used and only needs to be implemented when the OS * layer takes responsibility for pagination; this will be the case on * most systems that use proportionally-spaced (variable-pitch) fonts or * variable-sized windows, since on such platforms the OS layer must do * most of the formatting work, leaving the standard output layer unable * to guess where pagination should occur. * * If the portable output formatter handles the MORE prompt, which is the * usual case for character-mode or terminal-style implementations, this * routine is not used and you don't need to provide an implementation. * Note that HTML TADS provides an implementation of this routine, because * the HTML renderer handles line breaking and thus must handle * pagination. */ void os_more_prompt() { os_printz("\n[more]\n"); os_waitc(); } /* ------------------------------------------------------------------------ */ /* * User Input Routines */ /* * Ask the user for a filename, using a system-dependent dialog or other * mechanism. Returns one of the OS_AFE_xxx status codes (see below). * * prompt_type is the type of prompt to provide -- this is one of the * OS_AFP_xxx codes (see below). The OS implementation doesn't need to * pay any attention to this parameter, but it can be used if desired to * determine the type of dialog to present if the system provides * different types of dialogs for different types of operations. * * file_type is one of the OSFTxxx codes for system file type. The OS * implementation is free to ignore this information, but can use it to * filter the list of files displayed if desired; this can also be used * to apply a default suffix on systems that use suffixes to indicate * file type. If OSFTUNK is specified, it means that no filtering * should be performed, and no default suffix should be applied. */ int os_askfile(const char *prompt, char *fname_buf, int fname_buf_len, int prompt_type, os_filetype_t file_type) { frefid_t fileref; uint gprompt, gusage; if (prompt_type == OS_AFP_OPEN) gprompt = filemode_Read; else gprompt = filemode_ReadWrite; if (file_type == OSFTSAVE || file_type == OSFTT3SAV) gusage = fileusage_SavedGame; else if (file_type == OSFTLOG || file_type == OSFTTEXT) gusage = fileusage_Transcript; else gusage = fileusage_Data; fileref = g_vm->glk_fileref_create_by_prompt(gusage, (FileMode)gprompt, 0); if (fileref == NULL) return OS_AFE_CANCEL; strcpy(fname_buf, g_vm->garglk_fileref_get_name(fileref)); g_vm->glk_fileref_destroy(fileref); return OS_AFE_SUCCESS; } /* * Read a string of input. Fills in the buffer with a null-terminated * string containing a line of text read from the standard input. The * returned string should NOT contain a trailing newline sequence. On * success, returns 'buf'; on failure, including end of file, returns a * null pointer. */ unsigned char *os_gets(unsigned char *buf, size_t buflen) { event_t event; char *b = (char *)buf; os_get_buffer(b, buflen, 0); do { g_vm->glk_select(&event); if (event.type == evtype_Arrange) redraw_windows(); } while (event.type != evtype_LineInput); return (unsigned char *)os_fill_buffer(b, event.val1); } /* * Read a string of input with an optional timeout. This behaves like * os_gets(), in that it allows the user to edit a line of text (ideally * using the same editing keys that os_gets() does), showing the line of * text under construction during editing. This routine differs from * os_gets() in that it returns if the given timeout interval expires * before the user presses Return (or the local equivalent). * * If the user presses Return before the timeout expires, we store the * command line in the given buffer, just as os_gets() would, and we * return OS_EVT_LINE. We also update the display in the same manner that * os_gets() would, by moving the cursor to a new line and scrolling the * displayed text as needed. * * If a timeout occurs before the user presses Return, we store the * command line so far in the given buffer, statically store the cursor * position, insert mode, buffer text, and anything else relevant to the * editing state, and we return OS_EVT_TIMEOUT. * * If the implementation does not support the timeout operation, this * routine should simply return OS_EVT_NOTIMEOUT immediately when called; * the routine should not allow the user to perform any editing if the * timeout is not supported. Callers must use the ordinary os_gets() * routine, which has no timeout capabilities, if the timeout is not * supported. * * When we return OS_EVT_TIMEOUT, the caller is responsible for doing one * of two things. * * The first possibility is that the caller performs some work that * doesn't require any display operations (in other words, the caller * doesn't invoke os_printf, os_getc, or anything else that would update * the display), and then calls os_gets_timeout() again. In this case, we * will use the editing state that we statically stored before we returned * OS_EVT_TIMEOUT to continue editing where we left off. This allows the * caller to perform some computation in the middle of user command * editing without interrupting the user - the extra computation is * transparent to the user, because we act as though we were still in the * midst of the original editing. * * The second possibility is that the caller wants to update the display. * In this case, the caller must call os_gets_cancel() BEFORE making any * display changes. Then, the caller must do any post-input work of its * own, such as updating the display mode (for example, closing HTML font * tags that were opened at the start of the input). The caller is now * free to do any display work it wants. * * If we have information stored from a previous call that was interrupted * by a timeout, and os_gets_cancel(true) was never called, we will resume * editing where we left off when the cancelled call returned; this means * that we'll restore the cursor position, insertion state, and anything * else relevant. Note that if os_gets_cancel(false) was called, we must * re-display the command line under construction, but if os_gets_cancel() * was never called, we will not have to make any changes to the display * at all. * * Note that when resuming an interrupted editing session (interrupted via * os_gets_cancel()), the caller must re-display the prompt prior to * invoking this routine. * * Note that we can return OS_EVT_EOF in addition to the other codes * mentioned above. OS_EVT_EOF indicates that an error occurred reading, * which usually indicates that the application is being terminated or * that some hardware error occurred reading the keyboard. * * If 'use_timeout' is false, the timeout should be ignored. Without a * timeout, the function behaves the same as os_gets(), except that it * will resume editing of a previously-interrupted command line if * appropriate. (This difference is why the timeout is optional: a caller * might not need a timeout, but might still want to resume a previous * input that did time out, in which case the caller would invoke this * routine with use_timeout==false. The regular os_gets() would not * satisfy this need, because it cannot resume an interrupted input.) */ #if defined GLK_TIMERS && defined GLK_MODULE_LINE_ECHO static char * timebuf = NULL; static size_t timelen = 0; #endif int os_gets_timeout(unsigned char *buf, size_t bufl, unsigned long timeout_in_milliseconds, int use_timeout) { #if defined GLK_TIMERS && defined GLK_MODULE_LINE_ECHO int timer = use_timeout ? timeout_in_milliseconds : 0; int timeout = 0; int initlen = 0; event_t event; /* restore saved buffer contents */ if (timebuf) { assert(timelen && timelen <= bufl); memcpy(buf, timebuf, timelen); initlen = timelen - 1; buf[initlen] = 0; free(timebuf); timebuf = 0; } /* start timer and turn off line echo */ if (timer) { g_vm->glk_request_timer_events(timer); g_vm->glk_set_echo_line_event(mainwin, 0); } os_get_buffer(buf, bufl, initlen); do { g_vm->glk_select(&event); if (event.type == evtype_Arrange) redraw_windows(); else if (event.type == evtype_Timer && (timeout = 1)) g_vm->glk_cancel_line_event(mainwin, &event); } while (event.type != evtype_LineInput); char *res = os_fill_buffer(buf, event.val1); /* stop timer and turn on line echo */ if (timer) { g_vm->glk_request_timer_events(0); g_vm->glk_set_echo_line_event(mainwin, 1); } /* save or print buffer contents */ if (res && timer) { if (timeout) { timelen = strlen(buf) + 1; timebuf = malloc(timelen); memcpy(timebuf, buf, timelen); } else { g_vm->glk_set_style(style_Input); os_print(buf, strlen(buf)); os_print("\n", 1); g_vm->glk_set_style(style_Normal); } } return timeout ? OS_EVT_TIMEOUT : res ? OS_EVT_LINE : OS_EVT_EOF; #else return OS_EVT_NOTIMEOUT; #endif } /* * Cancel an interrupted editing session. This MUST be called if any * output is to be displayed after a call to os_gets_timeout() returns * OS_EVT_TIMEOUT. * * 'reset' indicates whether or not we will forget the input state saved * by os_gets_timeout() when it last returned. If 'reset' is true, we'll * clear the input state, so that the next call to os_gets_timeout() will * start with an empty input buffer. If 'reset' is false, we will retain * the previous input state, if any; this means that the next call to * os_gets_timeout() will re-display the same input buffer that was under * construction when it last returned. * * This routine need not be called if os_gets_timeout() is to be called * again with no other output operations between the previous * os_gets_timeout() call and the next one. * * Note that this routine needs only a trivial implementation when * os_gets_timeout() is not supported (i.e., the function always returns * OS_EVT_NOTIMEOUT). */ void os_gets_cancel(int reset) { #if defined GLK_TIMERS && defined GLK_MODULE_LINE_ECHO if (timebuf) { g_vm->glk_set_style(style_Input); os_print(timebuf, strlen(timebuf)); os_print("\n", 1); g_vm->glk_set_style(style_Normal); if (reset) { free(timebuf); timebuf = 0; } } #endif } /* * Read a character from the keyboard. For extended keystrokes, this * function returns zero, and then returns the CMD_xxx code for the * extended keystroke on the next call. For example, if the user * presses the up-arrow key, the first call to os_getc() should return * 0, and the next call should return CMD_UP. Refer to the CMD_xxx * codes below. * * os_getc() should return a high-level, translated command code for * command editing. This means that, where a functional interpretation * of a key and the raw key-cap interpretation both exist as CMD_xxx * codes, the functional interpretation should be returned. For * example, on Unix, Ctrl-E is conventionally used in command editing to * move to the end of the line, following Emacs key bindings. Hence, * os_getc() should return CMD_END for this keystroke, rather than * (CMD_CTRL + 'E' - 'A'), because CMD_END is the high-level command * code for the operation. * * The translation ability of this function allows for system-dependent * key mappings to functional meanings. */ static int glktotads(unsigned int key) { if (key < 256) return key; switch (key) { case keycode_Up: return CMD_UP; case keycode_Down: return CMD_DOWN; case keycode_Left: return CMD_LEFT; case keycode_Right: return CMD_RIGHT; case keycode_PageUp: return CMD_PGUP; case keycode_PageDown: return CMD_PGDN; case keycode_Home: return CMD_HOME; case keycode_End: return CMD_END; case keycode_Func1: return CMD_F1; case keycode_Func2: return CMD_F2; case keycode_Func3: return CMD_F3; case keycode_Func4: return CMD_F4; case keycode_Func5: return CMD_F5; case keycode_Func6: return CMD_F6; case keycode_Func7: return CMD_F7; case keycode_Func8: return CMD_F8; case keycode_Func9: return CMD_F9; case keycode_Func10: return CMD_F10; default: return 0; } } static int bufchar = 0; static int waitchar = 0; static int timechar = 0; static int getglkchar(void) { event_t event; timechar = 0; g_vm->glk_request_char_event(mainwin); do { g_vm->glk_select(&event); if (event.type == evtype_Arrange) redraw_windows(); else if (event.type == evtype_Timer) timechar = 1; } while (event.type != evtype_CharInput && event.type != evtype_Timer); g_vm->glk_cancel_char_event(mainwin); return timechar ? 0 : event.val1; } int os_getc(void) { unsigned int c; if (bufchar) { c = bufchar; bufchar = 0; return c; } c = waitchar ? waitchar : getglkchar(); waitchar = 0; if (c == keycode_Return) c = '\n'; else if (c == keycode_Tab) c = '\t'; else if (c == keycode_Escape) c = 27; if (c < 256) return c; bufchar = glktotads(c); return 0; } /* * Read a character from the keyboard, following the same protocol as * os_getc() for CMD_xxx codes (i.e., when an extended keystroke is * encountered, os_getc_raw() returns zero, then returns the CMD_xxx code * on the subsequent call). * * This function differs from os_getc() in that this function returns the * low-level, untranslated key code whenever possible. This means that, * when a functional interpretation of a key and the raw key-cap * interpretation both exist as CMD_xxx codes, this function returns the * key-cap interpretation. For the Unix Ctrl-E example in the comments * describing os_getc() above, this function should return 5 (the ASCII * code for Ctrl-E), because the CMD_CTRL interpretation is the low-level * key code. * * This function should return all control keys using their ASCII control * codes, whenever possible. Similarly, this function should return ASCII * 27 for the Escape key, if possible. * * For keys for which there is no portable ASCII representation, this * should return the CMD_xxx sequence. So, this function acts exactly the * same as os_getc() for arrow keys, function keys, and other special keys * that have no ASCII representation. This function returns a * non-translated version ONLY when an ASCII representation exists - in * practice, this means that this function and os_getc() vary only for * CTRL keys and Escape. */ int os_getc_raw(void) { return os_getc(); } /* wait for a character to become available from the keyboard */ void os_waitc(void) { waitchar = getglkchar(); } /* * Get an input event. The event types are shown above. If use_timeout * is false, this routine should simply wait until one of the events it * recognizes occurs, then return the appropriate information on the * event. If use_timeout is true, this routine should return * OS_EVT_TIMEOUT after the given number of milliseconds elapses if no * event occurs first. * * This function is not obligated to obey the timeout. If a timeout is * specified and it is not possible to obey the timeout, the function * should simply return OS_EVT_NOTIMEOUT. The trivial implementation * thus checks for a timeout, returns an error if specified, and * otherwise simply waits for the user to press a key. */ int os_get_event(unsigned long timeout_in_milliseconds, int use_timeout, os_event_info_t *info) { #ifdef GLK_TIMERS /* start timer */ int timer = use_timeout ? timeout_in_milliseconds : 0; if (timer) g_vm->glk_request_timer_events(timer); #else /* we can't handle timeouts */ if (use_timeout) return OS_EVT_NOTIMEOUT; #endif /* get a key */ info->key[0] = os_getc_raw(); if (info->key[0] == 0 && timechar == 0) info->key[1] = os_getc_raw(); #ifdef GLK_TIMERS /* stop timer */ if (timer) g_vm->glk_request_timer_events(0); #endif /* return the event */ return timechar ? OS_EVT_TIMEOUT : OS_EVT_KEY; } osfildef *os_exeseek(const char *argv0, const char *typ) { return nullptr; } int os_get_str_rsc(int id, char *buf, size_t buflen) { strcpy(buf, ""); return 0; } void os_dbg_printf(const char *fmt, ...) { // No implementation, since ScummGlk doesn't yet implement a debugger } void os_dbg_vprintf(const char *fmt, va_list args) { // No implementation, since ScummGlk doesn't yet implement a debugger } int os_vasprintf(char **bufptr, const char *fmt, va_list ap) { Common::String s = Common::String::vformat(fmt, ap); *bufptr = (char *)malloc(s.size() + 1); strcpy(*bufptr, s.c_str()); return s.size(); } int os_paramfile(char *buf) { return false; } void os_rand(long *val) { *val = g_vm->getRandomNumber(0x7fffffff); } long os_get_sys_clock_ms() { return g_system->getMillis(); } void os_xlat_html4(unsigned int html4_char, char *result, size_t result_len) { /* Return all standard Latin-1 characters as-is */ if (html4_char <= 128 || (html4_char >= 160 && html4_char <= 255)) result[0] = (unsigned char)html4_char; else { switch (html4_char) { case 130: /* single back quote */ result[0] = '`'; break; case 132: /* double back quote */ result[0] = '\"'; break; case 153: /* trade mark */ strcpy(result, "(tm)"); return; case 140: /* OE ligature */ case 338: /* OE ligature */ strcpy(result, "OE"); return; case 339: /* oe ligature */ strcpy(result, "oe"); return; case 159: /* Yuml */ result[0] = (char)255; case 376: /* Y with diaresis */ result[0] = 'Y'; break; case 352: /* S with caron */ result[0] = 'S'; break; case 353: /* s with caron */ result[0] = 's'; break; case 150: /* en dash */ case 8211: /* en dash */ result[0] = '-'; break; case 151: /* em dash */ case 8212: /* em dash */ strcpy(result, "--"); return; case 145: /* left single quote */ case 8216: /* left single quote */ result[0] = '`'; break; case 146: /* right single quote */ case 8217: /* right single quote */ case 8218: /* single low-9 quote */ result[0] = '\''; break; case 147: /* left double quote */ case 148: /* right double quote */ case 8220: /* left double quote */ case 8221: /* right double quote */ case 8222: /* double low-9 quote */ result[0] = '\"'; break; case 8224: /* dagger */ case 8225: /* double dagger */ case 8240: /* per mille sign */ result[0] = ' '; break; case 139: /* single left-pointing angle quote */ case 8249: /* single left-pointing angle quote */ result[0] = '<'; break; case 155: /* single right-pointing angle quote */ case 8250: /* single right-pointing angle quote */ result[0] = '>'; break; case 8482: /* small tilde */ result[0] = '~'; break; default: /* unmappable character - return space */ result[0] = (unsigned char)' '; } } result[1] = 0; } #ifndef os_tzset void os_tzset() {} #endif void os_nonstop_mode(int flag) {} void os_advise_load_charmap(const char *id, const char *ldesc, const char *sysinfo) {} void os_gen_charmap_filename(char *filename, char *internal_id, char *argv0) {} int os_input_dialog(int icon_id, const char *prompt, int standard_button_set, const char **buttons, int button_count, int default_index, int cancel_index) { // CUrrently unsupported return 0; } void os_flush() { g_vm->glk_tick(); } char *os_strlwr(char *s) { for (char *p = s; *p; ++p) *p = tolower(*p); return s; } void os_expause() { #ifdef USE_EXPAUSE os_printz("(Strike any key to exit...)"); os_flush(); os_waitc(); #endif /* USE_EXPAUSE */ } void os_plain(void) {} int memicmp(const char *s1, const char *s2, int len) { Common::String cs1(s1, len); Common::String cs2(s2, len); return cs1.compareToIgnoreCase(cs2); } } // End of namespace TADS } // End of namespace Glk