curses ">>" (terse "--More--")

I've noticed many instances of the game pausing and not being sure why,
then pressing <space> 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).
This commit is contained in:
PatR
2019-04-04 17:55:40 -07:00
parent 00aa7206f7
commit 8c4e792770
7 changed files with 131 additions and 80 deletions

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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();

View File

@@ -9,10 +9,14 @@
#include "cursmesg.h"
#include <ctype.h>
/*
* 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);

View File

@@ -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);

View File

@@ -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);