From e991dd1b0c4192495c7e9092c10fa83bb6d81a24 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 8 Feb 2019 14:54:40 -0800 Subject: [PATCH] curses: getline vs DEL, ESC Support (aka ) during getline(). It doesn't actually honor the current erase_char value set up for the terminal, just treats DEL the same as ^H. (The previous lack of support had nothing to do with terminfo specifying ^H; the handling is hard-coded.) tty treats escape while there's already some input as kill_char (erase the input but get more from scratch) and returns ESC if there isn't. curses was doing the first half but not the second, so not providing any way to communicate "cancel" back to the core. Fix is simple. Other getline() bug fixes: 1] there was a wprintw("%*something") which was passing the value from strlen (type 'size_t') to the "%*" argument (type 'int'). That's always wrong (size_t is guaranteed to be unsigned) and could be severe (if size_t is different width than int--as on current OSX systems-- depending upon the internals of argument passing). 2] strncpy() only supplies a terminating '\0' if the input is shorter than the number of characters specified. A lot of reformatting is warranted but I only did the getline routine (manually, so might have missed stuff). --- doc/fixes36.2 | 5 +- win/curses/cursmesg.c | 120 ++++++++++++++++++++++++++---------------- 2 files changed, 79 insertions(+), 46 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 3073017f8..da95df2c1 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.251 $ $NHDT-Date: 1549586901 2019/02/08 00:48:21 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.252 $ $NHDT-Date: 1549666475 2019/02/08 22:54:35 $ 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, @@ -442,6 +442,9 @@ curses: catch up with tty to not put dolook/whatis autodescribe feedback into curses: if the interface code ran out of memory, it would crash rather than attempt a controlled panic (which is fairly likely crash anyway if done when there's no memory available) +curses: when getting multi-character responses from player, support + as well as to remove last character entered; also, return + to core if ESC is typed when there is no input entered Platform- and/or Interface-Specific Fixes or Features diff --git a/win/curses/cursmesg.c b/win/curses/cursmesg.c index dc8aedc44..f5aa98f09 100644 --- a/win/curses/cursmesg.c +++ b/win/curses/cursmesg.c @@ -35,9 +35,7 @@ static nhprev_mesg *last_mesg = NULL; static int max_messages; static int num_messages = 0; - - -/* Write a string to the message window. Attributes set by calling function. */ +/* Write string to the message window. Attributes set by calling function. */ void curses_message_win_puts(const char *message, boolean recursed) @@ -56,8 +54,8 @@ curses_message_win_puts(const char *message, boolean recursed) * for intermediate counts, but get_count() also uses putmsghistory() * for the final count, to remember that without showing it. But * curses is using genl_putmsghistory() which just delivers the text - * via a normal pline(). This hides that at cost of not having in - * it ^P recall and being out of sync with DUMPLOG's message history. + * via a normal pline(). This hides that at cost of not having it + * in ^P recall and being out of sync with DUMPLOG's message history. */ if (strncmp("Count:", message, 6) == 0) { curses_count_window(message); @@ -199,11 +197,9 @@ curses_clear_unhighlight_message_window() WINDOW *win = curses_get_nhwin(MESSAGE_WIN); turn_lines = 1; - curses_get_window_size(MESSAGE_WIN, &mh, &mw); mx = 0; - if (border) { mx++; } @@ -214,7 +210,6 @@ curses_clear_unhighlight_message_window() mx += mw; /* 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); } @@ -223,7 +218,6 @@ curses_clear_unhighlight_message_window() mvwchgat(win, count, 0, mw, COLOR_PAIR(8), A_NORMAL, NULL); } } - wnoutrefresh(win); } } @@ -348,7 +342,7 @@ curses_count_window(const char *count_text) wrefresh(countwin); } - /* Gets a "line" (buffer) of input. */ +/* Gets a "line" (buffer) of input. */ void curses_message_win_getline(const char *prompt, char *answer, int buffer) { @@ -366,21 +360,24 @@ curses_message_win_getline(const char *prompt, char *answer, int buffer) int len = 0; /* of answer string */ boolean border = curses_window_has_border(MESSAGE_WIN); + *answer = '\0'; orig_cursor = curs_set(0); curses_get_window_size(MESSAGE_WIN, &height, &width); if (border) { border_space = 1; - if (mx < 1) mx = 1; - if (my < 1) my = 1; + if (mx < 1) + mx = 1; + if (my < 1) + my = 1; } maxy = height - 1 + border_space; maxx = width - 1 + border_space; tmpbuf = (char *) alloc((unsigned) ((int) strlen(prompt) + buffer + 2)); maxlines = buffer / width * 2; - strcpy(tmpbuf, prompt); - strcat(tmpbuf, " "); + Strcpy(tmpbuf, prompt); + Strcat(tmpbuf, " "); nlines = curses_num_lines(tmpbuf,width); maxlines += nlines * 2; linestarts = (char **) alloc((unsigned) (sizeof (char *) * maxlines)); @@ -388,44 +385,50 @@ curses_message_win_getline(const char *prompt, char *answer, int buffer) linestarts[0] = tmpbuf; if (mx > border_space) { /* newline */ - if (my >= maxy) scroll_window(MESSAGE_WIN); - else my++; + if (my >= maxy) + scroll_window(MESSAGE_WIN); + else + my++; mx = border_space; } curses_toggle_color_attr(win, NONE, A_BOLD, ON); - for (i = 0; i < nlines-1; i++) { - tmpstr = curses_break_str(linestarts[i],width-1,1); - linestarts[i+1] = linestarts[i] + strlen(tmpstr); - if (*linestarts[i+1] == ' ') linestarts[i+1]++; - mvwaddstr(win,my,mx,tmpstr); + for (i = 0; i < nlines - 1; i++) { + tmpstr = curses_break_str(linestarts[i], width - 1, 1); + linestarts[i + 1] = linestarts[i] + (int) strlen(tmpstr); + if (*linestarts[i + 1] == ' ') + linestarts[i + 1]++; + mvwaddstr(win, my, mx, tmpstr); free(tmpstr); if (++my >= maxy) { scroll_window(MESSAGE_WIN); my--; } } - mvwaddstr(win,my,mx,linestarts[nlines-1]); - mx = promptx = strlen(linestarts[nlines-1]) + border_space; + mvwaddstr(win, my, mx, linestarts[nlines - 1]); + mx = promptx = (int) strlen(linestarts[nlines - 1]) + border_space; promptline = nlines - 1; - while(1) { - mx = strlen(linestarts[nlines - 1]) + border_space; + while (1) { + mx = (int) strlen(linestarts[nlines - 1]) + border_space; if (mx > maxx) { if (nlines < maxlines) { - tmpstr = curses_break_str(linestarts[nlines - 1], width - 1, 1); - mx = strlen(tmpstr) + border_space; + tmpstr = curses_break_str(linestarts[nlines - 1], + width - 1, 1); + mx = (int) strlen(tmpstr) + border_space; mvwprintw(win, my, mx, "%*c", maxx - mx + 1, ' '); if (++my > maxy) { scroll_window(MESSAGE_WIN); my--; } mx = border_space; - linestarts[nlines] = linestarts[nlines - 1] + strlen(tmpstr); - if (*linestarts[nlines] == ' ') linestarts[nlines]++; + linestarts[nlines] = linestarts[nlines - 1] + + (int) strlen(tmpstr); + if (*linestarts[nlines] == ' ') + linestarts[nlines]++; mvwaddstr(win, my, mx, linestarts[nlines]); - mx = strlen(linestarts[nlines]) + border_space; + mx = (int) strlen(linestarts[nlines]) + border_space; nlines++; free(tmpstr); } else { @@ -440,15 +443,34 @@ curses_message_win_getline(const char *prompt, char *answer, int buffer) ch = wgetch(win); #else ch = getch(); +#endif +#if 0 /* [erase_char (delete one character) and kill_char (delete all + * characters) are from tty and not currently set up for curses] */ + if (ch == erase_char) { + ch = '\177'; /* match switch-case below */ + + /* honor kill_char if it's ^U or similar, but not if it's '@' */ + } else if (ch == kill_char && (ch < ' ' || ch >= '\177')) { /*ASCII*/ + if (len == 0) /* nothing to kill; just start over */ + continue; + ch = '\033'; /* get rid of all current input, then start over */ + } #endif curs_set(0); - switch(ch) { + switch (ch) { case '\033': /* DOESCAPE */ - /* blank the input but don't exit */ - while(nlines - 1 > promptline) { + /* if there isn't any input yet, return ESC */ + if (len == 0) { + Strcpy(answer, "\033"); + return; + } + /* otherwise, discard current input and start over; + first need to blank it from the screen */ + while (nlines - 1 > promptline) { if (nlines-- > height) { unscroll_window(MESSAGE_WIN); - tmpstr = curses_break_str(linestarts[nlines - height], width - 1, 1); + tmpstr = curses_break_str(linestarts[nlines - height], + width - 1, 1); mvwaddstr(win, border_space, border_space, tmpstr); free(tmpstr); } else { @@ -472,8 +494,9 @@ curses_message_win_getline(const char *prompt, char *answer, int buffer) case '\r': case '\n': free(linestarts); - strncpy(answer, p_answer, buffer); - strcpy(toplines, tmpbuf); + (void) strncpy(answer, p_answer, buffer); + answer[buffer - 1] = '\0'; + Strcpy(toplines, tmpbuf); mesg_add_line((char *) tmpbuf); free(tmpbuf); curs_set(orig_cursor); @@ -484,7 +507,9 @@ curses_message_win_getline(const char *prompt, char *answer, int buffer) } mx = border_space; return; - case '\b': + case '\177': /* DEL/Rubout */ + case KEY_DC: /* delete-character */ + case '\b': /* ^H (Backspace: '\011') */ case KEY_BACKSPACE: if (len < 1) { len = 1; @@ -493,23 +518,28 @@ curses_message_win_getline(const char *prompt, char *answer, int buffer) p_answer[--len] = '\0'; mvwaddch(win, my, --mx, ' '); /* try to unwrap back to the previous line if there is one */ - if (nlines > 1 && strlen(linestarts[nlines - 2]) < (size_t) width) { + if (nlines > 1 && (int) strlen(linestarts[nlines - 2]) < width) { mvwaddstr(win, my - 1, border_space, linestarts[nlines - 2]); if (nlines-- > height) { unscroll_window(MESSAGE_WIN); - tmpstr = curses_break_str(linestarts[nlines - height], width - 1, 1); + tmpstr = curses_break_str(linestarts[nlines - height], + width - 1, 1); mvwaddstr(win, border_space, border_space, tmpstr); free(tmpstr); } else { - /* clean up the leftovers on the next line, if we didn't scroll it away */ - mvwprintw(win, my--, border_space, "%*c", strlen(linestarts[nlines]), ' '); + /* clean up the leftovers on the next line, + if we didn't scroll it away */ + mvwprintw(win, my--, border_space, "%*c", + (int) strlen(linestarts[nlines]), ' '); } } break; default: p_answer[len++] = ch; - if (len >= buffer) len = buffer-1; - else mvwaddch(win, my, mx, ch); + if (len >= buffer) + len = buffer - 1; + else + mvwaddch(win, my, mx, ch); p_answer[len] = '\0'; } } @@ -519,13 +549,13 @@ curses_message_win_getline(const char *prompt, char *answer, int buffer) static void scroll_window(winid wid) { - directional_scroll(wid,1); + directional_scroll(wid, 1); } static void unscroll_window(winid wid) { - directional_scroll(wid,-1); + directional_scroll(wid, -1); } static void