X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=interpreters%2Ffrotz%2Fglkscreen.c;fp=interpreters%2Ffrotz%2Fglkscreen.c;h=4545124798ba85c4175cdf6a143968c32038d8f4;hb=b1f1dc50b22b30c4d7176e1ff7c0805e80fe0724;hp=0000000000000000000000000000000000000000;hpb=50176172d18ae72d019181725c5629d45d21c548;p=projects%2Fchimara%2Fchimara.git diff --git a/interpreters/frotz/glkscreen.c b/interpreters/frotz/glkscreen.c new file mode 100644 index 0000000..4545124 --- /dev/null +++ b/interpreters/frotz/glkscreen.c @@ -0,0 +1,767 @@ +/* screen.c - Generic screen manipulation + * + * Copyright (c) 2005 Tor Andersson -- Glk-ified and V6-disabled + * Copyright (c) 1995-1997 Stefan Jokisch + * + * This file is part of Frotz. + * + * Frotz is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Frotz is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "glkfrotz.h" + +static unsigned char statusline[256]; +static int oldstyle = 0; +static int curstyle = 0; +static int upperstyle = 0; +static int lowerstyle = 0; +static int cury = 1; +static int curx = 1; + +/* To make the common code happy */ + +int os_char_width (zchar z) +{ + return 1; +} + +int os_string_width (const zchar *s) +{ + int width = 0; + zchar c; + while ((c = *s++) != 0) + if (c == ZC_NEW_STYLE || c == ZC_NEW_FONT) + s++; + else + width += os_char_width(c); + return width; +} + +void os_prepare_sample (int a) +{ + glk_sound_load_hint(a, 1); +} + +void os_finish_with_sample (int a) +{ + glk_sound_load_hint(a, 0); +} + +/* + * os_start_sample + * + * Play the given sample at the given volume (ranging from 1 to 8 and + * 255 meaning a default volume). The sound is played once or several + * times in the background (255 meaning forever). In Z-code 3 the + * repeats value is always 0 and the number of repeats is taken from + * the sound file itself. The end_of_sound function is called as soon + * as the sound finishes. + * + */ + +void os_start_sample (int number, int volume, int repeats, zword eos) +{ + int vol; + + if (!gos_channel) + { + gos_channel = glk_schannel_create(0); + if (!gos_channel) + return; + } + + switch (volume) + { + case 1: vol = 0x02000; break; + case 2: vol = 0x04000; break; + case 3: vol = 0x06000; break; + case 4: vol = 0x08000; break; + case 5: vol = 0x0a000; break; + case 6: vol = 0x0c000; break; + case 7: vol = 0x0e000; break; + case 8: vol = 0x10000; break; + default: vol = 0x20000; break; + } + + /* we dont do repeating or eos-callback for now... */ + glk_schannel_play_ext(gos_channel, number, 1, 0); + glk_schannel_set_volume(gos_channel, vol); +} + +void os_stop_sample (int a) +{ + if (!gos_channel) + return; + glk_schannel_stop(gos_channel); +} + +void os_beep (int volume) +{ +} + +void gos_update_width(void) +{ + glui32 width; + if (gos_upper) + { + glk_window_get_size(gos_upper, &width, NULL); + h_screen_cols = width; + SET_BYTE(H_SCREEN_COLS, width); + if (curx > width) + { + glk_window_move_cursor(gos_upper, 0, cury-1); + curx = 1; + } + } +} + +void gos_update_height(void) +{ + glui32 height_upper; + glui32 height_lower; + if (gos_curwin) + { + glk_window_get_size(gos_upper, NULL, &height_upper); + glk_window_get_size(gos_lower, NULL, &height_lower); + h_screen_rows = height_upper + height_lower + 1; + SET_BYTE(H_SCREEN_ROWS, h_screen_rows); + } +} + +void reset_status_ht(void) +{ + glui32 height; + if (gos_upper) + { + glk_window_get_size(gos_upper, NULL, &height); + if (mach_status_ht != height) + glk_window_set_arrangement( + glk_window_get_parent(gos_upper), + winmethod_Above | winmethod_Fixed, + mach_status_ht, NULL); + } +} + +void erase_window (int w) +{ + if (w == 0) + glk_window_clear(gos_lower); + else if (gos_upper) + { + memset(statusline, ' ', sizeof statusline); + glk_window_clear(gos_upper); + reset_status_ht(); + curr_status_ht = 0; + } +} + +void split_window (int lines) +{ + if (!gos_upper) + return; + /* The top line is always set for V1 to V3 games */ + if (h_version < V4) + lines++; + + if (lines > curr_status_ht) + { + glui32 height; + + glk_window_get_size(gos_upper, NULL, &height); + if (lines != height) + glk_window_set_arrangement( + glk_window_get_parent(gos_upper), + winmethod_Above | winmethod_Fixed, + lines, NULL); + curr_status_ht = lines; + } + mach_status_ht = lines; + if (cury > lines) + { + glk_window_move_cursor(gos_upper, 0, 0); + curx = cury = 1; + } + gos_update_width(); + + if (h_version == V3) + glk_window_clear(gos_upper); +} + +void restart_screen (void) +{ + erase_window(0); + erase_window(1); + split_window(0); +} + +/* + * statusline overflowed the window size ... bad game! + * so ... split status text into regions, reformat and print anew. + */ + +void packspaces(unsigned char *src, unsigned char *dst) +{ + int killing = 0; + while (*src) + { + if (*src == ' ') + killing++; + else + killing = 0; + if (killing > 2) + src++; + else + *dst++ = *src++; + } + *dst = 0; +} + +void smartstatusline (void) +{ + unsigned char packed[256]; + unsigned char buf[256]; + unsigned char *a, *b, *c, *d; + int roomlen, scorelen, scoreofs; + int len; + + statusline[curx - 1] = 0; /* terminate! */ + + packspaces(statusline, packed); + //strcpy(packed, statusline); + len = strlen(packed); + + a = packed; + while (a[0] == ' ') + a ++; + + b = a; + while (b[0] != 0 && !(b[0] == ' ' && b[1] == ' ')) + b ++; + + c = b; + while (c[0] == ' ') + c ++; + + d = packed + len - 1; + while (d[0] == ' ' && d > c) + d --; + if (d[0] != ' ' && d[0] != 0) + d ++; + if (d < c) + d = c; + + //printf("smart '%s'\n", packed); + //printf("smart %d %d %d %d\n",a-packed,b-packed,c-packed,d-packed); + + roomlen = b - a; + scorelen = d - c; + scoreofs = h_screen_cols - scorelen - 2; + + memset(buf, ' ', h_screen_cols); + memcpy(buf + 1 + scoreofs, c, scorelen); + memcpy(buf + 1, a, roomlen); + if (roomlen >= scoreofs) + buf[roomlen + 1] = '|'; + + glk_window_move_cursor(gos_upper, 0, 0); + glk_set_style(style_User1); + glk_put_buffer(buf, h_screen_cols); + glk_window_move_cursor(gos_upper, cury - 1, curx - 1); +} + +void screen_char (zchar c) +{ + if (gos_linepending && (gos_curwin == gos_linewin)) + { + gos_cancel_pending_line(); + if (gos_curwin == gos_upper) + { + curx = 1; + cury ++; + } + if (c == '\n') + return; + } + + if (gos_upper && gos_curwin == gos_upper) { + if (cury > mach_status_ht) { + mach_status_ht = cury; + reset_status_ht(); + } + } + + /* check fixed flag in header, game can change it at whim */ + if (gos_curwin == gos_lower) + { + static int forcefix = -1; + int curfix = h_flags & FIXED_FONT_FLAG; + if (forcefix != curfix) + { + forcefix = curfix; + zargs[0] = 0xf000; /* tickle tickle! */ + z_set_text_style(); + } + } + + if (gos_upper && gos_curwin == gos_upper) + { + if (c == '\n' || c == ZC_RETURN) { + glk_put_char('\n'); + curx = 1; + cury ++; + } + else { + if (cury == 1) + { + if (curx < sizeof statusline) + statusline[curx - 1] = c; + curx++; + if (curx <= h_screen_cols) + glk_put_char(c); + else + smartstatusline(); + } + else + { + glk_put_char(c); + curx++; + if (curx > h_screen_cols) { + curx = 1; + cury++; + } + } + } + } + else if (gos_curwin == gos_lower) + { + if (c == ZC_RETURN) + glk_put_char('\n'); + else glk_put_char(c); + } +} + +void screen_new_line (void) +{ + screen_char('\n'); +} + +void screen_word (const zchar *s) +{ + zchar c; + while ((c = *s++) != 0) + if (c == ZC_NEW_FONT) + s++; + else if (c == ZC_NEW_STYLE) + s++; + else + screen_char (c); +} + +void screen_mssg_on (void) +{ + if (gos_curwin == gos_lower) + { + oldstyle = curstyle; + glk_set_style(style_Preformatted); + glk_put_string("\n "); + } +} + +void screen_mssg_off (void) +{ + if (gos_curwin == gos_lower) + { + glk_put_char('\n'); + zargs[0] = 0; + z_set_text_style(); + zargs[0] = oldstyle; + z_set_text_style(); + } +} + +/* + * z_buffer_mode, turn text buffering on/off. + * + * zargs[0] = new text buffering flag (0 or 1) + * + */ + +void z_buffer_mode (void) +{ +} + +/* + * z_erase_line, erase the line starting at the cursor position. + * + * zargs[0] = 1 + #units to erase (1 clears to the end of the line) + * + */ + +void z_erase_line (void) +{ + int i; + + if (gos_upper && gos_curwin == gos_upper) + { + for (i = 0; i < h_screen_cols + 1 - curx; i++) + glk_put_char(' '); + glk_window_move_cursor(gos_curwin, curx - 1, cury - 1); + } +} + +/* + * z_erase_window, erase a window or the screen to background colour. + * + * zargs[0] = window (-3 current, -2 screen, -1 screen & unsplit) + * + */ + +void z_erase_window (void) +{ + short w = zargs[0]; + if (w == -2) + { + if (gos_upper) + glk_window_clear(gos_upper); + glk_window_clear(gos_lower); + } + if (w == -1) + { + if (gos_upper) + glk_window_clear(gos_upper); + glk_window_clear(gos_lower); + split_window(0); + } + if (w == 0) + glk_window_clear(gos_lower); + if (w == 1 && gos_upper) + glk_window_clear(gos_upper); +} + +/* + * z_get_cursor, write the cursor coordinates into a table. + * + * zargs[0] = address to write information to + * + */ + +void z_get_cursor (void) +{ + storew ((zword) (zargs[0] + 0), cury); + storew ((zword) (zargs[0] + 2), curx); +} + +/* + * z_print_table, print ASCII text in a rectangular area. + * + * zargs[0] = address of text to be printed + * zargs[1] = width of rectangular area + * zargs[2] = height of rectangular area (optional) + * zargs[3] = number of char's to skip between lines (optional) + * + */ + +void z_print_table (void) +{ + zword addr = zargs[0]; + zword x; + int i, j; + + /* Supply default arguments */ + + if (zargc < 3) + zargs[2] = 1; + if (zargc < 4) + zargs[3] = 0; + + /* Write text in width x height rectangle */ + + x = curx; + + for (i = 0; i < zargs[2]; i++) { + + if (i != 0) { + cury += 1; + curx = x; + } + + for (j = 0; j < zargs[1]; j++) { + + zbyte c; + + LOW_BYTE (addr, c) + addr++; + + print_char (c); + } + + addr += zargs[3]; + } +} + +/* + * z_set_colour, set the foreground and background colours. + * + * zargs[0] = foreground colour + * zargs[1] = background colour + * zargs[2] = window (-3 is the current one, optional) + * + */ + +void z_set_colour (void) +{ + int zfore = zargs[0]; + int zback = zargs[1]; + + if (!(zfore == 0 && zback == 0)) + garglk_set_zcolors(zfore, zback); +} + +/* + * z_set_font, set the font for text output and store the previous font. + * + * zargs[0] = number of font or 0 to keep current font + * + */ + +void z_set_font (void) +{ +} + +/* + * z_set_cursor, set the cursor position or turn the cursor on/off. + * + * zargs[0] = y-coordinate or -2/-1 for cursor on/off + * zargs[1] = x-coordinate + * zargs[2] = window (-3 is the current one, optional) + * + */ + +void z_set_cursor (void) +{ + cury = zargs[0]; + curx = zargs[1]; + if (gos_upper) + glk_window_move_cursor(gos_upper, curx - 1, cury - 1); +} + +/* + * z_set_text_style, set the style for text output. + * + * zargs[0] = style flags to set or 0 to reset text style + * + */ + +void z_set_text_style (void) +{ + int style; + + if (zargs[0] == 0) + curstyle = 0; + else if (zargs[0] != 0xf000) /* not tickle time */ + curstyle |= zargs[0]; + + if (h_flags & FIXED_FONT_FLAG) + style = curstyle | FIXED_WIDTH_STYLE; + else + style = curstyle; + + if (gos_linepending && gos_curwin == gos_linewin) + return; + + if (style & REVERSE_STYLE) + { + if (gos_curwin == gos_upper && gos_upper) { + glk_set_style(style_User1); + } + garglk_set_reversevideo(TRUE); + } + else if (style & FIXED_WIDTH_STYLE) + glk_set_style(style_Preformatted); + else if (style & BOLDFACE_STYLE && style & EMPHASIS_STYLE) + glk_set_style(style_Alert); + else if (style & BOLDFACE_STYLE) + glk_set_style(style_Subheader); + else if (style & EMPHASIS_STYLE) + glk_set_style(style_Emphasized); + else + glk_set_style(style_Normal); + + if (curstyle == 0) { + garglk_set_reversevideo(FALSE); + } +} + +/* + * z_set_window, select the current window. + * + * zargs[0] = window to be selected (-3 is the current one) + * + */ + +void z_set_window (void) +{ + int win = zargs[0]; + + if (gos_curwin == gos_lower) + lowerstyle = curstyle; + else + upperstyle = curstyle; + + if (win == 0) + { + glk_set_window(gos_lower); + gos_curwin = gos_lower; + curstyle = lowerstyle; + } + else + { + if (gos_upper) + glk_set_window(gos_upper); + gos_curwin = gos_upper; + curstyle = upperstyle; + } + + if (win == 0) + enable_scripting = TRUE; + else + enable_scripting = FALSE; +} + +/* + * z_show_status, display the status line for V1 to V3 games. + * + * no zargs used + * + */ + +static void pad_status_line (int column) +{ + int spaces; + spaces = (h_screen_cols + 1 - curx) - column; + while (spaces-- > 0) + print_char(' '); +} + +void z_show_status (void) +{ + zword global0; + zword global1; + zword global2; + zword addr; + + bool brief = FALSE; + + if (!gos_upper) + return; + + /* One V5 game (Wishbringer Solid Gold) contains this opcode by + accident, so just return if the version number does not fit */ + + if (h_version >= V4) + return; + + /* Read all relevant global variables from the memory of the + Z-machine into local variables */ + + addr = h_globals; + LOW_WORD (addr, global0) + addr += 2; + LOW_WORD (addr, global1) + addr += 2; + LOW_WORD (addr, global2) + + /* Move to top of the status window, and print in reverse style. */ + + glk_set_window(gos_upper); + gos_curwin = gos_upper; + + curx = cury = 1; + glk_window_move_cursor(gos_upper, 0, 0); + glk_set_style(style_User1); + + /* If the screen width is below 55 characters then we have to use + the brief status line format */ + + if (h_screen_cols < 55) + brief = TRUE; + + /* Print the object description for the global variable 0 */ + + print_char (' '); + print_object (global0); + + /* A header flag tells us whether we have to display the current + time or the score/moves information */ + + if (h_config & CONFIG_TIME) { /* print hours and minutes */ + + zword hours = (global1 + 11) % 12 + 1; + + pad_status_line (brief ? 15 : 20); + + print_string ("Time: "); + + if (hours < 10) + print_char (' '); + print_num (hours); + + print_char (':'); + + if (global2 < 10) + print_char ('0'); + print_num (global2); + + print_char (' '); + + print_char ((global1 >= 12) ? 'p' : 'a'); + print_char ('m'); + + } else { /* print score and moves */ + + pad_status_line (brief ? 15 : 30); + + print_string (brief ? "S: " : "Score: "); + print_num (global1); + + pad_status_line (brief ? 8 : 14); + + print_string (brief ? "M: " : "Moves: "); + print_num (global2); + + } + + /* Pad the end of the status line with spaces */ + + pad_status_line (0); + + /* Return to the lower window */ + + glk_set_window(gos_lower); + gos_curwin = gos_lower; +} + +/* + * z_split_window, split the screen into an upper (1) and lower (0) window. + * + * zargs[0] = height of upper window in screen units (V6) or #lines + * + */ + +void z_split_window (void) +{ + split_window(zargs[0]); +} +