1 /******************************************************************************
3 * Copyright (C) 2006-2009 by Tor Andersson. *
4 * Copyright (C) 2010 by Ben Cressey, Chris Spiegel. *
6 * This file is part of Gargoyle. *
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. *
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. *
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 *
22 *****************************************************************************/
24 /* screen.c - Generic screen manipulation
26 * Portions copyright (c) 1995-1997 Stefan Jokisch.
31 static zchar statusline[256];
32 static int oldstyle = 0;
33 static int curstyle = 0;
36 static int fixforced = 0;
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;
44 /* To make the common code happy */
46 int os_char_width (zchar z)
51 int os_string_width (const zchar *s)
55 while ((c = *s++) != 0)
56 if (c == ZC_NEW_STYLE || c == ZC_NEW_FONT)
59 width += os_char_width(c);
63 int os_string_length (zchar *s)
66 while (*s++) length++;
70 void os_prepare_sample (int a)
72 glk_sound_load_hint(a, 1);
75 void os_finish_with_sample (int a)
77 glk_sound_load_hint(a, 0);
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.
92 void os_start_sample (int number, int volume, int repeats, zword eos)
98 gos_channel = glk_schannel_create(0);
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;
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);
121 void os_stop_sample (int a)
125 glk_schannel_stop(gos_channel);
128 void os_beep (int volume)
132 void gos_update_width(void)
137 glk_window_get_size(gos_upper, &width, NULL);
138 h_screen_cols = width;
139 SET_BYTE(H_SCREEN_COLS, width);
142 glk_window_move_cursor(gos_upper, 0, cury-1);
148 void gos_update_height(void)
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);
161 void reset_status_ht(void)
166 glk_window_get_size(gos_upper, NULL, &height);
167 if (mach_status_ht != height)
169 glk_window_set_arrangement(
170 glk_window_get_parent(gos_upper),
171 winmethod_Above | winmethod_Fixed,
172 mach_status_ht, NULL);
177 void erase_window (zword w)
180 glk_window_clear(gos_lower);
184 garglk_set_reversevideo_stream(
185 glk_window_get_stream(gos_upper),
188 memset(statusline, ' ', sizeof statusline);
189 glk_window_clear(gos_upper);
195 void split_window (zword lines)
199 /* The top line is always set for V1 to V3 games */
203 if (!lines || lines > curr_status_ht)
207 glk_window_get_size(gos_upper, NULL, &height);
209 glk_window_set_arrangement(
210 glk_window_get_parent(gos_upper),
211 winmethod_Above | winmethod_Fixed,
213 curr_status_ht = lines;
215 mach_status_ht = lines;
218 glk_window_move_cursor(gos_upper, 0, 0);
224 glk_window_clear(gos_upper);
227 void restart_screen (void)
235 * statusline overflowed the window size ... bad game!
236 * so ... split status text into regions, reformat and print anew.
239 void packspaces(zchar *src, zchar *dst)
244 if (*src == 0x20202020)
258 void smartstatusline (void)
262 zchar *a, *b, *c, *d;
263 int roomlen, scorelen, scoreofs;
266 packspaces(statusline, packed);
267 //strcpy(packed, statusline);
268 len = os_string_length(packed);
275 while (b[0] != 0 && !(b[0] == ' ' && b[1] == ' '))
282 d = packed + len - 1;
283 while (d[0] == ' ' && d > c)
285 if (d[0] != ' ' && d[0] != 0)
290 //printf("smart '%s'\n", packed);
291 //printf("smart %d %d %d %d\n",a-packed,b-packed,c-packed,d-packed);
295 scoreofs = h_screen_cols - scorelen - 2;
296 if (scoreofs <= roomlen)
297 scoreofs = roomlen + 2;
299 for (tmp = 0; tmp < h_screen_cols; tmp++)
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] = '|';
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);
312 void screen_char (zchar c)
314 if (gos_linepending && (gos_curwin == gos_linewin))
316 gos_cancel_pending_line();
317 if (gos_curwin == gos_upper)
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)
331 zargs[0] = 0xf000; /* tickle tickle! */
335 else if (!forcefix && fixforced)
337 zargs[0] = 0xf000; /* tickle tickle! */
342 if (gos_upper && gos_curwin == gos_upper)
344 if (c == '\n' || c == ZC_RETURN) {
352 if (curx <= ((sizeof statusline / sizeof(zchar)) - 1))
354 statusline[curx - 1] = c;
355 statusline[curx] = 0;
357 if (curx < h_screen_cols)
361 else if (curx == h_screen_cols)
364 glk_window_move_cursor(gos_curwin, curx-1, cury-1);
374 if (curx < h_screen_cols)
378 else if (curx == (h_screen_cols))
381 glk_window_move_cursor(gos_curwin, curx-1, cury-1);
387 else if (gos_curwin == gos_lower)
391 else glk_put_char_uni(c);
395 void screen_new_line (void)
400 void screen_word (const zchar *s)
403 while ((c = *s++) != 0)
404 if (c == ZC_NEW_FONT)
406 else if (c == ZC_NEW_STYLE)
412 void screen_mssg_on (void)
414 if (gos_curwin == gos_lower)
417 glk_set_style(style_Preformatted);
418 glk_put_string("\n ");
422 void screen_mssg_off (void)
424 if (gos_curwin == gos_lower)
435 * z_buffer_mode, turn text buffering on/off.
437 * zargs[0] = new text buffering flag (0 or 1)
441 void z_buffer_mode (void)
446 * z_buffer_screen, set the screen buffering mode.
452 void z_buffer_screen (void)
458 * z_erase_line, erase the line starting at the cursor position.
460 * zargs[0] = 1 + #units to erase (1 clears to the end of the line)
464 void z_erase_line (void)
468 if (gos_upper && gos_curwin == gos_upper)
470 for (i = 0; i < h_screen_cols + 1 - curx; i++)
472 glk_window_move_cursor(gos_curwin, curx - 1, cury - 1);
477 * z_erase_window, erase a window or the screen to background colour.
479 * zargs[0] = window (-3 current, -2 screen, -1 screen & unsplit)
483 void z_erase_window (void)
489 glk_set_window(gos_upper);
491 garglk_set_zcolors(curr_fg, curr_bg);
493 glk_window_clear(gos_upper);
494 glk_set_window(gos_curwin);
496 glk_window_clear(gos_lower);
501 glk_set_window(gos_upper);
503 garglk_set_zcolors(curr_fg, curr_bg);
505 glk_window_clear(gos_upper);
507 glk_window_clear(gos_lower);
509 glk_set_window(gos_lower);
510 gos_curwin = gos_lower;
513 glk_window_clear(gos_lower);
514 if (w == 1 && gos_upper)
515 glk_window_clear(gos_upper);
519 * z_get_cursor, write the cursor coordinates into a table.
521 * zargs[0] = address to write information to
525 void z_get_cursor (void)
527 storew ((zword) (zargs[0] + 0), cury);
528 storew ((zword) (zargs[0] + 2), curx);
532 * z_print_table, print ASCII text in a rectangular area.
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)
541 void z_print_table (void)
543 zword addr = zargs[0];
547 /* Supply default arguments */
554 /* Write text in width x height rectangle */
558 for (i = 0; i < zargs[2]; i++) {
565 for (j = 0; j < zargs[1]; j++) {
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))
583 #define zRGB(i) (zR(i) << 16 | zG(i) << 8 | zB(i))
586 * z_set_true_colour, set the foreground and background colours
587 * to specific RGB colour values.
589 * zargs[0] = foreground colour
590 * zargs[1] = background colour
591 * zargs[2] = window (-3 is the current one, optional)
595 void z_set_true_colour (void)
597 int zfore = zargs[0];
598 int zback = zargs[1];
601 zfore = zRGB(zargs[0]);
604 zback = zRGB(zargs[1]);
607 garglk_set_zcolors(zfore, zback);
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 */
630 #define zcolor_NUMCOLORS (13)
633 * z_set_colour, set the foreground and background colours.
635 * zargs[0] = foreground colour
636 * zargs[1] = background colour
637 * zargs[2] = window (-3 is the current one, optional)
641 void z_set_colour (void)
643 int zfore = zargs[0];
644 int zback = zargs[1];
653 zfore = zcolor_map[zfore];
657 if (zfore < zcolor_NUMCOLORS)
658 zfore = zRGB(zcolor_map[zfore]);
669 zback = zcolor_map[zback];
673 if (zback < zcolor_NUMCOLORS)
674 zback = zRGB(zcolor_map[zback]);
679 garglk_set_zcolors(zfore, zback);
687 * z_set_font, set the font for text output and store the previous font.
689 * zargs[0] = number of font or 0 to keep current font
693 void z_set_font (void)
695 zword font = zargs[0];
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! */
708 case 1: /* normal font */
709 prev_font = curr_font;
711 zargs[0] = 0xf000; /* tickle tickle! */
716 case 4: /* fixed-pitch font*/
717 prev_font = curr_font;
719 zargs[0] = 0xf000; /* tickle tickle! */
724 case 2: /* picture font, undefined per 1.1 */
725 case 3: /* character graphics font */
726 default: /* unavailable */
733 * z_set_cursor, set the cursor position or turn the cursor on/off.
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)
741 void z_set_cursor (void)
747 if (cury > mach_status_ht) {
748 mach_status_ht = cury;
752 glk_window_move_cursor(gos_upper, curx - 1, cury - 1);
757 * z_set_text_style, set the style for text output.
759 * zargs[0] = style flags to set or 0 to reset text style
763 void z_set_text_style (void)
769 else if (zargs[0] != 0xf000) /* not tickle time */
770 curstyle |= zargs[0];
772 if (h_flags & FIXED_FONT_FLAG || curr_font == 4)
773 style = curstyle | FIXED_WIDTH_STYLE;
777 if (gos_linepending && gos_curwin == gos_linewin)
780 if (style & REVERSE_STYLE)
783 garglk_set_reversevideo(TRUE);
787 if (style & FIXED_WIDTH_STYLE)
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 */
796 glk_set_style(style_Preformatted); /* monor */
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 */
807 glk_set_style(style_Normal); /* propr */
813 garglk_set_reversevideo(FALSE);
820 * z_set_window, select the current window.
822 * zargs[0] = window to be selected (-3 is the current one)
826 void z_set_window (void)
832 glk_set_window(gos_lower);
833 gos_curwin = gos_lower;
838 glk_set_window(gos_upper);
839 gos_curwin = gos_upper;
843 enable_scripting = TRUE;
845 enable_scripting = FALSE;
847 zargs[0] = 0xf000; /* tickle tickle! */
852 * z_show_status, display the status line for V1 to V3 games.
858 static void pad_status_line (int column)
861 spaces = (h_screen_cols + 1 - curx) - column;
866 void z_show_status (void)
878 /* One V5 game (Wishbringer Solid Gold) contains this opcode by
879 accident, so just return if the version number does not fit */
884 /* Read all relevant global variables from the memory of the
885 Z-machine into local variables */
888 LOW_WORD (addr, global0)
890 LOW_WORD (addr, global1)
892 LOW_WORD (addr, global2)
894 /* Move to top of the status window, and print in reverse style. */
896 glk_set_window(gos_upper);
897 gos_curwin = gos_upper;
900 garglk_set_reversevideo(TRUE);
904 glk_window_move_cursor(gos_upper, 0, 0);
906 /* If the screen width is below 55 characters then we have to use
907 the brief status line format */
909 if (h_screen_cols < 55)
912 /* Print the object description for the global variable 0 */
915 print_object (global0);
917 /* A header flag tells us whether we have to display the current
918 time or the score/moves information */
920 if (h_config & CONFIG_TIME) { /* print hours and minutes */
922 zword hours = (global1 + 11) % 12 + 1;
924 pad_status_line (brief ? 15 : 20);
926 print_string ("Time: ");
940 print_char ((global1 >= 12) ? 'p' : 'a');
943 } else { /* print score and moves */
945 pad_status_line (brief ? 15 : 30);
947 print_string (brief ? "S: " : "Score: ");
950 pad_status_line (brief ? 8 : 14);
952 print_string (brief ? "M: " : "Moves: ");
957 /* Pad the end of the status line with spaces */
961 /* Return to the lower window */
963 glk_set_window(gos_lower);
964 gos_curwin = gos_lower;
968 * z_split_window, split the screen into an upper (1) and lower (0) window.
970 * zargs[0] = height of upper window in screen units (V6) or #lines
974 void z_split_window (void)
976 split_window(zargs[0]);