1
/* Nitfol - z-machine interpreter using Glk for output.
2
Copyright (C) 1999 Evin Robertson
4
This program is free software; you can redistribute it and/or modify
5
it under the terms of the GNU General Public License as published by
6
the Free Software Foundation; either version 2 of the License, or
7
(at your option) any later version.
9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
GNU General Public License for more details.
14
You should have received a copy of the GNU General Public License
15
along with this program; if not, write to the Free Software
16
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
18
The author can be reached at nitfol@deja.com
25
typedef struct z_window *zwinid;
29
BOOL is_fixed; /* If we are forcing output to be fixed-width */
32
/* descriptions of z-machine colors, in glk 0x00rrggbb style.
33
-1 means to call glk_stylehint_clear instead of glk_stylehint_set.
34
The 'current color' will be overwritten on calls to set_colour.
36
Go ahead and customize these (making background colors lighter than
37
foreground colors might be interesting)
40
glsi32 bgcolortable[] = {
41
-1L, /* current color */
42
-1L, /* defualt setting */
43
0x00000000L, /* black */
44
0x00ff0000L, /* red */
45
0x00008000L, /* green */
46
0x00ffff00L, /* yellow */
47
0x000000ffL, /* blue */
48
0x00ff00ffL, /* magenta */
49
0x0000ffffL, /* cyan */
50
0x00ffffffL, /* white */
51
0x00c0c0c0L, /* light grey */
52
0x00808080L, /* medium grey */
53
0x00404040L /* dark grey */
56
glsi32 fgcolortable[] = {
57
-1L, /* current color */
58
-1L, /* defualt setting */
59
0x00000000L, /* black */
60
0x00ff0000L, /* red */
61
0x00008000L, /* green */
62
0x00ffff00L, /* yellow */
63
0x000000ffL, /* blue */
64
0x00ff00ffL, /* magenta */
65
0x0000ffffL, /* cyan */
66
0x00ffffffL, /* white */
67
0x00c0c0c0L, /* light grey */
68
0x00808080L, /* medium grey */
69
0x00404040L /* dark grey */
73
static void killglkwithcolor(glui32 styl, int fore, int back)
75
if(fgcolortable[fore] == -1)
76
glk_stylehint_clear(wintype_AllTypes, styl,
79
glk_stylehint_set(wintype_AllTypes, styl,
80
stylehint_TextColor, fgcolortable[fore]);
82
if(bgcolortable[back] == -1)
83
glk_stylehint_clear(wintype_AllTypes, styl,
86
glk_stylehint_set(wintype_AllTypes, styl,
87
stylehint_BackColor, bgcolortable[back]);
91
static void set_stylehints(char fore, char back)
94
for(n = 0; n < style_NUMSTYLES; n++)
95
killglkwithcolor(n, fore, back);
97
/* Subheader will be used for bold */
98
glk_stylehint_set(wintype_TextBuffer, style_Subheader,
101
/* BlockQuote will be used for reverse proportional text */
102
glk_stylehint_set(wintype_TextBuffer, style_BlockQuote,
103
stylehint_Proportional, 0);
104
glk_stylehint_set(wintype_TextBuffer, style_BlockQuote,
105
stylehint_Justification, stylehint_just_Centered);
106
#ifdef stylehint_ReverseColor
107
glk_stylehint_set(wintype_TextBuffer, style_BlockQuote,
108
stylehint_ReverseColor, 1);
111
/* User1 will be used for bold italics */
112
glk_stylehint_set(wintype_TextBuffer, style_User1,
113
stylehint_Weight, 1);
114
glk_stylehint_set(wintype_TextBuffer, style_User1,
115
stylehint_Oblique, 1);
117
/* User2 will be used for proportional bold/italic */
118
glk_stylehint_set(wintype_TextBuffer, style_User2,
119
stylehint_Proportional, 0);
120
glk_stylehint_set(wintype_TextBuffer, style_User2,
121
stylehint_Weight, 1);
131
static glui32 bitmap_to_style[16] = {
133
style_Subheader, /* sBOLD */
134
style_Emphasized, /* sITAL */
135
style_User1, /* sBOLD | sITAL */
136
style_Preformatted,/* sFIXE */
137
style_User2, /* sFIXE | sBOLD */
138
style_User2, /* sFIXE | sITAL */
139
style_User2, /* sFIXE | sBOLD | sITAL*/
140
style_BlockQuote, /* sREVE */
141
style_BlockQuote, /* sREVE | sBOLD */
142
style_BlockQuote, /* sREVE | sITAL */
143
style_BlockQuote, /* sREVE | sBOLD | sITAL */
144
style_BlockQuote, /* sFIXE | sREVE */
145
style_BlockQuote, /* sFIXE | sREVE | sBOLD */
146
style_BlockQuote, /* sFIXE | sREVE | sITAL */
147
style_BlockQuote /* sFIXE | sREVE | sBOLD | sITAL */
152
static glui32 bitmap_to_style[16] = {
154
style_Subheader, /* sBOLD */
155
style_Emphasized, /* sITAL */
156
style_Subheader, /* sBOLD | sITAL */
157
style_Preformatted,/* sFIXE */
158
style_Subheader, /* sFIXE | sBOLD */
159
style_Emphasized, /* sFIXE | sITAL */
160
style_Subheader, /* sFIXE | sBOLD | sITAL*/
162
style_Subheader, /* sBOLD */
163
style_Emphasized, /* sITAL */
164
style_Subheader, /* sBOLD | sITAL */
165
style_Preformatted,/* sFIXE */
166
style_Subheader, /* sFIXE | sBOLD */
167
style_Emphasized, /* sFIXE | sITAL */
168
style_Subheader /* sFIXE | sBOLD | sITAL*/
182
glui32 width, height;
194
BOOL glk_input_pending;
195
glui32 pending_input_type;
196
glui32 pending_input_length;
198
/* for upper window of v3 - returns # of lines drawn */
199
glui32 (*draw_callback)(winid_t win, glui32 width, glui32 height);
200
BOOL (*mouse_callback)(BOOL is_char_event, winid_t win, glui32 x, glui32 y);
202
glui32 width, height;
203
glui32 x1, y1, x2, y2;
205
glui32 last_height; /* What the height was last time we got input */
206
glui32 biggest_height;/* The biggest it's been since */
208
glui32 curr_offset; /* offset into text_buffer/color_buffer */
209
glui32 max_offset; /* curr_offset must stay < max_offset */
210
glui32 buffer_size; /* max_offset must stay < buffer_size */
212
BOOL dirty; /* Has window been changed since last redraw? */
213
BOOL defined; /* Is our location well defined? */
215
unsigned char *text_buffer; /* whole window for grid, current line for buffer */
216
colorstyle *color_buffer;
225
#define num_z_windows 16
227
static struct z_window game_windows[num_z_windows];
229
static glui32 upper_width, upper_height;
232
static int waitforinput(zwinid window, glui32 *val,
233
BOOL (*timer_callback)(zword), zword timer_arg);
236
void set_glk_stream_current(void)
238
z_flush_text(&game_windows[0]);
239
glk_stream_set_current(game_windows[0].str);
242
void draw_intext_picture(zwinid window, glui32 picture, glui32 alignment)
244
z_flush_text(window);
245
wrap_glk_image_draw(window->win, picture, alignment, 0);
248
void draw_picture(zwinid window, glui32 picture, glui32 x, glui32 y)
252
glui32 width, height;
254
wrap_glk_image_get_info(operand[0], &width, &height);
256
for(i = 0; i < 12; i++) {
257
if(is_in_bounds(window->images[i].x, window->images[i].y,
258
window->images[i].width, window->images[i].height,
259
x, y, width, height))
263
for(i = 0; i < 12; i++)
264
if(window->images[i].image_num == 0)
269
window->images[useimage].image_num = picture;
270
window->images[useimage].x = x;
271
window->images[useimage].y = y;
272
window->images[useimage].width = width;
273
window->images[useimage].height = height;
277
static int showstuffcount = 0;
279
/* Show an interpreter message */
280
void showstuff(const char *title, const char *type, const char *message, offset number)
282
static BOOL loopy = FALSE;
285
n_show_fatal(E_SYSTEM, "loopy message reporting", 0);
289
z_pause_timed_input(&game_windows[0]);
290
z_flush_text(&game_windows[0]);
291
glk_stream_set_current(game_windows[0].str);
293
glk_set_style(style_Alert);
294
w_glk_put_string("\n[");
295
w_glk_put_string(title);
296
w_glk_put_string(": ");
297
w_glk_put_string(type);
298
w_glk_put_string("]: ");
299
w_glk_put_string(message);
300
w_glk_put_string(" (");
301
g_print_snumber(number);
302
w_glk_put_string(") ");
305
infix_gprint_loc(stack_get_depth(), 0);
307
w_glk_put_string("PC=");
308
g_print_number(oldPC);
312
if(++showstuffcount == 100) {
313
w_glk_put_string("[pausing every 100 errors]\n");
314
z_wait_for_key(&game_windows[0]);
317
glk_set_style(style_Normal);
323
void init_lower(zwinid *lower)
325
zwinid lower_win = &game_windows[0];
332
z_pause_timed_input(lower_win);
333
glk_window_close(lower_win->win, NULL);
336
set_stylehints(lower_win->current.fore,
337
lower_win->current.back);
340
lower_win->dirty = TRUE;
341
lower_win->wintype = wintype_TextBuffer;
342
lower_win->method = winmethod_Below;
343
lower_win->curr_offset = 0;
344
lower_win->max_offset = 80 * 24;
345
lower_win->buffer_size = lower_win->max_offset;
347
if(!lower_win->text_buffer)
348
lower_win->text_buffer = (unsigned char *) n_malloc(lower_win->buffer_size);
349
if(!lower_win->color_buffer)
350
lower_win->color_buffer = (colorstyle *) n_malloc(lower_win->buffer_size * sizeof(colorstyle));
352
for(i = 0; i < lower_win->buffer_size; i++) {
353
lower_win->text_buffer[i] = ' ';
354
lower_win->color_buffer[i] = lower_win->current;
357
lower_win->actual = lower_win->current;
359
lower_win->win = glk_window_open(game_windows[1].win,
360
winmethod_Below | winmethod_Proportional,
361
50, /* Percent doesn't matter */
362
wintype_TextBuffer, 0);
365
if(lower_win->win == 0) {
366
n_show_fatal(E_OUTPUT, "cannot open lower window", 0);
370
lower_win->str = glk_window_get_stream(lower_win->win);
372
if(lower_win->transcript)
373
glk_window_set_echo_stream(lower_win->win, lower_win->transcript);
377
void init_upper(zwinid *upper)
379
zwinid upper_win = &game_windows[1];
386
z_pause_timed_input(upper_win);
387
glk_window_close(upper_win->win, NULL);
390
upper_win->dirty = TRUE;
391
upper_win->wintype = wintype_TextGrid;
392
upper_win->method = winmethod_Above | winmethod_Fixed;
393
upper_win->height = 0;
394
upper_win->width = upper_width;
395
upper_win->curr_offset = 0;
396
upper_win->max_offset = upper_height * upper_width;
397
upper_win->buffer_size = upper_win->max_offset;
399
if(!upper_win->text_buffer)
400
upper_win->text_buffer = (unsigned char *) n_malloc(upper_win->buffer_size);
401
if(!upper_win->color_buffer)
402
upper_win->color_buffer = (colorstyle *) n_malloc(upper_win->buffer_size * sizeof(colorstyle));
404
for(i = 0; i < upper_win->buffer_size; i++) {
405
upper_win->text_buffer[i] = ' ';
406
upper_win->color_buffer[i] = upper_win->current;
409
upper_win->actual = upper_win->current;
411
upper_win->win = glk_window_open(game_windows[0].win,
412
winmethod_Above | winmethod_Fixed,
413
1, /* XXX huh? upper_height, */
414
wintype_TextGrid, 1);
416
if(upper_win->win == 0) {
421
upper_win->str = glk_window_get_stream(upper_win->win);
423
if(upper_win->str == 0) {
424
glk_window_close(upper_win->win, NULL);
431
void z_init_windows(BOOL dofixed,
432
glui32 (*draw_callback)(winid_t, glui32, glui32),
433
BOOL (*mouse_callback)(BOOL, winid_t, glui32, glui32),
434
glui32 maxwidth, glui32 maxheight,
435
zwinid *upper, zwinid *lower)
437
colorstyle defaultstyle;
438
defaultstyle.fore = 1; defaultstyle.back = 1; defaultstyle.style = 0;
444
upper_width = maxwidth; upper_height = maxheight;
446
game_windows[0].current = game_windows[1].current = defaultstyle;
447
game_windows[1].draw_callback = draw_callback;
448
game_windows[1].mouse_callback = mouse_callback;
455
zwinid z_split_screen(glui32 wintype, glui32 method,
456
glui32 (*draw_callback)(winid_t, glui32, glui32),
457
BOOL (*mouse_callback)(BOOL, winid_t, glui32, glui32))
460
for(i = 0; i < num_z_windows; i++) {
461
if(!game_windows[i].win) {
462
winid_t root = glk_window_get_root();
463
game_windows[i].win = glk_window_open(root, method, 0, wintype, 0);
464
game_windows[i].str = glk_window_get_stream(game_windows[i].win);
465
game_windows[i].wintype = wintype;
466
game_windows[i].method = method;
467
game_windows[i].transcript = NULL;
468
game_windows[i].glk_input_pending = FALSE;
469
game_windows[i].draw_callback = draw_callback;
470
game_windows[i].mouse_callback = mouse_callback;
471
game_windows[i].width = 0;
472
game_windows[i].height = 0;
473
game_windows[i].curr_offset = 0;
474
game_windows[i].max_offset = 1;
475
game_windows[i].buffer_size = 2;
476
game_windows[i].text_buffer = n_malloc(2);
477
game_windows[i].color_buffer = n_malloc(2);
478
game_windows[i].dirty = TRUE;
479
return &game_windows[i];
486
void z_kill_window(zwinid win)
490
n_free(win->text_buffer);
491
win->text_buffer = NULL;
492
n_free(win->color_buffer);
493
win->color_buffer = NULL;
494
win->transcript = NULL;
495
glk_window_close(win->win, NULL);
501
/* close any open windows */
502
void kill_windows(void)
506
for(i = 0; i < num_z_windows; i++)
507
z_clear_window(&game_windows[i]);
510
for(i = 0; i < num_z_windows; i++) {
511
if(game_windows[i].win) {
512
game_windows[i].transcript = NULL;
514
glk_window_close(game_windows[i].win, NULL);
515
game_windows[i].win = NULL;
516
game_windows[i].str = NULL;
523
/* free memory space used by windows, but don't close them */
524
void free_windows(void)
528
z_flush_all_windows();
530
for(i = 0; i < num_z_windows; i++) {
531
if(game_windows[i].win) {
532
n_free(game_windows[i].text_buffer);
533
game_windows[i].text_buffer = NULL;
535
n_free(game_windows[i].color_buffer);
536
game_windows[i].color_buffer = NULL;
541
zwinid z_find_win(winid_t win)
544
for(i = 0; i < num_z_windows; i++) {
545
if(game_windows[i].win == win)
546
return &game_windows[i];
552
static BOOL coloreq(colorstyle a, colorstyle b) /* return true if colors are equivalent */
554
return (fgcolortable[(int) a.fore] == fgcolortable[(int) b.fore]) &&
555
(bgcolortable[(int) a.back] == bgcolortable[(int) b.back]);
559
static void checkforblockquote(zwinid window, zwinid dest_win)
561
if(window->biggest_height > window->last_height &&
562
window->biggest_height > window->height) {
563
/* find borders of the blockquote */
564
unsigned leftx = window->width, rightx = 0;
565
unsigned topy = window->biggest_height;
566
unsigned bottomy = window->height;
569
i = window->height * window->width;
570
for(y = window->height; y < window->biggest_height; y++)
571
for(x = 0; x < window->width; x++)
572
if(window->text_buffer[i++] != ' ') {
583
z_pause_timed_input(dest_win);
584
glk_stream_set_current(game_windows[1].str);
586
glk_set_style(style_BlockQuote);
588
/* draw the blockquote */
589
for(y = topy; y <= bottomy; y++) {
590
i = y * window->width + leftx;
591
for(x = leftx; x <= rightx; x++)
592
glk_put_char(window->text_buffer[i++]);
599
void z_pause_timed_input(zwinid window)
602
if(window->glk_input_pending) {
603
window->glk_input_pending = FALSE;
605
switch(window->pending_input_type) {
606
case evtype_CharInput:
607
glk_cancel_char_event(window->win);
609
case evtype_LineInput:
610
glk_cancel_line_event(window->win, &eep);
611
window->pending_input_length = eep.val1;
617
void z_flush_all_windows(void)
620
for(window = 0; window < num_z_windows; window++) {
621
if(game_windows[window].dirty) {
622
z_pause_timed_input(&game_windows[window]);
624
switch(game_windows[window].wintype) {
625
case wintype_TextBuffer:
626
z_flush_text(&game_windows[window]);
628
case wintype_TextGrid:
629
z_flush_fixed(&game_windows[window]);
631
case wintype_Graphics:
632
z_flush_graphics(&game_windows[window]);
639
void z_draw_all_windows(void)
642
for(window = 0; window < num_z_windows; window++) {
643
if(game_windows[window].wintype == wintype_TextGrid) {
644
game_windows[window].dirty = TRUE;
645
z_flush_fixed(&game_windows[window]);
651
static void z_put_styled_string(zwinid window, unsigned char *text,
652
colorstyle *color, glui32 length)
655
colorstyle laststyle = color[0];
660
glk_set_style_stream(window->str, bitmap_to_style[laststyle.style]);
662
for(n = 0; n < length; n++) {
663
if(color[n].style != laststyle.style)
664
glk_set_style_stream(window->str, bitmap_to_style[color[n].style]);
665
glk_put_char_stream(window->str, text[n]);
666
laststyle = color[n];
670
void z_flush_fixed(zwinid window)
674
glui32 start_line, end_line;
676
/* If there's no such window, give up */
677
if(!window->win || !window->str ||
678
!window->text_buffer || !window->color_buffer)
681
/* glk doesn't allow writing to a window while input is pending */
682
z_pause_timed_input(window);
684
end_line = window->height;
686
/* Has the window grown and shrunk? If so, probably because someone wants
687
to draw a box quote - don't let them shrink the window quite so fast */
688
if(window->biggest_height > window->last_height &&
689
window->biggest_height > window->height)
690
end_line = window->biggest_height;
692
/* For v3 games, there's a callback function to draw the room name and
693
score; if this is present, we start drawing at a lower position */
695
if(window->draw_callback)
696
start_line = window->draw_callback(NULL, 0, 0);
697
end_line += start_line;
699
o = glk_window_get_parent(window->win);
701
glk_window_get_size(window->win, &winx, &winy);
702
if (!(window->method & winmethod_Above || window->method & winmethod_Below)
704
glk_window_set_arrangement(o, window->method,
705
end_line, window->win);
707
glk_window_set_arrangement(o, window->method, end_line, window->win);
709
glk_window_get_size(window->win, &winx, &winy);
711
if(window->draw_callback) {
712
glk_stream_set_current(window->str);
713
glk_window_clear(window->win);
714
glk_window_move_cursor(window->win, 0, 0);
715
window->draw_callback(window->win, winx, winy);
718
if(end_line > start_line && window->dirty) {
720
unsigned padleft = 0, padmiddle = 0, padright = 0;
721
unsigned skipleft = 0, skipmiddle = 0, skipright = 0;
723
unsigned firstwidth, lastwidth;
727
/* Calculate how much space is used for margins */
729
unsigned left_margin = window->width, right_margin = window->width;
732
for(y = start_line; y < end_line; y++) {
734
for(x = 0; x < window->width; x++)
735
if(window->text_buffer[i + x] != ' ') {
741
for(x = 0; x < window->width; x++)
742
if(window->text_buffer[i + window->width - x - 1] != ' ') {
751
firstwidth = window->width; lastwidth = 0;
753
if(start_line + 1 == end_line) {
754
unsigned longestx = 0;
755
unsigned longestlen = 0;
757
unsigned thislen = 0;
758
colorstyle lastcolor;
759
width = window->width;
761
for(x = skipleft; x < width + skipleft; x++) {
762
if(window->text_buffer[x] == ' '
763
&& (!thislen || coloreq(window->color_buffer[x], lastcolor))) {
767
lastcolor = window->color_buffer[x];
769
if(thislen > longestlen) {
771
longestlen = thislen;
777
firstwidth = longestx - skipleft;
778
skipmiddle = longestlen - 1;
779
lastwidth = width - firstwidth - skipmiddle;
783
if(skipmiddle && winx < firstwidth + 2 + lastwidth)
786
if(lastwidth && winx >= firstwidth + padmiddle + lastwidth) {
787
padmiddle = winx - firstwidth - lastwidth;
789
if(winx >= window->width)
790
width = window->width;
794
if(right_margin + left_margin) {
795
if(winx > window->width)
796
padleft = (unsigned) ((winx - window->width) * (((float) left_margin) / (right_margin + left_margin)));
798
skipleft = (unsigned) ((window->width - winx) * (((float) left_margin) / (right_margin + left_margin)));
801
padleft = winx - window->width;
804
if(skipleft > left_margin)
805
skipleft = left_margin;
807
if(winx > window->width)
808
padright = winx - window->width - padleft;
810
skipright = window->width - winx - skipleft;
812
if(width < firstwidth + padmiddle) {
816
} else if(width < firstwidth + padmiddle + lastwidth) {
817
lastwidth = width - firstwidth - padmiddle;
822
glk_stream_set_current(window->str);
823
glk_window_move_cursor(window->win, 0, start_line);
825
/* draw to the upper window */
827
for(y = start_line; y < end_line; y++) {
829
for(x = 0; x < padleft; x++)
834
z_put_styled_string(window, window->text_buffer + i,
835
window->color_buffer + i, firstwidth);
838
for(x = 0; x < padmiddle; x++)
842
z_put_styled_string(window, window->text_buffer + i,
843
window->color_buffer + i, lastwidth);
846
for(x = 0; x < padright; x++)
852
/* Bureaucracy needs the cursor positioned and visible in upper window. */
853
glk_window_move_cursor(window->win,
854
window->curr_offset % window->width,
855
window->curr_offset / window->width);
856
window->dirty = FALSE;
861
void z_flush_text(zwinid window)
863
z_pause_timed_input(window);
865
if(!window->win || !window->str
866
|| !window->text_buffer || !window->color_buffer
867
|| window->curr_offset == 0) {
868
window->curr_offset = 0;
872
z_put_styled_string(window, window->text_buffer, window->color_buffer,
873
window->curr_offset);
875
window->curr_offset = 0;
876
window->dirty = FALSE;
880
void z_flush_graphics(zwinid window)
884
float xratio, yratio;
890
glk_window_get_size(window->win, &winx, &winy);
891
xratio = ((float) winx) / window->width;
892
yratio = ((float) winy) / window->height;
894
parent = glk_window_get_parent(window->win);
896
/* We want the window to maintain its original height/width ratio */
897
switch(window->method & winmethod_DirMask) {
898
case winmethod_Left: /* Left and right splits mean height is fixed - */
899
case winmethod_Right: /* adjust width to the yratio */
900
glk_window_set_arrangement(parent, window->method,
901
(glui32) (window->width * yratio), 0);
903
case winmethod_Above: /* Above and below splits mean width is fixed - */
904
case winmethod_Below: /* adjust height to the xratio */
905
glk_window_set_arrangement(parent, window->method,
906
(glui32) (window->height * xratio), 0);
910
/* Check to see what it became, and if it's still off, don't worry */
911
glk_window_get_size(window->win, &winx, &winy);
912
xratio = ((float) winx) / window->width;
913
yratio = ((float) winy) / window->height;
915
for(i = 0; i < 12; i++) {
916
if(window->images[i].image_num) {
917
wrap_glk_image_draw_scaled(window->win, window->images[i].image_num,
918
(glui32) (window->images[i].x * xratio),
919
(glui32) (window->images[i].y * yratio),
920
(glui32) (window->images[i].width * xratio),
921
(glui32) (window->images[i].height * yratio));
926
void z_print_number(zwinid window, int number)
930
int length = n_to_decimal(buffer, number);
932
for(i = length - 1; i >= 0; i--)
933
z_put_char(window, buffer[i]);
936
void z_put_char(zwinid window, unsigned c)
938
colorstyle color = window->current;
940
color.style |= sFIXE;
942
if(c == 0) /* Section 3.8.2.1 */
945
window->dirty = TRUE;
947
if((c < 32 && c != 13) || (c >= 127 && c <= 159)) { /*Undefined in latin-1*/
948
switch(window->wintype) {
949
case wintype_TextBuffer:
950
z_put_char(window, '[');
951
z_print_number(window, c);
952
z_put_char(window, ']');
954
case wintype_TextGrid:
961
z_put_char(window, 'O');
965
z_put_char(window, 'o');
971
if(c > 255) /* Section 3.8.5.4.3 */
974
if(c == 13) { /* Section 7.1.2.2.1 */
975
switch(window->wintype) {
976
case wintype_TextBuffer:
977
window->text_buffer[window->curr_offset] = 10;
978
window->curr_offset++;
979
z_flush_text(window);
981
case wintype_TextGrid:
982
window->curr_offset += window->width;
983
window->curr_offset -= window->curr_offset % window->width;
986
window->text_buffer[window->curr_offset] = c;
987
window->color_buffer[window->curr_offset] = color;
988
window->curr_offset++;
991
if(window->curr_offset >= window->max_offset) {
992
switch(window->wintype) {
993
case wintype_TextBuffer:
994
z_flush_text(window);
996
case wintype_TextGrid:
997
if(!window->defined) /* Section 8.6.2 */
998
n_show_port(E_OUTPUT, "writing past end of window", c);
1000
if(window->max_offset)
1001
window->curr_offset = window->max_offset - 1;
1003
window->curr_offset = 0;
1005
window->defined = FALSE;
1010
void z_setxy(zwinid window, zword x, zword y)
1012
window->curr_offset = (y - 1) * window->width + (x - 1);
1013
window->defined = TRUE;
1016
void z_getxy(zwinid window, zword *x, zword *y)
1019
*x = (window->curr_offset % window->width) + 1;
1020
*y = (window->curr_offset / window->width) + 1;
1022
*x = window->curr_offset + 1;
1027
void z_getsize(zwinid window, unsigned *width, unsigned *height)
1029
*width = window->width;
1030
*height = window->height;
1033
void z_find_size(glui32 *wid, glui32 *hei)
1035
glui32 oldwid, oldhei;
1036
zwinid upper = &game_windows[1];
1037
winid_t o = glk_window_get_parent(upper->win);
1038
glk_window_get_size(upper->win, &oldwid, &oldhei);
1039
glk_window_set_arrangement(o, (upper->method & ~winmethod_Fixed) |
1040
winmethod_Proportional, 100, upper->win);
1041
glk_window_get_size(upper->win, wid, hei);
1042
glk_window_set_arrangement(o, upper->method, oldhei, upper->win);
1044
upper_width = *wid; upper_height = *hei;
1048
void z_set_height(zwinid window, unsigned height)
1051
if(height * window->width > window->buffer_size) {
1052
n_show_error(E_OUTPUT, "height too large", height);
1056
window->height = height;
1057
if(height > window->biggest_height)
1058
window->biggest_height = height;
1060
x = window->max_offset;
1061
window->max_offset = height * window->width;
1063
for(; x < window->max_offset; x++) {
1064
window->text_buffer[x] = ' ';
1065
window->color_buffer[x] = window->current;
1068
window->dirty = TRUE;
1071
void z_set_color(zwinid window, unsigned fore, unsigned back)
1073
if(fore >= sizeof(fgcolortable) / sizeof(*fgcolortable)) {
1074
n_show_error(E_OUTPUT, "illegal foreground color", fore);
1077
if(back >= sizeof(bgcolortable) / sizeof(*bgcolortable)) {
1078
n_show_error(E_OUTPUT, "illegal background color", back);
1082
fgcolortable[0] = fgcolortable[fore];
1083
bgcolortable[0] = bgcolortable[back];
1085
window->current.fore = fore;
1086
window->current.back = back;
1089
void z_set_style(zwinid window, int style)
1092
case 0: window->current.style = 0; break;
1093
case 1: window->current.style |= sREVE; break;
1094
case 2: window->current.style |= sBOLD; break;
1095
case 4: window->current.style |= sITAL; break;
1096
case 8: window->current.style |= sFIXE; break;
1097
default: n_show_error(E_OUTPUT, "undefined style", style);
1101
void set_fixed(BOOL p)
1110
void z_set_transcript(zwinid window, strid_t stream)
1112
window->transcript = stream;
1113
glk_window_set_echo_stream(window->win, stream);
1116
void z_clear_window(zwinid window)
1120
if(window == &game_windows[0] && showstuffcount) {
1121
z_pause_timed_input(&game_windows[0]);
1122
z_flush_text(&game_windows[0]);
1123
glk_stream_set_current(game_windows[0].str);
1124
w_glk_put_string("[pausing to show unread error message]\n");
1125
z_wait_for_key(&game_windows[0]);
1128
window->dirty = TRUE;
1129
window->curr_offset = 0;
1131
if(window->win && window->text_buffer && window->color_buffer) {
1132
switch(window->wintype) {
1133
case wintype_TextGrid:
1134
for(i = 0; i < window->max_offset; i++) {
1135
window->text_buffer[i] = ' ';
1136
window->color_buffer[i] = window->current;
1138
window->curr_offset = 0;
1139
window->dirty = TRUE;
1141
case wintype_TextBuffer:
1142
z_pause_timed_input(window);
1143
z_flush_text(window);
1144
if(coloreq(window->actual, window->current)) {
1145
glk_window_clear(window->win);
1147
init_lower(NULL); /* **FIXME** This is wrong, but deal with it later */
1153
void z_erase_line(zwinid window)
1155
if(window->wintype == wintype_TextGrid) {
1157
int x = window->curr_offset % window->width;
1158
int endoffset = window->curr_offset + (window->width - x);
1160
window->dirty = TRUE;
1161
for(i = window->curr_offset; i < endoffset; i++) {
1162
window->text_buffer[i] = ' ';
1163
window->color_buffer[i] = window->current;
1169
/* Waits for input or timeout
1171
* 0 - output during wait; may need to redraw or somesuch
1172
* -1 - callback routine said to stop
1175
* char and line events will be canceled by the time it exits
1177
static int waitforinput(zwinid window, glui32 *val,
1178
BOOL (*timer_callback)(zword), zword timer_arg)
1186
for(i = 0; i < num_z_windows; i++)
1187
if(game_windows[i].mouse_callback && game_windows[i].win)
1188
glk_request_mouse_event(game_windows[i].win);
1190
window->glk_input_pending = TRUE;
1192
while(window->glk_input_pending) {
1199
if(timer_callback && timer_callback(timer_arg)) {
1200
if(window->pending_input_type == evtype_CharInput) {
1201
glk_cancel_char_event(window->win);
1204
glk_cancel_line_event(window->win, &moo);
1207
window->glk_input_pending = FALSE;
1212
case evtype_CharInput:
1214
window->glk_input_pending = FALSE;
1217
case evtype_LineInput:
1219
window->glk_input_pending = FALSE;
1222
case evtype_MouseInput:
1223
t = z_find_win(moo.win);
1224
if(t && t->mouse_callback &&
1225
t->mouse_callback(window->pending_input_type == evtype_CharInput,
1226
moo.win, moo.val1, moo.val2)) {
1227
if(window->pending_input_type == evtype_CharInput) {
1228
glk_cancel_char_event(window->win);
1231
glk_cancel_line_event(window->win, &moo);
1234
window->glk_input_pending = FALSE;
1237
glk_request_mouse_event(moo.win);
1240
case evtype_Arrange:
1241
z_draw_all_windows();
1244
z_flush_all_windows();
1247
if(window->pending_input_type == evtype_LineInput)
1248
*val = window->pending_input_length;
1256
void z_wait_for_key(zwinid window)
1260
z_draw_all_windows();
1261
glk_request_char_event(window->win);
1262
window->pending_input_type = evtype_CharInput;
1263
} while(waitforinput(window, &ch, NULL, 0) == 0);
1264
window->pending_input_type = 0;
1268
zwinid check_valid_for_input(zwinid window)
1272
zwinid newwin = NULL;
1273
for(i = 0; i < num_z_windows; i++) {
1274
if(game_windows[i].win) {
1275
newwin = &game_windows[i];
1282
if(window->wintype == wintype_TextGrid) {
1284
for(y = 0; y < window->height; y++) {
1285
z_put_char(newwin, 13);
1286
z_put_styled_string(newwin, window->text_buffer + i,
1287
window->color_buffer + i, window->width);
1290
z_put_char(newwin, 13);
1299
/* returns number of characters read */
1300
int z_read(zwinid window, char *dest, unsigned maxlen, unsigned initlen,
1301
zword timer, BOOL (*timer_callback)(zword), zword timer_arg,
1302
unsigned char *terminator)
1304
/* FIXME: support terminating characters when (if) glk gets support for
1311
if(automap_unexplore()) {
1318
if(initlen > maxlen) {
1319
n_show_error(E_OUTPUT, "initlen > maxlen", initlen);
1324
window = &game_windows[0];
1326
if(window->pending_input_type != 0) {
1327
n_show_error(E_OUTPUT, "nested input attempted", 0);
1334
const char *dir = automap_explore();
1336
length = n_strlen(dir);
1339
n_strncpy(dest, dir, length);
1345
glk_request_timer_events(timer * 100); /* if time is zero, does nothing */
1347
if(initlen != 0 && window->wintype == wintype_TextBuffer) {
1349
if(initlen <= window->curr_offset) {
1351
for(i = 0; i < initlen; i++) /* check the end of the linebuffer */
1352
if(window->text_buffer[window->curr_offset - initlen + i] != dest[i]) {
1362
window->curr_offset -= initlen; /* Remove initial text from linebuffer */
1366
if(window->wintype == wintype_TextGrid) {
1367
ux = window->curr_offset % window->width;
1368
uy = window->curr_offset / window->width;
1371
z_flush_all_windows();
1372
window = check_valid_for_input(window);
1379
if(window->wintype == wintype_TextGrid)
1380
glk_window_move_cursor(window->win, ux, uy);
1383
glui32 len = maxlen;
1384
*terminator = transcript_getline(dest, &len);
1387
if(input_stream1) { /* If didn't EOF, input_stream1 will be non-zero */
1388
glk_stream_set_current(window->str);
1389
set_glk_stream_current();
1390
glk_set_style(style_Input);
1391
glk_put_buffer(dest, length);
1395
glk_request_line_event(window->win, dest, maxlen, length);
1396
window->pending_input_type = evtype_LineInput;
1398
t = waitforinput(window, &length, timer_callback, timer_arg);
1409
stream4line(dest, length, *terminator);
1412
if(done && length >= 2 && dest[0] == '/') {
1413
if(dest[1] == '/') { /* "//" means no command, but start with "/" */
1414
for(i = 1; i < length; i++)
1415
dest[i-1] = dest[i];
1421
process_debug_command(dest+1);
1431
glk_request_timer_events(0); /* stop timer */
1433
window->pending_input_type = 0;
1435
for(i = 0; i < num_z_windows; i++)
1436
game_windows[i].biggest_height = game_windows[i].last_height = game_windows[i].height;
1441
zword z_read_char(zwinid window,
1442
zword timer, BOOL (*timer_callback)(zword), zword timer_arg)
1448
if(automap_unexplore()) {
1455
validch = transcript_getchar(&num);
1465
glk_request_timer_events(timer * 100);
1467
z_flush_all_windows();
1468
window = check_valid_for_input(window);
1472
z_draw_all_windows();
1473
glk_request_char_event(window->win);
1474
window->pending_input_type = evtype_CharInput;
1475
} while(waitforinput(window, &ch, timer_callback, timer_arg) == 0);
1477
if(' ' <= ch && ch <= '~')
1482
case keycode_Delete: validch = 8; break;
1484
case keycode_Tab: validch = 9; break;
1486
case keycode_Return: validch = 13; break;
1494
case keycode_Escape: validch = 27; break;
1496
case keycode_Up: validch = 129; break;
1498
case keycode_Down: validch = 130; break;
1500
case keycode_Left: validch = 131; break;
1502
case keycode_Right: validch = 132; break;
1503
case keycode_Func1: validch = 133; break;
1504
case keycode_Func2: validch = 134; break;
1505
case keycode_Func3: validch = 135; break;
1506
case keycode_Func4: validch = 136; break;
1507
case keycode_Func5: validch = 137; break;
1508
case keycode_Func6: validch = 138; break;
1509
case keycode_Func7: validch = 139; break;
1510
case keycode_Func8: validch = 140; break;
1511
case keycode_Func9: validch = 141; break;
1512
case keycode_Func10: validch = 142; break;
1513
case keycode_Func11: validch = 143; break;
1514
case keycode_Func12: validch = 144; break;
1516
} while(!(validch || ch == 0));
1518
glk_request_timer_events(0); /* stop timer */
1520
window->pending_input_type = 0;
1522
for(i = 0; i < num_z_windows; i++)
1523
game_windows[i].biggest_height = game_windows[i].last_height = game_windows[i].height;
1530
void zwin_init(int number, glui32 wintype,
1531
glui32 x_coord, glui32 y_coord, glui32 x_size, glui32 y_size)
1533
zwinid self = game_windows + number;
1535
if(x_coord == self->x1) {
1536
if(y_coord == self->y1) {
1539
if(game_windows[number].win) {
1540
z_pause_timed_input(game_windows[number].win);
1541
glk_window_close(game_windows[number].win, NULL);
1544
game_windows[number].win = glk_window_open(