1 /* screen.c - Generic screen manipulation
3 * Copyright (c) 2005 Tor Andersson -- Glk-ified and V6-disabled
4 * Copyright (c) 1995-1997 Stefan Jokisch
6 * This file is part of Frotz.
8 * Frotz 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 * Frotz 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 this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
25 static unsigned char statusline[256];
26 static int oldstyle = 0;
27 static int curstyle = 0;
28 static int upperstyle = 0;
29 static int lowerstyle = 0;
33 /* To make the common code happy */
35 int os_char_width (zchar z)
40 int os_string_width (const zchar *s)
44 while ((c = *s++) != 0)
45 if (c == ZC_NEW_STYLE || c == ZC_NEW_FONT)
48 width += os_char_width(c);
52 void os_prepare_sample (int a)
54 glk_sound_load_hint(a, 1);
57 void os_finish_with_sample (int a)
59 glk_sound_load_hint(a, 0);
65 * Play the given sample at the given volume (ranging from 1 to 8 and
66 * 255 meaning a default volume). The sound is played once or several
67 * times in the background (255 meaning forever). In Z-code 3 the
68 * repeats value is always 0 and the number of repeats is taken from
69 * the sound file itself. The end_of_sound function is called as soon
70 * as the sound finishes.
74 void os_start_sample (int number, int volume, int repeats, zword eos)
80 gos_channel = glk_schannel_create(0);
87 case 1: vol = 0x02000; break;
88 case 2: vol = 0x04000; break;
89 case 3: vol = 0x06000; break;
90 case 4: vol = 0x08000; break;
91 case 5: vol = 0x0a000; break;
92 case 6: vol = 0x0c000; break;
93 case 7: vol = 0x0e000; break;
94 case 8: vol = 0x10000; break;
95 default: vol = 0x20000; break;
98 /* we dont do repeating or eos-callback for now... */
99 glk_schannel_play_ext(gos_channel, number, 1, 0);
100 glk_schannel_set_volume(gos_channel, vol);
103 void os_stop_sample (int a)
107 glk_schannel_stop(gos_channel);
110 void os_beep (int volume)
114 void gos_update_width(void)
119 glk_window_get_size(gos_upper, &width, NULL);
120 h_screen_cols = width;
121 SET_BYTE(H_SCREEN_COLS, width);
124 glk_window_move_cursor(gos_upper, 0, cury-1);
130 void gos_update_height(void)
136 glk_window_get_size(gos_upper, NULL, &height_upper);
137 glk_window_get_size(gos_lower, NULL, &height_lower);
138 h_screen_rows = height_upper + height_lower + 1;
139 SET_BYTE(H_SCREEN_ROWS, h_screen_rows);
143 void reset_status_ht(void)
148 glk_window_get_size(gos_upper, NULL, &height);
149 if (mach_status_ht != height)
150 glk_window_set_arrangement(
151 glk_window_get_parent(gos_upper),
152 winmethod_Above | winmethod_Fixed,
153 mach_status_ht, NULL);
157 void erase_window (int w)
160 glk_window_clear(gos_lower);
163 memset(statusline, ' ', sizeof statusline);
164 glk_window_clear(gos_upper);
170 void split_window (int lines)
174 /* The top line is always set for V1 to V3 games */
178 if (lines > curr_status_ht)
182 glk_window_get_size(gos_upper, NULL, &height);
184 glk_window_set_arrangement(
185 glk_window_get_parent(gos_upper),
186 winmethod_Above | winmethod_Fixed,
188 curr_status_ht = lines;
190 mach_status_ht = lines;
193 glk_window_move_cursor(gos_upper, 0, 0);
199 glk_window_clear(gos_upper);
202 void restart_screen (void)
210 * statusline overflowed the window size ... bad game!
211 * so ... split status text into regions, reformat and print anew.
214 void packspaces(unsigned char *src, unsigned char *dst)
231 void smartstatusline (void)
233 unsigned char packed[256];
234 unsigned char buf[256];
235 unsigned char *a, *b, *c, *d;
236 int roomlen, scorelen, scoreofs;
239 statusline[curx - 1] = 0; /* terminate! */
241 packspaces(statusline, packed);
242 //strcpy(packed, statusline);
243 len = strlen(packed);
250 while (b[0] != 0 && !(b[0] == ' ' && b[1] == ' '))
257 d = packed + len - 1;
258 while (d[0] == ' ' && d > c)
260 if (d[0] != ' ' && d[0] != 0)
265 //printf("smart '%s'\n", packed);
266 //printf("smart %d %d %d %d\n",a-packed,b-packed,c-packed,d-packed);
270 scoreofs = h_screen_cols - scorelen - 2;
272 memset(buf, ' ', h_screen_cols);
273 memcpy(buf + 1 + scoreofs, c, scorelen);
274 memcpy(buf + 1, a, roomlen);
275 if (roomlen >= scoreofs)
276 buf[roomlen + 1] = '|';
278 glk_window_move_cursor(gos_upper, 0, 0);
279 glk_set_style(style_User1);
280 glk_put_buffer(buf, h_screen_cols);
281 glk_window_move_cursor(gos_upper, cury - 1, curx - 1);
284 void screen_char (zchar c)
286 if (gos_linepending && (gos_curwin == gos_linewin))
288 gos_cancel_pending_line();
289 if (gos_curwin == gos_upper)
298 if (gos_upper && gos_curwin == gos_upper) {
299 if (cury > mach_status_ht) {
300 mach_status_ht = cury;
305 /* check fixed flag in header, game can change it at whim */
306 if (gos_curwin == gos_lower)
308 static int forcefix = -1;
309 int curfix = h_flags & FIXED_FONT_FLAG;
310 if (forcefix != curfix)
313 zargs[0] = 0xf000; /* tickle tickle! */
318 if (gos_upper && gos_curwin == gos_upper)
320 if (c == '\n' || c == ZC_RETURN) {
328 if (curx < sizeof statusline)
329 statusline[curx - 1] = c;
331 if (curx <= h_screen_cols)
340 if (curx > h_screen_cols) {
347 else if (gos_curwin == gos_lower)
351 else glk_put_char(c);
355 void screen_new_line (void)
360 void screen_word (const zchar *s)
363 while ((c = *s++) != 0)
364 if (c == ZC_NEW_FONT)
366 else if (c == ZC_NEW_STYLE)
372 void screen_mssg_on (void)
374 if (gos_curwin == gos_lower)
377 glk_set_style(style_Preformatted);
378 glk_put_string("\n ");
382 void screen_mssg_off (void)
384 if (gos_curwin == gos_lower)
395 * z_buffer_mode, turn text buffering on/off.
397 * zargs[0] = new text buffering flag (0 or 1)
401 void z_buffer_mode (void)
406 * z_erase_line, erase the line starting at the cursor position.
408 * zargs[0] = 1 + #units to erase (1 clears to the end of the line)
412 void z_erase_line (void)
416 if (gos_upper && gos_curwin == gos_upper)
418 for (i = 0; i < h_screen_cols + 1 - curx; i++)
420 glk_window_move_cursor(gos_curwin, curx - 1, cury - 1);
425 * z_erase_window, erase a window or the screen to background colour.
427 * zargs[0] = window (-3 current, -2 screen, -1 screen & unsplit)
431 void z_erase_window (void)
437 glk_window_clear(gos_upper);
438 glk_window_clear(gos_lower);
443 glk_window_clear(gos_upper);
444 glk_window_clear(gos_lower);
448 glk_window_clear(gos_lower);
449 if (w == 1 && gos_upper)
450 glk_window_clear(gos_upper);
454 * z_get_cursor, write the cursor coordinates into a table.
456 * zargs[0] = address to write information to
460 void z_get_cursor (void)
462 storew ((zword) (zargs[0] + 0), cury);
463 storew ((zword) (zargs[0] + 2), curx);
467 * z_print_table, print ASCII text in a rectangular area.
469 * zargs[0] = address of text to be printed
470 * zargs[1] = width of rectangular area
471 * zargs[2] = height of rectangular area (optional)
472 * zargs[3] = number of char's to skip between lines (optional)
476 void z_print_table (void)
478 zword addr = zargs[0];
482 /* Supply default arguments */
489 /* Write text in width x height rectangle */
493 for (i = 0; i < zargs[2]; i++) {
500 for (j = 0; j < zargs[1]; j++) {
515 * z_set_colour, set the foreground and background colours.
517 * zargs[0] = foreground colour
518 * zargs[1] = background colour
519 * zargs[2] = window (-3 is the current one, optional)
523 void z_set_colour (void)
525 int zfore = zargs[0];
526 int zback = zargs[1];
528 if (!(zfore == 0 && zback == 0))
529 garglk_set_zcolors(zfore, zback);
533 * z_set_font, set the font for text output and store the previous font.
535 * zargs[0] = number of font or 0 to keep current font
539 void z_set_font (void)
544 * z_set_cursor, set the cursor position or turn the cursor on/off.
546 * zargs[0] = y-coordinate or -2/-1 for cursor on/off
547 * zargs[1] = x-coordinate
548 * zargs[2] = window (-3 is the current one, optional)
552 void z_set_cursor (void)
557 glk_window_move_cursor(gos_upper, curx - 1, cury - 1);
561 * z_set_text_style, set the style for text output.
563 * zargs[0] = style flags to set or 0 to reset text style
567 void z_set_text_style (void)
573 else if (zargs[0] != 0xf000) /* not tickle time */
574 curstyle |= zargs[0];
576 if (h_flags & FIXED_FONT_FLAG)
577 style = curstyle | FIXED_WIDTH_STYLE;
581 if (gos_linepending && gos_curwin == gos_linewin)
584 if (style & REVERSE_STYLE)
586 if (gos_curwin == gos_upper && gos_upper) {
587 glk_set_style(style_User1);
589 garglk_set_reversevideo(TRUE);
591 else if (style & FIXED_WIDTH_STYLE)
592 glk_set_style(style_Preformatted);
593 else if (style & BOLDFACE_STYLE && style & EMPHASIS_STYLE)
594 glk_set_style(style_Alert);
595 else if (style & BOLDFACE_STYLE)
596 glk_set_style(style_Subheader);
597 else if (style & EMPHASIS_STYLE)
598 glk_set_style(style_Emphasized);
600 glk_set_style(style_Normal);
603 garglk_set_reversevideo(FALSE);
608 * z_set_window, select the current window.
610 * zargs[0] = window to be selected (-3 is the current one)
614 void z_set_window (void)
618 if (gos_curwin == gos_lower)
619 lowerstyle = curstyle;
621 upperstyle = curstyle;
625 glk_set_window(gos_lower);
626 gos_curwin = gos_lower;
627 curstyle = lowerstyle;
632 glk_set_window(gos_upper);
633 gos_curwin = gos_upper;
634 curstyle = upperstyle;
638 enable_scripting = TRUE;
640 enable_scripting = FALSE;
644 * z_show_status, display the status line for V1 to V3 games.
650 static void pad_status_line (int column)
653 spaces = (h_screen_cols + 1 - curx) - column;
658 void z_show_status (void)
670 /* One V5 game (Wishbringer Solid Gold) contains this opcode by
671 accident, so just return if the version number does not fit */
676 /* Read all relevant global variables from the memory of the
677 Z-machine into local variables */
680 LOW_WORD (addr, global0)
682 LOW_WORD (addr, global1)
684 LOW_WORD (addr, global2)
686 /* Move to top of the status window, and print in reverse style. */
688 glk_set_window(gos_upper);
689 gos_curwin = gos_upper;
692 glk_window_move_cursor(gos_upper, 0, 0);
693 glk_set_style(style_User1);
695 /* If the screen width is below 55 characters then we have to use
696 the brief status line format */
698 if (h_screen_cols < 55)
701 /* Print the object description for the global variable 0 */
704 print_object (global0);
706 /* A header flag tells us whether we have to display the current
707 time or the score/moves information */
709 if (h_config & CONFIG_TIME) { /* print hours and minutes */
711 zword hours = (global1 + 11) % 12 + 1;
713 pad_status_line (brief ? 15 : 20);
715 print_string ("Time: ");
729 print_char ((global1 >= 12) ? 'p' : 'a');
732 } else { /* print score and moves */
734 pad_status_line (brief ? 15 : 30);
736 print_string (brief ? "S: " : "Score: ");
739 pad_status_line (brief ? 8 : 14);
741 print_string (brief ? "M: " : "Moves: ");
746 /* Pad the end of the status line with spaces */
750 /* Return to the lower window */
752 glk_set_window(gos_lower);
753 gos_curwin = gos_lower;
757 * z_split_window, split the screen into an upper (1) and lower (0) window.
759 * zargs[0] = height of upper window in screen units (V6) or #lines
763 void z_split_window (void)
765 split_window(zargs[0]);