Update Andrew Plotkin's unit tests
[projects/chimara/chimara.git] / interpreters / frotz / glkscreen.c
1 /******************************************************************************
2  *                                                                            *
3  * Copyright (C) 2006-2009 by Tor Andersson.                                  *
4  * Copyright (C) 2010 by Ben Cressey, Chris Spiegel.                          *
5  *                                                                            *
6  * This file is part of Gargoyle.                                             *
7  *                                                                            *
8  * Gargoyle is free software; you can redistribute it and/or modify           *
9  * it under the terms of the GNU General Public License as published by       *
10  * the Free Software Foundation; either version 2 of the License, or          *
11  * (at your option) any later version.                                        *
12  *                                                                            *
13  * Gargoyle is distributed in the hope that it will be useful,                *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
16  * GNU General Public License for more details.                               *
17  *                                                                            *
18  * You should have received a copy of the GNU General Public License          *
19  * along with Gargoyle; if not, write to the Free Software                    *
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA *
21  *                                                                            *
22  *****************************************************************************/
23
24 /* screen.c - Generic screen manipulation
25  *
26  *  Portions copyright (c) 1995-1997 Stefan Jokisch.
27  */
28
29 #include "glkfrotz.h"
30
31 static zchar statusline[256];
32 static int oldstyle = 0;
33 static int curstyle = 0;
34 static int cury = 1;
35 static int curx = 1;
36 static int fixforced = 0;
37
38 static int curr_fg = -2;
39 static int curr_bg = -2;
40 static int curr_font = 1;
41 static int prev_font = 1;
42 static int temp_font = 0;
43
44 /* To make the common code happy */
45
46 int os_char_width (zchar z)
47 {
48         return 1;
49 }
50
51 int os_string_width (const zchar *s)
52 {
53         int width = 0;
54         zchar c;
55         while ((c = *s++) != 0)
56                 if (c == ZC_NEW_STYLE || c == ZC_NEW_FONT)
57                         s++;
58                 else
59                         width += os_char_width(c);
60         return width;
61 }
62
63 int os_string_length (zchar *s)
64 {
65         int length = 0;
66         while (*s++) length++;
67         return length;
68 }
69
70 void os_prepare_sample (int a)
71 {
72         glk_sound_load_hint(a, 1);
73 }
74
75 void os_finish_with_sample (int a)
76 {
77         glk_sound_load_hint(a, 0);
78 }
79
80 /*
81  * os_start_sample
82  *
83  * Play the given sample at the given volume (ranging from 1 to 8 and
84  * 255 meaning a default volume). The sound is played once or several
85  * times in the background (255 meaning forever). In Z-code 3 the
86  * repeats value is always 0 and the number of repeats is taken from
87  * the sound file itself. The end_of_sound function is called as soon
88  * as the sound finishes.
89  *
90  */
91
92 void os_start_sample (int number, int volume, int repeats, zword eos)
93 {
94         int vol;
95
96         if (!gos_channel)
97         {
98                 gos_channel = glk_schannel_create(0);
99                 if (!gos_channel)
100                         return;
101         }
102
103         switch (volume)
104         {
105         case   1: vol = 0x02000; break;
106         case   2: vol = 0x04000; break;
107         case   3: vol = 0x06000; break;
108         case   4: vol = 0x08000; break;
109         case   5: vol = 0x0a000; break;
110         case   6: vol = 0x0c000; break;
111         case   7: vol = 0x0e000; break;
112         case   8: vol = 0x10000; break;
113         default:  vol = 0x20000; break;
114         }
115
116         /* we dont do repeating or eos-callback for now... */
117         glk_schannel_play_ext(gos_channel, number, 1, 0);
118         glk_schannel_set_volume(gos_channel, vol);
119 }
120
121 void os_stop_sample (int a)
122 {
123         if (!gos_channel)
124                 return;
125         glk_schannel_stop(gos_channel);
126 }
127
128 void os_beep (int volume)
129 {
130 }
131
132 void gos_update_width(void)
133 {
134         glui32 width;
135         if (gos_upper)
136         {
137                 glk_window_get_size(gos_upper, &width, NULL);
138                 h_screen_cols = width;
139                 SET_BYTE(H_SCREEN_COLS, width);
140                 if (curx > width)
141                 {
142                         glk_window_move_cursor(gos_upper, 0, cury-1);
143                         curx = 1;
144                 }
145         }
146 }
147
148 void gos_update_height(void)
149 {
150         glui32 height_upper;
151         glui32 height_lower;
152         if (gos_curwin)
153         {
154                 glk_window_get_size(gos_upper, NULL, &height_upper);
155                 glk_window_get_size(gos_lower, NULL, &height_lower);
156                 h_screen_rows = height_upper + height_lower + 1;
157                 SET_BYTE(H_SCREEN_ROWS, h_screen_rows);
158         }
159 }
160
161 void reset_status_ht(void)
162 {
163         glui32 height;
164         if (gos_upper)
165         {
166                 glk_window_get_size(gos_upper, NULL, &height);
167                 if (mach_status_ht != height)
168                 {
169                         glk_window_set_arrangement(
170                                 glk_window_get_parent(gos_upper),
171                                 winmethod_Above | winmethod_Fixed,
172                                 mach_status_ht, NULL);
173                 }
174         }
175 }
176
177 void erase_window (zword w)
178 {
179         if (w == 0)
180                 glk_window_clear(gos_lower);
181         else if (gos_upper)
182         {
183 #ifdef GARGLK
184                         garglk_set_reversevideo_stream(
185                                 glk_window_get_stream(gos_upper),
186                                 TRUE);
187 #endif /* GARGLK */
188                 memset(statusline, ' ', sizeof statusline);
189                 glk_window_clear(gos_upper);
190                 reset_status_ht();
191                 curr_status_ht = 0;
192         }
193 }
194
195 void split_window (zword lines)
196 {
197         if (!gos_upper)
198                 return;
199         /* The top line is always set for V1 to V3 games */
200         if (h_version < V4)
201                 lines++;
202
203         if (!lines || lines > curr_status_ht)
204         {
205                 glui32 height;
206
207                 glk_window_get_size(gos_upper, NULL, &height);
208                 if (lines != height)
209                         glk_window_set_arrangement(
210                                 glk_window_get_parent(gos_upper),
211                                 winmethod_Above | winmethod_Fixed,
212                                 lines, NULL);
213                 curr_status_ht = lines;
214         }
215         mach_status_ht = lines;
216         if (cury > lines)
217         {
218                 glk_window_move_cursor(gos_upper, 0, 0);
219                 curx = cury = 1;
220         }
221         gos_update_width();
222
223         if (h_version == V3)
224                 glk_window_clear(gos_upper);
225 }
226
227 void restart_screen (void)
228 {
229         erase_window(0);
230         erase_window(1);
231         split_window(0);
232 }
233
234 /*
235  * statusline overflowed the window size ... bad game!
236  * so ... split status text into regions, reformat and print anew.
237  */
238
239 void packspaces(zchar *src, zchar *dst)
240 {
241         int killing = 0;
242         while (*src)
243         {
244                 if (*src == 0x20202020)
245                         *src = ' ';
246                 if (*src == ' ')
247                         killing++;
248                 else
249                         killing = 0;
250                 if (killing > 2)
251                         src++;
252                 else
253                         *dst++ = *src++;
254         }
255         *dst = 0;
256 }
257
258 void smartstatusline (void)
259 {
260         zchar packed[256];
261         zchar buf[256];
262         zchar *a, *b, *c, *d;
263         int roomlen, scorelen, scoreofs;
264         int len, tmp;
265
266         packspaces(statusline, packed);
267         //strcpy(packed, statusline);
268         len = os_string_length(packed);
269
270         a = packed;
271         while (a[0] == ' ')
272                 a ++;
273
274         b = a;
275         while (b[0] != 0 && !(b[0] == ' ' && b[1] == ' '))
276                 b ++;
277
278         c = b;
279         while (c[0] == ' ')
280                 c ++;
281
282         d = packed + len - 1;
283         while (d[0] == ' ' && d > c)
284                 d --;
285         if (d[0] != ' ' && d[0] != 0)
286                 d ++;
287         if (d < c)
288                 d = c;
289
290         //printf("smart '%s'\n", packed);
291         //printf("smart %d %d %d %d\n",a-packed,b-packed,c-packed,d-packed);
292
293         roomlen = b - a;
294         scorelen = d - c;
295         scoreofs = h_screen_cols - scorelen - 2;
296         if (scoreofs <= roomlen)
297                 scoreofs = roomlen + 2;
298
299         for (tmp = 0; tmp < h_screen_cols; tmp++)
300                 buf[tmp] = ' ';
301
302         memcpy(buf + 1 + scoreofs, c, scorelen * sizeof(zchar));
303         memcpy(buf + 1, a, roomlen * sizeof(zchar));
304         //if (roomlen >= scoreofs)
305         //      buf[roomlen + 1] = '|';
306
307         glk_window_move_cursor(gos_upper, 0, 0);
308         glk_put_buffer_uni(buf, h_screen_cols);
309         glk_window_move_cursor(gos_upper, cury - 1, curx - 1);
310 }
311
312 void screen_char (zchar c)
313 {
314         if (gos_linepending && (gos_curwin == gos_linewin))
315         {
316                 gos_cancel_pending_line();
317                 if (gos_curwin == gos_upper)
318                 {
319                         curx = 1;
320                         cury ++;
321                 }
322                 if (c == '\n')
323                         return;
324         }
325
326         /* check fixed flag in header, game can change it at whim */
327         int forcefix = ((h_flags & FIXED_FONT_FLAG) != 0);
328         int curfix = ((curstyle & FIXED_WIDTH_STYLE) != 0);
329         if (forcefix && !curfix)
330         {
331                 zargs[0] = 0xf000;      /* tickle tickle! */
332                 z_set_text_style();
333                 fixforced = TRUE;
334         }
335         else if (!forcefix && fixforced)
336         {
337                 zargs[0] = 0xf000;      /* tickle tickle! */
338                 z_set_text_style();
339                 fixforced = FALSE;
340         }
341
342         if (gos_upper && gos_curwin == gos_upper)
343         {
344                 if (c == '\n' || c == ZC_RETURN) {
345                         glk_put_char('\n');
346                         curx = 1;
347                         cury ++;
348                 }
349                 else {
350                         if (cury == 1)
351                         {
352                                 if (curx <= ((sizeof statusline / sizeof(zchar)) - 1))
353                                 {
354                                         statusline[curx - 1] = c;
355                                         statusline[curx] = 0;
356                                 }
357                                 if (curx < h_screen_cols)
358                                 {
359                                         glk_put_char_uni(c);
360                                 }
361                                 else if (curx == h_screen_cols)
362                                 {
363                                         glk_put_char_uni(c);
364                                         glk_window_move_cursor(gos_curwin, curx-1, cury-1);
365                                 }
366                                 else
367                                 {
368                                         smartstatusline();
369                                 }
370                                 curx ++;
371                         }
372                         else
373                         {
374                                 if (curx < h_screen_cols)
375                                 {
376                                         glk_put_char_uni(c);
377                                 }
378                                 else if (curx == (h_screen_cols))
379                                 {
380                                         glk_put_char_uni(c);
381                                         glk_window_move_cursor(gos_curwin, curx-1, cury-1);
382                                 }
383                                 curx++;
384                         }
385                 }
386         }
387         else if (gos_curwin == gos_lower)
388         {
389                 if (c == ZC_RETURN)
390                         glk_put_char('\n');
391                 else glk_put_char_uni(c);
392         }
393 }
394
395 void screen_new_line (void)
396 {
397         screen_char('\n');
398 }
399
400 void screen_word (const zchar *s)
401 {
402         zchar c;
403         while ((c = *s++) != 0)
404                 if (c == ZC_NEW_FONT)
405                         s++;
406                 else if (c == ZC_NEW_STYLE)
407                         s++;
408                 else
409                         screen_char (c); 
410 }
411
412 void screen_mssg_on (void)
413 {
414         if (gos_curwin == gos_lower)
415         {
416                 oldstyle = curstyle;
417                 glk_set_style(style_Preformatted);
418                 glk_put_string("\n    ");
419         }
420 }
421
422 void screen_mssg_off (void)
423 {
424         if (gos_curwin == gos_lower)
425         {
426                 glk_put_char('\n');
427                 zargs[0] = 0;
428                 z_set_text_style();
429                 zargs[0] = oldstyle;
430                 z_set_text_style();
431         }
432 }
433
434 /*
435  * z_buffer_mode, turn text buffering on/off.
436  *
437  *              zargs[0] = new text buffering flag (0 or 1)
438  *
439  */
440
441 void z_buffer_mode (void)
442 {
443 }
444
445 /*
446  * z_buffer_screen, set the screen buffering mode.
447  *
448  *      zargs[0] = mode
449  *
450  */
451
452 void z_buffer_screen (void)
453 {
454         store (0);
455 }
456
457 /*
458  * z_erase_line, erase the line starting at the cursor position.
459  *
460  *              zargs[0] = 1 + #units to erase (1 clears to the end of the line)
461  *
462  */
463
464 void z_erase_line (void)
465 {
466         int i;
467
468         if (gos_upper && gos_curwin == gos_upper)
469         {
470                 for (i = 0; i < h_screen_cols + 1 - curx; i++)
471                         glk_put_char(' ');
472                 glk_window_move_cursor(gos_curwin, curx - 1, cury - 1);
473         }
474 }
475
476 /*
477  * z_erase_window, erase a window or the screen to background colour.
478  *
479  *              zargs[0] = window (-3 current, -2 screen, -1 screen & unsplit)
480  *
481  */
482
483 void z_erase_window (void)
484 {
485         short w = zargs[0];
486         if (w == -2)
487         {
488                 if (gos_upper) {
489                         glk_set_window(gos_upper);
490 #ifdef GARGLK
491                         garglk_set_zcolors(curr_fg, curr_bg);
492 #endif /* GARGLK */
493                         glk_window_clear(gos_upper);
494                         glk_set_window(gos_curwin);
495                 }
496                 glk_window_clear(gos_lower);
497         }
498         if (w == -1)
499         {
500                 if (gos_upper) {
501                         glk_set_window(gos_upper);
502 #ifdef GARGLK
503                         garglk_set_zcolors(curr_fg, curr_bg);
504 #endif /* GARGLK */
505                         glk_window_clear(gos_upper);
506                 }
507                 glk_window_clear(gos_lower);
508                 split_window(0);
509                 glk_set_window(gos_lower);
510                 gos_curwin = gos_lower;
511         }
512         if (w == 0)
513                 glk_window_clear(gos_lower);
514         if (w == 1 && gos_upper)
515                 glk_window_clear(gos_upper);
516 }
517
518 /*
519  * z_get_cursor, write the cursor coordinates into a table.
520  *
521  *              zargs[0] = address to write information to
522  *
523  */
524
525 void z_get_cursor (void)
526 {
527         storew ((zword) (zargs[0] + 0), cury);
528         storew ((zword) (zargs[0] + 2), curx);
529 }
530
531 /*
532  * z_print_table, print ASCII text in a rectangular area.
533  *
534  *              zargs[0] = address of text to be printed
535  *              zargs[1] = width of rectangular area
536  *              zargs[2] = height of rectangular area (optional)
537  *              zargs[3] = number of char's to skip between lines (optional)
538  *
539  */
540
541 void z_print_table (void)
542 {
543         zword addr = zargs[0];
544         zword x;
545         int i, j;
546
547         /* Supply default arguments */
548
549         if (zargc < 3)
550                 zargs[2] = 1;
551         if (zargc < 4)
552                 zargs[3] = 0;
553
554         /* Write text in width x height rectangle */
555
556         x = curx;
557
558         for (i = 0; i < zargs[2]; i++) {
559
560                 if (i != 0) {
561                         cury += 1;
562                         curx = x;
563                 }
564
565                 for (j = 0; j < zargs[1]; j++) {
566
567                         zbyte c;
568
569                         LOW_BYTE (addr, c)
570                         addr++;
571
572                         print_char (c);
573                 }
574
575                 addr += zargs[3];
576         }
577 }
578
579 #define zB(i) ((((i >> 10) & 0x1F) << 3) | (((i >> 10) & 0x1F) >> 2))
580 #define zG(i) ((((i >>  5) & 0x1F) << 3) | (((i >>  5) & 0x1F) >> 2))
581 #define zR(i) ((((i      ) & 0x1F) << 3) | (((i      ) & 0x1F) >> 2))
582
583 #define zRGB(i) (zR(i) << 16 | zG(i) << 8 | zB(i))
584
585 /*
586  * z_set_true_colour, set the foreground and background colours
587  * to specific RGB colour values.
588  *
589  *      zargs[0] = foreground colour
590  *      zargs[1] = background colour
591  *      zargs[2] = window (-3 is the current one, optional)
592  *
593  */
594
595 void z_set_true_colour (void)
596 {
597         int zfore = zargs[0];
598         int zback = zargs[1];
599
600         if (!(zfore < 0))
601                 zfore = zRGB(zargs[0]);
602
603         if (!(zback < 0))
604                 zback = zRGB(zargs[1]);
605
606 #ifdef GARGLK
607         garglk_set_zcolors(zfore, zback);
608 #endif /* GARGLK */
609
610         curr_fg = zfore;
611         curr_bg = zback;
612 }
613
614 static int zcolor_map[] = {
615         -2,                                             /*  0 = current */
616         -1,                                             /*  1 = default */
617         0x0000,                                 /*  2 = black */
618         0x001D,                                 /*  3 = red */
619         0x0340,                                 /*  4 = green */
620         0x03BD,                                 /*  5 = yellow */
621         0x59A0,                                 /*  6 = blue */
622         0x7C1F,                                 /*  7 = magenta */
623         0x77A0,                                 /*  8 = cyan */
624         0x7FFF,                                 /*  9 = white */
625         0x5AD6,                                 /* 10 = light grey */
626         0x4631,                                 /* 11 = medium grey */
627         0x2D6B,                                 /* 12 = dark grey */
628 };
629
630 #define zcolor_NUMCOLORS    (13)
631
632 /*
633  * z_set_colour, set the foreground and background colours.
634  *
635  *              zargs[0] = foreground colour
636  *              zargs[1] = background colour
637  *              zargs[2] = window (-3 is the current one, optional)
638  *
639  */
640
641 void z_set_colour (void)
642 {
643         int zfore = zargs[0];
644         int zback = zargs[1];
645
646         switch (zfore)
647         {
648         case -1:
649                 zfore = -3;
650
651         case 0:
652         case 1:
653                 zfore = zcolor_map[zfore];
654                 break;
655
656         default:
657                 if (zfore < zcolor_NUMCOLORS)
658                         zfore = zRGB(zcolor_map[zfore]);
659                 break;
660         }
661
662         switch (zback)
663         {
664         case -1:
665                 zback = -3;
666
667         case 0:
668         case 1:
669                 zback = zcolor_map[zback];
670                 break;
671
672         default:
673                 if (zback < zcolor_NUMCOLORS)
674                         zback = zRGB(zcolor_map[zback]);
675                 break;
676         }
677
678 #ifdef GARGLK
679         garglk_set_zcolors(zfore, zback);
680 #endif /* GARGLK */
681
682         curr_fg = zfore;
683         curr_bg = zback;
684 }
685
686 /*
687  * z_set_font, set the font for text output and store the previous font.
688  *
689  *               zargs[0] = number of font or 0 to keep current font
690  *
691  */
692
693 void z_set_font (void)
694 {
695         zword font = zargs[0];
696
697         switch (font)
698         {
699                 case 0: /* previous font */
700                         temp_font = curr_font;
701                         curr_font = prev_font;
702                         prev_font = temp_font;
703                         zargs[0] = 0xf000;      /* tickle tickle! */
704                         z_set_text_style();
705                         store (curr_font);
706                         break;
707
708                 case 1: /* normal font */
709                         prev_font = curr_font;
710                         curr_font = 1;
711                         zargs[0] = 0xf000;      /* tickle tickle! */
712                         z_set_text_style();
713                         store (prev_font);
714                         break; 
715
716                 case 4: /* fixed-pitch font*/
717                         prev_font = curr_font;
718                         curr_font = 4;
719                         zargs[0] = 0xf000;      /* tickle tickle! */
720                         z_set_text_style();
721                         store (prev_font);
722                         break;
723
724                 case 2: /* picture font, undefined per 1.1 */
725                 case 3: /* character graphics font */
726                 default: /* unavailable */
727                         store (0);
728                         break;
729         }
730 }
731
732 /*
733  * z_set_cursor, set the cursor position or turn the cursor on/off.
734  *
735  *              zargs[0] = y-coordinate or -2/-1 for cursor on/off
736  *              zargs[1] = x-coordinate
737  *              zargs[2] = window (-3 is the current one, optional)
738  *
739  */
740
741 void z_set_cursor (void)
742 {
743         cury = zargs[0];
744         curx = zargs[1];
745
746         if (gos_upper) {
747                 if (cury > mach_status_ht) {
748                         mach_status_ht = cury;
749                         reset_status_ht();
750                 }
751
752                 glk_window_move_cursor(gos_upper, curx - 1, cury - 1);
753         }
754 }
755
756 /*
757  * z_set_text_style, set the style for text output.
758  *
759  *               zargs[0] = style flags to set or 0 to reset text style
760  *
761  */
762
763 void z_set_text_style (void)
764 {
765         int style;
766
767         if (zargs[0] == 0)
768                 curstyle = 0;
769         else if (zargs[0] != 0xf000) /* not tickle time */
770                 curstyle |= zargs[0];
771
772         if (h_flags & FIXED_FONT_FLAG || curr_font == 4)
773                 style = curstyle | FIXED_WIDTH_STYLE;
774         else
775                 style = curstyle;
776
777         if (gos_linepending && gos_curwin == gos_linewin)
778                 return;
779
780         if (style & REVERSE_STYLE)
781         {
782 #ifdef GARGLK
783                 garglk_set_reversevideo(TRUE);
784 #endif /* GARGLK */
785         }
786
787         if (style & FIXED_WIDTH_STYLE)
788         {
789                 if (style & BOLDFACE_STYLE && style & EMPHASIS_STYLE)
790                         glk_set_style(style_BlockQuote);        /* monoz */
791                 else if (style & EMPHASIS_STYLE)
792                         glk_set_style(style_Alert);                     /* monoi */
793                 else if (style & BOLDFACE_STYLE)
794                         glk_set_style(style_Subheader);         /* monob */
795                 else
796                         glk_set_style(style_Preformatted);      /* monor */
797         }
798         else
799         {
800                 if (style & BOLDFACE_STYLE && style & EMPHASIS_STYLE)
801                         glk_set_style(style_Note);                      /* propz */
802                 else if (style & EMPHASIS_STYLE)
803                         glk_set_style(style_Emphasized);        /* propi */
804                 else if (style & BOLDFACE_STYLE)
805                         glk_set_style(style_Header);            /* propb */
806                 else
807                         glk_set_style(style_Normal);            /* propr */
808         }
809
810         if (curstyle == 0)
811         {
812 #ifdef GARGLK
813                 garglk_set_reversevideo(FALSE);
814 #endif /* GARGLK */
815         }
816
817 }
818
819 /*
820  * z_set_window, select the current window.
821  *
822  *              zargs[0] = window to be selected (-3 is the current one)
823  *
824  */
825
826 void z_set_window (void)
827 {
828         int win = zargs[0];
829
830         if (win == 0)
831         {
832                 glk_set_window(gos_lower);
833                 gos_curwin = gos_lower;
834         }
835         else
836         {
837                 if (gos_upper)
838                         glk_set_window(gos_upper);
839                 gos_curwin = gos_upper;
840         }
841
842         if (win == 0)
843                 enable_scripting = TRUE;
844         else
845                 enable_scripting = FALSE;
846
847         zargs[0] = 0xf000;      /* tickle tickle! */
848         z_set_text_style();
849 }
850
851 /*
852  * z_show_status, display the status line for V1 to V3 games.
853  *
854  *              no zargs used
855  *
856  */
857
858 static void pad_status_line (int column)
859 {
860         int spaces;
861         spaces = (h_screen_cols + 1 - curx) - column;
862         while (spaces-- > 0)
863                 print_char(' ');
864 }
865
866 void z_show_status (void)
867 {
868         zword global0;
869         zword global1;
870         zword global2;
871         zword addr;
872
873         bool brief = FALSE;
874
875         if (!gos_upper)
876                 return;
877
878         /* One V5 game (Wishbringer Solid Gold) contains this opcode by
879            accident, so just return if the version number does not fit */
880
881         if (h_version >= V4)
882                 return;
883
884         /* Read all relevant global variables from the memory of the
885            Z-machine into local variables */
886
887         addr = h_globals;
888         LOW_WORD (addr, global0)
889         addr += 2;
890         LOW_WORD (addr, global1)
891         addr += 2;
892         LOW_WORD (addr, global2)
893
894         /* Move to top of the status window, and print in reverse style. */
895
896         glk_set_window(gos_upper);
897         gos_curwin = gos_upper;
898
899 #ifdef GARGLK
900         garglk_set_reversevideo(TRUE);
901 #endif /* GARGLK */
902
903         curx = cury = 1;
904         glk_window_move_cursor(gos_upper, 0, 0);
905
906         /* If the screen width is below 55 characters then we have to use
907            the brief status line format */
908
909         if (h_screen_cols < 55)
910                 brief = TRUE;
911
912         /* Print the object description for the global variable 0 */
913
914         print_char (' ');
915         print_object (global0);
916
917         /* A header flag tells us whether we have to display the current
918            time or the score/moves information */
919
920         if (h_config & CONFIG_TIME) {           /* print hours and minutes */
921
922                 zword hours = (global1 + 11) % 12 + 1;
923
924                 pad_status_line (brief ? 15 : 20);
925
926                 print_string ("Time: ");
927
928                 if (hours < 10)
929                         print_char (' ');
930                 print_num (hours);
931
932                 print_char (':');
933
934                 if (global2 < 10)
935                         print_char ('0');
936                 print_num (global2);
937
938                 print_char (' ');
939
940                 print_char ((global1 >= 12) ? 'p' : 'a');
941                 print_char ('m');
942
943         } else {                                                                /* print score and moves */
944
945                 pad_status_line (brief ? 15 : 30);
946
947                 print_string (brief ? "S: " : "Score: ");
948                 print_num (global1);
949
950                 pad_status_line (brief ? 8 : 14);
951
952                 print_string (brief ? "M: " : "Moves: ");
953                 print_num (global2);
954
955         }
956
957         /* Pad the end of the status line with spaces */
958
959         pad_status_line (0);
960
961         /* Return to the lower window */
962
963         glk_set_window(gos_lower);
964         gos_curwin = gos_lower;
965 }
966
967 /*
968  * z_split_window, split the screen into an upper (1) and lower (0) window.
969  *
970  *              zargs[0] = height of upper window in screen units (V6) or #lines
971  *
972  */
973
974 void z_split_window (void)
975 {
976         split_window(zargs[0]);
977 }
978