1
/******************************************************************************
3
* Copyright (C) 2006-2009 by Tor Andersson. *
5
* This file is part of Gargoyle. *
7
* Gargoyle is free software; you can redistribute it and/or modify *
8
* it under the terms of the GNU General Public License as published by *
9
* the Free Software Foundation; either version 2 of the License, or *
10
* (at your option) any later version. *
12
* Gargoyle is distributed in the hope that it will be useful, *
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15
* GNU General Public License for more details. *
17
* You should have received a copy of the GNU General Public License *
18
* along with Gargoyle; if not, write to the Free Software *
19
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
21
*****************************************************************************/
23
/* screen.c - Generic screen manipulation
25
* Portions copyright (c) 1995-1997 Stefan Jokisch.
30
static unsigned char statusline[256];
31
static int oldstyle = 0;
32
static int curstyle = 0;
33
static int upperstyle = 0;
34
static int lowerstyle = 0;
41
/* To make the common code happy */
43
int os_char_width (zchar z)
48
int os_string_width (const zchar *s)
52
while ((c = *s++) != 0)
53
if (c == ZC_NEW_STYLE || c == ZC_NEW_FONT)
56
width += os_char_width(c);
60
void os_prepare_sample (int a)
62
glk_sound_load_hint(a, 1);
65
void os_finish_with_sample (int a)
67
glk_sound_load_hint(a, 0);
73
* Play the given sample at the given volume (ranging from 1 to 8 and
74
* 255 meaning a default volume). The sound is played once or several
75
* times in the background (255 meaning forever). In Z-code 3 the
76
* repeats value is always 0 and the number of repeats is taken from
77
* the sound file itself. The end_of_sound function is called as soon
78
* as the sound finishes.
82
void os_start_sample (int number, int volume, int repeats, zword eos)
88
gos_channel = glk_schannel_create(0);
95
case 1: vol = 0x02000; break;
96
case 2: vol = 0x04000; break;
97
case 3: vol = 0x06000; break;
98
case 4: vol = 0x08000; break;
99
case 5: vol = 0x0a000; break;
100
case 6: vol = 0x0c000; break;
101
case 7: vol = 0x0e000; break;
102
case 8: vol = 0x10000; break;
103
default: vol = 0x20000; break;
106
/* we dont do repeating or eos-callback for now... */
107
glk_schannel_play_ext(gos_channel, number, 1, 0);
108
glk_schannel_set_volume(gos_channel, vol);
111
void os_stop_sample (int a)
115
glk_schannel_stop(gos_channel);
118
void os_beep (int volume)
122
void gos_update_width(void)
127
glk_window_get_size(gos_upper, &width, NULL);
128
h_screen_cols = width;
129
SET_BYTE(H_SCREEN_COLS, width);
132
glk_window_move_cursor(gos_upper, 0, cury-1);
138
void gos_update_height(void)
144
glk_window_get_size(gos_upper, NULL, &height_upper);
145
glk_window_get_size(gos_lower, NULL, &height_lower);
146
h_screen_rows = height_upper + height_lower + 1;
147
SET_BYTE(H_SCREEN_ROWS, h_screen_rows);
151
void reset_status_ht(void)
156
glk_window_get_size(gos_upper, NULL, &height);
157
if (mach_status_ht != height)
158
glk_window_set_arrangement(
159
glk_window_get_parent(gos_upper),
160
winmethod_Above | winmethod_Fixed,
161
mach_status_ht, NULL);
165
void erase_window (int w)
168
glk_window_clear(gos_lower);
171
memset(statusline, ' ', sizeof statusline);
172
glk_window_clear(gos_upper);
178
void split_window (int lines)
182
/* The top line is always set for V1 to V3 games */
186
if (!lines || lines > curr_status_ht)
190
glk_window_get_size(gos_upper, NULL, &height);
192
glk_window_set_arrangement(
193
glk_window_get_parent(gos_upper),
194
winmethod_Above | winmethod_Fixed,
196
curr_status_ht = lines;
198
mach_status_ht = lines;
201
glk_window_move_cursor(gos_upper, 0, 0);
207
glk_window_clear(gos_upper);
210
void restart_screen (void)
218
* statusline overflowed the window size ... bad game!
219
* so ... split status text into regions, reformat and print anew.
222
void packspaces(unsigned char *src, unsigned char *dst)
239
void smartstatusline (void)
241
unsigned char packed[256];
242
unsigned char buf[256];
243
unsigned char *a, *b, *c, *d;
244
int roomlen, scorelen, scoreofs;
247
statusline[curx - 1] = 0; /* terminate! */
249
packspaces(statusline, packed);
250
//strcpy(packed, statusline);
251
len = strlen(packed);
258
while (b[0] != 0 && !(b[0] == ' ' && b[1] == ' '))
265
d = packed + len - 1;
266
while (d[0] == ' ' && d > c)
268
if (d[0] != ' ' && d[0] != 0)
273
//printf("smart '%s'\n", packed);
274
//printf("smart %d %d %d %d\n",a-packed,b-packed,c-packed,d-packed);
278
scoreofs = h_screen_cols - scorelen - 2;
279
if (scoreofs <= roomlen)
280
scoreofs = roomlen + 2;
282
memset(buf, ' ', h_screen_cols);
283
memcpy(buf + 1 + scoreofs, c, scorelen);
284
memcpy(buf + 1, a, roomlen);
285
//if (roomlen >= scoreofs)
286
// buf[roomlen + 1] = '|';
288
glk_window_move_cursor(gos_upper, 0, 0);
289
glk_set_style(style_User1);
290
glk_put_buffer(buf, h_screen_cols);
291
glk_window_move_cursor(gos_upper, cury - 1, curx - 1);
294
void screen_char (zchar c)
296
if (gos_linepending && (gos_curwin == gos_linewin))
298
gos_cancel_pending_line();
299
if (gos_curwin == gos_upper)
308
if (gos_upper && gos_curwin == gos_upper) {
309
if (cury > mach_status_ht) {
310
mach_status_ht = cury;
315
/* check fixed flag in header, game can change it at whim */
316
if (gos_curwin == gos_lower)
318
static int forcefix = -1;
319
int curfix = h_flags & FIXED_FONT_FLAG;
320
if (forcefix != curfix)
323
zargs[0] = 0xf000; /* tickle tickle! */
328
if (gos_upper && gos_curwin == gos_upper)
330
if (c == '\n' || c == ZC_RETURN) {
338
if (curx < sizeof statusline)
339
statusline[curx - 1] = c;
341
if (curx <= h_screen_cols)
350
if (curx > h_screen_cols) {
357
else if (gos_curwin == gos_lower)
361
else glk_put_char(c);
365
void screen_new_line (void)
370
void screen_word (const zchar *s)
373
while ((c = *s++) != 0)
374
if (c == ZC_NEW_FONT)
376
else if (c == ZC_NEW_STYLE)
382
void screen_mssg_on (void)
384
if (gos_curwin == gos_lower)
387
glk_set_style(style_Preformatted);
388
glk_put_string("\n ");
392
void screen_mssg_off (void)
394
if (gos_curwin == gos_lower)
405
* z_buffer_mode, turn text buffering on/off.
407
* zargs[0] = new text buffering flag (0 or 1)
411
void z_buffer_mode (void)
416
* z_erase_line, erase the line starting at the cursor position.
418
* zargs[0] = 1 + #units to erase (1 clears to the end of the line)
422
void z_erase_line (void)
426
if (gos_upper && gos_curwin == gos_upper)
428
for (i = 0; i < h_screen_cols + 1 - curx; i++)
430
glk_window_move_cursor(gos_curwin, curx - 1, cury - 1);
435
* z_erase_window, erase a window or the screen to background colour.
437
* zargs[0] = window (-3 current, -2 screen, -1 screen & unsplit)
441
void z_erase_window (void)
447
glk_set_window(gos_upper);
449
garglk_set_zcolors(curr_fg, curr_bg);
451
glk_window_clear(gos_upper);
452
glk_set_window(gos_curwin);
454
glk_window_clear(gos_lower);
459
glk_set_window(gos_upper);
461
garglk_set_zcolors(curr_fg, curr_bg);
463
glk_window_clear(gos_upper);
465
glk_window_clear(gos_lower);
467
glk_set_window(gos_lower);
468
gos_curwin = gos_lower;
471
glk_window_clear(gos_lower);
472
if (w == 1 && gos_upper)
473
glk_window_clear(gos_upper);
477
* z_get_cursor, write the cursor coordinates into a table.
479
* zargs[0] = address to write information to
483
void z_get_cursor (void)
485
storew ((zword) (zargs[0] + 0), cury);
486
storew ((zword) (zargs[0] + 2), curx);
490
* z_print_table, print ASCII text in a rectangular area.
492
* zargs[0] = address of text to be printed
493
* zargs[1] = width of rectangular area
494
* zargs[2] = height of rectangular area (optional)
495
* zargs[3] = number of char's to skip between lines (optional)
499
void z_print_table (void)
501
zword addr = zargs[0];
505
/* Supply default arguments */
512
/* Write text in width x height rectangle */
516
for (i = 0; i < zargs[2]; i++) {
523
for (j = 0; j < zargs[1]; j++) {
538
* z_set_colour, set the foreground and background colours.
540
* zargs[0] = foreground colour
541
* zargs[1] = background colour
542
* zargs[2] = window (-3 is the current one, optional)
546
void z_set_colour (void)
548
int zfore = zargs[0];
549
int zback = zargs[1];
552
if (!(zfore == 0 && zback == 0)) {
554
garglk_set_zcolors(zfore, zback);
563
* z_set_font, set the font for text output and store the previous font.
565
* zargs[0] = number of font or 0 to keep current font
569
void z_set_font (void)
574
* z_set_cursor, set the cursor position or turn the cursor on/off.
576
* zargs[0] = y-coordinate or -2/-1 for cursor on/off
577
* zargs[1] = x-coordinate
578
* zargs[2] = window (-3 is the current one, optional)
582
void z_set_cursor (void)
587
glk_window_move_cursor(gos_upper, curx - 1, cury - 1);
591
* z_set_text_style, set the style for text output.
593
* zargs[0] = style flags to set or 0 to reset text style
597
void z_set_text_style (void)
603
else if (zargs[0] != 0xf000) /* not tickle time */
604
curstyle |= zargs[0];
606
if (h_flags & FIXED_FONT_FLAG)
607
style = curstyle | FIXED_WIDTH_STYLE;
611
if (gos_linepending && gos_curwin == gos_linewin)
614
if (style & REVERSE_STYLE)
616
if (gos_curwin == gos_upper && gos_upper) {
617
glk_set_style(style_User1);
620
garglk_set_reversevideo(TRUE);
623
else if (style & FIXED_WIDTH_STYLE)
624
glk_set_style(style_Preformatted);
625
else if (style & BOLDFACE_STYLE && style & EMPHASIS_STYLE)
626
glk_set_style(style_Alert);
627
else if (style & BOLDFACE_STYLE)
628
glk_set_style(style_Subheader);
629
else if (style & EMPHASIS_STYLE)
630
glk_set_style(style_Emphasized);
632
glk_set_style(style_Normal);
636
garglk_set_reversevideo(FALSE);
642
* z_set_window, select the current window.
644
* zargs[0] = window to be selected (-3 is the current one)
648
void z_set_window (void)
652
if (gos_curwin == gos_lower)
653
lowerstyle = curstyle;
655
upperstyle = curstyle;
659
glk_set_window(gos_lower);
660
gos_curwin = gos_lower;
661
curstyle = lowerstyle;
666
glk_set_window(gos_upper);
667
gos_curwin = gos_upper;
668
curstyle = upperstyle;
672
enable_scripting = TRUE;
674
enable_scripting = FALSE;
678
* z_show_status, display the status line for V1 to V3 games.
684
static void pad_status_line (int column)
687
spaces = (h_screen_cols + 1 - curx) - column;
692
void z_show_status (void)
704
/* One V5 game (Wishbringer Solid Gold) contains this opcode by
705
accident, so just return if the version number does not fit */
710
/* Read all relevant global variables from the memory of the
711
Z-machine into local variables */
714
LOW_WORD (addr, global0)
716
LOW_WORD (addr, global1)
718
LOW_WORD (addr, global2)
720
/* Move to top of the status window, and print in reverse style. */
722
glk_set_window(gos_upper);
723
gos_curwin = gos_upper;
726
glk_window_move_cursor(gos_upper, 0, 0);
727
glk_set_style(style_User1);
729
/* If the screen width is below 55 characters then we have to use
730
the brief status line format */
732
if (h_screen_cols < 55)
735
/* Print the object description for the global variable 0 */
738
print_object (global0);
740
/* A header flag tells us whether we have to display the current
741
time or the score/moves information */
743
if (h_config & CONFIG_TIME) { /* print hours and minutes */
745
zword hours = (global1 + 11) % 12 + 1;
747
pad_status_line (brief ? 15 : 20);
749
print_string ("Time: ");
763
print_char ((global1 >= 12) ? 'p' : 'a');
766
} else { /* print score and moves */
768
pad_status_line (brief ? 15 : 30);
770
print_string (brief ? "S: " : "Score: ");
773
pad_status_line (brief ? 8 : 14);
775
print_string (brief ? "M: " : "Moves: ");
780
/* Pad the end of the status line with spaces */
784
/* Return to the lower window */
786
glk_set_window(gos_lower);
787
gos_curwin = gos_lower;
791
* z_split_window, split the screen into an upper (1) and lower (0) window.
793
* zargs[0] = height of upper window in screen units (V6) or #lines
797
void z_split_window (void)
799
split_window(zargs[0]);