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