X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=interpreters%2Fnitfol%2Fio.c;fp=interpreters%2Fnitfol%2Fio.c;h=ee297990d92fe0317514dbce72c0bfd11fde64da;hb=b1f1dc50b22b30c4d7176e1ff7c0805e80fe0724;hp=0000000000000000000000000000000000000000;hpb=50176172d18ae72d019181725c5629d45d21c548;p=projects%2Fchimara%2Fchimara.git diff --git a/interpreters/nitfol/io.c b/interpreters/nitfol/io.c new file mode 100644 index 0000000..ee29799 --- /dev/null +++ b/interpreters/nitfol/io.c @@ -0,0 +1,1546 @@ +/* Nitfol - z-machine interpreter using Glk for output. + Copyright (C) 1999 Evin Robertson + + This program 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. + + This program 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, USA. + + The author can be reached at nitfol@deja.com +*/ +#include "nitfol.h" +#include "nio.h" + +#ifdef HEADER + +typedef struct z_window *zwinid; + +#endif + +BOOL is_fixed; /* If we are forcing output to be fixed-width */ + + +/* descriptions of z-machine colors, in glk 0x00rrggbb style. + -1 means to call glk_stylehint_clear instead of glk_stylehint_set. + The 'current color' will be overwritten on calls to set_colour. + + Go ahead and customize these (making background colors lighter than + foreground colors might be interesting) +*/ + +glsi32 bgcolortable[] = { + -1L, /* current color */ + -1L, /* defualt setting */ + 0x00000000L, /* black */ + 0x00ff0000L, /* red */ + 0x00008000L, /* green */ + 0x00ffff00L, /* yellow */ + 0x000000ffL, /* blue */ + 0x00ff00ffL, /* magenta */ + 0x0000ffffL, /* cyan */ + 0x00ffffffL, /* white */ + 0x00c0c0c0L, /* light grey */ + 0x00808080L, /* medium grey */ + 0x00404040L /* dark grey */ +}; + +glsi32 fgcolortable[] = { + -1L, /* current color */ + -1L, /* defualt setting */ + 0x00000000L, /* black */ + 0x00ff0000L, /* red */ + 0x00008000L, /* green */ + 0x00ffff00L, /* yellow */ + 0x000000ffL, /* blue */ + 0x00ff00ffL, /* magenta */ + 0x0000ffffL, /* cyan */ + 0x00ffffffL, /* white */ + 0x00c0c0c0L, /* light grey */ + 0x00808080L, /* medium grey */ + 0x00404040L /* dark grey */ +}; + + +static void killglkwithcolor(glui32 styl, int fore, int back) +{ + if(fgcolortable[fore] == -1) + glk_stylehint_clear(wintype_AllTypes, styl, + stylehint_TextColor); + else + glk_stylehint_set(wintype_AllTypes, styl, + stylehint_TextColor, fgcolortable[fore]); + + if(bgcolortable[back] == -1) + glk_stylehint_clear(wintype_AllTypes, styl, + stylehint_BackColor); + else + glk_stylehint_set(wintype_AllTypes, styl, + stylehint_BackColor, bgcolortable[back]); +} + + +static void set_stylehints(char fore, char back) +{ + glui32 n; + for(n = 0; n < style_NUMSTYLES; n++) + killglkwithcolor(n, fore, back); + + /* Subheader will be used for bold */ + glk_stylehint_set(wintype_TextBuffer, style_Subheader, + stylehint_Weight, 1); + + /* BlockQuote will be used for reverse proportional text */ + glk_stylehint_set(wintype_TextBuffer, style_BlockQuote, + stylehint_Proportional, 0); + glk_stylehint_set(wintype_TextBuffer, style_BlockQuote, + stylehint_Justification, stylehint_just_Centered); +#ifdef stylehint_ReverseColor + glk_stylehint_set(wintype_TextBuffer, style_BlockQuote, + stylehint_ReverseColor, 1); +#endif + + /* User1 will be used for bold italics */ + glk_stylehint_set(wintype_TextBuffer, style_User1, + stylehint_Weight, 1); + glk_stylehint_set(wintype_TextBuffer, style_User1, + stylehint_Oblique, 1); + + /* User2 will be used for proportional bold/italic */ + glk_stylehint_set(wintype_TextBuffer, style_User2, + stylehint_Proportional, 0); + glk_stylehint_set(wintype_TextBuffer, style_User2, + stylehint_Weight, 1); +} + +#define sBOLD 1 +#define sITAL 2 +#define sFIXE 4 +#define sREVE 8 + +#if 0 + +static glui32 bitmap_to_style[16] = { + style_Normal, + style_Subheader, /* sBOLD */ + style_Emphasized, /* sITAL */ + style_User1, /* sBOLD | sITAL */ + style_Preformatted,/* sFIXE */ + style_User2, /* sFIXE | sBOLD */ + style_User2, /* sFIXE | sITAL */ + style_User2, /* sFIXE | sBOLD | sITAL*/ + style_BlockQuote, /* sREVE */ + style_BlockQuote, /* sREVE | sBOLD */ + style_BlockQuote, /* sREVE | sITAL */ + style_BlockQuote, /* sREVE | sBOLD | sITAL */ + style_BlockQuote, /* sFIXE | sREVE */ + style_BlockQuote, /* sFIXE | sREVE | sBOLD */ + style_BlockQuote, /* sFIXE | sREVE | sITAL */ + style_BlockQuote /* sFIXE | sREVE | sBOLD | sITAL */ +}; + +#else + +static glui32 bitmap_to_style[16] = { + style_Normal, + style_Subheader, /* sBOLD */ + style_Emphasized, /* sITAL */ + style_Subheader, /* sBOLD | sITAL */ + style_Preformatted,/* sFIXE */ + style_Subheader, /* sFIXE | sBOLD */ + style_Emphasized, /* sFIXE | sITAL */ + style_Subheader, /* sFIXE | sBOLD | sITAL*/ + style_Normal, + style_Subheader, /* sBOLD */ + style_Emphasized, /* sITAL */ + style_Subheader, /* sBOLD | sITAL */ + style_Preformatted,/* sFIXE */ + style_Subheader, /* sFIXE | sBOLD */ + style_Emphasized, /* sFIXE | sITAL */ + style_Subheader /* sFIXE | sBOLD | sITAL*/ +}; + +#endif + +typedef struct { + char fore; + char back; + unsigned char style; +} colorstyle; + +typedef struct { + glui32 image_num; + glui32 x, y; + glui32 width, height; +} z_image; + + +struct z_window { + winid_t win; + strid_t str; + glui32 wintype; + glui32 method; + + strid_t transcript; + + BOOL glk_input_pending; + glui32 pending_input_type; + glui32 pending_input_length; + + /* for upper window of v3 - returns # of lines drawn */ + glui32 (*draw_callback)(winid_t win, glui32 width, glui32 height); + BOOL (*mouse_callback)(BOOL is_char_event, winid_t win, glui32 x, glui32 y); + + glui32 width, height; + glui32 x1, y1, x2, y2; + + glui32 last_height; /* What the height was last time we got input */ + glui32 biggest_height;/* The biggest it's been since */ + + glui32 curr_offset; /* offset into text_buffer/color_buffer */ + glui32 max_offset; /* curr_offset must stay < max_offset */ + glui32 buffer_size; /* max_offset must stay < buffer_size */ + + BOOL dirty; /* Has window been changed since last redraw? */ + BOOL defined; /* Is our location well defined? */ + + unsigned char *text_buffer; /* whole window for grid, current line for buffer */ + colorstyle *color_buffer; + + z_image images[12]; + + colorstyle current; + colorstyle actual; +}; + + +#define num_z_windows 16 + +static struct z_window game_windows[num_z_windows]; + +static glui32 upper_width, upper_height; + + +static int waitforinput(zwinid window, glui32 *val, + BOOL (*timer_callback)(zword), zword timer_arg); + + +void set_glk_stream_current(void) +{ + z_flush_text(&game_windows[0]); + glk_stream_set_current(game_windows[0].str); +} + +void draw_intext_picture(zwinid window, glui32 picture, glui32 alignment) +{ + z_flush_text(window); + wrap_glk_image_draw(window->win, picture, alignment, 0); +} + +void draw_picture(zwinid window, glui32 picture, glui32 x, glui32 y) +{ + int i; + int useimage = -1; + glui32 width, height; + + wrap_glk_image_get_info(operand[0], &width, &height); + + for(i = 0; i < 12; i++) { + if(is_in_bounds(window->images[i].x, window->images[i].y, + window->images[i].width, window->images[i].height, + x, y, width, height)) + useimage = i; + } + if(useimage == -1) + for(i = 0; i < 12; i++) + if(window->images[i].image_num == 0) + useimage = i; + if(useimage == -1) + return; + + window->images[useimage].image_num = picture; + window->images[useimage].x = x; + window->images[useimage].y = y; + window->images[useimage].width = width; + window->images[useimage].height = height; +} + + +static int showstuffcount = 0; + +/* Show an interpreter message */ +void showstuff(const char *title, const char *type, const char *message, offset number) +{ + static BOOL loopy = FALSE; + if(loopy) { + loopy = FALSE; + n_show_fatal(E_SYSTEM, "loopy message reporting", 0); + } + loopy = TRUE; + + z_pause_timed_input(&game_windows[0]); + z_flush_text(&game_windows[0]); + glk_stream_set_current(game_windows[0].str); + + glk_set_style(style_Alert); + w_glk_put_string("\n["); + w_glk_put_string(title); + w_glk_put_string(": "); + w_glk_put_string(type); + w_glk_put_string("]: "); + w_glk_put_string(message); + w_glk_put_string(" ("); + g_print_snumber(number); + w_glk_put_string(") "); + +#ifdef DEBUGGING + infix_gprint_loc(stack_get_depth(), 0); +#else + w_glk_put_string("PC="); + g_print_number(oldPC); + glk_put_char('\n'); +#endif + + if(++showstuffcount == 100) { + w_glk_put_string("[pausing every 100 errors]\n"); + z_wait_for_key(&game_windows[0]); + } + + glk_set_style(style_Normal); + + loopy = FALSE; +} + + +void init_lower(zwinid *lower) +{ + zwinid lower_win = &game_windows[0]; + glui32 i; + + if(lower) + *lower = lower_win; + + if(lower_win->win) { + z_pause_timed_input(lower_win); + glk_window_close(lower_win->win, NULL); + } + + set_stylehints(lower_win->current.fore, + lower_win->current.back); + + + lower_win->dirty = TRUE; + lower_win->wintype = wintype_TextBuffer; + lower_win->method = winmethod_Below; + lower_win->curr_offset = 0; + lower_win->max_offset = 80 * 24; + lower_win->buffer_size = lower_win->max_offset; + + if(!lower_win->text_buffer) + lower_win->text_buffer = (unsigned char *) n_malloc(lower_win->buffer_size); + if(!lower_win->color_buffer) + lower_win->color_buffer = (colorstyle *) n_malloc(lower_win->buffer_size * sizeof(colorstyle)); + + for(i = 0; i < lower_win->buffer_size; i++) { + lower_win->text_buffer[i] = ' '; + lower_win->color_buffer[i] = lower_win->current; + } + + lower_win->actual = lower_win->current; + + lower_win->win = glk_window_open(game_windows[1].win, + winmethod_Below | winmethod_Proportional, + 50, /* Percent doesn't matter */ + wintype_TextBuffer, 0); + + + if(lower_win->win == 0) { + n_show_fatal(E_OUTPUT, "cannot open lower window", 0); + return; + } + + lower_win->str = glk_window_get_stream(lower_win->win); + + if(lower_win->transcript) + glk_window_set_echo_stream(lower_win->win, lower_win->transcript); +} + + +void init_upper(zwinid *upper) +{ + zwinid upper_win = &game_windows[1]; + glui32 i; + + if(upper) + *upper = upper_win; + + if(upper_win->win) { + z_pause_timed_input(upper_win); + glk_window_close(upper_win->win, NULL); + } + + upper_win->dirty = TRUE; + upper_win->wintype = wintype_TextGrid; + upper_win->method = winmethod_Above | winmethod_Fixed; + upper_win->height = 0; + upper_win->width = upper_width; + upper_win->curr_offset = 0; + upper_win->max_offset = upper_height * upper_width; + upper_win->buffer_size = upper_win->max_offset; + + if(!upper_win->text_buffer) + upper_win->text_buffer = (unsigned char *) n_malloc(upper_win->buffer_size); + if(!upper_win->color_buffer) + upper_win->color_buffer = (colorstyle *) n_malloc(upper_win->buffer_size * sizeof(colorstyle)); + + for(i = 0; i < upper_win->buffer_size; i++) { + upper_win->text_buffer[i] = ' '; + upper_win->color_buffer[i] = upper_win->current; + } + + upper_win->actual = upper_win->current; + + upper_win->win = glk_window_open(game_windows[0].win, + winmethod_Above | winmethod_Fixed, + 1, /* XXX huh? upper_height, */ + wintype_TextGrid, 1); + + if(upper_win->win == 0) { + upper_win->str = 0; + return; + } + + upper_win->str = glk_window_get_stream(upper_win->win); + + if(upper_win->str == 0) { + glk_window_close(upper_win->win, NULL); + upper_win->win = 0; + return; + } +} + + +void z_init_windows(BOOL dofixed, + glui32 (*draw_callback)(winid_t, glui32, glui32), + BOOL (*mouse_callback)(BOOL, winid_t, glui32, glui32), + glui32 maxwidth, glui32 maxheight, + zwinid *upper, zwinid *lower) +{ + colorstyle defaultstyle; + defaultstyle.fore = 1; defaultstyle.back = 1; defaultstyle.style = 0; + + is_fixed = dofixed; + + kill_windows(); + + upper_width = maxwidth; upper_height = maxheight; + + game_windows[0].current = game_windows[1].current = defaultstyle; + game_windows[1].draw_callback = draw_callback; + game_windows[1].mouse_callback = mouse_callback; + + init_lower(lower); + init_upper(upper); +} + + +zwinid z_split_screen(glui32 wintype, glui32 method, + glui32 (*draw_callback)(winid_t, glui32, glui32), + BOOL (*mouse_callback)(BOOL, winid_t, glui32, glui32)) +{ + int i; + for(i = 0; i < num_z_windows; i++) { + if(!game_windows[i].win) { + winid_t root = glk_window_get_root(); + game_windows[i].win = glk_window_open(root, method, 0, wintype, 0); + game_windows[i].str = glk_window_get_stream(game_windows[i].win); + game_windows[i].wintype = wintype; + game_windows[i].method = method; + game_windows[i].transcript = NULL; + game_windows[i].glk_input_pending = FALSE; + game_windows[i].draw_callback = draw_callback; + game_windows[i].mouse_callback = mouse_callback; + game_windows[i].width = 0; + game_windows[i].height = 0; + game_windows[i].curr_offset = 0; + game_windows[i].max_offset = 1; + game_windows[i].buffer_size = 2; + game_windows[i].text_buffer = n_malloc(2); + game_windows[i].color_buffer = n_malloc(2); + game_windows[i].dirty = TRUE; + return &game_windows[i]; + } + } + return NULL; +} + + +void z_kill_window(zwinid win) +{ + if(!win) + return; + n_free(win->text_buffer); + win->text_buffer = NULL; + n_free(win->color_buffer); + win->color_buffer = NULL; + win->transcript = NULL; + glk_window_close(win->win, NULL); + win->win = NULL; + win->str = NULL; +} + + +/* close any open windows */ +void kill_windows(void) +{ + int i; + + for(i = 0; i < num_z_windows; i++) + z_clear_window(&game_windows[i]); + + free_windows(); + for(i = 0; i < num_z_windows; i++) { + if(game_windows[i].win) { + game_windows[i].transcript = NULL; + + glk_window_close(game_windows[i].win, NULL); + game_windows[i].win = NULL; + game_windows[i].str = NULL; + } + } + showstuffcount = 0; +} + + +/* free memory space used by windows, but don't close them */ +void free_windows(void) +{ + int i; + + z_flush_all_windows(); + + for(i = 0; i < num_z_windows; i++) { + if(game_windows[i].win) { + n_free(game_windows[i].text_buffer); + game_windows[i].text_buffer = NULL; + + n_free(game_windows[i].color_buffer); + game_windows[i].color_buffer = NULL; + } + } +} + +zwinid z_find_win(winid_t win) +{ + int i; + for(i = 0; i < num_z_windows; i++) { + if(game_windows[i].win == win) + return &game_windows[i]; + } + return NULL; +} + + +static BOOL coloreq(colorstyle a, colorstyle b) /* return true if colors are equivalent */ +{ + return (fgcolortable[(int) a.fore] == fgcolortable[(int) b.fore]) && + (bgcolortable[(int) a.back] == bgcolortable[(int) b.back]); +} + + +static void checkforblockquote(zwinid window, zwinid dest_win) +{ + if(window->biggest_height > window->last_height && + window->biggest_height > window->height) { + /* find borders of the blockquote */ + unsigned leftx = window->width, rightx = 0; + unsigned topy = window->biggest_height; + unsigned bottomy = window->height; + unsigned x, y, i; + + i = window->height * window->width; + for(y = window->height; y < window->biggest_height; y++) + for(x = 0; x < window->width; x++) + if(window->text_buffer[i++] != ' ') { + if(x < leftx) + leftx = x; + if(x > rightx) + rightx = x; + if(y < topy) + topy = y; + if(y > bottomy) + bottomy = y; + } + + z_pause_timed_input(dest_win); + glk_stream_set_current(game_windows[1].str); + glk_put_char(10); + glk_set_style(style_BlockQuote); + + /* draw the blockquote */ + for(y = topy; y <= bottomy; y++) { + i = y * window->width + leftx; + for(x = leftx; x <= rightx; x++) + glk_put_char(window->text_buffer[i++]); + glk_put_char(10); + } + } +} + + +void z_pause_timed_input(zwinid window) +{ + event_t eep; + if(window->glk_input_pending) { + window->glk_input_pending = FALSE; + + switch(window->pending_input_type) { + case evtype_CharInput: + glk_cancel_char_event(window->win); + break; + case evtype_LineInput: + glk_cancel_line_event(window->win, &eep); + window->pending_input_length = eep.val1; + } + } +} + + +void z_flush_all_windows(void) +{ + int window; + for(window = 0; window < num_z_windows; window++) { + if(game_windows[window].dirty) { + z_pause_timed_input(&game_windows[window]); + + switch(game_windows[window].wintype) { + case wintype_TextBuffer: + z_flush_text(&game_windows[window]); + break; + case wintype_TextGrid: + z_flush_fixed(&game_windows[window]); + break; + case wintype_Graphics: + z_flush_graphics(&game_windows[window]); + break; + } + } + } +} + +void z_draw_all_windows(void) +{ + int window; + for(window = 0; window < num_z_windows; window++) { + if(game_windows[window].wintype == wintype_TextGrid) { + game_windows[window].dirty = TRUE; + z_flush_fixed(&game_windows[window]); + } + } +} + + +static void z_put_styled_string(zwinid window, unsigned char *text, + colorstyle *color, glui32 length) +{ + glui32 n; + colorstyle laststyle = color[0]; + + if(!length) + return; + + glk_set_style_stream(window->str, bitmap_to_style[laststyle.style]); + + for(n = 0; n < length; n++) { + if(color[n].style != laststyle.style) + glk_set_style_stream(window->str, bitmap_to_style[color[n].style]); + glk_put_char_stream(window->str, text[n]); + laststyle = color[n]; + } +} + +void z_flush_fixed(zwinid window) +{ + glui32 winx, winy; + winid_t o; + glui32 start_line, end_line; + + /* If there's no such window, give up */ + if(!window->win || !window->str || + !window->text_buffer || !window->color_buffer) + return; + + /* glk doesn't allow writing to a window while input is pending */ + z_pause_timed_input(window); + + end_line = window->height; + + /* Has the window grown and shrunk? If so, probably because someone wants + to draw a box quote - don't let them shrink the window quite so fast */ + if(window->biggest_height > window->last_height && + window->biggest_height > window->height) + end_line = window->biggest_height; + + /* For v3 games, there's a callback function to draw the room name and + score; if this is present, we start drawing at a lower position */ + start_line = 0; + if(window->draw_callback) + start_line = window->draw_callback(NULL, 0, 0); + end_line += start_line; + + o = glk_window_get_parent(window->win); +#if 0 + glk_window_get_size(window->win, &winx, &winy); + if (!(window->method & winmethod_Above || window->method & winmethod_Below) + || winy != end_line) + glk_window_set_arrangement(o, window->method, + end_line, window->win); +#else + glk_window_set_arrangement(o, window->method, end_line, window->win); +#endif + glk_window_get_size(window->win, &winx, &winy); + + if(window->draw_callback) { + glk_stream_set_current(window->str); + glk_window_clear(window->win); + glk_window_move_cursor(window->win, 0, 0); + window->draw_callback(window->win, winx, winy); + } + + if(end_line > start_line && window->dirty) { + + unsigned padleft = 0, padmiddle = 0, padright = 0; + unsigned skipleft = 0, skipmiddle = 0, skipright = 0; + unsigned width; + unsigned firstwidth, lastwidth; + + unsigned x, y, i; + + /* Calculate how much space is used for margins */ + + unsigned left_margin = window->width, right_margin = window->width; + + i = 0; + for(y = start_line; y < end_line; y++) { + + for(x = 0; x < window->width; x++) + if(window->text_buffer[i + x] != ' ') { + if(x < left_margin) + left_margin = x; + break; + } + + for(x = 0; x < window->width; x++) + if(window->text_buffer[i + window->width - x - 1] != ' ') { + if(x < right_margin) + right_margin = x; + break; + } + + i += window->width; + } + + firstwidth = window->width; lastwidth = 0; + + if(start_line + 1 == end_line) { + unsigned longestx = 0; + unsigned longestlen = 0; + unsigned thisx = 0; + unsigned thislen = 0; + colorstyle lastcolor; + width = window->width; + + for(x = skipleft; x < width + skipleft; x++) { + if(window->text_buffer[x] == ' ' + && (!thislen || coloreq(window->color_buffer[x], lastcolor))) { + if(!thislen) + thisx = x; + thislen++; + lastcolor = window->color_buffer[x]; + } else { + if(thislen > longestlen) { + longestx = thisx; + longestlen = thislen; + } + thislen = 0; + } + } + if(longestlen > 4) { + firstwidth = longestx - skipleft; + skipmiddle = longestlen - 1; + lastwidth = width - firstwidth - skipmiddle; + } + } + + if(skipmiddle && winx < firstwidth + 2 + lastwidth) + padmiddle = 2; + + if(lastwidth && winx >= firstwidth + padmiddle + lastwidth) { + padmiddle = winx - firstwidth - lastwidth; + } else { + if(winx >= window->width) + width = window->width; + else + width = winx; + + if(right_margin + left_margin) { + if(winx > window->width) + padleft = (unsigned) ((winx - window->width) * (((float) left_margin) / (right_margin + left_margin))); + else + skipleft = (unsigned) ((window->width - winx) * (((float) left_margin) / (right_margin + left_margin))); + } + else { + padleft = winx - window->width; + } + + if(skipleft > left_margin) + skipleft = left_margin; + + if(winx > window->width) + padright = winx - window->width - padleft; + else + skipright = window->width - winx - skipleft; + + if(width < firstwidth + padmiddle) { + firstwidth = width; + padmiddle = 0; + lastwidth = 0; + } else if(width < firstwidth + padmiddle + lastwidth) { + lastwidth = width - firstwidth - padmiddle; + } + } + + + glk_stream_set_current(window->str); + glk_window_move_cursor(window->win, 0, start_line); + + /* draw to the upper window */ + i = 0; + for(y = start_line; y < end_line; y++) { + + for(x = 0; x < padleft; x++) + glk_put_char(' '); + + i += skipleft; + + z_put_styled_string(window, window->text_buffer + i, + window->color_buffer + i, firstwidth); + i += firstwidth; + + for(x = 0; x < padmiddle; x++) + glk_put_char(' '); + i += skipmiddle; + + z_put_styled_string(window, window->text_buffer + i, + window->color_buffer + i, lastwidth); + i += lastwidth; + + for(x = 0; x < padright; x++) + glk_put_char(' '); + + i += skipright; + } + + /* Bureaucracy needs the cursor positioned and visible in upper window. */ + glk_window_move_cursor(window->win, + window->curr_offset % window->width, + window->curr_offset / window->width); + window->dirty = FALSE; + } +} + + +void z_flush_text(zwinid window) +{ + z_pause_timed_input(window); + + if(!window->win || !window->str + || !window->text_buffer || !window->color_buffer + || window->curr_offset == 0) { + window->curr_offset = 0; + return; + } + + z_put_styled_string(window, window->text_buffer, window->color_buffer, + window->curr_offset); + + window->curr_offset = 0; + window->dirty = FALSE; +} + + +void z_flush_graphics(zwinid window) +{ + int i; + glui32 winx, winy; + float xratio, yratio; + winid_t parent; + + if(!window->win) + return; + + glk_window_get_size(window->win, &winx, &winy); + xratio = ((float) winx) / window->width; + yratio = ((float) winy) / window->height; + + parent = glk_window_get_parent(window->win); + + /* We want the window to maintain its original height/width ratio */ + switch(window->method & winmethod_DirMask) { + case winmethod_Left: /* Left and right splits mean height is fixed - */ + case winmethod_Right: /* adjust width to the yratio */ + glk_window_set_arrangement(parent, window->method, + (glui32) (window->width * yratio), 0); + break; + case winmethod_Above: /* Above and below splits mean width is fixed - */ + case winmethod_Below: /* adjust height to the xratio */ + glk_window_set_arrangement(parent, window->method, + (glui32) (window->height * xratio), 0); + break; + } + + /* Check to see what it became, and if it's still off, don't worry */ + glk_window_get_size(window->win, &winx, &winy); + xratio = ((float) winx) / window->width; + yratio = ((float) winy) / window->height; + + for(i = 0; i < 12; i++) { + if(window->images[i].image_num) { + wrap_glk_image_draw_scaled(window->win, window->images[i].image_num, + (glui32) (window->images[i].x * xratio), + (glui32) (window->images[i].y * yratio), + (glui32) (window->images[i].width * xratio), + (glui32) (window->images[i].height * yratio)); + } + } +} + +void z_print_number(zwinid window, int number) +{ + int i; + char buffer[12]; + int length = n_to_decimal(buffer, number); + + for(i = length - 1; i >= 0; i--) + z_put_char(window, buffer[i]); +} + +void z_put_char(zwinid window, unsigned c) +{ + colorstyle color = window->current; + if(is_fixed) + color.style |= sFIXE; + + if(c == 0) /* Section 3.8.2.1 */ + return; + + window->dirty = TRUE; + + if((c < 32 && c != 13) || (c >= 127 && c <= 159)) { /*Undefined in latin-1*/ + switch(window->wintype) { + case wintype_TextBuffer: + z_put_char(window, '['); + z_print_number(window, c); + z_put_char(window, ']'); + return; + case wintype_TextGrid: + c = '?'; + } + } + + switch(c) { + case 0x152: + z_put_char(window, 'O'); + c = 'E'; + break; + case 0x153: + z_put_char(window, 'o'); + c = 'e'; + break; + } + + + if(c > 255) /* Section 3.8.5.4.3 */ + c = '?'; + + if(c == 13) { /* Section 7.1.2.2.1 */ + switch(window->wintype) { + case wintype_TextBuffer: + window->text_buffer[window->curr_offset] = 10; + window->curr_offset++; + z_flush_text(window); + break; + case wintype_TextGrid: + window->curr_offset += window->width; + window->curr_offset -= window->curr_offset % window->width; + } + } else { + window->text_buffer[window->curr_offset] = c; + window->color_buffer[window->curr_offset] = color; + window->curr_offset++; + } + + if(window->curr_offset >= window->max_offset) { + switch(window->wintype) { + case wintype_TextBuffer: + z_flush_text(window); + break; + case wintype_TextGrid: + if(!window->defined) /* Section 8.6.2 */ + n_show_port(E_OUTPUT, "writing past end of window", c); + + if(window->max_offset) + window->curr_offset = window->max_offset - 1; + else + window->curr_offset = 0; + + window->defined = FALSE; + } + } +} + +void z_setxy(zwinid window, zword x, zword y) +{ + window->curr_offset = (y - 1) * window->width + (x - 1); + window->defined = TRUE; +} + +void z_getxy(zwinid window, zword *x, zword *y) +{ + if(window->width) { + *x = (window->curr_offset % window->width) + 1; + *y = (window->curr_offset / window->width) + 1; + } else { + *x = window->curr_offset + 1; + *y = 1; + } +} + +void z_getsize(zwinid window, unsigned *width, unsigned *height) +{ + *width = window->width; + *height = window->height; +} + +void z_find_size(glui32 *wid, glui32 *hei) +{ + glui32 oldwid, oldhei; + zwinid upper = &game_windows[1]; + winid_t o = glk_window_get_parent(upper->win); + glk_window_get_size(upper->win, &oldwid, &oldhei); + glk_window_set_arrangement(o, (upper->method & ~winmethod_Fixed) | + winmethod_Proportional, 100, upper->win); + glk_window_get_size(upper->win, wid, hei); + glk_window_set_arrangement(o, upper->method, oldhei, upper->win); + + upper_width = *wid; upper_height = *hei; + init_upper(&upper); +} + +void z_set_height(zwinid window, unsigned height) +{ + unsigned x; + if(height * window->width > window->buffer_size) { + n_show_error(E_OUTPUT, "height too large", height); + return; + } + + window->height = height; + if(height > window->biggest_height) + window->biggest_height = height; + + x = window->max_offset; + window->max_offset = height * window->width; + + for(; x < window->max_offset; x++) { + window->text_buffer[x] = ' '; + window->color_buffer[x] = window->current; + } + + window->dirty = TRUE; +} + +void z_set_color(zwinid window, unsigned fore, unsigned back) +{ + if(fore >= sizeof(fgcolortable) / sizeof(*fgcolortable)) { + n_show_error(E_OUTPUT, "illegal foreground color", fore); + return; + } + if(back >= sizeof(bgcolortable) / sizeof(*bgcolortable)) { + n_show_error(E_OUTPUT, "illegal background color", back); + return; + } + + fgcolortable[0] = fgcolortable[fore]; + bgcolortable[0] = bgcolortable[back]; + + window->current.fore = fore; + window->current.back = back; +} + +void z_set_style(zwinid window, int style) +{ + switch(style) { + case 0: window->current.style = 0; break; + case 1: window->current.style |= sREVE; break; + case 2: window->current.style |= sBOLD; break; + case 4: window->current.style |= sITAL; break; + case 8: window->current.style |= sFIXE; break; + default: n_show_error(E_OUTPUT, "undefined style", style); + } +} + +void set_fixed(BOOL p) +{ + if(!p) { + is_fixed = FALSE; + } else { + is_fixed = TRUE; + } +} + +void z_set_transcript(zwinid window, strid_t stream) +{ + window->transcript = stream; + glk_window_set_echo_stream(window->win, stream); +} + +void z_clear_window(zwinid window) +{ + glui32 i; + + if(window == &game_windows[0] && showstuffcount) { + z_pause_timed_input(&game_windows[0]); + z_flush_text(&game_windows[0]); + glk_stream_set_current(game_windows[0].str); + w_glk_put_string("[pausing to show unread error message]\n"); + z_wait_for_key(&game_windows[0]); + } + + window->dirty = TRUE; + window->curr_offset = 0; + + if(window->win && window->text_buffer && window->color_buffer) { + switch(window->wintype) { + case wintype_TextGrid: + for(i = 0; i < window->max_offset; i++) { + window->text_buffer[i] = ' '; + window->color_buffer[i] = window->current; + } + window->curr_offset = 0; + window->dirty = TRUE; + break; + case wintype_TextBuffer: + z_pause_timed_input(window); + z_flush_text(window); + if(coloreq(window->actual, window->current)) { + glk_window_clear(window->win); + } else { + init_lower(NULL); /* **FIXME** This is wrong, but deal with it later */ + } + } + } +} + +void z_erase_line(zwinid window) +{ + if(window->wintype == wintype_TextGrid) { + int i; + int x = window->curr_offset % window->width; + int endoffset = window->curr_offset + (window->width - x); + + window->dirty = TRUE; + for(i = window->curr_offset; i < endoffset; i++) { + window->text_buffer[i] = ' '; + window->color_buffer[i] = window->current; + } + } +} + + +/* Waits for input or timeout + * Returns: + * 0 - output during wait; may need to redraw or somesuch + * -1 - callback routine said to stop + * 10 - read input + * 254 - mouse input + * char and line events will be canceled by the time it exits + */ +static int waitforinput(zwinid window, glui32 *val, + BOOL (*timer_callback)(zword), zword timer_arg) +{ + int i; + event_t moo; + zwinid t; + + showstuffcount = 0; + + for(i = 0; i < num_z_windows; i++) + if(game_windows[i].mouse_callback && game_windows[i].win) + glk_request_mouse_event(game_windows[i].win); + + window->glk_input_pending = TRUE; + + while(window->glk_input_pending) { + glk_select(&moo); + + check_sound(moo); + + switch(moo.type) { + case evtype_Timer: + if(timer_callback && timer_callback(timer_arg)) { + if(window->pending_input_type == evtype_CharInput) { + glk_cancel_char_event(window->win); + *val = 0; + } else { + glk_cancel_line_event(window->win, &moo); + *val = moo.val1; + } + window->glk_input_pending = FALSE; + return -1; + } + break; + + case evtype_CharInput: + *val = moo.val1; + window->glk_input_pending = FALSE; + return 10; + + case evtype_LineInput: + *val = moo.val1; + window->glk_input_pending = FALSE; + return 10; + + case evtype_MouseInput: + t = z_find_win(moo.win); + if(t && t->mouse_callback && + t->mouse_callback(window->pending_input_type == evtype_CharInput, + moo.win, moo.val1, moo.val2)) { + if(window->pending_input_type == evtype_CharInput) { + glk_cancel_char_event(window->win); + *val = 254; + } else { + glk_cancel_line_event(window->win, &moo); + *val = moo.val1; + } + window->glk_input_pending = FALSE; + return 254; + } + glk_request_mouse_event(moo.win); + break; + + case evtype_Arrange: + z_draw_all_windows(); + } + + z_flush_all_windows(); + } + + if(window->pending_input_type == evtype_LineInput) + *val = window->pending_input_length; + else + *val = 0; + + return 0; +} + + +void z_wait_for_key(zwinid window) +{ + glui32 ch; + do { + z_draw_all_windows(); + glk_request_char_event(window->win); + window->pending_input_type = evtype_CharInput; + } while(waitforinput(window, &ch, NULL, 0) == 0); + window->pending_input_type = 0; +} + + +zwinid check_valid_for_input(zwinid window) +{ + glui32 y, i; + if(!window->win) { + zwinid newwin = NULL; + for(i = 0; i < num_z_windows; i++) { + if(game_windows[i].win) { + newwin = &game_windows[i]; + break; + } + } + if(!newwin) + return NULL; + + if(window->wintype == wintype_TextGrid) { + i = 0; + for(y = 0; y < window->height; y++) { + z_put_char(newwin, 13); + z_put_styled_string(newwin, window->text_buffer + i, + window->color_buffer + i, window->width); + i += window->width; + } + z_put_char(newwin, 13); + } + + window = newwin; + } + return window; +} + + +/* returns number of characters read */ +int z_read(zwinid window, char *dest, unsigned maxlen, unsigned initlen, + zword timer, BOOL (*timer_callback)(zword), zword timer_arg, + unsigned char *terminator) +{ + /* FIXME: support terminating characters when (if) glk gets support for + them */ + unsigned i; + unsigned ux, uy; + glui32 length; + BOOL done; + + if(automap_unexplore()) { + read_abort = TRUE; + return 0; + } + + read_abort = FALSE; + + if(initlen > maxlen) { + n_show_error(E_OUTPUT, "initlen > maxlen", initlen); + return 0; + } + + if(window == 0) + window = &game_windows[0]; + + if(window->pending_input_type != 0) { + n_show_error(E_OUTPUT, "nested input attempted", 0); + return 0; + } + +#ifdef DEBUGGING + + if(do_automap) { + const char *dir = automap_explore(); + if(dir) { + length = n_strlen(dir); + if(length > maxlen) + length = maxlen; + n_strncpy(dest, dir, length); + return length; + } + } +#endif + + glk_request_timer_events(timer * 100); /* if time is zero, does nothing */ + + if(initlen != 0 && window->wintype == wintype_TextBuffer) { + BOOL good = FALSE; + if(initlen <= window->curr_offset) { + good = TRUE; + for(i = 0; i < initlen; i++) /* check the end of the linebuffer */ + if(window->text_buffer[window->curr_offset - initlen + i] != dest[i]) { + good = FALSE; + break; + } + } + if(!good) { + /* bleah */ + /* argh */ + /* oof */ + } else { + window->curr_offset -= initlen; /* Remove initial text from linebuffer */ + } + } + + if(window->wintype == wintype_TextGrid) { + ux = window->curr_offset % window->width; + uy = window->curr_offset / window->width; + } + + z_flush_all_windows(); + window = check_valid_for_input(window); + + done = FALSE; + length = initlen; + while(!done) { + int t; + + if(window->wintype == wintype_TextGrid) + glk_window_move_cursor(window->win, ux, uy); + + if(input_stream1) { + glui32 len = maxlen; + *terminator = transcript_getline(dest, &len); + length = len; + } + if(input_stream1) { /* If didn't EOF, input_stream1 will be non-zero */ + glk_stream_set_current(window->str); + set_glk_stream_current(); + glk_set_style(style_Input); + glk_put_buffer(dest, length); + glk_put_char(10); + done = TRUE; + } else { + glk_request_line_event(window->win, dest, maxlen, length); + window->pending_input_type = evtype_LineInput; + + t = waitforinput(window, &length, timer_callback, timer_arg); + if(t != 0) { + if(t == -1) + *terminator = 0; + else + *terminator = t; + done = TRUE; + } + } + + if(done) + stream4line(dest, length, *terminator); + +#ifdef DEBUGGING + if(done && length >= 2 && dest[0] == '/') { + if(dest[1] == '/') { /* "//" means no command, but start with "/" */ + for(i = 1; i < length; i++) + dest[i-1] = dest[i]; + length--; + } else { + done = FALSE; + dest[length] = 0; + + process_debug_command(dest+1); + + if(read_abort) + done = TRUE; + + length = 0; + } + } +#endif + } + glk_request_timer_events(0); /* stop timer */ + + window->pending_input_type = 0; + + for(i = 0; i < num_z_windows; i++) + game_windows[i].biggest_height = game_windows[i].last_height = game_windows[i].height; + + return length; +} + +zword z_read_char(zwinid window, + zword timer, BOOL (*timer_callback)(zword), zword timer_arg) +{ + unsigned i; + glui32 ch; + zword validch = 0; + + if(automap_unexplore()) { + read_abort = TRUE; + return 0; + } + + if(input_stream1) { + unsigned num; + validch = transcript_getchar(&num); + if(!validch) + validch = num; + } + if(input_stream1) { + return validch; + } + + read_abort = FALSE; + + glk_request_timer_events(timer * 100); + + z_flush_all_windows(); + window = check_valid_for_input(window); + + do { + do { + z_draw_all_windows(); + glk_request_char_event(window->win); + window->pending_input_type = evtype_CharInput; + } while(waitforinput(window, &ch, timer_callback, timer_arg) == 0); + + if(' ' <= ch && ch <= '~') + validch = ch; + + switch(ch) { + case 8: + case keycode_Delete: validch = 8; break; + case 9: + case keycode_Tab: validch = 9; break; + case 13: + case keycode_Return: validch = 13; break; +/* case 21: + if(restoreundo()) { + read_abort = TRUE; + return 0; + } + break; */ + case 27: + case keycode_Escape: validch = 27; break; + case 16: + case keycode_Up: validch = 129; break; + case 14: + case keycode_Down: validch = 130; break; + case 2: + case keycode_Left: validch = 131; break; + case 6: + case keycode_Right: validch = 132; break; + case keycode_Func1: validch = 133; break; + case keycode_Func2: validch = 134; break; + case keycode_Func3: validch = 135; break; + case keycode_Func4: validch = 136; break; + case keycode_Func5: validch = 137; break; + case keycode_Func6: validch = 138; break; + case keycode_Func7: validch = 139; break; + case keycode_Func8: validch = 140; break; + case keycode_Func9: validch = 141; break; + case keycode_Func10: validch = 142; break; + case keycode_Func11: validch = 143; break; + case keycode_Func12: validch = 144; break; + } + } while(!(validch || ch == 0)); + + glk_request_timer_events(0); /* stop timer */ + + window->pending_input_type = 0; + + for(i = 0; i < num_z_windows; i++) + game_windows[i].biggest_height = game_windows[i].last_height = game_windows[i].height; + + return validch; +} + + +/* +void zwin_init(int number, glui32 wintype, + glui32 x_coord, glui32 y_coord, glui32 x_size, glui32 y_size) +{ + zwinid self = game_windows + number; + + if(x_coord == self->x1) { + if(y_coord == self->y1) { + + + if(game_windows[number].win) { + z_pause_timed_input(game_windows[number].win); + glk_window_close(game_windows[number].win, NULL); + } + set_style_hints(); + game_windows[number].win = glk_window_open( +} +*/