From 0d31bb8c8803f2a33f86e4d85f59c1e1d1e27a59 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 6 Apr 2023 01:56:08 -0700 Subject: [PATCH] change program_state.getting_a_command to program_state.input_state Rename program_state.getting_a_command and give it an int value instead of treating it as boolean. Start using to it for Qt to suppress commands initiated from the drop down menus in the title bar if nethack isn't expecting to get a command from the user. Those menu choices inject extended command text into the input stream and should be avoided when entering text or waiting for a menu to be dismissed. These menus would be better if they grayed out unavailable choices when pulled down instead of accepting any choice and then treating that no good. I'm not going to try to figure out to do that with Qt. And this workaround for the menus doesn't have any affect on the toolbar buttons below the title bar. Those execute core commands directly and when I tried to use jacket routines instead, the tool bar stopped showing up so I've given up. This won't fix the reported problem of getting stuck in a loop. --- include/hack.h | 21 ++++++++++++++++----- src/cmd.c | 40 ++++++++++++++++++++++++++-------------- src/do_name.c | 1 - win/Qt/qt_main.cpp | 25 ++++++++++++++++++++++++- win/Qt/qt_main.h | 1 + win/curses/cursmisc.c | 2 +- 6 files changed, 68 insertions(+), 22 deletions(-) diff --git a/include/hack.h b/include/hack.h index ad215db96..46721704c 100644 --- a/include/hack.h +++ b/include/hack.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 hack.h $NHDT-Date: 1664837602 2022/10/03 22:53:22 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.196 $ */ +/* NetHack 3.7 hack.h $NHDT-Date: 1680771353 2023/04/06 08:55:53 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.215 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Pasi Kallinen, 2017. */ /* NetHack may be freely redistributed. See license for details. */ @@ -749,10 +749,21 @@ struct sinfo { int in_paniclog; /* writing a panicloc entry */ #endif int wizkit_wishing; /* starting wizard mode game w/ WIZKIT file */ - /* getting_a_command: only used for ALTMETA config to process ESC, but - present and updated unconditionally; set by parse() when requesting - next command keystroke, reset by readchar() as it returns a key */ - int getting_a_command; /* next key pressed will be entering a cmnd */ + /* input_state: used in the core for the 'altmeta' option to process ESC; + used in the curses interface to avoid arrow keys when user is doing + something other than entering a command or direction and in the Qt + interface to suppress menu commands in similar conditions; + readchar() alrways resets it to 'otherInp' prior to returning */ + int input_state; /* whether next key pressed will be entering a command */ +}; + +/* value of program_state.input_state, significant during readchar(); + get_count() expects digits then a command so sets it to commandInp */ +enum InputState { + otherInp = 0, /* 'other' */ + commandInp = 1, /* readchar() */ + getposInp = 2, /* getpos() */ + getdirInp = 3, /* getdir() */ }; /* sortloot() return type; needed before extern.h */ diff --git a/src/cmd.c b/src/cmd.c index a23155505..08ad9efa3 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -5209,7 +5209,7 @@ getdir(const char *s) } retry: - gp.program_state.getting_a_command = 1; /* arrow key support for curses */ + gp.program_state.input_state = getdirInp; if (gi.in_doagain || *readchar_queue) dirsym = readchar(); else @@ -6219,7 +6219,7 @@ get_count( unsigned gc_flags) /* control flags: GC_SAVEHIST, GC_ECHOFIRST */ { char qbuf[QBUFSZ]; - int key; + int key, save_input_state = gp.program_state.input_state; long cnt = 0L, first = inkey ? (long) (inkey - '0') : 0L; boolean backspaced = FALSE, showzero = TRUE, /* should "Count: 123" go into message history? */ @@ -6239,8 +6239,9 @@ get_count( key = inkey; inkey = '\0'; } else { - gp.program_state.getting_a_command = 1; /* readchar altmeta - * compatibility */ + /* if readchar() has already been called in this loop, it will + have reset input_state; put that back to its previous value */ + gp.program_state.input_state = save_input_state; key = readchar(); } @@ -6300,9 +6301,9 @@ parse(void) gc.context.move = TRUE; /* assume next command will take game time */ flush_screen(1); /* Flush screen buffer. Put the cursor on the hero. */ - gp.program_state.getting_a_command = 1; /* affects readchar() behavior for - * ESC iff 'altmeta' option is On; - * reset to 0 by readchar() */ + /* affects readchar() behavior for ESC iff 'altmeta' option is On; + reset to 0 by readchar() */ + gp.program_state.input_state = commandInp; if (!gc.Cmd.num_pad || (foo = readchar()) == gc.Cmd.spkeys[NHKF_COUNT]) { foo = get_count((char *) 0, '\0', LARGEST_INT, &gc.command_count, GC_NOFLAGS); @@ -6400,8 +6401,10 @@ readchar_core(coordxy *x, coordxy *y, int *mod) { register int sym; - if (iflags.debug_fuzzer) - return randomkey(); + if (iflags.debug_fuzzer) { + sym = randomkey(); + goto readchar_done; + } if (*readchar_queue) sym = *readchar_queue++; else if (gi.in_doagain) @@ -6431,9 +6434,11 @@ readchar_core(coordxy *x, coordxy *y, int *mod) sym = '\033'; #ifdef ALTMETA } else if (sym == '\033' && iflags.altmeta - && gp.program_state.getting_a_command) { + && gp.program_state.input_state != otherInp) { /* iflags.altmeta: treat two character ``ESC c'' as single `M-c' but - only when we're called by parse() [possibly via get_count()] */ + only when we're called by parse() [possibly via get_count()] + or getpos() [to support Alt+digit] or getdir() [for arrow keys + under curses] */ sym = *readchar_queue ? *readchar_queue++ : pgetchar(); if (sym == EOF || sym == 0) sym = '\033'; @@ -6445,12 +6450,15 @@ readchar_core(coordxy *x, coordxy *y, int *mod) gc.clicklook_cc.x = gc.clicklook_cc.y = -1; click_to_cmd(*x, *y, *mod); } - gp.program_state.getting_a_command = 0; /* next readchar() will be for an - * ordinary char unless parse() - * sets this back to 1 */ + + readchar_done: + /* next readchar() will be for an ordinary char unless parse() + sets this back to non-zero */ + gp.program_state.input_state = otherInp; return (char) sym; } +/* get a character */ char readchar(void) { @@ -6462,11 +6470,13 @@ readchar(void) return ch; } +/* used by getpos() to accept mouse input as well as keyboard input */ char readchar_poskey(coordxy *x, coordxy *y, int *mod) { char ch; + gp.program_state.input_state = getposInp; ch = readchar_core(x, y, mod); return ch; } @@ -6632,6 +6642,8 @@ yn_function( dumplogmsg(dumplog_buf); } #endif + /* in case we're called via getdir() which sets input_state */ + gp.program_state.input_state = otherInp; return res; } diff --git a/src/do_name.c b/src/do_name.c index 3371145de..9ddf98019 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -838,7 +838,6 @@ getpos(coord *ccp, boolean force, const char *goal) rushrun = FALSE; - gp.program_state.getting_a_command = 1; if ((cmdq = cmdq_pop()) != 0) { if (cmdq->typ == CMDQ_KEY) { c = cmdq->key; diff --git a/win/Qt/qt_main.cpp b/win/Qt/qt_main.cpp index 0bbddf9c6..a4c27a903 100644 --- a/win/Qt/qt_main.cpp +++ b/win/Qt/qt_main.cpp @@ -459,7 +459,8 @@ aboutMsg() char *p, vbuf[BUFSZ]; /* nethack's getversionstring() includes a final period but we're using it mid-sentence so strip period off */ - if ((p = strrchr(::getversionstring(vbuf, sizeof vbuf), '.')) != 0 && *(p + 1) == '\0') + if ((p = strrchr(::getversionstring(vbuf, sizeof vbuf), '.')) != 0 + && *(p + 1) == '\0') *p = '\0'; /* it's also long; break it into two pieces */ (void) strsubst(vbuf, " - ", "\n- "); @@ -1001,8 +1002,30 @@ public: }; #endif +/* used by doMenuItem() and for the toolbar buttons */ +bool NetHackQtMainWindow::ok_for_command() +{ + /* + * If the core expects text to be entered (perhaps typing in a wish, + * assigning a name to something, or answering a y/n prompt), or a + * map position or a direction is being picked, don't accept commands + * from the toolbar. + * + * FIXME: it would be much better to gray-out inapplicable entries + * when popping up a command menu instead of needing this. + */ + if (::gp.program_state.input_state != commandInp) { + NetHackQtBind::qt_nhbell(); + // possibly call doKeys("\033"); here? + return false; + } + return true; +} + void NetHackQtMainWindow::doMenuItem(QAction *action) { + if (!ok_for_command()) + return; /* this converts meta characters to '?'; menu processing has been changed to send multi-character "#abc" instead (already needed for commands that didn't have either a regular keystroke or a diff --git a/win/Qt/qt_main.h b/win/Qt/qt_main.h index 6925d156e..3aa5de79c 100644 --- a/win/Qt/qt_main.h +++ b/win/Qt/qt_main.h @@ -98,6 +98,7 @@ private: void ShowIfReady(); void AddToolButton(QToolBar *toolbar, QSignalMapper *sm, const char *name, int (*func)(void), QPixmap xpm); + static bool ok_for_command(); #ifdef KDE KMenuBar* menubar; diff --git a/win/curses/cursmisc.c b/win/curses/cursmisc.c index 916fe6daa..ef07fb602 100644 --- a/win/curses/cursmisc.c +++ b/win/curses/cursmisc.c @@ -856,7 +856,7 @@ Currently this is limited to arrow keys, but this may be expanded. */ int curses_convert_keys(int key) { - boolean reject = !gp.program_state.getting_a_command, + boolean reject = (gp.program_state.input_state == otherInp), as_is = FALSE, numpad_esc; int ret = key;