1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "glk/tads/os_glk.h"
24 #include "glk/tads/tads.h"
25 #include "glk/tads/os_buffer.h"
26
27 namespace Glk {
28 namespace TADS {
29
30 static void redraw_windows(void);
31 static void os_status_redraw(void);
32 extern void os_banners_redraw(void);
33
34 static char lbuf[256], rbuf[256];
35 static int curwin = 0;
36 static int curattr = 0;
37
38 winid_t mainwin;
39 winid_t statuswin;
40
41 uint mainfg;
42 uint mainbg;
43
44 uint statusfg;
45 uint statusbg;
46
47 int G_os_pagelength;
48 int G_os_linewidth;
49 int G_os_moremode;
50 char G_os_gamename[OSFNMAX];
51
52 /* ------------------------------------------------------------------------ */
53
54 /*
55 * Initialize. This should be called during program startup to
56 * initialize the OS layer and check OS-specific command-line arguments.
57 *
58 * If 'prompt' and 'buf' are non-null, and there are no arguments on the
59 * given command line, the OS code can use the prompt to ask the user to
60 * supply a filename, then store the filename in 'buf' and set up
61 * argc/argv to give a one-argument command string. (This mechanism for
62 * prompting for a filename is obsolescent, and is retained for
63 * compatibility with a small number of existing implementations only;
64 * new implementations should ignore this mechanism and leave the
65 * argc/argv values unchanged.)
66 */
os_init(int * argc,char * argv[],const char * prompt,char * buf,int bufsiz)67 int os_init(int *argc, char *argv[], const char *prompt,
68 char *buf, int bufsiz)
69 {
70 mainwin = g_vm->glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
71 if (!mainwin)
72 error("fatal: could not open window!\n");
73
74 /* get default colors for main window */
75 if (!g_vm->glk_style_measure(mainwin, style_Normal, stylehint_TextColor, &mainfg))
76 mainfg = 0;
77
78 if (!g_vm->glk_style_measure(mainwin, style_Normal, stylehint_BackColor, &mainbg))
79 mainbg = 0;
80
81 /* get default colors for status window */
82 statuswin = g_vm->glk_window_open(mainwin,
83 winmethod_Above | winmethod_Fixed, 1,
84 wintype_TextGrid, 0);
85
86 if (!g_vm->glk_style_measure(statuswin, style_Normal, stylehint_TextColor, &statusfg))
87 statusfg = 0;
88
89 if (!g_vm->glk_style_measure(statuswin, style_Normal, stylehint_BackColor, &statusbg))
90 statusbg = 0;
91
92 /* close statuswin; reopened on request */
93 g_vm->glk_window_close(statuswin, 0);
94
95 statuswin = nullptr;
96
97 g_vm->glk_set_window(mainwin);
98
99 strcpy(rbuf, "");
100
101 return 0;
102 }
103
104 /*
105 * Uninitialize. This is called prior to progam termination to reverse
106 * the effect of any changes made in os_init(). For example, if
107 * os_init() put the terminal in raw mode, this should restore the
108 * previous terminal mode. This routine should not terminate the
109 * program (so don't call exit() here) - the caller might have more
110 * processing to perform after this routine returns.
111 */
os_uninit(void)112 void os_uninit(void)
113 {
114 }
115
os_term(int status)116 void os_term(int status) {
117 g_vm->quitGame();
118 }
119
os_instbrk(int install)120 void os_instbrk(int install) {
121 // No implementation
122 }
123
os_break()124 bool os_break() {
125 return false;
126 }
127
os_sleep_ms(long delay_in_milliseconds)128 void os_sleep_ms(long delay_in_milliseconds) {
129 g_system->delayMillis(delay_in_milliseconds);
130 }
131
132 /* ------------------------------------------------------------------------ */
133 /*
134 * Get system information. 'code' is a SYSINFO_xxx code, which
135 * specifies what type of information to get. The 'param' argument's
136 * meaning depends on which code is selected. 'result' is a pointer to
137 * an integer that is to be filled in with the result value. If the
138 * code is not known, this function should return false. If the code is
139 * known, the function should fill in *result and return true.
140 */
os_get_sysinfo(int code,void * param,long * result)141 int os_get_sysinfo(int code, void *param, long *result) {
142 switch (code)
143 {
144 case SYSINFO_TEXT_HILITE:
145 *result = 1;
146 return true;
147 case SYSINFO_BANNERS:
148 *result = 1;
149 return true;
150 case SYSINFO_TEXT_COLORS:
151 *result = SYSINFO_TXC_RGB;
152 return true;
153
154 #ifdef USE_HTML
155 case SYSINFO_INTERP_CLASS:
156 *result = SYSINFO_ICLASS_HTML;
157 return true;
158 case SYSINFO_HTML:
159 *result = 1;
160 return true;
161 #else
162 case SYSINFO_INTERP_CLASS:
163 *result = SYSINFO_ICLASS_TEXTGUI;
164 return true;
165 case SYSINFO_HTML:
166 *result = 0;
167 return true;
168 #endif
169
170 case SYSINFO_JPEG:
171 case SYSINFO_PNG:
172 case SYSINFO_WAV:
173 case SYSINFO_MIDI:
174 case SYSINFO_WAV_MIDI_OVL:
175 case SYSINFO_WAV_OVL:
176 case SYSINFO_PREF_IMAGES:
177 case SYSINFO_PREF_SOUNDS:
178 case SYSINFO_PREF_MUSIC:
179 case SYSINFO_PREF_LINKS:
180 case SYSINFO_MPEG:
181 case SYSINFO_MPEG1:
182 case SYSINFO_MPEG2:
183 case SYSINFO_MPEG3:
184 case SYSINFO_LINKS_HTTP:
185 case SYSINFO_LINKS_FTP:
186 case SYSINFO_LINKS_NEWS:
187 case SYSINFO_LINKS_MAILTO:
188 case SYSINFO_LINKS_TELNET:
189 case SYSINFO_PNG_TRANS:
190 case SYSINFO_PNG_ALPHA:
191 case SYSINFO_OGG:
192 *result = 0;
193 return true;
194
195 default:
196 return false;
197 }
198 }
199
200 /* ------------------------------------------------------------------------ */
201 /*
202 * Display routines.
203 *
204 * Our display model is a simple stdio-style character stream.
205 *
206 * In addition, we provide an optional "status line," which is a
207 * non-scrolling area where a line of text can be displayed. If the status
208 * line is supported, text should only be displayed in this area when
209 * os_status() is used to enter status-line mode (mode 1); while in status
210 * line mode, text is written to the status line area, otherwise (mode 0)
211 * it's written to the normal main text area. The status line is normally
212 * shown in a different color to set it off from the rest of the text.
213 *
214 * The OS layer can provide its own formatting (word wrapping in
215 * particular) if it wants, in which case it should also provide pagination
216 * using os_more_prompt().
217 */
218
219 /*
220 * Print a string on the console. These routines come in two varieties:
221 *
222 * os_printz - write a NULL-TERMINATED string
223 *. os_print - write a COUNTED-LENGTH string, which may not end with a null
224 *
225 * These two routines are identical except that os_printz() takes a string
226 * which is terminated by a null byte, and os_print() instead takes an
227 * explicit length, and a string that may not end with a null byte.
228 *
229 * os_printz(str) may be implemented as simply os_print(str, strlen(str)).
230 *
231 * The string is written in one of three ways, depending on the status mode
232 * set by os_status():
233 *
234 * status mode == 0 -> write to main text window
235 *. status mode == 1 -> write to status line
236 *. anything else -> do not display the text at all
237 *
238 * Implementations are free to omit any status line support, in which case
239 * they should simply suppress all output when the status mode is anything
240 * other than zero.
241 *
242 * The following special characters must be recognized in the displayed
243 * text:
244 *
245 * '\n' - newline: end the current line and move the cursor to the start of
246 * the next line. If the status line is supported, and the current status
247 * mode is 1 (i.e., displaying in the status line), then two special rules
248 * apply to newline handling: newlines preceding any other text should be
249 * ignored, and a newline following any other text should set the status
250 * mode to 2, so that all subsequent output is suppressed until the status
251 * mode is changed with an explicit call by the client program to
252 * os_status().
253 *
254 * '\r' - carriage return: end the current line and move the cursor back to
255 * the beginning of the current line. Subsequent output is expected to
256 * overwrite the text previously on this same line. The implementation
257 * may, if desired, IMMEDIATELY clear the previous text when the '\r' is
258 * written, rather than waiting for subsequent text to be displayed.
259 *
260 * All other characters may be assumed to be ordinary printing characters.
261 * The routine need not check for any other special characters.
262 *
263 */
264
os_printz(const char * str)265 void os_printz(const char *str) {
266 os_print(str, strlen(str));
267 }
268
os_print(const char * str,size_t len)269 void os_print(const char *str, size_t len) {
270 if (curwin == 0 && str)
271 os_put_buffer(str, len);
272
273 if (curwin == 1)
274 {
275 const char *p;
276 size_t rem, max;
277
278 /* The string requires some fiddling for the status window */
279 for (p = str, rem = len ; rem != 0 && *p == '\n'; p++, --rem)
280 ;
281 if (rem != 0 && p[rem-1] == '\n')
282 --rem;
283
284 /* if that leaves anything, update the statusline */
285 if (rem != 0)
286 {
287 max = sizeof(lbuf) - strlen(lbuf) - 1;
288 strncat(lbuf, p, rem > max ? max : rem);
289 os_status_redraw();
290 }
291 }
292 }
293
294
295 /*
296 * Set the status line mode. There are three possible settings:
297 *
298 * 0 -> main text mode. In this mode, all subsequent text written with
299 * os_print() and os_printz() is to be displayed to the main text area.
300 * This is the normal mode that should be in effect initially. This mode
301 * stays in effect until an explicit call to os_status().
302 *
303 * 1 -> statusline mode. In this mode, text written with os_print() and
304 * os_printz() is written to the status line, which is usually rendered as
305 * a one-line area across the top of the terminal screen or application
306 * window. In statusline mode, leading newlines ('\n' characters) are to
307 * be ignored, and any newline following any other character must change
308 * the mode to 2, as though os_status(2) had been called.
309 *
310 * 2 -> suppress mode. In this mode, all text written with os_print() and
311 * os_printz() must simply be ignored, and not displayed at all. This mode
312 * stays in effect until an explicit call to os_status().
313 */
314
os_status(int stat)315 void os_status(int stat)
316 {
317 curwin = stat;
318
319 if (stat == 1)
320 {
321 if (statuswin == NULL)
322 {
323 g_vm->glk_stylehint_set(wintype_TextGrid, style_User1, stylehint_ReverseColor, 1);
324 statuswin = g_vm->glk_window_open(mainwin,
325 winmethod_Above | winmethod_Fixed, 1,
326 wintype_TextGrid, 0);
327 }
328 strcpy(lbuf, "");
329 }
330 }
331
332 /* get the status line mode */
os_get_status()333 int os_get_status()
334 {
335 return curwin;
336 }
337
338 /*
339 * Set the score value. This displays the given score and turn counts on
340 * the status line. In most cases, these values are displayed at the right
341 * edge of the status line, in the format "score/turns", but the format is
342 * up to the implementation to determine. In most cases, this can simply
343 * be implemented as follows:
344 *
345 */
os_score(int score,int turncount)346 void os_score(int score, int turncount)
347 {
348 char buf[40];
349 sprintf(buf, "%d/%d", score, turncount);
350 os_strsc(buf);
351 }
352
353 /* display a string in the score area in the status line */
os_strsc(const char * p)354 void os_strsc(const char *p)
355 {
356 snprintf(rbuf, sizeof rbuf, "%s", p);
357 os_status_redraw();
358 }
359
os_status_redraw(void)360 static void os_status_redraw(void) {
361 char fmt[32];
362 char buf[256];
363 uint wid;
364 uint div;
365
366 if (!statuswin)
367 return;
368
369 g_vm->glk_window_get_size(statuswin, &wid, NULL);
370 div = wid - strlen(rbuf) - 3;
371
372 sprintf(fmt, " %%%ds %%s ", - (int)div);
373 sprintf(buf, fmt, lbuf, rbuf);
374
375 g_vm->glk_window_clear(statuswin);
376 g_vm->glk_set_window(statuswin);
377 g_vm->glk_set_style(style_User1);
378 os_put_buffer(buf, strlen(buf));
379 g_vm->glk_set_window(mainwin);
380 }
381
redraw_windows(void)382 static void redraw_windows(void)
383 {
384 os_status_redraw();
385 os_banners_redraw();
386 }
387
388 /* clear the screen */
oscls(void)389 void oscls(void)
390 {
391 g_vm->glk_window_clear(mainwin);
392 }
393
394 /* ------------------------------------------------------------------------ */
395 /*
396 * Set text attributes. Text subsequently displayed through os_print() and
397 * os_printz() are to be displayed with the given attributes.
398 *
399 * 'attr' is a (bitwise-OR'd) combination of OS_ATTR_xxx values. A value
400 * of zero indicates normal text, with no extra attributes.
401 */
os_set_text_attr(int attr)402 void os_set_text_attr(int attr)
403 {
404 curattr = attr;
405 if (curattr & OS_ATTR_BOLD && curattr & OS_ATTR_ITALIC)
406 g_vm->glk_set_style(style_Alert);
407 else if (curattr & OS_ATTR_BOLD)
408 g_vm->glk_set_style(style_Subheader);
409 else if (curattr & OS_ATTR_ITALIC)
410 g_vm->glk_set_style(style_Emphasized);
411 else
412 g_vm->glk_set_style(style_Normal);
413 }
414
415 /*
416 * Set the text foreground and background colors. This sets the text
417 * color for subsequent os_printf() and os_vprintf() calls.
418 *
419 * The background color can be OS_COLOR_TRANSPARENT, in which case the
420 * background color is "inherited" from the current screen background.
421 * Note that if the platform is capable of keeping old text for
422 * "scrollback," then the transparency should be a permanent attribute of
423 * the character - in other words, it should not be mapped to the current
424 * screen color in the scrollback buffer, because doing so would keep the
425 * current screen color even if the screen color changes in the future.
426 *
427 * Text color support is optional. If the platform doesn't support text
428 * colors, this can simply do nothing. If the platform supports text
429 * colors, but the requested color or attributes cannot be displayed, the
430 * implementation should use the best available approximation.
431 */
os_set_text_color(os_color_t fg,os_color_t bg)432 void os_set_text_color(os_color_t fg, os_color_t bg) {
433 }
434
435 /*
436 * Set the screen background color. This sets the text color for the
437 * background of the screen. If possible, this should immediately redraw
438 * the main text area with this background color. The color is given as an
439 * OS_COLOR_xxx value.
440 *
441 * If the platform is capable of redisplaying the existing text, then any
442 * existing text that was originally displayed with 'transparent'
443 * background color should be redisplayed with the new screen background
444 * color. In other words, the 'transparent' background color of previously
445 * drawn text should be a permanent attribute of the character - the color
446 * should not be mapped on display to the then-current background color,
447 * because doing so would lose the transparency and thus retain the old
448 * screen color on a screen color change.
449 */
os_set_screen_color(os_color_t color)450 void os_set_screen_color(os_color_t color)
451 {
452 }
453
454 /*
455 * Set the game title. The output layer calls this routine when a game
456 * sets its title (via an HTML <title> tag, for example). If it's
457 * convenient to do so, the OS layer can use this string to set a window
458 * caption, or whatever else makes sense on each system. Most
459 * character-mode implementations will provide an empty implementation,
460 * since there's not usually any standard way to show the current
461 * application title on a character-mode display.
462 */
os_set_title(const char * title)463 void os_set_title(const char *title)
464 {
465 #ifdef GARGLK
466 g_vm->garglk_set_story_title(title);
467 #endif
468 }
469
470 /*
471 * Show the system-specific MORE prompt, and wait for the user to respond.
472 * Before returning, remove the MORE prompt from the screen.
473 *
474 * This routine is only used and only needs to be implemented when the OS
475 * layer takes responsibility for pagination; this will be the case on
476 * most systems that use proportionally-spaced (variable-pitch) fonts or
477 * variable-sized windows, since on such platforms the OS layer must do
478 * most of the formatting work, leaving the standard output layer unable
479 * to guess where pagination should occur.
480 *
481 * If the portable output formatter handles the MORE prompt, which is the
482 * usual case for character-mode or terminal-style implementations, this
483 * routine is not used and you don't need to provide an implementation.
484 * Note that HTML TADS provides an implementation of this routine, because
485 * the HTML renderer handles line breaking and thus must handle
486 * pagination.
487 */
os_more_prompt()488 void os_more_prompt()
489 {
490 os_printz("\n[more]\n");
491 os_waitc();
492 }
493
494 /* ------------------------------------------------------------------------ */
495 /*
496 * User Input Routines
497 */
498
499 /*
500 * Ask the user for a filename, using a system-dependent dialog or other
501 * mechanism. Returns one of the OS_AFE_xxx status codes (see below).
502 *
503 * prompt_type is the type of prompt to provide -- this is one of the
504 * OS_AFP_xxx codes (see below). The OS implementation doesn't need to
505 * pay any attention to this parameter, but it can be used if desired to
506 * determine the type of dialog to present if the system provides
507 * different types of dialogs for different types of operations.
508 *
509 * file_type is one of the OSFTxxx codes for system file type. The OS
510 * implementation is free to ignore this information, but can use it to
511 * filter the list of files displayed if desired; this can also be used
512 * to apply a default suffix on systems that use suffixes to indicate
513 * file type. If OSFTUNK is specified, it means that no filtering
514 * should be performed, and no default suffix should be applied.
515 */
os_askfile(const char * prompt,char * fname_buf,int fname_buf_len,int prompt_type,os_filetype_t file_type)516 int os_askfile(const char *prompt, char *fname_buf, int fname_buf_len,
517 int prompt_type, os_filetype_t file_type)
518 {
519 frefid_t fileref;
520 uint gprompt, gusage;
521
522 if (prompt_type == OS_AFP_OPEN)
523 gprompt = filemode_Read;
524 else
525 gprompt = filemode_ReadWrite;
526
527 if (file_type == OSFTSAVE || file_type == OSFTT3SAV)
528 gusage = fileusage_SavedGame;
529 else if (file_type == OSFTLOG || file_type == OSFTTEXT)
530 gusage = fileusage_Transcript;
531 else
532 gusage = fileusage_Data;
533
534 fileref = g_vm->glk_fileref_create_by_prompt(gusage, (FileMode)gprompt, 0);
535 if (fileref == NULL)
536 return OS_AFE_CANCEL;
537
538 strcpy(fname_buf, g_vm->garglk_fileref_get_name(fileref));
539
540 g_vm->glk_fileref_destroy(fileref);
541
542 return OS_AFE_SUCCESS;
543 }
544
545 /*
546 * Read a string of input. Fills in the buffer with a null-terminated
547 * string containing a line of text read from the standard input. The
548 * returned string should NOT contain a trailing newline sequence. On
549 * success, returns 'buf'; on failure, including end of file, returns a
550 * null pointer.
551 */
os_gets(unsigned char * buf,size_t buflen)552 unsigned char *os_gets(unsigned char *buf, size_t buflen)
553 {
554 event_t event;
555 char *b = (char *)buf;
556
557 os_get_buffer(b, buflen, 0);
558
559 do
560 {
561 g_vm->glk_select(&event);
562 if (event.type == evtype_Arrange)
563 redraw_windows();
564 }
565 while (event.type != evtype_LineInput);
566
567 return (unsigned char *)os_fill_buffer(b, event.val1);
568 }
569
570 /*
571 * Read a string of input with an optional timeout. This behaves like
572 * os_gets(), in that it allows the user to edit a line of text (ideally
573 * using the same editing keys that os_gets() does), showing the line of
574 * text under construction during editing. This routine differs from
575 * os_gets() in that it returns if the given timeout interval expires
576 * before the user presses Return (or the local equivalent).
577 *
578 * If the user presses Return before the timeout expires, we store the
579 * command line in the given buffer, just as os_gets() would, and we
580 * return OS_EVT_LINE. We also update the display in the same manner that
581 * os_gets() would, by moving the cursor to a new line and scrolling the
582 * displayed text as needed.
583 *
584 * If a timeout occurs before the user presses Return, we store the
585 * command line so far in the given buffer, statically store the cursor
586 * position, insert mode, buffer text, and anything else relevant to the
587 * editing state, and we return OS_EVT_TIMEOUT.
588 *
589 * If the implementation does not support the timeout operation, this
590 * routine should simply return OS_EVT_NOTIMEOUT immediately when called;
591 * the routine should not allow the user to perform any editing if the
592 * timeout is not supported. Callers must use the ordinary os_gets()
593 * routine, which has no timeout capabilities, if the timeout is not
594 * supported.
595 *
596 * When we return OS_EVT_TIMEOUT, the caller is responsible for doing one
597 * of two things.
598 *
599 * The first possibility is that the caller performs some work that
600 * doesn't require any display operations (in other words, the caller
601 * doesn't invoke os_printf, os_getc, or anything else that would update
602 * the display), and then calls os_gets_timeout() again. In this case, we
603 * will use the editing state that we statically stored before we returned
604 * OS_EVT_TIMEOUT to continue editing where we left off. This allows the
605 * caller to perform some computation in the middle of user command
606 * editing without interrupting the user - the extra computation is
607 * transparent to the user, because we act as though we were still in the
608 * midst of the original editing.
609 *
610 * The second possibility is that the caller wants to update the display.
611 * In this case, the caller must call os_gets_cancel() BEFORE making any
612 * display changes. Then, the caller must do any post-input work of its
613 * own, such as updating the display mode (for example, closing HTML font
614 * tags that were opened at the start of the input). The caller is now
615 * free to do any display work it wants.
616 *
617 * If we have information stored from a previous call that was interrupted
618 * by a timeout, and os_gets_cancel(true) was never called, we will resume
619 * editing where we left off when the cancelled call returned; this means
620 * that we'll restore the cursor position, insertion state, and anything
621 * else relevant. Note that if os_gets_cancel(false) was called, we must
622 * re-display the command line under construction, but if os_gets_cancel()
623 * was never called, we will not have to make any changes to the display
624 * at all.
625 *
626 * Note that when resuming an interrupted editing session (interrupted via
627 * os_gets_cancel()), the caller must re-display the prompt prior to
628 * invoking this routine.
629 *
630 * Note that we can return OS_EVT_EOF in addition to the other codes
631 * mentioned above. OS_EVT_EOF indicates that an error occurred reading,
632 * which usually indicates that the application is being terminated or
633 * that some hardware error occurred reading the keyboard.
634 *
635 * If 'use_timeout' is false, the timeout should be ignored. Without a
636 * timeout, the function behaves the same as os_gets(), except that it
637 * will resume editing of a previously-interrupted command line if
638 * appropriate. (This difference is why the timeout is optional: a caller
639 * might not need a timeout, but might still want to resume a previous
640 * input that did time out, in which case the caller would invoke this
641 * routine with use_timeout==false. The regular os_gets() would not
642 * satisfy this need, because it cannot resume an interrupted input.)
643 */
644 #if defined GLK_TIMERS && defined GLK_MODULE_LINE_ECHO
645 static char * timebuf = NULL;
646 static size_t timelen = 0;
647 #endif
648
os_gets_timeout(unsigned char * buf,size_t bufl,unsigned long timeout_in_milliseconds,int use_timeout)649 int os_gets_timeout(unsigned char *buf, size_t bufl,
650 unsigned long timeout_in_milliseconds, int use_timeout)
651 {
652 #if defined GLK_TIMERS && defined GLK_MODULE_LINE_ECHO
653 int timer = use_timeout ? timeout_in_milliseconds : 0;
654 int timeout = 0;
655 int initlen = 0;
656 event_t event;
657
658 /* restore saved buffer contents */
659 if (timebuf)
660 {
661 assert(timelen && timelen <= bufl);
662 memcpy(buf, timebuf, timelen);
663 initlen = timelen - 1;
664 buf[initlen] = 0;
665 free(timebuf);
666 timebuf = 0;
667 }
668
669 /* start timer and turn off line echo */
670 if (timer)
671 {
672 g_vm->glk_request_timer_events(timer);
673 g_vm->glk_set_echo_line_event(mainwin, 0);
674 }
675
676 os_get_buffer(buf, bufl, initlen);
677
678 do
679 {
680 g_vm->glk_select(&event);
681 if (event.type == evtype_Arrange)
682 redraw_windows();
683 else if (event.type == evtype_Timer && (timeout = 1))
684 g_vm->glk_cancel_line_event(mainwin, &event);
685 }
686 while (event.type != evtype_LineInput);
687
688 char *res = os_fill_buffer(buf, event.val1);
689
690 /* stop timer and turn on line echo */
691 if (timer)
692 {
693 g_vm->glk_request_timer_events(0);
694 g_vm->glk_set_echo_line_event(mainwin, 1);
695 }
696
697 /* save or print buffer contents */
698 if (res && timer)
699 {
700 if (timeout)
701 {
702 timelen = strlen(buf) + 1;
703 timebuf = malloc(timelen);
704 memcpy(timebuf, buf, timelen);
705 }
706 else
707 {
708 g_vm->glk_set_style(style_Input);
709 os_print(buf, strlen(buf));
710 os_print("\n", 1);
711 g_vm->glk_set_style(style_Normal);
712 }
713 }
714
715 return timeout ? OS_EVT_TIMEOUT : res ? OS_EVT_LINE : OS_EVT_EOF;
716 #else
717 return OS_EVT_NOTIMEOUT;
718 #endif
719 }
720
721 /*
722 * Cancel an interrupted editing session. This MUST be called if any
723 * output is to be displayed after a call to os_gets_timeout() returns
724 * OS_EVT_TIMEOUT.
725 *
726 * 'reset' indicates whether or not we will forget the input state saved
727 * by os_gets_timeout() when it last returned. If 'reset' is true, we'll
728 * clear the input state, so that the next call to os_gets_timeout() will
729 * start with an empty input buffer. If 'reset' is false, we will retain
730 * the previous input state, if any; this means that the next call to
731 * os_gets_timeout() will re-display the same input buffer that was under
732 * construction when it last returned.
733 *
734 * This routine need not be called if os_gets_timeout() is to be called
735 * again with no other output operations between the previous
736 * os_gets_timeout() call and the next one.
737 *
738 * Note that this routine needs only a trivial implementation when
739 * os_gets_timeout() is not supported (i.e., the function always returns
740 * OS_EVT_NOTIMEOUT).
741 */
os_gets_cancel(int reset)742 void os_gets_cancel(int reset)
743 {
744 #if defined GLK_TIMERS && defined GLK_MODULE_LINE_ECHO
745 if (timebuf)
746 {
747 g_vm->glk_set_style(style_Input);
748 os_print(timebuf, strlen(timebuf));
749 os_print("\n", 1);
750 g_vm->glk_set_style(style_Normal);
751
752 if (reset)
753 {
754 free(timebuf);
755 timebuf = 0;
756 }
757 }
758 #endif
759 }
760
761 /*
762 * Read a character from the keyboard. For extended keystrokes, this
763 * function returns zero, and then returns the CMD_xxx code for the
764 * extended keystroke on the next call. For example, if the user
765 * presses the up-arrow key, the first call to os_getc() should return
766 * 0, and the next call should return CMD_UP. Refer to the CMD_xxx
767 * codes below.
768 *
769 * os_getc() should return a high-level, translated command code for
770 * command editing. This means that, where a functional interpretation
771 * of a key and the raw key-cap interpretation both exist as CMD_xxx
772 * codes, the functional interpretation should be returned. For
773 * example, on Unix, Ctrl-E is conventionally used in command editing to
774 * move to the end of the line, following Emacs key bindings. Hence,
775 * os_getc() should return CMD_END for this keystroke, rather than
776 * (CMD_CTRL + 'E' - 'A'), because CMD_END is the high-level command
777 * code for the operation.
778 *
779 * The translation ability of this function allows for system-dependent
780 * key mappings to functional meanings.
781 */
glktotads(unsigned int key)782 static int glktotads(unsigned int key)
783 {
784 if (key < 256)
785 return key;
786 switch (key)
787 {
788 case keycode_Up:
789 return CMD_UP;
790 case keycode_Down:
791 return CMD_DOWN;
792 case keycode_Left:
793 return CMD_LEFT;
794 case keycode_Right:
795 return CMD_RIGHT;
796 case keycode_PageUp:
797 return CMD_PGUP;
798 case keycode_PageDown:
799 return CMD_PGDN;
800 case keycode_Home:
801 return CMD_HOME;
802 case keycode_End:
803 return CMD_END;
804 case keycode_Func1:
805 return CMD_F1;
806 case keycode_Func2:
807 return CMD_F2;
808 case keycode_Func3:
809 return CMD_F3;
810 case keycode_Func4:
811 return CMD_F4;
812 case keycode_Func5:
813 return CMD_F5;
814 case keycode_Func6:
815 return CMD_F6;
816 case keycode_Func7:
817 return CMD_F7;
818 case keycode_Func8:
819 return CMD_F8;
820 case keycode_Func9:
821 return CMD_F9;
822 case keycode_Func10:
823 return CMD_F10;
824 default:
825 return 0;
826 }
827 }
828
829 static int bufchar = 0;
830 static int waitchar = 0;
831 static int timechar = 0;
832
getglkchar(void)833 static int getglkchar(void)
834 {
835 event_t event;
836
837 timechar = 0;
838
839 g_vm->glk_request_char_event(mainwin);
840
841 do
842 {
843 g_vm->glk_select(&event);
844 if (event.type == evtype_Arrange)
845 redraw_windows();
846 else if (event.type == evtype_Timer)
847 timechar = 1;
848 }
849 while (event.type != evtype_CharInput && event.type != evtype_Timer);
850
851 g_vm->glk_cancel_char_event(mainwin);
852
853 return timechar ? 0 : event.val1;
854 }
855
os_getc(void)856 int os_getc(void)
857 {
858 unsigned int c;
859
860 if (bufchar)
861 {
862 c = bufchar;
863 bufchar = 0;
864 return c;
865 }
866
867 c = waitchar ? waitchar : getglkchar();
868 waitchar = 0;
869
870 if (c == keycode_Return)
871 c = '\n';
872 else if (c == keycode_Tab)
873 c = '\t';
874 else if (c == keycode_Escape)
875 c = 27;
876
877 if (c < 256)
878 return c;
879
880 bufchar = glktotads(c);
881
882 return 0;
883 }
884
885 /*
886 * Read a character from the keyboard, following the same protocol as
887 * os_getc() for CMD_xxx codes (i.e., when an extended keystroke is
888 * encountered, os_getc_raw() returns zero, then returns the CMD_xxx code
889 * on the subsequent call).
890 *
891 * This function differs from os_getc() in that this function returns the
892 * low-level, untranslated key code whenever possible. This means that,
893 * when a functional interpretation of a key and the raw key-cap
894 * interpretation both exist as CMD_xxx codes, this function returns the
895 * key-cap interpretation. For the Unix Ctrl-E example in the comments
896 * describing os_getc() above, this function should return 5 (the ASCII
897 * code for Ctrl-E), because the CMD_CTRL interpretation is the low-level
898 * key code.
899 *
900 * This function should return all control keys using their ASCII control
901 * codes, whenever possible. Similarly, this function should return ASCII
902 * 27 for the Escape key, if possible.
903 *
904 * For keys for which there is no portable ASCII representation, this
905 * should return the CMD_xxx sequence. So, this function acts exactly the
906 * same as os_getc() for arrow keys, function keys, and other special keys
907 * that have no ASCII representation. This function returns a
908 * non-translated version ONLY when an ASCII representation exists - in
909 * practice, this means that this function and os_getc() vary only for
910 * CTRL keys and Escape.
911 */
os_getc_raw(void)912 int os_getc_raw(void)
913 {
914 return os_getc();
915 }
916
917 /* wait for a character to become available from the keyboard */
os_waitc(void)918 void os_waitc(void)
919 {
920 waitchar = getglkchar();
921 }
922
923 /*
924 * Get an input event. The event types are shown above. If use_timeout
925 * is false, this routine should simply wait until one of the events it
926 * recognizes occurs, then return the appropriate information on the
927 * event. If use_timeout is true, this routine should return
928 * OS_EVT_TIMEOUT after the given number of milliseconds elapses if no
929 * event occurs first.
930 *
931 * This function is not obligated to obey the timeout. If a timeout is
932 * specified and it is not possible to obey the timeout, the function
933 * should simply return OS_EVT_NOTIMEOUT. The trivial implementation
934 * thus checks for a timeout, returns an error if specified, and
935 * otherwise simply waits for the user to press a key.
936 */
os_get_event(unsigned long timeout_in_milliseconds,int use_timeout,os_event_info_t * info)937 int os_get_event(unsigned long timeout_in_milliseconds, int use_timeout,
938 os_event_info_t *info)
939 {
940 #ifdef GLK_TIMERS
941 /* start timer */
942 int timer = use_timeout ? timeout_in_milliseconds : 0;
943 if (timer)
944 g_vm->glk_request_timer_events(timer);
945 #else
946 /* we can't handle timeouts */
947 if (use_timeout)
948 return OS_EVT_NOTIMEOUT;
949 #endif
950
951 /* get a key */
952 info->key[0] = os_getc_raw();
953 if (info->key[0] == 0 && timechar == 0)
954 info->key[1] = os_getc_raw();
955
956 #ifdef GLK_TIMERS
957 /* stop timer */
958 if (timer)
959 g_vm->glk_request_timer_events(0);
960 #endif
961
962 /* return the event */
963 return timechar ? OS_EVT_TIMEOUT : OS_EVT_KEY;
964 }
965
os_exeseek(const char * argv0,const char * typ)966 osfildef *os_exeseek(const char *argv0, const char *typ) {
967 return nullptr;
968 }
969
os_get_str_rsc(int id,char * buf,size_t buflen)970 int os_get_str_rsc(int id, char *buf, size_t buflen) {
971 strcpy(buf, "");
972 return 0;
973 }
974
os_dbg_printf(const char * fmt,...)975 void os_dbg_printf(const char *fmt, ...) {
976 // No implementation, since we haven't set up a ScummVM debugger
977 }
978
os_dbg_vprintf(const char * fmt,va_list args)979 void os_dbg_vprintf(const char *fmt, va_list args) {
980 // No implementation, since we haven't set up a ScummVM debugger
981 }
982
os_vasprintf(char ** bufptr,const char * fmt,va_list ap)983 int os_vasprintf(char **bufptr, const char *fmt, va_list ap) {
984 Common::String s = Common::String::vformat(fmt, ap);
985
986 *bufptr = (char *)malloc(s.size() + 1);
987 strcpy(*bufptr, s.c_str());
988 return s.size();
989 }
990
os_paramfile(char * buf)991 int os_paramfile(char *buf) {
992 return false;
993 }
994
os_rand(long * val)995 void os_rand(long *val) {
996 *val = g_vm->getRandomNumber(0x7fffffff);
997 }
998
os_get_sys_clock_ms()999 long os_get_sys_clock_ms() {
1000 return g_system->getMillis();
1001 }
1002
os_xlat_html4(unsigned int html4_char,char * result,size_t result_len)1003 void os_xlat_html4(unsigned int html4_char, char *result, size_t result_len) {
1004 /* Return all standard Latin-1 characters as-is */
1005 if (html4_char <= 128 || (html4_char >= 160 && html4_char <= 255))
1006 result[0] = (unsigned char)html4_char;
1007 else {
1008 switch (html4_char) {
1009 case 130: /* single back quote */
1010 result[0] = '`'; break;
1011 case 132: /* double back quote */
1012 result[0] = '\"'; break;
1013 case 153: /* trade mark */
1014 strcpy(result, "(tm)"); return;
1015 case 140: /* OE ligature */
1016 case 338: /* OE ligature */
1017 strcpy(result, "OE"); return;
1018 case 339: /* oe ligature */
1019 strcpy(result, "oe"); return;
1020 case 159: /* Yuml */
1021 result[0] = (char)255; return;
1022 case 376: /* Y with diaresis */
1023 result[0] = 'Y'; break;
1024 case 352: /* S with caron */
1025 result[0] = 'S'; break;
1026 case 353: /* s with caron */
1027 result[0] = 's'; break;
1028 case 150: /* en dash */
1029 case 8211: /* en dash */
1030 result[0] = '-'; break;
1031 case 151: /* em dash */
1032 case 8212: /* em dash */
1033 strcpy(result, "--"); return;
1034 case 145: /* left single quote */
1035 case 8216: /* left single quote */
1036 result[0] = '`'; break;
1037 case 146: /* right single quote */
1038 case 8217: /* right single quote */
1039 case 8218: /* single low-9 quote */
1040 result[0] = '\''; break;
1041 case 147: /* left double quote */
1042 case 148: /* right double quote */
1043 case 8220: /* left double quote */
1044 case 8221: /* right double quote */
1045 case 8222: /* double low-9 quote */
1046 result[0] = '\"'; break;
1047 case 8224: /* dagger */
1048 case 8225: /* double dagger */
1049 case 8240: /* per mille sign */
1050 result[0] = ' '; break;
1051 case 139: /* single left-pointing angle quote */
1052 case 8249: /* single left-pointing angle quote */
1053 result[0] = '<'; break;
1054 case 155: /* single right-pointing angle quote */
1055 case 8250: /* single right-pointing angle quote */
1056 result[0] = '>'; break;
1057 case 8482: /* small tilde */
1058 result[0] = '~'; break;
1059
1060 default:
1061 /* unmappable character - return space */
1062 result[0] = (unsigned char)' ';
1063 }
1064 }
1065 result[1] = 0;
1066 }
1067
1068 #ifndef os_tzset
os_tzset()1069 void os_tzset() {}
1070 #endif
1071
os_nonstop_mode(int flag)1072 void os_nonstop_mode(int flag) {}
1073
os_advise_load_charmap(const char * id,const char * ldesc,const char * sysinfo)1074 void os_advise_load_charmap(const char *id, const char *ldesc, const char *sysinfo) {}
1075
os_gen_charmap_filename(char * filename,char * internal_id,char * argv0)1076 void os_gen_charmap_filename(char *filename, char *internal_id, char *argv0) {}
1077
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)1078 int os_input_dialog(int icon_id, const char *prompt, int standard_button_set,
1079 const char **buttons, int button_count, int default_index, int cancel_index) {
1080 // CUrrently unsupported
1081 return 0;
1082 }
1083
os_flush()1084 void os_flush() {
1085 g_vm->glk_tick();
1086 }
1087
os_strlwr(char * s)1088 char *os_strlwr(char *s) {
1089 for (char *p = s; *p; ++p)
1090 *p = tolower(*p);
1091 return s;
1092 }
1093
os_expause()1094 void os_expause() {
1095 #ifdef USE_EXPAUSE
1096 os_printz("(Strike any key to exit...)");
1097 os_flush();
1098 os_waitc();
1099 #endif /* USE_EXPAUSE */
1100 }
1101
os_plain(void)1102 void os_plain(void) {}
1103
memicmp(const char * s1,const char * s2,int len)1104 int memicmp(const char *s1, const char *s2, int len) {
1105 Common::String cs1(s1, len);
1106 Common::String cs2(s2, len);
1107
1108 return cs1.compareToIgnoreCase(cs2);
1109 }
1110
1111 } // End of namespace TADS
1112 } // End of namespace Glk
1113