Add Bocfel interpreter
[projects/chimara/chimara.git] / interpreters / bocfel / screen.c
1 /*-
2  * Copyright 2010-2012 Chris Spiegel.
3  *
4  * This file is part of Bocfel.
5  *
6  * Bocfel is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License, version
8  * 2 or 3, as published by the Free Software Foundation.
9  *
10  * Bocfel is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Bocfel.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <stdlib.h>
20 #include <string.h>
21 #include <stddef.h>
22 #include <stdarg.h>
23
24 #ifdef ZTERP_GLK
25 #include <glk.h>
26 #include <libchimara/garglk.h>
27 #endif
28
29 #include "screen.h"
30 #include "branch.h"
31 #include "dict.h"
32 #include "io.h"
33 #include "memory.h"
34 #include "objects.h"
35 #include "osdep.h"
36 #include "process.h"
37 #include "stack.h"
38 #include "unicode.h"
39 #include "util.h"
40 #include "zterp.h"
41
42 static struct window
43 {
44   unsigned style;
45
46   enum font { FONT_NONE = -1, FONT_PREVIOUS, FONT_NORMAL, FONT_PICTURE, FONT_CHARACTER, FONT_FIXED } font;
47   enum font prev_font;
48
49 #ifdef ZTERP_GLK
50   winid_t id;
51   long x, y; /* Only meaningful for window 1 */
52   int pending_read;
53   union line
54   {
55     char latin1[256];
56     glui32 unicode[256];
57   } *line;
58   int has_echo;
59 #endif
60 } windows[8], *mainwin = &windows[0], *curwin = &windows[0];
61 #ifdef ZTERP_GLK
62 static struct window *upperwin = &windows[1];
63 static struct window statuswin;
64 static long upper_window_height = 0;
65 static long upper_window_width = 0;
66 static winid_t errorwin;
67 #endif
68
69 /* In all versions but 6, styles are global and stored in mainwin.  For
70  * V6, styles are tracked per window and thus stored in each individual
71  * window.  For convenience, this macro expands to the “style window”
72  * for any version.
73  */
74 #define style_window    (zversion == 6 ? curwin : mainwin)
75
76 /* If the window needs to be temporarily switched (@show_status and
77  * @print_form print to specific windows, and window_change() might
78  * temporarily need to switch to the upper window), the code that
79  * requires a specific window can be wrapped in these macros.
80  */
81 #ifdef ZTERP_GLK
82 #define SWITCH_WINDOW_START(win)        { struct window *saved_ = curwin; curwin = (win); glk_set_window((win)->id);
83 #define SWITCH_WINDOW_END()             curwin = saved_; glk_set_window(curwin->id); }
84 #else
85 #define SWITCH_WINDOW_START(win)        { struct window *saved_ = curwin; curwin = (win);
86 #define SWITCH_WINDOW_END()             curwin = saved_; }
87 #endif
88
89 /* Output stream bits. */
90 #define STREAM_SCREEN           (1U << 1)
91 #define STREAM_TRANS            (1U << 2)
92 #define STREAM_MEMORY           (1U << 3)
93 #define STREAM_SCRIPT           (1U << 4)
94
95 static unsigned int streams = STREAM_SCREEN;
96 static zterp_io *transio, *scriptio;
97
98 static struct
99 {
100   uint16_t table;
101   uint16_t i;
102 } stables[16];
103 static int stablei = -1;
104
105 static int istream = ISTREAM_KEYBOARD;
106 static zterp_io *istreamio;
107
108 struct input
109 {
110   enum { INPUT_CHAR, INPUT_LINE } type;
111
112   /* ZSCII value of key read for @read_char. */
113   uint8_t key;
114
115   /* Unicode line of chars read for @read. */
116   uint32_t *line;
117   uint8_t maxlen;
118   uint8_t len;
119   uint8_t preloaded;
120
121   /* Character used to terminate input.  If terminating keys are not
122    * supported by the Glk implementation being used (or if Glk is not
123    * used at all) this will be ZSCII_NEWLINE; or in the case of
124    * cancellation, 0.
125    */
126   uint8_t term;
127 };
128
129 /* This macro makes it so that code elsewhere needn’t check have_unicode before printing. */
130 #define GLK_PUT_CHAR(c)         do { if(!have_unicode) glk_put_char(unicode_to_latin1[c]); else glk_put_char_uni(c); } while(0)
131
132 void show_message(const char *fmt, ...)
133 {
134   va_list ap;
135   char message[1024];
136
137   va_start(ap, fmt);
138   vsnprintf(message, sizeof message, fmt, ap);
139   va_end(ap);
140
141 #ifdef ZTERP_GLK
142   static int error_lines = 0;
143
144   if(errorwin != NULL)
145   {
146     glui32 w, h;
147
148     /* Allow multiple messages to stack, but force at least 5 lines to
149      * always be visible in the main window.  This is less than perfect
150      * because it assumes that each message will be less than the width
151      * of the screen, but it’s not a huge deal, really; even if the
152      * lines are too long, at least Gargoyle and glktermw are graceful
153      * enough.
154      */
155     glk_window_get_size(mainwin->id, &w, &h);
156
157     if(h > 5) glk_window_set_arrangement(glk_window_get_parent(errorwin), winmethod_Below | winmethod_Fixed, ++error_lines, errorwin);
158     glk_put_char_stream(glk_window_get_stream(errorwin), UNICODE_LINEFEED);
159   }
160   else
161   {
162     errorwin = glk_window_open(mainwin->id, winmethod_Below | winmethod_Fixed, error_lines = 2, wintype_TextBuffer, 0);
163   }
164
165   /* If windows are not supported (e.g. in cheapglk), messages will not
166    * get displayed.  If this is the case, print to the main window.
167    */
168   if(errorwin != NULL)
169   {
170     glk_set_style_stream(glk_window_get_stream(errorwin), style_Alert);
171     glk_put_string_stream(glk_window_get_stream(errorwin), message);
172   }
173   else
174   {
175     SWITCH_WINDOW_START(mainwin);
176     glk_put_string("\12[");
177     glk_put_string(message);
178     glk_put_string("]\12");
179     SWITCH_WINDOW_END();
180   }
181 #else
182   /* In Glk messages go to a separate window, but they're interleaved in
183    * non-Glk.  Put brackets around the message in an attempt to offset
184    * it from the game a bit.
185    */
186   fprintf(stderr, "\n[%s]\n", message);
187 #endif
188 }
189
190 /* See §7.
191  * This returns true if the stream was successfully selected.
192  * Deselecting a stream is always successful.
193  */
194 int output_stream(int16_t number, uint16_t table)
195 {
196   if(number > 0)
197   {
198     streams |= 1U << number;
199   }
200   else if(number < 0)
201   {
202     if(number != -3 || stablei == 0) streams &= ~(1U << -number);
203   }
204
205   if(number == 2)
206   {
207     STORE_WORD(0x10, WORD(0x10) | FLAGS2_TRANSCRIPT);
208     if(transio == NULL)
209     {
210       transio = zterp_io_open(options.transcript_name, ZTERP_IO_TRANS | (options.overwrite_transcript ? ZTERP_IO_WRONLY : ZTERP_IO_APPEND));
211       if(transio == NULL)
212       {
213         STORE_WORD(0x10, WORD(0x10) & ~FLAGS2_TRANSCRIPT);
214         streams &= ~STREAM_TRANS;
215         warning("unable to open the transcript");
216       }
217     }
218   }
219   else if(number == -2)
220   {
221     STORE_WORD(0x10, WORD(0x10) & ~FLAGS2_TRANSCRIPT);
222   }
223
224   if(number == 3)
225   {
226     stablei++;
227     ZASSERT(stablei < 16, "too many stream tables");
228
229     stables[stablei].table = table;
230     user_store_word(stables[stablei].table, 0);
231     stables[stablei].i = 2;
232   }
233   else if(number == -3 && stablei >= 0)
234   {
235     user_store_word(stables[stablei].table, stables[stablei].i - 2);
236     stablei--;
237   }
238
239   if(number == 4)
240   {
241     if(scriptio == NULL)
242     {
243       scriptio = zterp_io_open(options.record_name, ZTERP_IO_WRONLY | ZTERP_IO_INPUT);
244       if(scriptio == NULL)
245       {
246         streams &= ~STREAM_SCRIPT;
247         warning("unable to open the script");
248       }
249     }
250   }
251   /* XXX v6 has even more handling */
252
253   return number < 0 || (streams & (1U << number));
254 }
255
256 void zoutput_stream(void)
257 {
258   output_stream(zargs[0], zargs[1]);
259 }
260
261 /* See §10.
262  * This returns true if the stream was successfully selected.
263  */
264 int input_stream(int which)
265 {
266   istream = which;
267
268   if(istream == ISTREAM_KEYBOARD)
269   {
270     if(istreamio != NULL)
271     {
272       zterp_io_close(istreamio);
273       istreamio = NULL;
274     }
275   }
276   else if(istream == ISTREAM_FILE)
277   {
278     if(istreamio == NULL)
279     {
280       istreamio = zterp_io_open(options.replay_name, ZTERP_IO_INPUT | ZTERP_IO_RDONLY);
281       if(istreamio == NULL)
282       {
283         warning("unable to open the command script");
284         istream = ISTREAM_KEYBOARD;
285       }
286     }
287   }
288   else
289   {
290     ZASSERT(0, "invalid input stream: %d", istream);
291   }
292
293   return istream == which;
294 }
295
296 void zinput_stream(void)
297 {
298   input_stream(zargs[0]);
299 }
300
301 /* This does not even pretend to understand V6 windows. */
302 static void set_current_window(struct window *window)
303 {
304   curwin = window;
305
306 #ifdef ZTERP_GLK
307   if(curwin == upperwin && upperwin->id != NULL)
308   {
309     upperwin->x = upperwin->y = 0;
310     glk_window_move_cursor(upperwin->id, 0, 0);
311   }
312
313   glk_set_window(curwin->id);
314 #endif
315
316   set_current_style();
317 }
318
319 /* Find and validate a window.  If window is -3 and the story is V6,
320  * return the current window.
321  */
322 static struct window *find_window(uint16_t window)
323 {
324   int16_t w = window;
325
326   ZASSERT(zversion == 6 ? w == -3 || (w >= 0 && w < 8) : w == 0 || w == 1, "invalid window selected: %d", w);
327
328   if(w == -3) return curwin;
329
330   return &windows[w];
331 }
332
333 #ifdef ZTERP_GLK
334 /* When resizing the upper window, the screen’s contents should not
335  * change (§8.6.1); however, the way windows are handled with Glk makes
336  * this slightly impossible.  When an Inform game tries to display
337  * something with “box”, it expands the upper window, displays the quote
338  * box, and immediately shrinks the window down again.  This is a
339  * problem under Glk because the window immediately disappears.  Other
340  * games, such as Bureaucracy, expect the upper window to shrink as soon
341  * as it has been requested.  Thus the following system is used:
342  *
343  * If a request is made to shrink the upper window, it is granted
344  * immediately if there has been user input since the last window resize
345  * request.  If there has not been user input, the request is delayed
346  * until after the next user input is read.
347  */
348 static long delayed_window_shrink = -1;
349 static int saw_input;
350
351 static void update_delayed(void)
352 {
353   glui32 height;
354
355   if(delayed_window_shrink == -1 || upperwin->id == NULL) return;
356
357   glk_window_set_arrangement(glk_window_get_parent(upperwin->id), winmethod_Above | winmethod_Fixed, delayed_window_shrink, upperwin->id);
358   upper_window_height = delayed_window_shrink;
359
360   /* Glk might resize the window to a smaller height than was requested,
361    * so track the actual height, not the requested height.
362    */
363   glk_window_get_size(upperwin->id, NULL, &height);
364   if(height != upper_window_height)
365   {
366     /* This message probably won’t be seen in a window since the upper
367      * window is likely covering everything, but try anyway.
368      */
369     show_message("Unable to fulfill window size request: wanted %ld, got %lu", delayed_window_shrink, (unsigned long)height);
370     upper_window_height = height;
371   }
372
373   delayed_window_shrink = -1;
374 }
375
376 /* Both the upper and lower windows have their own issues to deal with
377  * when there is line input.  This function ensures that the cursor
378  * position is properly tracked in the upper window, and if possible,
379  * aids in the suppression of newline printing on input cancellation in
380  * the lower window.
381  */
382 static void cleanup_screen(struct input *input)
383 {
384   if(input->type != INPUT_LINE) return;
385
386   /* If the current window is the upper window, the position of the
387    * cursor needs to be tracked, so after a line has successfully been
388    * read, advance the cursor to the initial position of the next line,
389    * or if a terminating key was used or input was canceled, to the end
390    * of the input.
391    */
392   if(curwin == upperwin)
393   {
394     if(input->term != ZSCII_NEWLINE) upperwin->x += input->len;
395
396     if(input->term == ZSCII_NEWLINE || upperwin->x >= upper_window_width)
397     {
398       upperwin->x = 0;
399       if(upperwin->y < upper_window_height) upperwin->y++;
400     }
401
402     glk_window_move_cursor(upperwin->id, upperwin->x, upperwin->y);
403   }
404
405   /* If line input echoing is turned off, newlines will not be printed
406    * when input is canceled, but neither will the input line.  Fix that.
407    */
408   if(curwin->has_echo)
409   {
410     glk_set_style(style_Input);
411     for(int i = 0; i < input->len; i++) GLK_PUT_CHAR(input->line[i]);
412     if(input->term == ZSCII_NEWLINE) glk_put_char(UNICODE_LINEFEED);
413     set_current_style();
414   }
415 }
416
417 /* In an interrupt, if the story tries to read or write, the previous
418  * read event (which triggered the interrupt) needs to be canceled.
419  * This function does the cancellation.
420  */
421 static void cancel_read_events(struct window *window)
422 {
423   if(window->pending_read)
424   {
425     event_t ev;
426
427     glk_cancel_char_event(window->id);
428     glk_cancel_line_event(window->id, &ev);
429
430     /* If the pending read was a line input, zero terminate the string
431      * so when it’s re-requested the length of the already-loaded
432      * portion can be discovered.  Also deal with cursor positioning in
433      * the upper window, and line echoing in the lower window.
434      */
435     if(ev.type == evtype_LineInput && window->line != NULL)
436     {
437       uint32_t line[ev.val1];
438       struct input input = { .type = INPUT_LINE, .line = line, .term = 0, .len = ev.val1 };
439
440       if(have_unicode) window->line->unicode[ev.val1] = 0;
441       else             window->line->latin1 [ev.val1] = 0;
442
443       for(int i = 0; i < input.len; i++)
444       {
445         if(have_unicode) line[i] = window->line->unicode[i];
446         else             line[i] = window->line->latin1 [i];
447       }
448
449       cleanup_screen(&input);
450     }
451
452     window->pending_read = 0;
453     window->line = NULL;
454   }
455 }
456
457 static void clear_window(struct window *window)
458 {
459   if(window->id == NULL) return;
460
461   /* glk_window_clear() cannot be used while there are pending read requests. */
462   cancel_read_events(window);
463
464   glk_window_clear(window->id);
465
466   window->x = window->y = 0;
467 }
468 #endif
469
470 /* If restoring from an interrupt (which is a bad idea to begin with),
471  * it’s entirely possible that there will be pending read events that
472  * need to be canceled, so allow that.
473  */
474 void cancel_all_events(void)
475 {
476 #ifdef ZTERP_GLK
477   for(int i = 0; i < 8; i++) cancel_read_events(&windows[i]);
478 #endif
479 }
480
481 static void resize_upper_window(long nlines)
482 {
483 #ifdef ZTERP_GLK
484   if(upperwin->id == NULL) return;
485
486   /* To avoid code duplication, put all window resizing code in
487    * update_delayed() and, if necessary, call it from here.
488    */
489   delayed_window_shrink = nlines;
490   if(upper_window_height <= nlines || saw_input) update_delayed();
491
492   saw_input = 0;
493
494   /* §8.6.1.1.2 */
495   if(zversion == 3) clear_window(upperwin);
496
497   /* As in a few other areas, changing the upper window causes reverse
498    * video to be deactivated, so reapply the current style.
499    */
500   set_current_style();
501 #endif
502 }
503
504 void close_upper_window(void)
505 {
506   /* The upper window is never destroyed; rather, when it’s closed, it
507    * shrinks to zero height.
508    */
509   resize_upper_window(0);
510
511 #ifdef ZTERP_GLK
512   delayed_window_shrink = -1;
513   saw_input = 0;
514 #endif
515
516   set_current_window(mainwin);
517 }
518
519 void get_screen_size(unsigned int *width, unsigned int *height)
520 {
521   *width  = 80;
522   *height = 24;
523
524 #ifdef ZTERP_GLK
525   glui32 w, h;
526
527   /* The main window can be proportional, and if so, its width is not
528    * generally useful because games tend to care about width with a
529    * fixed font.  If a status window is available, or if an upper window
530    * is available, use that to calculate the width, because these
531    * windows will have a fixed-width font.  The height is the combined
532    * height of all windows.
533    */
534   glk_window_get_size(mainwin->id, &w, &h);
535   *height = h;
536   if(statuswin.id != NULL)
537   {
538     glk_window_get_size(statuswin.id, &w, &h);
539     *height += h;
540   }
541   if(upperwin->id != NULL)
542   {
543     glk_window_get_size(upperwin->id, &w, &h);
544     *height += h;
545   }
546   *width = w;
547 #else
548   zterp_os_get_screen_size(width, height);
549 #endif
550
551   /* XGlk does not report the size of textbuffer windows, so here’s a safety net. */
552   if(*width  == 0) *width  = 80;
553   if(*height == 0) *height = 24;
554
555   /* Terrible hack: Because V6 is not properly supported, the window to
556    * which Journey writes its story is completely covered up by window
557    * 1.  For the same reason, only the bottom 6 lines of window 1 are
558    * actually useful, even though the game expands it to cover the whole
559    * screen.  By pretending that the screen height is only 6, the main
560    * window, where text is actually sent, becomes visible.
561    */
562   if(is_story("83-890706") && *height > 6) *height = 6;
563 }
564
565 #ifdef GLK_MODULE_LINE_TERMINATORS
566 static uint32_t *term_keys, term_size, term_nkeys;
567
568 void term_keys_reset(void)
569 {
570   free(term_keys);
571   term_keys = NULL;
572   term_size = 0;
573   term_nkeys = 0;
574 }
575
576 static void insert_key(uint32_t key)
577 {
578   if(term_nkeys == term_size)
579   {
580     term_size += 32;
581
582     term_keys = realloc(term_keys, term_size * sizeof *term_keys);
583     if(term_keys == NULL) die("unable to allocate memory for terminating keys");
584   }
585
586   term_keys[term_nkeys++] = key;
587 }
588
589 void term_keys_add(uint8_t key)
590 {
591   switch(key)
592   {
593     case 129: insert_key(keycode_Up); break;
594     case 130: insert_key(keycode_Down); break;
595     case 131: insert_key(keycode_Left); break;
596     case 132: insert_key(keycode_Right); break;
597     case 133: insert_key(keycode_Func1); break;
598     case 134: insert_key(keycode_Func2); break;
599     case 135: insert_key(keycode_Func3); break;
600     case 136: insert_key(keycode_Func4); break;
601     case 137: insert_key(keycode_Func5); break;
602     case 138: insert_key(keycode_Func6); break;
603     case 139: insert_key(keycode_Func7); break;
604     case 140: insert_key(keycode_Func8); break;
605     case 141: insert_key(keycode_Func9); break;
606     case 142: insert_key(keycode_Func10); break;
607     case 143: insert_key(keycode_Func11); break;
608     case 144: insert_key(keycode_Func12); break;
609
610     /* Keypad 0–9 should be here, but Glk doesn’t support that. */
611     case 145: case 146: case 147: case 148: case 149:
612     case 150: case 151: case 152: case 153: case 154:
613       break;
614
615     /* Mouse clicks would go here if I supported them. */
616     case 252: case 253: case 254:
617       break;
618
619     case 255:
620       for(int i = 129; i <= 144; i++) term_keys_add(i);
621       break;
622
623     default:
624       ZASSERT(0, "invalid terminating key: %u", (unsigned)key);
625       break;
626   }
627 }
628 #endif
629
630 /* Print out a character.  The character is in “c” and is either Unicode
631  * or ZSCII; if the former, “unicode” is true.
632  */
633 static void put_char_base(uint16_t c, int unicode)
634 {
635   if(c == 0) return;
636
637   if(streams & STREAM_MEMORY)
638   {
639     ZASSERT(stablei != -1, "invalid stream table");
640
641     /* When writing to memory, ZSCII should always be used (§7.5.3). */
642     if(unicode) c = unicode_to_zscii_q[c];
643
644     user_store_byte(stables[stablei].table + stables[stablei].i++, c);
645   }
646   else
647   {
648     /* For screen and transcription, always prefer Unicode. */
649     if(!unicode) c = zscii_to_unicode[c];
650
651     if(c != 0)
652     {
653       uint8_t zscii = 0;
654
655       /* §16 makes no mention of what a newline in font 3 should map to.
656        * Other interpreters that implement font 3 assume it stays a
657        * newline, and this makes the most sense, so don’t do any
658        * translation in that case.
659        */
660       if(curwin->font == FONT_CHARACTER && !options.disable_graphics_font && c != UNICODE_LINEFEED)
661       {
662         zscii = unicode_to_zscii[c];
663
664         /* These four characters have a “built-in” reverse video (see §16). */
665         if(zscii >= 123 && zscii <= 126)
666         {
667           style_window->style ^= STYLE_REVERSE;
668           set_current_style();
669         }
670
671         c = zscii_to_font3[zscii];
672       }
673 #ifdef ZTERP_GLK
674       if((streams & STREAM_SCREEN) && curwin->id != NULL)
675       {
676         cancel_read_events(curwin);
677
678         if(curwin == upperwin)
679         {
680           /* Interpreters seem to have differing ideas about what
681            * happens when the cursor reaches the end of a line in the
682            * upper window.  Some wrap, some let it run off the edge (or,
683            * at least, stop the text at the edge).  The standard, from
684            * what I can see, says nothing on this issue.  Follow Windows
685            * Frotz and don’t wrap.
686            */
687
688           if(c == UNICODE_LINEFEED)
689           {
690             if(upperwin->y < upper_window_height)
691             {
692               /* Glk wraps, so printing a newline when the cursor has
693                * already reached the edge of the screen will produce two
694                * newlines.
695                */
696               if(upperwin->x < upper_window_width) GLK_PUT_CHAR(c);
697
698               /* Even if a newline isn’t explicitly printed here
699                * (because the cursor is at the edge), setting
700                * upperwin->x to 0 will cause the next character to be on
701                * the next line because the text will have wrapped.
702                */
703               upperwin->x = 0;
704               upperwin->y++;
705             }
706           }
707           else if(upperwin->x < upper_window_width && upperwin->y < upper_window_height)
708           {
709             upperwin->x++;
710             GLK_PUT_CHAR(c);
711           }
712         }
713         else
714         {
715           GLK_PUT_CHAR(c);
716         }
717       }
718 #else
719       if((streams & STREAM_SCREEN) && curwin == mainwin) zterp_io_putc(zterp_io_stdout(), c);
720 #endif
721
722       /* If the reverse video bit was flipped (for the character font), flip it back. */
723       if(zscii >= 123 && zscii <= 126)
724       {
725         style_window->style ^= STYLE_REVERSE;
726         set_current_style();
727       }
728
729       if((streams & STREAM_TRANS) && curwin == mainwin) zterp_io_putc(transio, c);
730     }
731   }
732 }
733
734 void put_char_u(uint16_t c)
735 {
736   put_char_base(c, 1);
737 }
738
739 void put_char(uint8_t c)
740 {
741   put_char_base(c, 0);
742 }
743
744 static void put_string(const char *s)
745 {
746   for(; *s != 0; s++)
747   {
748     if(*s == '\n') put_char(ZSCII_NEWLINE);
749     else           put_char(*s);
750   }
751 }
752
753 /* Decode and print a zcode string at address “addr”.  This can be
754  * called recursively thanks to abbreviations; the initial call should
755  * have “in_abbr” set to 0.
756  * Each time a character is decoded, it is passed to the function
757  * “outc”.
758  */
759 static int print_zcode(uint32_t addr, int in_abbr, void (*outc)(uint8_t))
760 {
761   int abbrev = 0, shift = 0, special = 0;
762   int c, lastc = 0; /* Initialize lastc to shut gcc up */
763   uint16_t w;
764   uint32_t counter = addr;
765   int current_alphabet = 0;
766
767   do
768   {
769     ZASSERT(counter < memory_size - 1, "string runs beyond the end of memory");
770
771     w = WORD(counter);
772
773     for(int i = 10; i >= 0; i -= 5)
774     {
775       c = (w >> i) & 0x1f;
776
777       if(special)
778       {
779         if(special == 2) lastc = c;
780         else             outc((lastc << 5) | c);
781
782         special--;
783       }
784
785       else if(abbrev)
786       {
787         uint32_t new_addr;
788
789         new_addr = user_word(header.abbr + 64 * (abbrev - 1) + 2 * c);
790
791         /* new_addr is a word address, so multiply by 2 */
792         print_zcode(new_addr * 2, 1, outc);
793
794         abbrev = 0;
795       }
796
797       else switch(c)
798       {
799         case 0:
800           outc(ZSCII_SPACE);
801           shift = 0;
802           break;
803         case 1:
804           if(zversion == 1)
805           {
806             outc(ZSCII_NEWLINE);
807             shift = 0;
808             break;
809           }
810           /* fallthrough */
811         case 2: case 3:
812           if(zversion >= 3 || (zversion == 2 && c == 1))
813           {
814             ZASSERT(!in_abbr, "abbreviation being used recursively");
815             abbrev = c;
816             shift = 0;
817           }
818           else
819           {
820             shift = c - 1;
821           }
822           break;
823         case 4: case 5:
824           if(zversion <= 2)
825           {
826             current_alphabet = (current_alphabet + (c - 3)) % 3;
827             shift = 0;
828           }
829           else
830           {
831             shift = c - 3;
832           }
833           break;
834         case 6:
835           if(zversion <= 2) shift = (current_alphabet + shift) % 3;
836
837           if(shift == 2)
838           {
839             shift = 0;
840             special = 2;
841             break;
842           }
843           /* fallthrough */
844         default:
845           if(zversion <= 2 && c != 6) shift = (current_alphabet + shift) % 3;
846
847           outc(atable[(26 * shift) + (c - 6)]);
848           shift = 0;
849           break;
850       }
851     }
852
853     counter += 2;
854   } while((w & 0x8000) == 0);
855
856   return counter - addr;
857 }
858
859 /* Prints the string at addr “addr”.
860  *
861  * Returns the number of bytes the string took up.  “outc” is passed as
862  * the character-print function to print_zcode(); if it is NULL,
863  * put_char is used.
864  */
865 int print_handler(uint32_t addr, void (*outc)(uint8_t))
866 {
867   return print_zcode(addr, 0, outc != NULL ? outc : put_char);
868 }
869
870 void zprint(void)
871 {
872   pc += print_handler(pc, NULL);
873 }
874
875 void zprint_ret(void)
876 {
877   zprint();
878   put_char(ZSCII_NEWLINE);
879   zrtrue();
880 }
881
882 void znew_line(void)
883 {
884   put_char(ZSCII_NEWLINE);
885 }
886
887 void zerase_window(void)
888 {
889 #ifdef ZTERP_GLK
890   switch((int16_t)zargs[0])
891   {
892     case -2:
893       for(int i = 0; i < 8; i++) clear_window(&windows[i]);
894       break;
895     case -1:
896       close_upper_window();
897       /* fallthrough */
898     case 0:
899       /* 8.7.3.2.1 says V5+ should have the cursor set to 1, 1 of the
900        * erased window; V4 the lower window goes bottom left, the upper
901        * to 1, 1.  Glk doesn’t give control over the cursor when
902        * clearing, and that doesn’t really seem to be an issue; so just
903        * call glk_window_clear().
904        */
905       clear_window(mainwin);
906       break;
907     case 1:
908       clear_window(upperwin);
909       break;
910     default:
911       show_message("@erase_window: unhandled window: %d", (int16_t)zargs[0]);
912       break;
913   }
914
915   /* glk_window_clear() kills reverse video in Gargoyle.  Reapply style. */
916   set_current_style();
917 #endif
918 }
919
920 void zerase_line(void)
921 {
922 #ifdef ZTERP_GLK
923   /* XXX V6 does pixel handling here. */
924   if(zargs[0] != 1 || curwin != upperwin || upperwin->id == NULL) return;
925
926   for(long i = upperwin->x; i < upper_window_width; i++) GLK_PUT_CHAR(UNICODE_SPACE);
927
928   glk_window_move_cursor(upperwin->id, upperwin->x, upperwin->y);
929 #endif
930 }
931
932 /* XXX This is more complex in V6 and needs to be updated when V6 windowing is implemented. */
933 static void set_cursor(uint16_t y, uint16_t x)
934 {
935 #ifdef ZTERP_GLK
936   /* All the windows in V6 can have their cursor positioned; if V6 ever
937    * comes about this should be fixed.
938    */
939   if(curwin != upperwin) return;
940
941   /* -1 and -2 are V6 only, but at least Zracer passes -1 (or it’s
942    * trying to position the cursor to line 65535; unlikely!)
943    */
944   if((int16_t)y == -1 || (int16_t)y == -2) return;
945
946   /* §8.7.2.3 says 1,1 is the top-left, but at least one program (Paint
947    * and Corners) uses @set_cursor 0 0 to go to the top-left; so
948    * special-case it.
949    */
950   if(y == 0) y = 1;
951   if(x == 0) x = 1;
952
953   /* This is actually illegal, but some games (e.g. Beyond Zork) expect it to work. */
954   if(y > upper_window_height) resize_upper_window(y);
955
956   if(upperwin->id != NULL)
957   {
958     upperwin->x = x - 1;
959     upperwin->y = y - 1;
960
961     glk_window_move_cursor(upperwin->id, x - 1, y - 1);
962   }
963 #endif
964 }
965
966 void zset_cursor(void)
967 {
968   set_cursor(zargs[0], zargs[1]);
969 }
970
971 void zget_cursor(void)
972 {
973 #ifdef ZTERP_GLK
974   user_store_word(zargs[0] + 0, upperwin->y + 1);
975   user_store_word(zargs[0] + 2, upperwin->x + 1);
976 #else
977   user_store_word(zargs[0] + 0, 1);
978   user_store_word(zargs[0] + 2, 1);
979 #endif
980 }
981
982 #ifndef ZTERP_GLK
983 static int16_t fg_color = 1, bg_color = 1;
984 #elif defined(GARGLK)
985 static glui32 zcolor_map[] = {
986   zcolor_Default,
987
988   0x000000,     /* Black */
989   0xef0000,     /* Red */
990   0x00d600,     /* Green */
991   0xefef00,     /* Yellow */
992   0x006bb5,     /* Blue */
993   0xff00ff,     /* Magenta */
994   0x00efef,     /* Cyan */
995   0xffffff,     /* White */
996   0xb5b5b5,     /* Light grey */
997   0x8c8c8c,     /* Medium grey */
998   0x5a5a5a,     /* Dark grey */
999 };
1000 static glui32 fg_color = zcolor_Default, bg_color = zcolor_Default;
1001
1002 void update_color(int which, unsigned long color)
1003 {
1004   if(which < 2 || which > 12) return;
1005
1006   zcolor_map[which - 1] = color;
1007 }
1008 #endif
1009
1010 /* A window argument may be supplied in V6, and this needs to be implemented. */
1011 void zset_colour(void)
1012 {
1013   /* Glk (apart from Gargoyle) has no color support. */
1014 #if !defined(ZTERP_GLK) || defined(GARGLK)
1015   int16_t fg = zargs[0], bg = zargs[1];
1016
1017   /* In V6, each window has its own color settings.  Since multiple
1018    * windows are not supported, simply ignore all color requests except
1019    * those in the main window.
1020    */
1021   if(zversion == 6 && curwin != mainwin) return;
1022
1023   if(options.disable_color) return;
1024
1025   /* XXX -1 is a valid color in V6. */
1026 #ifdef GARGLK
1027   if(fg >= 1 && fg <= (zversion >= 5 ? 12 : 9)) fg_color = zcolor_map[fg - 1];
1028   if(bg >= 1 && bg <= (zversion >= 5 ? 12 : 9)) bg_color = zcolor_map[bg - 1];
1029
1030 #else
1031   if(fg >= 1 && fg <= 9) fg_color = fg;
1032   if(bg >= 1 && bg <= 9) bg_color = bg;
1033 #endif
1034
1035   set_current_style();
1036 #endif
1037 }
1038
1039 #ifdef GARGLK
1040 /* Convert a 15-bit color to a 24-bit color. */
1041 static glui32 convert_color(unsigned long color)
1042 {
1043   /* Map 5-bit color values to 8-bit. */
1044   const uint8_t table[] = {
1045     0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
1046     0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
1047     0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
1048     0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff
1049   };
1050
1051   return table[(color & 0x001f) >>  0] << 16 |
1052          table[(color & 0x03e0) >>  5] << 8  |
1053          table[(color & 0x7c00) >> 10] << 0;
1054 }
1055 #endif
1056
1057 void zset_true_colour(void)
1058 {
1059 #ifdef GARGLK
1060   long fg = (int16_t)zargs[0], bg = (int16_t)zargs[1];
1061
1062   if     (fg >=  0) fg_color = convert_color(fg);
1063   else if(fg == -1) fg_color = zcolor_Default;
1064
1065   if     (bg >=  0) bg_color = convert_color(bg);
1066   else if(bg == -1) bg_color = zcolor_Default;
1067
1068   set_current_style();
1069 #endif
1070 }
1071
1072 int header_fixed_font;
1073
1074 #ifdef GARGLK
1075 /* Idea from Nitfol. */
1076 static const int style_map[] =
1077 {
1078   style_Normal,
1079   style_Normal,
1080
1081   style_Subheader,      /* Bold */
1082   style_Subheader,      /* Bold */
1083   style_Emphasized,     /* Italic */
1084   style_Emphasized,     /* Italic */
1085   style_Alert,          /* Bold Italic */
1086   style_Alert,          /* Bold Italic */
1087   style_Preformatted,   /* Fixed */
1088   style_Preformatted,   /* Fixed */
1089   style_User1,          /* Bold Fixed */
1090   style_User1,          /* Bold Fixed */
1091   style_User2,          /* Italic Fixed */
1092   style_User2,          /* Italic Fixed */
1093   style_Note,           /* Bold Italic Fixed */
1094   style_Note,           /* Bold Italic Fixed */
1095 };
1096 #endif
1097
1098 /* Yes, there are three ways to indicate that a fixed-width font should be used. */
1099 #define use_fixed_font() (header_fixed_font || curwin->font == FONT_FIXED || (style & STYLE_FIXED))
1100
1101 void set_current_style(void)
1102 {
1103   unsigned style = style_window->style;
1104 #ifdef ZTERP_GLK
1105   if(curwin->id == NULL) return;
1106
1107 #ifdef GARGLK
1108   if(use_fixed_font()) style |= STYLE_FIXED;
1109
1110   if(options.disable_fixed) style &= ~STYLE_FIXED;
1111
1112   ZASSERT(style < 16, "invalid style selected: %x", (unsigned)style);
1113
1114   glk_set_style(style_map[style]);
1115
1116   garglk_set_reversevideo(style & STYLE_REVERSE);
1117
1118   garglk_set_zcolors(fg_color, bg_color);
1119 #else
1120   /* Glk can’t mix other styles with fixed-width, but the upper window
1121    * is always fixed, so if it is selected, there is no need to
1122    * explicitly request it here.  In addition, the user can disable
1123    * fixed-width fonts or tell Bocfel to assume that the output font is
1124    * already fixed (e.g. in an xterm); in either case, there is no need
1125    * to request a fixed font.
1126    * This means that another style can also be applied if applicable.
1127    */
1128   if(use_fixed_font() &&
1129      !options.disable_fixed &&
1130      !options.assume_fixed &&
1131      curwin != upperwin)
1132   {
1133     glk_set_style(style_Preformatted);
1134     return;
1135   }
1136
1137   /* According to standard 1.1, if mixed styles aren't available, the
1138    * priority is Fixed, Italic, Bold, Reverse.
1139    */
1140   if     (style & STYLE_ITALIC)  glk_set_style(style_Emphasized);
1141   else if(style & STYLE_BOLD)    glk_set_style(style_Subheader);
1142   else if(style & STYLE_REVERSE) glk_set_style(style_Alert);
1143   else                           glk_set_style(style_Normal);
1144 #endif
1145 #else
1146   zterp_os_set_style(style, fg_color, bg_color);
1147 #endif
1148 }
1149
1150 #undef use_fixed_font
1151
1152 /* V6 has per-window styles, but all others have a global style; in this
1153  * case, track styles via the main window.
1154  */
1155 void zset_text_style(void)
1156 {
1157   /* A style of 0 means all others go off. */
1158   if(zargs[0] == 0) style_window->style = STYLE_NONE;
1159   else              style_window->style |= zargs[0];
1160
1161   set_current_style();
1162 }
1163
1164 /* Interpreters seem to disagree on @set_font.  Given the code
1165
1166    @set_font 4 -> i;
1167    @set_font 1 -> j;
1168    @set_font 0 -> k;
1169    @set_font 1 -> l;
1170
1171  * the following values are returned:
1172  * Frotz 2.43:         0, 1, 1, 1
1173  * Gargoyle r384:      1, 4, 4, 4
1174  * Fizmo 0.6.5:        1, 4, 1, 0
1175  * Nitfol 0.5:         1, 4, 0, 1
1176  * Filfre .987:        1, 4, 0, 1
1177  * Zoom 1.1.4:         1, 1, 0, 1
1178  * ZLR 0.07:           0, 1, 0, 1
1179  * Windows Frotz 1.15: 1, 4, 1, 1
1180  * XZip 1.8.2:         0, 4, 0, 0
1181  *
1182  * The standard says that “ID 0 means ‘the previous font’.” (§8.1.2).
1183  * The Frotz 2.43 source code says that “zargs[0] = number of font or 0
1184  * to keep current font”.
1185  *
1186  * How to implement @set_font turns on the meaning of “previous”.  Does
1187  * it mean the previous font _after_ the @set_font call, meaning Frotz
1188  * is right?  Or is it the previous font _before_ the @set_font call,
1189  * meaning the identity of two fonts needs to be tracked?
1190  *
1191  * Currently I do the latter.  That yields the following:
1192  * 1, 4, 1, 4
1193  * Almost comically, no interpreters agree with each other.
1194  */
1195 void zset_font(void)
1196 {
1197   struct window *win = curwin;
1198
1199   if(zversion == 6 && znargs == 2 && (int16_t)zargs[1] != -3)
1200   {
1201     ZASSERT(zargs[1] < 8, "invalid window selected: %d", (int16_t)zargs[1]);
1202     win = &windows[zargs[1]];
1203   }
1204
1205   /* If no previous font has been stored, consider that an error. */
1206   if(zargs[0] == FONT_PREVIOUS && win->prev_font != FONT_NONE)
1207   {
1208     zargs[0] = win->prev_font;
1209     zset_font();
1210   }
1211   else if(zargs[0] == FONT_NORMAL ||
1212          (zargs[0] == FONT_CHARACTER && !options.disable_graphics_font) ||
1213          (zargs[0] == FONT_FIXED     && !options.disable_fixed))
1214   {
1215     store(win->font);
1216     win->prev_font = win->font;
1217     win->font = zargs[0];
1218   }
1219   else
1220   {
1221     store(0);
1222   }
1223
1224   set_current_style();
1225 }
1226
1227 void zprint_table(void)
1228 {
1229   uint16_t text = zargs[0], width = zargs[1], height = zargs[2], skip = zargs[3];
1230   uint16_t n = 0;
1231
1232 #ifdef ZTERP_GLK
1233   uint16_t start = 0; /* initialize to appease gcc */
1234
1235   if(curwin == upperwin) start = upperwin->x + 1;
1236 #endif
1237
1238   if(znargs < 3) height = 1;
1239   if(znargs < 4) skip = 0;
1240
1241   for(uint16_t i = 0; i < height; i++)
1242   {
1243     for(uint16_t j = 0; j < width; j++)
1244     {
1245       put_char(user_byte(text + n++));
1246     }
1247
1248     if(i + 1 != height)
1249     {
1250       n += skip;
1251 #ifdef ZTERP_GLK
1252       if(curwin == upperwin)
1253       {
1254         set_cursor(upperwin->y + 2, start);
1255       }
1256       else
1257 #endif
1258       {
1259         put_char(ZSCII_NEWLINE);
1260       }
1261     }
1262   }
1263 }
1264
1265 void zprint_char(void)
1266 {
1267   /* Check 32 (space) first: a cursory examination of story files
1268    * indicates that this is the most common value passed to @print_char.
1269    * This appears to be due to V4+ games blanking the upper window.
1270    */
1271 #define valid_zscii_output(c)   ((c) == 32 || (c) == 0 || (c) == 9 || (c) == 11 || (c) == 13 || ((c) > 32 && (c) <= 126) || ((c) >= 155 && (c) <= 251))
1272   ZASSERT(valid_zscii_output(zargs[0]), "@print_char called with invalid character: %u", (unsigned)zargs[0]);
1273 #undef valid_zscii_output
1274
1275   put_char(zargs[0]);
1276 }
1277
1278 void zprint_num(void)
1279 {
1280   char buf[7];
1281   int i = 0;
1282   long v = (int16_t)zargs[0];
1283
1284   if(v < 0) v = -v;
1285
1286   do
1287   {
1288     buf[i++] = '0' + (v % 10);
1289   } while(v /= 10);
1290
1291   if((int16_t)zargs[0] < 0) buf[i++] = '-';
1292
1293   while(i--) put_char(buf[i]);
1294 }
1295
1296 void zprint_addr(void)
1297 {
1298   print_handler(zargs[0], NULL);
1299 }
1300
1301 void zprint_paddr(void)
1302 {
1303   print_handler(unpack(zargs[0], 1), NULL);
1304 }
1305
1306 /* XXX This is more complex in V6 and needs to be updated when V6 windowing is implemented. */
1307 void zsplit_window(void)
1308 {
1309   if(zargs[0] == 0) close_upper_window();
1310   else              resize_upper_window(zargs[0]);
1311 }
1312
1313 void zset_window(void)
1314 {
1315   set_current_window(find_window(zargs[0]));
1316 }
1317
1318 #ifdef ZTERP_GLK
1319 static void window_change(void)
1320 {
1321   /* When a textgrid (the upper window) in Gargoyle is rearranged, it
1322    * forgets about reverse video settings, so reapply any styles to the
1323    * current window (it doesn’t hurt if the window is a textbuffer).  If
1324    * the current window is not the upper window that’s OK, because
1325    * set_current_style() is called when a @set_window is requested.
1326    */
1327   set_current_style();
1328
1329   /* If the new window is smaller, the cursor of the upper window might
1330    * be out of bounds.  Pull it back in if so.
1331    */
1332   if(zversion >= 3 && upperwin->id != NULL && upper_window_height > 0)
1333   {
1334     long x = upperwin->x, y = upperwin->y;
1335     glui32 w, h;
1336
1337     glk_window_get_size(upperwin->id, &w, &h);
1338
1339     upper_window_width = w;
1340     upper_window_height = h;
1341
1342     if(x > w) x = w;
1343     if(y > h) y = h;
1344
1345     SWITCH_WINDOW_START(upperwin);
1346     set_cursor(y + 1, x + 1);
1347     SWITCH_WINDOW_END();
1348   }
1349
1350   /* §8.4
1351    * Only 0x20 and 0x21 are mentioned; what of 0x22 and 0x24?  Zoom and
1352    * Windows Frotz both update the V5 header entries, so do that here,
1353    * too.
1354    *
1355    * Also, no version restrictions are given, but assume V4+ per §11.1.
1356    */
1357   if(zversion >= 4)
1358   {
1359     unsigned width, height;
1360
1361     get_screen_size(&width, &height);
1362
1363     STORE_BYTE(0x20, height > 254 ? 254 : height);
1364     STORE_BYTE(0x21, width > 255 ? 255 : width);
1365
1366     if(zversion >= 5)
1367     {
1368       STORE_WORD(0x22, width > UINT16_MAX ? UINT16_MAX : width);
1369       STORE_WORD(0x24, height > UINT16_MAX ? UINT16_MAX : height);
1370     }
1371   }
1372   else
1373   {
1374     zshow_status();
1375   }
1376 }
1377 #endif
1378
1379 #ifdef ZTERP_GLK
1380 static int timer_running;
1381
1382 static void start_timer(uint16_t n)
1383 {
1384   if(!TIMER_AVAILABLE()) return;
1385
1386   if(timer_running) die("nested timers unsupported");
1387   glk_request_timer_events(n * 100);
1388   timer_running = 1;
1389 }
1390
1391 static void stop_timer(void)
1392 {
1393   if(!TIMER_AVAILABLE()) return;
1394
1395   glk_request_timer_events(0);
1396   timer_running = 0;
1397 }
1398
1399 static void request_char(void)
1400 {
1401   if(have_unicode) glk_request_char_event_uni(curwin->id);
1402   else             glk_request_char_event(curwin->id);
1403
1404   curwin->pending_read = 1;
1405 }
1406
1407 static void request_line(union line *line, glui32 maxlen, glui32 initlen)
1408 {
1409   if(have_unicode) glk_request_line_event_uni(curwin->id, line->unicode, maxlen, initlen);
1410   else             glk_request_line_event(curwin->id, line->latin1, maxlen, initlen);
1411
1412   curwin->pending_read = 1;
1413   curwin->line = line;
1414 }
1415 #endif
1416
1417 #define special_zscii(c) ((c) >= 129 && (c) <= 154)
1418
1419 /* This is called when input stream 1 (read from file) is selected.  If
1420  * it succefully reads a character/line from the file, it fills the
1421  * struct at “input” with the appropriate information and returns true.
1422  * If it fails to read (likely due to EOF) then it sets the input stream
1423  * back to the keyboard and returns false.
1424  */
1425 static int istream_read_from_file(struct input *input)
1426 {
1427   if(input->type == INPUT_CHAR)
1428   {
1429     long c;
1430
1431     c = zterp_io_getc(istreamio);
1432     if(c == -1)
1433     {
1434       input_stream(ISTREAM_KEYBOARD);
1435       return 0;
1436     }
1437
1438     /* Don’t translate special ZSCII characters (cursor keys, function keys, keypad). */
1439     if(special_zscii(c)) input->key = c;
1440     else                 input->key = unicode_to_zscii_q[c];
1441   }
1442   else
1443   {
1444     long n;
1445     uint16_t line[1024];
1446
1447     n = zterp_io_readline(istreamio, line, sizeof line / sizeof *line);
1448     if(n == -1)
1449     {
1450       input_stream(ISTREAM_KEYBOARD);
1451       return 0;
1452     }
1453
1454     if(n > input->maxlen) n = input->maxlen;
1455
1456     input->len = n;
1457
1458 #ifdef ZTERP_GLK
1459     if(curwin->id != NULL)
1460     {
1461       glk_set_style(style_Input);
1462       for(long i = 0; i < n; i++) GLK_PUT_CHAR(line[i]);
1463       GLK_PUT_CHAR(UNICODE_LINEFEED);
1464       set_current_style();
1465     }
1466 #else
1467     for(long i = 0; i < n; i++) zterp_io_putc(zterp_io_stdout(), line[i]);
1468     zterp_io_putc(zterp_io_stdout(), UNICODE_LINEFEED);
1469 #endif
1470
1471     for(long i = 0; i < n; i++) input->line[i] = line[i];
1472   }
1473
1474 #ifdef ZTERP_GLK
1475   event_t ev;
1476
1477   /* It’s possible that output is buffered, meaning that until
1478    * glk_select() is called, output will not be displayed.  When reading
1479    * from a command-script, flush on each command so that output is
1480    * visible while the script is being replayed.
1481    */
1482   glk_select_poll(&ev);
1483   switch(ev.type)
1484   {
1485     case evtype_None:
1486       break;
1487     case evtype_Arrange:
1488       window_change();
1489       break;
1490     default:
1491       /* No other events should arrive.  Timers are only started in
1492        * get_input() and are stopped before that function returns.
1493        * Input events will not happen with glk_select_poll(), and no
1494        * other event type is expected to be raised.
1495        */
1496       break;
1497   }
1498
1499   saw_input = 1;
1500 #endif
1501
1502   return 1;
1503 }
1504
1505 #ifdef GLK_MODULE_LINE_TERMINATORS
1506 /* Glk returns terminating characters as keycode_*, but we need them as
1507  * ZSCII.  This should only ever be called with values that are matched
1508  * in the switch, because those are the only ones that Glk was told are
1509  * terminating characters.  In the event that another keycode comes
1510  * through, though, treat it as Enter.
1511  */
1512 static uint8_t zscii_from_glk(glui32 key)
1513 {
1514   switch(key)
1515   {
1516     case 13:             return ZSCII_NEWLINE;
1517     case keycode_Up:     return 129;
1518     case keycode_Down:   return 130;
1519     case keycode_Left:   return 131;
1520     case keycode_Right:  return 131;
1521     case keycode_Func1:  return 133;
1522     case keycode_Func2:  return 134;
1523     case keycode_Func3:  return 135;
1524     case keycode_Func4:  return 136;
1525     case keycode_Func5:  return 137;
1526     case keycode_Func6:  return 138;
1527     case keycode_Func7:  return 139;
1528     case keycode_Func8:  return 140;
1529     case keycode_Func9:  return 141;
1530     case keycode_Func10: return 142;
1531     case keycode_Func11: return 143;
1532     case keycode_Func12: return 144;
1533   }
1534
1535   return ZSCII_NEWLINE;
1536 }
1537 #endif
1538
1539 #ifdef ZTERP_GLK
1540 /* This is like strlen() but in addition to C strings it can find the
1541  * length of a Unicode string (which is assumed to be zero terminated)
1542  * if Unicode is being used.
1543  */
1544 static size_t line_len(const union line *line)
1545 {
1546   size_t i;
1547
1548   if(!have_unicode) return strlen(line->latin1);
1549
1550   for(i = 0; line->unicode[i] != 0; i++)
1551   {
1552   }
1553
1554   return i;
1555 }
1556 #endif
1557
1558 /* Attempt to read input from the user.  The input type can be either a
1559  * single character or a full line.  If “timer” is not zero, a timer is
1560  * started that fires off every “timer” tenths of a second (if the value
1561  * is 1, it will timeout 10 times a second, etc.).  Each time the timer
1562  * times out the routine at address “routine” is called.  If the routine
1563  * returns true, the input is canceled.
1564  *
1565  * The function returns 1 if input was stored, 0 if there was a
1566  * cancellation as described above.
1567  */
1568 static int get_input(int16_t timer, int16_t routine, struct input *input)
1569 {
1570   /* If either of these is zero, no timeout should happen. */
1571   if(timer   == 0) routine = 0;
1572   if(routine == 0) timer   = 0;
1573
1574   /* Flush all streams when input is requested. */
1575 #ifndef ZTERP_GLK
1576   zterp_io_flush(zterp_io_stdout());
1577 #endif
1578   zterp_io_flush(scriptio);
1579   zterp_io_flush(transio);
1580
1581   /* Generally speaking, newline will be the reason the line input
1582    * stopped, so set it by default.  It will be overridden where
1583    * necessary.
1584    */
1585   input->term = ZSCII_NEWLINE;
1586
1587   if(istream == ISTREAM_FILE && istream_read_from_file(input)) return 1;
1588 #ifdef ZTERP_GLK
1589   int status = 0;
1590   union line line;
1591   struct window *saved = NULL;
1592
1593   /* In V6, input might be requested on an unsupported window.  If so,
1594    * switch to the main window temporarily.
1595    */
1596   if(curwin->id == NULL)
1597   {
1598     saved = curwin;
1599     curwin = mainwin;
1600     glk_set_window(curwin->id);
1601   }
1602
1603   if(input->type == INPUT_CHAR)
1604   {
1605     request_char();
1606   }
1607   else
1608   {
1609     for(int i = 0; i < input->preloaded; i++)
1610     {
1611       if(have_unicode) line.unicode[i] = input->line[i];
1612       else             line.latin1 [i] = input->line[i];
1613     }
1614
1615     request_line(&line, input->maxlen, input->preloaded);
1616   }
1617
1618   if(timer != 0) start_timer(timer);
1619
1620   while(status == 0)
1621   {
1622     event_t ev;
1623
1624     glk_select(&ev);
1625
1626     switch(ev.type)
1627     {
1628       case evtype_Arrange:
1629         window_change();
1630         break;
1631
1632       case evtype_Timer:
1633         {
1634           ZASSERT(timer != 0, "got unexpected evtype_Timer");
1635
1636           struct window *saved2 = curwin;
1637           int ret;
1638
1639           stop_timer();
1640
1641           ret = direct_call(routine);
1642
1643           /* It’s possible for an interrupt to switch windows; if it
1644            * does, simply switch back.  This is the easiest way to deal
1645            * with an undefined bit of the Z-machine.
1646            */
1647           if(curwin != saved2) set_current_window(saved2);
1648
1649           if(ret)
1650           {
1651             status = 2;
1652           }
1653           else
1654           {
1655             /* If this got reset to 0, that means an interrupt had to
1656              * cancel the read event in order to either read or write.
1657              */
1658             if(!curwin->pending_read)
1659             {
1660               if(input->type == INPUT_CHAR) request_char();
1661               else                          request_line(&line, input->maxlen, line_len(&line));
1662             }
1663
1664             start_timer(timer);
1665           }
1666         }
1667
1668         break;
1669
1670       case evtype_CharInput:
1671         ZASSERT(input->type == INPUT_CHAR, "got unexpected evtype_CharInput");
1672         ZASSERT(ev.win == curwin->id, "got evtype_CharInput on unexpected window");
1673
1674         status = 1;
1675
1676         switch(ev.val1)
1677         {
1678           case keycode_Delete: input->key =   8; break;
1679           case keycode_Return: input->key =  13; break;
1680           case keycode_Escape: input->key =  27; break;
1681           case keycode_Up:     input->key = 129; break;
1682           case keycode_Down:   input->key = 130; break;
1683           case keycode_Left:   input->key = 131; break;
1684           case keycode_Right:  input->key = 132; break;
1685           case keycode_Func1:  input->key = 133; break;
1686           case keycode_Func2:  input->key = 134; break;
1687           case keycode_Func3:  input->key = 135; break;
1688           case keycode_Func4:  input->key = 136; break;
1689           case keycode_Func5:  input->key = 137; break;
1690           case keycode_Func6:  input->key = 138; break;
1691           case keycode_Func7:  input->key = 139; break;
1692           case keycode_Func8:  input->key = 140; break;
1693           case keycode_Func9:  input->key = 141; break;
1694           case keycode_Func10: input->key = 142; break;
1695           case keycode_Func11: input->key = 143; break;
1696           case keycode_Func12: input->key = 144; break;
1697
1698           default:
1699             input->key = ZSCII_QUESTIONMARK;
1700
1701             if(ev.val1 <= UINT16_MAX)
1702             {
1703               uint8_t c = unicode_to_zscii[ev.val1];
1704
1705               if(c != 0) input->key = c;
1706             }
1707
1708             break;
1709         }
1710
1711         break;
1712
1713       case evtype_LineInput:
1714         ZASSERT(input->type == INPUT_LINE, "got unexpected evtype_LineInput");
1715         ZASSERT(ev.win == curwin->id, "got evtype_LineInput on unexpected window");
1716         input->len = ev.val1;
1717 #ifdef GLK_MODULE_LINE_TERMINATORS
1718         if(zversion >= 5) input->term = zscii_from_glk(ev.val2);
1719 #endif
1720         status = 1;
1721         break;
1722     }
1723   }
1724
1725   stop_timer();
1726
1727   if(input->type == INPUT_CHAR)
1728   {
1729     glk_cancel_char_event(curwin->id);
1730   }
1731   else
1732   {
1733     /* On cancellation, the buffer still needs to be filled, because
1734      * it’s possible that line input echoing has been turned off and the
1735      * contents will need to be written out.
1736      */
1737     if(status == 2)
1738     {
1739       event_t ev;
1740
1741       glk_cancel_line_event(curwin->id, &ev);
1742       input->len = ev.val1;
1743       input->term = 0;
1744     }
1745
1746     for(glui32 i = 0; i < input->len; i++)
1747     {
1748       if(have_unicode) input->line[i] = line.unicode[i] > UINT16_MAX ? UNICODE_QUESTIONMARK : line.unicode[i];
1749       else             input->line[i] = (uint8_t)line.latin1[i];
1750     }
1751   }
1752
1753   curwin->pending_read = 0;
1754   curwin->line = NULL;
1755
1756   if(status == 1) saw_input = 1;
1757
1758   if(errorwin != NULL)
1759   {
1760     glk_window_close(errorwin, NULL);
1761     errorwin = NULL;
1762   }
1763
1764   if(saved != NULL)
1765   {
1766     curwin = saved;
1767     glk_set_window(curwin->id);
1768   }
1769
1770   return status != 2;
1771 #else
1772   if(input->type == INPUT_CHAR)
1773   {
1774     long n;
1775     uint16_t line[64];
1776
1777     n = zterp_io_readline(zterp_io_stdin(), line, sizeof line / sizeof *line);
1778
1779     /* On error/eof, or if an invalid key was typed, pretend “Enter” was hit. */
1780     if(n <= 0)
1781     {
1782       input->key = ZSCII_NEWLINE;
1783     }
1784     else
1785     {
1786       input->key = unicode_to_zscii[line[0]];
1787       if(input->key == 0) input->key = ZSCII_NEWLINE;
1788     }
1789   }
1790   else
1791   {
1792     input->len = input->preloaded;
1793
1794     if(input->maxlen > input->preloaded)
1795     {
1796       long n;
1797       uint16_t line[1024];
1798
1799       n = zterp_io_readline(zterp_io_stdin(), line, sizeof line / sizeof *line);
1800       if(n != -1)
1801       {
1802         if(n > input->maxlen - input->preloaded) n = input->maxlen - input->preloaded;
1803         for(long i = 0; i < n; i++) input->line[i + input->preloaded] = line[i];
1804         input->len += n;
1805       }
1806     }
1807   }
1808
1809   return 1;
1810 #endif
1811 }
1812
1813 void zread_char(void)
1814 {
1815   uint16_t timer = 0;
1816   uint16_t routine = zargs[2];
1817   struct input input = { .type = INPUT_CHAR };
1818
1819 #ifdef ZTERP_GLK
1820   cancel_read_events(curwin);
1821 #endif
1822
1823   if(zversion >= 4 && znargs > 1) timer = zargs[1];
1824
1825   if(!get_input(timer, routine, &input))
1826   {
1827     store(0);
1828     return;
1829   }
1830
1831 #ifdef ZTERP_GLK
1832   update_delayed();
1833 #endif
1834
1835   if(streams & STREAM_SCRIPT)
1836   {
1837     /* Values 127 to 159 are not valid Unicode, and these just happen to
1838      * match up to the values needed for special ZSCII keys, so store
1839      * them as-is.
1840      */
1841     if(special_zscii(input.key)) zterp_io_putc(scriptio, input.key);
1842     else                         zterp_io_putc(scriptio, zscii_to_unicode[input.key]);
1843   }
1844
1845   store(input.key);
1846 }
1847
1848 #ifdef ZTERP_GLK
1849 static void status_putc(uint8_t c)
1850 {
1851   glk_put_char(zscii_to_unicode[c]);
1852 }
1853 #endif
1854
1855 void zshow_status(void)
1856 {
1857 #ifdef ZTERP_GLK
1858   glui32 width, height;
1859   char rhs[64];
1860   int first = variable(0x11), second = variable(0x12);
1861
1862   if(statuswin.id == NULL) return;
1863
1864   glk_window_clear(statuswin.id);
1865
1866   SWITCH_WINDOW_START(&statuswin);
1867
1868   glk_window_get_size(statuswin.id, &width, &height);
1869
1870 #ifdef GARGLK
1871   garglk_set_reversevideo(1);
1872 #else
1873   glk_set_style(style_Alert);
1874 #endif
1875   for(glui32 i = 0; i < width; i++) glk_put_char(ZSCII_SPACE);
1876
1877   glk_window_move_cursor(statuswin.id, 1, 0);
1878
1879   /* Variable 0x10 is global variable 1. */
1880   print_object(variable(0x10), status_putc);
1881
1882   if(STATUS_IS_TIME())
1883   {
1884     snprintf(rhs, sizeof rhs, "Time: %d:%02d%s ", (first + 11) % 12 + 1, second, first < 12 ? "am" : "pm");
1885     if(strlen(rhs) > width) snprintf(rhs, sizeof rhs, "%02d:%02d", first, second);
1886   }
1887   else
1888   {
1889     snprintf(rhs, sizeof rhs, "Score: %d  Moves: %d ", first, second);
1890     if(strlen(rhs) > width) snprintf(rhs, sizeof rhs, "%d/%d", first, second);
1891   }
1892
1893   if(strlen(rhs) <= width)
1894   {
1895     glk_window_move_cursor(statuswin.id, width - strlen(rhs), 0);
1896     glk_put_string(rhs);
1897   }
1898
1899   SWITCH_WINDOW_END();
1900 #endif
1901 }
1902
1903 /* This is strcmp() except that the first string is Unicode. */
1904 static int unicmp(const uint32_t *s1, const char *s2)
1905 {
1906   while(*s1 != 0 && *s2 == *s1)
1907   {
1908     s1++;
1909     s2++;
1910   }
1911
1912   return *s1 - *s2;
1913 }
1914
1915 uint32_t read_pc;
1916
1917 /* Try to parse a meta command.  Returns true if input should be
1918  * restarted, false to indicate no more input is required.  In most
1919  * cases input will be required because the game has requested it, but
1920  * /undo and /restore jump to different locations, so the current @read
1921  * no longer exists.
1922  */
1923 static int handle_meta_command(const uint32_t *string)
1924 {
1925   if(unicmp(string, "undo") == 0)
1926   {
1927     uint16_t flags2 = WORD(0x10);
1928     int success = pop_save();
1929
1930     if(success != 0)
1931     {
1932       /* §6.1.2. */
1933       STORE_WORD(0x10, flags2);
1934
1935       if(zversion >= 5) store(success);
1936       else              put_string("[undone]\n\n>");
1937
1938       return 0;
1939     }
1940     else
1941     {
1942       put_string("[no save found, unable to undo]");
1943     }
1944   }
1945   else if(unicmp(string, "scripton") == 0)
1946   {
1947     if(output_stream(OSTREAM_SCRIPT, 0)) put_string("[transcripting on]");
1948     else                                 put_string("[transcripting failed]");
1949   }
1950   else if(unicmp(string, "scriptoff") == 0)
1951   {
1952     output_stream(-OSTREAM_SCRIPT, 0);
1953     put_string("[transcripting off]");
1954   }
1955   else if(unicmp(string, "recon") == 0)
1956   {
1957     if(output_stream(OSTREAM_RECORD, 0)) put_string("[command recording on]");
1958     else                                 put_string("[command recording failed]");
1959   }
1960   else if(unicmp(string, "recoff") == 0)
1961   {
1962     output_stream(-OSTREAM_RECORD, 0);
1963     put_string("[command recording off]");
1964   }
1965   else if(unicmp(string, "replay") == 0)
1966   {
1967     if(input_stream(ISTREAM_FILE)) put_string("[replaying commands]");
1968     else                           put_string("[replaying commands failed]");
1969   }
1970   else if(unicmp(string, "save") == 0)
1971   {
1972     if(interrupt_level() != 0)
1973     {
1974       put_string("[cannot call /save while in an interrupt]");
1975     }
1976     else
1977     {
1978       uint32_t tmp = pc;
1979
1980       /* pc is currently set to the next instruction, but the restore
1981        * needs to come back to *this* instruction; so temporarily set
1982        * pc back before saving.
1983        */
1984       pc = read_pc;
1985       if(do_save(1)) put_string("[saved]");
1986       else           put_string("[save failed]");
1987       pc = tmp;
1988     }
1989   }
1990   else if(unicmp(string, "restore") == 0)
1991   {
1992     if(do_restore(1))
1993     {
1994       put_string("[restored]\n\n>");
1995       return 0;
1996     }
1997     else
1998     {
1999       put_string("[restore failed]");
2000     }
2001   }
2002   else if(unicmp(string, "help") == 0)
2003   {
2004     put_string(
2005         "/undo: undo a turn\n"
2006         "/scripton: start a transcript\n"
2007         "/scriptoff: stop a transcript\n"
2008         "/recon: start a command record\n"
2009         "/recoff: stop a command record\n"
2010         "/replay: replay a command record\n"
2011         "/save: save the game\n"
2012         "/restore: restore a game saved by /save"
2013         );
2014   }
2015   else
2016   {
2017     put_string("[unknown command]");
2018   }
2019
2020   return 1;
2021 }
2022
2023 void zread(void)
2024 {
2025   uint16_t text = zargs[0], parse = zargs[1];
2026   uint8_t maxchars = zversion >= 5 ? user_byte(text) : user_byte(text) - 1;
2027   uint8_t zscii_string[maxchars];
2028   uint32_t string[maxchars + 1];
2029   struct input input = { .type = INPUT_LINE, .line = string, .maxlen = maxchars };
2030   uint16_t timer = 0;
2031   uint16_t routine = zargs[3];
2032
2033 #ifdef ZTERP_GLK
2034   cancel_read_events(curwin);
2035 #endif
2036
2037   if(zversion <= 3) zshow_status();
2038
2039   if(zversion >= 4 && znargs > 2) timer = zargs[2];
2040
2041   if(zversion >= 5)
2042   {
2043     int i;
2044
2045     input.preloaded = user_byte(text + 1);
2046     ZASSERT(input.preloaded <= maxchars, "too many preloaded characters: %d when max is %d", input.preloaded, maxchars);
2047
2048     for(i = 0; i < input.preloaded; i++) string[i] = zscii_to_unicode[user_byte(text + i + 2)];
2049     string[i] = 0;
2050
2051     /* Under garglk, preloaded input works as it’s supposed to.
2052      * Under Glk, it can fail one of two ways:
2053      * 1. The preloaded text is printed out once, but is not editable.
2054      * 2. The preloaded text is printed out twice, the second being editable.
2055      * I have chosen option #2.  For non-Glk, option #1 is done by necessity.
2056      */
2057 #ifdef GARGLK
2058     if(curwin->id != NULL) garglk_unput_string_uni(string);
2059 #endif
2060   }
2061
2062   if(!get_input(timer, routine, &input))
2063   {
2064 #ifdef ZTERP_GLK
2065     cleanup_screen(&input);
2066 #endif
2067     if(zversion >= 5) store(0);
2068     return;
2069   }
2070
2071 #ifdef ZTERP_GLK
2072   cleanup_screen(&input);
2073 #endif
2074
2075 #ifdef ZTERP_GLK
2076   update_delayed();
2077 #endif
2078
2079   if(options.enable_escape && (streams & STREAM_TRANS))
2080   {
2081     zterp_io_putc(transio, 033);
2082     zterp_io_putc(transio, '[');
2083     for(int i = 0; options.escape_string[i] != 0; i++) zterp_io_putc(transio, options.escape_string[i]);
2084   }
2085
2086   for(int i = 0; i < input.len; i++)
2087   {
2088     zscii_string[i] = unicode_to_zscii_q[unicode_tolower(string[i])];
2089     if(streams & STREAM_TRANS)  zterp_io_putc(transio, string[i]);
2090     if(streams & STREAM_SCRIPT) zterp_io_putc(scriptio, string[i]);
2091   }
2092
2093   if(options.enable_escape && (streams & STREAM_TRANS))
2094   {
2095     zterp_io_putc(transio, 033);
2096     zterp_io_putc(transio, '[');
2097     zterp_io_putc(transio, '0');
2098     zterp_io_putc(transio, 'm');
2099   }
2100
2101   if(streams & STREAM_TRANS)  zterp_io_putc(transio, UNICODE_LINEFEED);
2102   if(streams & STREAM_SCRIPT) zterp_io_putc(scriptio, UNICODE_LINEFEED);
2103
2104   if(!options.disable_meta_commands)
2105   {
2106     string[input.len] = 0;
2107
2108     if(string[0] == '/')
2109     {
2110       if(handle_meta_command(string + 1))
2111       {
2112         /* The game still wants input, so try again. */
2113         put_string("\n\n>");
2114         zread();
2115       }
2116
2117       return;
2118     }
2119
2120     /* V1–4 do not have @save_undo, so simulate one each time @read is
2121      * called.
2122      *
2123      * pc is currently set to the next instruction, but the undo needs
2124      * to come back to *this* instruction; so temporarily set pc back
2125      * before pushing the save.
2126      */
2127     if(zversion <= 4)
2128     {
2129       uint32_t tmp_pc = pc;
2130
2131       pc = read_pc;
2132       push_save();
2133       pc = tmp_pc;
2134     }
2135   }
2136
2137   if(zversion >= 5)
2138   {
2139     user_store_byte(text + 1, input.len); /* number of characters read */
2140
2141     for(int i = 0; i < input.len; i++)
2142     {
2143       user_store_byte(text + i + 2, zscii_string[i]);
2144     }
2145
2146     if(parse != 0) tokenize(text, parse, 0, 0);
2147
2148     store(input.term);
2149   }
2150   else
2151   {
2152     for(int i = 0; i < input.len; i++)
2153     {
2154       user_store_byte(text + i + 1, zscii_string[i]);
2155     }
2156
2157     user_store_byte(text + input.len + 1, 0);
2158
2159     tokenize(text, parse, 0, 0);
2160   }
2161 }
2162
2163 void zprint_unicode(void)
2164 {
2165   if(valid_unicode(zargs[0])) put_char_u(zargs[0]);
2166   else                        put_char_u(UNICODE_QUESTIONMARK);
2167 }
2168
2169 void zcheck_unicode(void)
2170 {
2171   uint16_t res = 0;
2172
2173   /* valid_unicode() will tell which Unicode characters can be printed;
2174    * and if the Unicode character is in the Unicode input table, it can
2175    * also be read.  If Unicode is not available, then any character >255
2176    * is invalid for both reading and writing.
2177    */
2178   if(have_unicode || zargs[0] < 256)
2179   {
2180     if(valid_unicode(zargs[0]))         res |= 0x01;
2181     if(unicode_to_zscii[zargs[0]] != 0) res |= 0x02;
2182   }
2183
2184   store(res);
2185 }
2186
2187 /* Should picture_data and get_wind_prop be moved to a V6 source file? */
2188 void zpicture_data(void)
2189 {
2190   if(zargs[0] == 0)
2191   {
2192     user_store_word(zargs[1] + 0, 0);
2193     user_store_word(zargs[1] + 2, 0);
2194   }
2195
2196   /* No pictures means no valid pictures, so never branch. */
2197   branch_if(0);
2198 }
2199
2200 void zget_wind_prop(void)
2201 {
2202   uint16_t val;
2203   struct window *win;
2204
2205   win = find_window(zargs[0]);
2206
2207   /* These are mostly bald-faced lies. */
2208   switch(zargs[1])
2209   {
2210     case 0: /* y coordinate */
2211       val = 0;
2212       break;
2213     case 1: /* x coordinate */
2214       val = 0;
2215       break;
2216     case 2:  /* y size */
2217       val = 100;
2218       break;
2219     case 3:  /* x size */
2220       val = 100;
2221       break;
2222     case 4:  /* y cursor */
2223       val = 0;
2224       break;
2225     case 5:  /* x cursor */
2226       val = 0;
2227       break;
2228     case 6: /* left margin size */
2229       val = 0;
2230       break;
2231     case 7: /* right margin size */
2232       val = 0;
2233       break;
2234     case 8: /* newline interrupt routine */
2235       val = 0;
2236       break;
2237     case 9: /* interrupt countdown */
2238       val = 0;
2239       break;
2240     case 10: /* text style */
2241       val = win->style;
2242       break;
2243     case 11: /* colour data */
2244       val = (9 << 8) | 2;
2245       break;
2246     case 12: /* font number */
2247       val = win->font;
2248       break;
2249     case 13: /* font size */
2250       val = (10 << 8) | 10;
2251       break;
2252     case 14: /* attributes */
2253       val = 0;
2254       break;
2255     case 15: /* line count */
2256       val = 0;
2257       break;
2258     case 16: /* true foreground colour */
2259       val = 0;
2260       break;
2261     case 17: /* true background colour */
2262       val = 0;
2263       break;
2264     default:
2265       die("unknown window property: %u", (unsigned)zargs[1]);
2266   }
2267
2268   store(val);
2269 }
2270
2271 /* This is not correct, because @output_stream does not work as it
2272  * should with a width argument; however, this does print out the
2273  * contents of a table that was sent to stream 3, so it’s at least
2274  * somewhat useful.
2275  *
2276  * Output should be to the currently-selected window, but since V6 is
2277  * only marginally supported, other windows are not active.  Send to the
2278  * main window for the time being.
2279  */
2280 void zprint_form(void)
2281 {
2282   SWITCH_WINDOW_START(mainwin);
2283
2284   for(uint16_t i = 0; i < user_word(zargs[0]); i++)
2285   {
2286     put_char(user_byte(zargs[0] + 2 + i));
2287   }
2288
2289   put_char(ZSCII_NEWLINE);
2290
2291   SWITCH_WINDOW_END();
2292 }
2293
2294 void zmake_menu(void)
2295 {
2296   branch_if(0);
2297 }
2298
2299 void zbuffer_screen(void)
2300 {
2301   store(0);
2302 }
2303
2304 #ifdef GARGLK
2305 /* Glk does not guarantee great control over how various styles are
2306  * going to look, but Gargoyle does.  Abusing the Glk “style hints”
2307  * functions allows for quite fine-grained control over style
2308  * appearance.  First, clear the (important) attributes for each style,
2309  * and then recreate each in whatever mold is necessary.  Re-use some
2310  * that are expected to be correct (emphasized for italic, subheader for
2311  * bold, and so on).
2312  */
2313 static void set_default_styles(void)
2314 {
2315   int styles[] = { style_Subheader, style_Emphasized, style_Alert, style_Preformatted, style_User1, style_User2, style_Note };
2316
2317   for(int i = 0; i < 7; i++)
2318   {
2319     glk_stylehint_set(wintype_AllTypes, styles[i], stylehint_Size, 0);
2320     glk_stylehint_set(wintype_AllTypes, styles[i], stylehint_Weight, 0);
2321     glk_stylehint_set(wintype_AllTypes, styles[i], stylehint_Oblique, 0);
2322
2323     /* This sets wintype_TextGrid to be proportional, which of course is
2324      * wrong; but text grids are required to be fixed, so Gargoyle
2325      * simply ignores this hint for those windows.
2326      */
2327     glk_stylehint_set(wintype_AllTypes, styles[i], stylehint_Proportional, 1);
2328   }
2329 }
2330 #endif
2331
2332 int create_mainwin(void)
2333 {
2334 #ifdef ZTERP_GLK
2335
2336 #ifdef GARGLK
2337   set_default_styles();
2338
2339   /* Bold */
2340   glk_stylehint_set(wintype_AllTypes, style_Subheader, stylehint_Weight, 1);
2341
2342   /* Italic */
2343   glk_stylehint_set(wintype_AllTypes, style_Emphasized, stylehint_Oblique, 1);
2344
2345   /* Bold Italic */
2346   glk_stylehint_set(wintype_AllTypes, style_Alert, stylehint_Weight, 1);
2347   glk_stylehint_set(wintype_AllTypes, style_Alert, stylehint_Oblique, 1);
2348
2349   /* Fixed */
2350   glk_stylehint_set(wintype_AllTypes, style_Preformatted, stylehint_Proportional, 0);
2351
2352   /* Bold Fixed */
2353   glk_stylehint_set(wintype_AllTypes, style_User1, stylehint_Weight, 1);
2354   glk_stylehint_set(wintype_AllTypes, style_User1, stylehint_Proportional, 0);
2355
2356   /* Italic Fixed */
2357   glk_stylehint_set(wintype_AllTypes, style_User2, stylehint_Oblique, 1);
2358   glk_stylehint_set(wintype_AllTypes, style_User2, stylehint_Proportional, 0);
2359
2360   /* Bold Italic Fixed */
2361   glk_stylehint_set(wintype_AllTypes, style_Note, stylehint_Weight, 1);
2362   glk_stylehint_set(wintype_AllTypes, style_Note, stylehint_Oblique, 1);
2363   glk_stylehint_set(wintype_AllTypes, style_Note, stylehint_Proportional, 0);
2364 #endif
2365
2366   mainwin->id = glk_window_open(0, 0, 0, wintype_TextBuffer, 1);
2367   if(mainwin->id == NULL) return 0;
2368   glk_set_window(mainwin->id);
2369
2370 #ifdef GLK_MODULE_LINE_ECHO
2371   mainwin->has_echo = glk_gestalt(gestalt_LineInputEcho, 0);
2372   if(mainwin->has_echo) glk_set_echo_line_event(mainwin->id, 0);
2373 #endif
2374
2375   return 1;
2376 #else
2377   return 1;
2378 #endif
2379 }
2380
2381 int create_statuswin(void)
2382 {
2383 #ifdef ZTERP_GLK
2384   if(statuswin.id == NULL) statuswin.id = glk_window_open(mainwin->id, winmethod_Above | winmethod_Fixed, 1, wintype_TextGrid, 0);
2385   return statuswin.id != NULL;
2386 #else
2387   return 0;
2388 #endif
2389 }
2390
2391 int create_upperwin(void)
2392 {
2393 #ifdef ZTERP_GLK
2394   /* On a restart, this function will get called again.  It would be
2395    * possible to try to resize the upper window to 0 if it already
2396    * exists, but it’s easier to just destroy and recreate it.
2397    */
2398   if(upperwin->id != NULL) glk_window_close(upperwin->id, NULL);
2399
2400   /* The upper window appeared in V3. */
2401   if(zversion >= 3)
2402   {
2403     upperwin->id = glk_window_open(mainwin->id, winmethod_Above | winmethod_Fixed, 0, wintype_TextGrid, 0);
2404     upperwin->x = upperwin->y = 0;
2405     upper_window_height = 0;
2406
2407     if(upperwin->id != NULL)
2408     {
2409       glui32 w, h;
2410
2411       glk_window_get_size(upperwin->id, &w, &h);
2412       upper_window_width = w;
2413
2414       if(h != 0 || upper_window_width == 0)
2415       {
2416         glk_window_close(upperwin->id, NULL);
2417         upperwin->id = NULL;
2418       }
2419     }
2420   }
2421
2422   return upperwin->id != NULL;
2423 #else
2424   return 0;
2425 #endif
2426 }
2427
2428 void init_screen(void)
2429 {
2430   for(int i = 0; i < 8; i++)
2431   {
2432     windows[i].style = STYLE_NONE;
2433     windows[i].font = FONT_NORMAL;
2434     windows[i].prev_font = FONT_NONE;
2435
2436 #ifdef ZTERP_GLK
2437     clear_window(&windows[i]);
2438 #ifdef GLK_MODULE_LINE_TERMINATORS
2439     if(windows[i].id != NULL && term_nkeys != 0 && glk_gestalt(gestalt_LineTerminators, 0)) glk_set_terminators_line_event(windows[i].id, term_keys, term_nkeys);
2440 #endif
2441 #endif
2442   }
2443
2444   close_upper_window();
2445
2446 #ifdef ZTERP_GLK
2447   if(statuswin.id != NULL) glk_window_clear(statuswin.id);
2448
2449   if(errorwin != NULL)
2450   {
2451     glk_window_close(errorwin, NULL);
2452     errorwin = NULL;
2453   }
2454
2455   stop_timer();
2456
2457 #ifdef GARGLK
2458   fg_color = zcolor_Default;
2459   bg_color = zcolor_Default;
2460 #endif
2461
2462 #else
2463   fg_color = 1;
2464   bg_color = 1;
2465 #endif
2466
2467   if(scriptio != NULL) zterp_io_close(scriptio);
2468   scriptio = NULL;
2469
2470   input_stream(ISTREAM_KEYBOARD);
2471
2472   streams = STREAM_SCREEN;
2473   stablei = -1;
2474   set_current_window(mainwin);
2475 }