From 8c4e7927708ec6ebeb2b5ef1b291233d861e9d8c Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 4 Apr 2019 17:55:40 -0700 Subject: [PATCH] curses ">>" (terse "--More--") I've noticed many instances of the game pausing and not being sure why, then pressing and having it resume. The curses interface had a tendency to put its equivalent of the --More-- prompt, >>, somewhere where that wasn't visible, either off the right hand edge (possibly) or underneath the window borders if those were enabled. Especially the very last one it issues prior to exit. (An extra one compared to tty behavior.) This ended up being a pretty substantial overhaul of message window handling. I wouldn't be surprised if it has off-by-one errors which happen to be paired up and cancel each other out. ">>" is still drawn in orange if guicolor is on, now in inverse video when that is off. If it happens to be drawn at the same screen location in consecutive instances, the first ">" will toggle between blink and not blink so that there'll be no doubt as to whether the keypress registered when dismissing it (moot if the text preceding it is different but there's no attempt to be smart enough to check that, just screen placement). --- doc/fixes36.2 | 4 +- include/wincurs.h | 1 + win/curses/cursdial.c | 6 +- win/curses/cursmain.c | 4 +- win/curses/cursmesg.c | 192 ++++++++++++++++++++++++++---------------- win/curses/cursmesg.h | 1 + win/curses/cursmisc.c | 3 + 7 files changed, 131 insertions(+), 80 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 788d819bf..68709069d 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.297 $ $NHDT-Date: 1554411130 2019/04/04 20:52:10 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.298 $ $NHDT-Date: 1554425733 2019/04/05 00:55:33 $ This fixes36.2 file is here to capture information about updates in the 3.6.x lineage following the release of 3.6.1 in April 2018. Please note, however, @@ -536,6 +536,8 @@ curses: if player pressed ESC at More>> prompt to suppress remaining messages for the current move and then hero got another move on the same turn, messages and most prompts would stay suppressed during that extra move curses: don't let MSGTYPE=hide block prompts from being shown +curses: the --More-- prompt (">>") wasn't always placed in a visible location, + especially when window borders were displayed vms: add compile of isaac64.c to Makefile.src and vmsbuild.com vms+curses: add compile support but it is known to fail to build diff --git a/include/wincurs.h b/include/wincurs.h index 1dc6400d5..18b3be289 100644 --- a/include/wincurs.h +++ b/include/wincurs.h @@ -213,6 +213,7 @@ extern void curses_cleanup(void); /* cursmesg.c */ extern void curses_message_win_puts(const char *message, boolean recursed); +extern void curses_got_input(void); extern int curses_block(boolean require_tab); /* for MSGTYPE=STOP */ extern int curses_more(void); extern void curses_clear_unhighlight_message_window(void); diff --git a/win/curses/cursdial.c b/win/curses/cursdial.c index 062400b93..fd5385b90 100644 --- a/win/curses/cursdial.c +++ b/win/curses/cursdial.c @@ -17,8 +17,6 @@ #define strncasecmp strncmpi #endif -extern long curs_mesg_suppress_turn; /* from cursmesg.c */ - /* * Note: * @@ -127,7 +125,7 @@ curses_line_input_dialog(const char *prompt, char *answer, int buffer) /* if messages were being suppressed for the remainder of the turn, re-activate them now that input is being requested */ - curs_mesg_suppress_turn = -1; + curses_got_input(); if (buffer >= (int) sizeof input) buffer = (int) sizeof input - 1; @@ -212,7 +210,7 @@ curses_character_input_dialog(const char *prompt, const char *choices, /* if messages were being suppressed for the remainder of the turn, re-activate them now that input is being requested */ - curs_mesg_suppress_turn = -1; + curses_got_input(); if (invent || (moves > 1)) { curses_get_window_size(MAP_WIN, &map_height, &map_width); diff --git a/win/curses/cursmain.c b/win/curses/cursmain.c index c3cceb1f7..9757e111a 100644 --- a/win/curses/cursmain.c +++ b/win/curses/cursmain.c @@ -232,6 +232,7 @@ curses_get_nh_event() if (do_reset) { getmaxyx(base_term, term_rows, term_cols); + curses_got_input(); /* reset More>> */ /* status_initialize, create_main_windows, last_messages, doredraw */ curs_reset_windows(TRUE, TRUE); } @@ -689,9 +690,6 @@ curses_nhgetch() { int ch; - /* if messages are being suppressed, reenable them */ - curs_mesg_suppress_turn = -1; - curses_prehousekeeping(); ch = curses_read_char(); curses_posthousekeeping(); diff --git a/win/curses/cursmesg.c b/win/curses/cursmesg.c index 2c1decb9a..656dce57d 100644 --- a/win/curses/cursmesg.c +++ b/win/curses/cursmesg.c @@ -9,10 +9,14 @@ #include "cursmesg.h" #include +/* + * Note: references to "More>>" mean ">>", the curses rendition of "--More--". + */ + /* player can type ESC at More>> prompt to avoid seeing more messages for the current move; but hero might get more than one move per turn, so the input routines need to be able to cancel this */ -long curs_mesg_suppress_turn = -1; /* also used in cursdial.c && cursmain.c */ +long curs_mesg_suppress_turn = -1; /* Message window routines for curses interface */ @@ -31,30 +35,30 @@ static void directional_scroll(winid wid, int nlines); static void mesg_add_line(const char *mline); static nhprev_mesg *get_msg_line(boolean reverse, int mindex); -static int turn_lines = 1; +static int turn_lines = 0; static int mx = 0; static int my = 0; /* message window text location */ static nhprev_mesg *first_mesg = NULL; static nhprev_mesg *last_mesg = NULL; static int max_messages; static int num_messages = 0; +static int last_messages = 0; /* Write string to the message window. Attributes set by calling function. */ void curses_message_win_puts(const char *message, boolean recursed) { - int height, width, linespace; + int height, width, border_space, linespace; char *tmpstr; WINDOW *win = curses_get_nhwin(MESSAGE_WIN); - boolean border = curses_window_has_border(MESSAGE_WIN); - int message_length = strlen(message); - int border_space = 0; + boolean bold, border = curses_window_has_border(MESSAGE_WIN); + int message_length = (int) strlen(message); #if 0 /* * This was useful when curses used genl_putmsghistory() but is not - * needed now that it has its own curses_putmsghistory() that's + * needed now that it has its own curses_putmsghistory() which is * capable of putting something into the ^P recall history without * displaying it at the same time. */ @@ -69,17 +73,11 @@ curses_message_win_puts(const char *message, boolean recursed) } curses_get_window_size(MESSAGE_WIN, &height, &width); - if (border) { - border_space = 1; - if (mx < 1) { - mx = 1; - } - if (my < 1) { - my = 1; - } - } - - linespace = ((width + border_space) - 3) - mx; + border_space = (border ? 1 : 0); + if (mx < border_space) + mx = border_space; + if (my < border_space) + my = border_space; if (strcmp(message, "#") == 0) { /* Extended command or Count: */ if ((strcmp(toplines, "#") != 0) @@ -90,7 +88,6 @@ curses_message_win_puts(const char *message, boolean recursed) my--; strcpy(toplines, message); } - return; } @@ -99,88 +96,142 @@ curses_message_win_puts(const char *message, boolean recursed) mesg_add_line(message); } + linespace = width - 3 - (mx - border_space); + if (linespace < message_length) { - if (my >= (height - 1 + border_space)) { + if (my - border_space >= height - 1) { /* bottom of message win */ - if ((turn_lines > height) || (height == 1)) { + if (++turn_lines >= height) { /* || height == 1) */ /* Pause until key is hit - Esc suppresses any further messages that turn */ if (curses_more() == '\033') { curs_mesg_suppress_turn = moves; return; } + /* turn_lines reset to 0 by more()->block()->got_input() */ } else { scroll_window(MESSAGE_WIN); - turn_lines++; } } else { if (mx != border_space) { my++; mx = border_space; + ++turn_lines; } } } - if (height > 1) { + bold = (height > 1 && !last_messages); + if (bold) curses_toggle_color_attr(win, NONE, A_BOLD, ON); - } - if ((mx == border_space) && ((message_length + 2) > width)) { + /* will this message fit as-is or do we need to split it? */ + if (mx == border_space && message_length > width - 2) { + /* split needed */ tmpstr = curses_break_str(message, (width - 2), 1); - mvwprintw(win, my, mx, "%s", tmpstr); - mx += strlen(tmpstr); - if (strlen(tmpstr) < (size_t) (width - 2)) { - mx++; - } + mvwprintw(win, my, mx, "%s", tmpstr), mx += (int) strlen(tmpstr); + /* one space to separate first part of message from rest [is this + actually useful?] */ + if (mx < width - 2) + ++mx; free(tmpstr); - if (height > 1) { + if (bold) curses_toggle_color_attr(win, NONE, A_BOLD, OFF); - } tmpstr = curses_str_remainder(message, (width - 2), 1); curses_message_win_puts(tmpstr, TRUE); free(tmpstr); } else { - mvwprintw(win, my, mx, "%s", message); - curses_toggle_color_attr(win, NONE, A_BOLD, OFF); - mx += message_length + 1; + mvwprintw(win, my, mx, "%s", message), mx += message_length; + /* two spaces to separate this message from next one if they happen + to fit on the same line; (FIXME: it would be better if this was + done at start of next message rather than end of this one since + it impacts placement of "More>>") */ + if (mx < width - 2) { + if (++mx < width - 2) + ++mx; + } + if (bold) + curses_toggle_color_attr(win, NONE, A_BOLD, OFF); } wrefresh(win); } +void +curses_got_input(void) +{ + /* if messages are being suppressed, reenable them */ + curs_mesg_suppress_turn = -1; + + /* misleadingly named; represents number of lines delivered since + player was sure to have had a chance to read them; if player + has just given input then there aren't any such lines right; + that includes responding to More>> even though it stays same turn */ + turn_lines = 0; +} int curses_block(boolean noscroll) /* noscroll - blocking because of msgtype * = stop/alert else blocking because * window is full, so need to scroll after */ { - int height, width, ret = 0; + static const char resp[] = " \r\n\033"; /* space, enter, esc */ + static int prev_x = -1, prev_y = -1, blink = 0; + int height, width, moreattr, oldcrsr, ret = 0, + brdroffset = curses_window_has_border(MESSAGE_WIN) ? 1 : 0; WINDOW *win = curses_get_nhwin(MESSAGE_WIN); - const char *resp = " \r\n\033"; /* space, enter, esc */ - - /* if messages are being suppressed, reenable them */ - curs_mesg_suppress_turn = -1; curses_get_window_size(MESSAGE_WIN, &height, &width); - curses_toggle_color_attr(win, MORECOLOR, NONE, ON); - mvwprintw(win, my, mx, ">>"); - curses_toggle_color_attr(win, MORECOLOR, NONE, OFF); + if (mx - brdroffset > width - 3) { /* -3: room for ">>_" */ + if (my - brdroffset < height - 1) + ++my, mx = brdroffset; + else + mx = width - 3 + brdroffset; + } + /* if ">>" (--More--) is being rendered at the same spot as before, + toggle attributes so that the first '>' starts blinking if it wasn't + or stops blinking if it was */ + if (mx == prev_x && my == prev_y) { + blink = 1 - blink; + } else { + prev_x = mx, prev_y = my; + blink = 0; + } + moreattr = !iflags.wc2_guicolor ? A_REVERSE : NONE; + curses_toggle_color_attr(win, MORECOLOR, moreattr, ON); + if (blink) { + wattron(win, A_BLINK); + mvwprintw(win, my, mx, ">"), mx += 1; + wattroff(win, A_BLINK); + waddstr(win, ">"), mx += 1; + } else { + mvwprintw(win, my, mx, ">>"), mx += 2; + } + curses_toggle_color_attr(win, MORECOLOR, moreattr, OFF); wrefresh(win); - /* msgtype=stop should require space/enter rather than - * just any key, as we want to prevent YASD from - * riding direction keys. */ - while ((ret = wgetch(win)) != 0 && !index(resp, (char) ret)) - continue; + + /* cancel mesg suppression; all messages will have had chance to be read */ + curses_got_input(); + + oldcrsr = curs_set(1); + do { + ret = wgetch(win); + if (ret == ERR || ret == '\0') + ret = '\n'; + /* msgtype=stop should require space/enter rather than any key, + as we want to prevent YASD from direction keys. */ + } while (!index(resp, (char) ret)); + if (oldcrsr >= 0) + (void) curs_set(oldcrsr); + if (height == 1) { curses_clear_unhighlight_message_window(); } else { - mvwprintw(win, my, mx, " "); + mx -= 2, mvwprintw(win, my, mx, " "); /* back up and blank out ">>" */ if (!noscroll) { scroll_window(MESSAGE_WIN); - turn_lines = 1; } wrefresh(win); } - return ret; } @@ -196,34 +247,25 @@ curses_more() void curses_clear_unhighlight_message_window() { - int mh, mw, count; - boolean border = curses_window_has_border(MESSAGE_WIN); + int mh, mw, count, + brdroffset = curses_window_has_border(MESSAGE_WIN) ? 1 : 0; WINDOW *win = curses_get_nhwin(MESSAGE_WIN); - turn_lines = 1; + turn_lines = 0; curses_get_window_size(MESSAGE_WIN, &mh, &mw); - mx = 0; - if (border) { - mx++; - } - if (mh == 1) { curses_clear_nhwin(MESSAGE_WIN); + mx = my = brdroffset; } else { - mx += mw; /* Force new line on new turn */ + mx = mw + brdroffset; /* Force new line on new turn */ - if (border) { - for (count = 0; count < mh; count++) { - mvwchgat(win, count + 1, 1, mw, COLOR_PAIR(8), A_NORMAL, NULL); - } - } else { - for (count = 0; count < mh; count++) { - mvwchgat(win, count, 0, mw, COLOR_PAIR(8), A_NORMAL, NULL); - } - } + for (count = 0; count < mh; count++) + mvwchgat(win, count + brdroffset, brdroffset, + mw, COLOR_PAIR(8), A_NORMAL, NULL); wnoutrefresh(win); } + wmove(win, my, mx); } @@ -235,19 +277,23 @@ curses_last_messages() { boolean border = curses_window_has_border(MESSAGE_WIN); nhprev_mesg *mesg; - int i; + int i, j, height, width; + + curses_get_window_size(MESSAGE_WIN, &height, &width); if (border) mx = my = 1; else mx = my = 0; - for (i = (num_messages - 1); i > 0; i--) { + ++last_messages; + for (j = 0, i = num_messages - 1; i > 0 && j < height; --i, ++j) { mesg = get_msg_line(TRUE, i); if (mesg && mesg->str && *mesg->str) curses_message_win_puts(mesg->str, TRUE); } curses_message_win_puts(toplines, TRUE); + --last_messages; } @@ -402,7 +448,7 @@ curses_message_win_getline(const char *prompt, char *answer, int buffer) maxlines = buffer / width * 2; Strcpy(tmpbuf, prompt); Strcat(tmpbuf, " "); - nlines = curses_num_lines(tmpbuf,width); + nlines = curses_num_lines(tmpbuf, width); maxlines += nlines * 2; linestarts = (char **) alloc((unsigned) (maxlines * sizeof (char *))); p_answer = tmpbuf + strlen(tmpbuf); @@ -463,6 +509,7 @@ curses_message_win_getline(const char *prompt, char *answer, int buffer) wmove(win, my, mx); curs_set(1); wrefresh(win); + curses_got_input(); /* despite its name, before rathre than after... */ #ifdef PDCURSES ch = wgetch(win); #else @@ -786,6 +833,7 @@ boolean restoring_msghist; --stash_count; mesg_turn = mesg->turn; mesg_add_line(mesg->str); + /* added line became new tail */ last_mesg->turn = mesg_turn; free((genericptr_t) mesg->str); free((genericptr_t) mesg); diff --git a/win/curses/cursmesg.h b/win/curses/cursmesg.h index 803b0fb5b..ca0752eea 100644 --- a/win/curses/cursmesg.h +++ b/win/curses/cursmesg.h @@ -10,6 +10,7 @@ /* Global declarations */ void curses_message_win_puts(const char *message, boolean recursed); +void curses_got_input(void); int curses_block(boolean require_tab); int curses_more(void); void curses_clear_unhighlight_message_window(void); diff --git a/win/curses/cursmisc.c b/win/curses/cursmisc.c index fd712fac6..223a8be82 100644 --- a/win/curses/cursmisc.c +++ b/win/curses/cursmisc.c @@ -42,6 +42,9 @@ curses_read_char() { int ch, tmpch; + /* cancel message suppression; all messages have had a chance to be read */ + curses_got_input(); + ch = getch(); tmpch = ch; ch = curses_convert_keys(ch);