implement "--More--" for Qt
Support MSGTYPE=stop by having qt_display_nhwindow(WIN_MESSAGE,TRUE) issue a tty-style --More-- prompt. For popup_dialog Off, the prompt gets appended to the most recent message; for popup_dialog On, it is issued via a popup and not displayed in the message window. It accepts <space>, ^J, ^M, and ^[ (ESC) to dismiss. There's no way to dismiss it with the mouse (for !popup_dialog) which might need some fix.... Several adventures along the way. The '^C-in-parent-terminal triggers infinite loop repeatedly complaining about "event loop already running"' is now a one-shot complaint. It isn't fixed but the severity of having it happen is greatly reduced.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.309 $ $NHDT-Date: 1600933440 2020/09/24 07:44:00 $
|
||||
NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.315 $ $NHDT-Date: 1601547360 2020/10/01 10:16:00 $
|
||||
|
||||
General Fixes and Modified Features
|
||||
-----------------------------------
|
||||
@@ -424,6 +424,7 @@ Qt: enable the popup_dialog WC option (result is a bit flakey but usable)
|
||||
Qt: 3.6 catchup - show unexplored locations as unexplored rather than as stone
|
||||
Qt: tried to honor 'showexp' but the value was unintentionally supressed by
|
||||
[lack of] obsolete conditional EXP_ON_BOTL
|
||||
Qt: implement --More-- prompt to support MSGTYPE=stop
|
||||
Qt+QSX: fix control key
|
||||
Qt+OSX: rename menu entry "nethack->Preferences..." for invoking nethack's
|
||||
'O' command to "Game->Run-time options" and entry "Game->Qt settings"
|
||||
|
||||
@@ -6,11 +6,15 @@ whether to [ignore], [report], or [restart] will eventually appear
|
||||
(it's slow). That should be repressed even if the report choice could
|
||||
be directed at nethack.org.
|
||||
|
||||
Urgent: launching Qt nethack as a synchronous subprocess (ie, no
|
||||
trailing '&') from a Terminal window, changing focus back to that
|
||||
terminal after NetHack has started, and typing ^C sends the program
|
||||
into an endless loop with repeated messages to stderr (the terminal)
|
||||
complaining that the event loop is already running.
|
||||
Launching Qt nethack as a synchronous subprocess (ie, no trailing '&')
|
||||
from a Terminal window, changing focus back to that terminal after
|
||||
NetHack has started, and typing ^C was sending the program into an
|
||||
endless loop in qt_nhgetch() with repeated messages to stderr
|
||||
(the terminal) complaining that the event loop is already running.
|
||||
Triggered by yn_function("Really quit?") in the core. That situation
|
||||
has been reduced to a single event loop complaint, do downgraded from
|
||||
"Urgent", but the prompt is auto-answered with ESC instead of letting
|
||||
the user respond.
|
||||
|
||||
On OSX, if the program is run from nethackdir/nethack rather than from
|
||||
NetHack.app/Contents/MacOS/nethack (plus having NetHack.app/Contents/
|
||||
|
||||
@@ -313,22 +313,26 @@ winid NetHackQtBind::qt_create_nhwindow(int type)
|
||||
void NetHackQtBind::qt_clear_nhwindow(winid wid)
|
||||
{
|
||||
NetHackQtWindow* window=id_to_window[(int)wid];
|
||||
window->Clear();
|
||||
if (window)
|
||||
window->Clear();
|
||||
}
|
||||
|
||||
void NetHackQtBind::qt_display_nhwindow(winid wid, BOOLEAN_P block)
|
||||
{
|
||||
NetHackQtWindow* window=id_to_window[(int)wid];
|
||||
window->Display(block);
|
||||
if (window)
|
||||
window->Display(block);
|
||||
}
|
||||
|
||||
void NetHackQtBind::qt_destroy_nhwindow(winid wid)
|
||||
{
|
||||
NetHackQtWindow* window=id_to_window[(int)wid];
|
||||
main->RemoveWindow(window);
|
||||
if (window->Destroy())
|
||||
delete window;
|
||||
id_to_window[(int)wid] = 0;
|
||||
if (window) {
|
||||
main->RemoveWindow(window);
|
||||
if (window->Destroy())
|
||||
delete window;
|
||||
id_to_window[(int) wid] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void NetHackQtBind::qt_curs(winid wid, int x, int y)
|
||||
@@ -476,7 +480,22 @@ int NetHackQtBind::qt_nhgetch()
|
||||
// Process events until a key arrives.
|
||||
//
|
||||
while (keybuffer.Empty()) {
|
||||
qApp->exec();
|
||||
int exc = qApp->exec();
|
||||
/*
|
||||
* On OSX (possibly elsewhere), this prevents an infinite
|
||||
* loop repeatedly issuing the complaint:
|
||||
QCoreApplication::exec: The event loop is already running
|
||||
* to stderr if you syncronously start nethack from a terminal
|
||||
* then switch focus back to that terminal and type ^C.
|
||||
* SIGINT -> done1() -> done2() -> yn_function("Really quit?")
|
||||
* in the core asks for another keystroke.
|
||||
*
|
||||
* However, it still issues one such complaint, and whatever
|
||||
* prompt wanted a response ("Really quit?") is shown in the
|
||||
* message window but is auto-answered with ESC.
|
||||
*/
|
||||
if (exc == -1)
|
||||
keybuffer.Put('\033');
|
||||
}
|
||||
|
||||
// after getting a key rather than before
|
||||
@@ -494,7 +513,10 @@ int NetHackQtBind::qt_nh_poskey(int *x, int *y, int *mod)
|
||||
// Process events until a key or map-click arrives.
|
||||
//
|
||||
while (keybuffer.Empty() && clickbuffer.Empty()) {
|
||||
qApp->exec();
|
||||
int exc = qApp->exec();
|
||||
// [see comment above in qt_nhgetch()]
|
||||
if (exc == -1)
|
||||
keybuffer.Put('\033');
|
||||
}
|
||||
|
||||
// after getting a key or click rather than before
|
||||
@@ -524,6 +546,70 @@ int NetHackQtBind::qt_doprev_message()
|
||||
return 0;
|
||||
}
|
||||
|
||||
// display "--More--" as a prompt and wait for a response from the user
|
||||
//
|
||||
// Used by qt_display_nhwindow(WIN_MESSAGE, TRUE) where second argument
|
||||
// True requests blocking. We need it to support MSGTYPE=stop but the
|
||||
// core also uses that in various other situations.
|
||||
char NetHackQtBind::qt_more()
|
||||
{
|
||||
char ch = '\033';
|
||||
|
||||
// without this gameover hack, quitting via menu or window close
|
||||
// button ends up provoking a complaint from qt_nhgetch() [see the
|
||||
// ^C comment in that routine] when the core triggers --More-- via
|
||||
// done2() -> really_done() -> display_nhwindow(WIN_MESSAGE, TRUE)
|
||||
// (get rid of this if the exec() loop issue gets properly fixed)
|
||||
if (::g.program_state.gameover)
|
||||
return ch; // bypass --More-- and just continue with program exit
|
||||
|
||||
NetHackQtMessageWindow *mesgwin = main ? main->GetMessageWindow() : NULL;
|
||||
|
||||
// kill any typeahead; for '!popup_dialog' this forces qt_nhgetch()
|
||||
keybuffer.Drain();
|
||||
|
||||
if (mesgwin && !::iflags.wc_popup_dialog && WIN_MESSAGE != WIN_ERR) {
|
||||
|
||||
mesgwin->AddToStr("--More--");
|
||||
bool retry = false;
|
||||
int complain = 0;
|
||||
do {
|
||||
ch = NetHackQtBind::qt_nhgetch();
|
||||
switch (ch) {
|
||||
case '\0': // hypothetical
|
||||
ch = '\033';
|
||||
/*FALLTHRU*/
|
||||
case ' ':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\033':
|
||||
retry = false;
|
||||
break;
|
||||
default:
|
||||
if (++complain > 1)
|
||||
NetHackQtBind::qt_nhbell();
|
||||
retry = true;
|
||||
break;
|
||||
}
|
||||
} while (retry);
|
||||
// unhighlight the line with the prompt; does not erase the window
|
||||
NetHackQtBind::qt_clear_nhwindow(WIN_MESSAGE);
|
||||
|
||||
} else {
|
||||
// use a popup dialog box; unlike yn_function(), we don't show
|
||||
// the prompt+response in the message window
|
||||
NetHackQtYnDialog dialog(main, "--More--", " \033\n\r", ' ');
|
||||
ch = dialog.Exec();
|
||||
if (ch == '\0') {
|
||||
ch = '\033';
|
||||
}
|
||||
// discard any input that YnDialog() might have left pending
|
||||
keybuffer.Drain();
|
||||
}
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
char NetHackQtBind::qt_yn_function(const char *question_,
|
||||
const char *choices, CHAR_P def)
|
||||
{
|
||||
@@ -533,10 +619,13 @@ char NetHackQtBind::qt_yn_function(const char *question_,
|
||||
int result = -1;
|
||||
|
||||
if (choices) {
|
||||
// anything beyond <esc> is hidden>
|
||||
QString choicebuf = choices;
|
||||
size_t cb = choicebuf.indexOf('\033');
|
||||
choicebuf = choicebuf.mid(0U, cb);
|
||||
QString choicebuf((int) strlen(choices) + 1, QChar('\0'));
|
||||
for (const char *p = choices; *p; ++p) {
|
||||
if (*p == '\033') // <esc> and anything beyond is hidden
|
||||
break;
|
||||
choicebuf += visctrl(*p);
|
||||
}
|
||||
choicebuf.truncate(QBUFSZ - 1); // no effect if already shorter
|
||||
message = QString("%1 [%2] ").arg(question, choicebuf);
|
||||
if (def)
|
||||
message += QString("(%1) ").arg(QChar(def));
|
||||
@@ -617,14 +706,16 @@ char NetHackQtBind::qt_yn_function(const char *question_,
|
||||
Sprintf(eos(cbuf), " %ld", ::yn_number);
|
||||
message += QString(" %1").arg(cbuf);
|
||||
|
||||
// add the prompt with appended response to the messsage window
|
||||
NetHackQtBind::qt_putstr(WIN_MESSAGE, ATR_BOLD, message);
|
||||
// add the prompt with appended response to the message window
|
||||
if (WIN_MESSAGE != WIN_ERR)
|
||||
NetHackQtBind::qt_putstr(WIN_MESSAGE, ATR_BOLD, message);
|
||||
|
||||
result = ret;
|
||||
}
|
||||
|
||||
// unhighlight the prompt; does not erase the multi-line message window
|
||||
NetHackQtBind::qt_clear_nhwindow(WIN_MESSAGE);
|
||||
if (WIN_MESSAGE != WIN_ERR)
|
||||
NetHackQtBind::qt_clear_nhwindow(WIN_MESSAGE);
|
||||
|
||||
return (char) result;
|
||||
}
|
||||
@@ -747,6 +838,7 @@ void NetHackQtBind::qt_putmsghistory(const char *msg, BOOLEAN_P is_restoring)
|
||||
}
|
||||
}
|
||||
|
||||
// event loop callback
|
||||
bool NetHackQtBind::notify(QObject *receiver, QEvent *event)
|
||||
{
|
||||
// Ignore Alt-key navigation to menubar, it's annoying when you
|
||||
@@ -756,7 +848,9 @@ bool NetHackQtBind::notify(QObject *receiver, QEvent *event)
|
||||
return true;
|
||||
|
||||
bool result = QApplication::notify(receiver, event);
|
||||
if (event->type() == QEvent::KeyPress) {
|
||||
int evtyp = event->type();
|
||||
|
||||
if (evtyp == QEvent::KeyPress) {
|
||||
QKeyEvent *key_event = (QKeyEvent *) event;
|
||||
|
||||
if (!key_event->isAccepted()) {
|
||||
@@ -778,10 +872,11 @@ bool NetHackQtBind::notify(QObject *receiver, QEvent *event)
|
||||
if (ch > 128)
|
||||
ch = 0;
|
||||
// on OSX, ascii control codes are not sent, force them
|
||||
if ((mod & Qt::ControlModifier) != 0) {
|
||||
if (ch == 0 && k >= Qt::Key_A && k <= Qt::Key_Underscore)
|
||||
if (ch == 0 && (mod & Qt::ControlModifier) != 0) {
|
||||
if (k >= Qt::Key_A && k <= Qt::Key_Underscore)
|
||||
ch = (QChar) (k - (Qt::Key_A - 1));
|
||||
}
|
||||
//raw_printf("notify()=%d \"%s\"", k, visctrl(ch.cell()));
|
||||
// if we have a valid character, queue it up
|
||||
if (ch != 0) {
|
||||
bool alt = ((mod & Qt::AltModifier) != 0
|
||||
@@ -792,6 +887,18 @@ bool NetHackQtBind::notify(QObject *receiver, QEvent *event)
|
||||
qApp->exit();
|
||||
result = true;
|
||||
}
|
||||
|
||||
#if 0 /* this was a failed attempt to prevent qt_more() from looping
|
||||
* after command+q (on OSX) is used to bring up the quit dialog;
|
||||
* now qt_more() uses an early return if program_state.gameover
|
||||
* is set */
|
||||
} else if (evtyp == QEvent::FocusOut
|
||||
|| evtyp == QEvent::ShortcutOverride
|
||||
|| evtyp == QEvent::PlatformSurface) {
|
||||
// leave qt_nhgetch()'s event loop if focus switches somewhere else
|
||||
qApp->exit();
|
||||
result = false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -71,6 +71,7 @@ public:
|
||||
static int qt_nh_poskey(int *x, int *y, int *mod);
|
||||
static void qt_nhbell();
|
||||
static int qt_doprev_message();
|
||||
static char qt_more();
|
||||
static char qt_yn_function(const char *question,
|
||||
const char *choices, CHAR_P def);
|
||||
static void qt_getlin(const char *prompt, char *line);
|
||||
|
||||
@@ -25,7 +25,7 @@ bool NetHackQtKeyBuffer::Full() const { return (in+1)%maxkey==out; }
|
||||
|
||||
void NetHackQtKeyBuffer::Put(int k, int a, uint kbstate)
|
||||
{
|
||||
//raw_printf("k:%3d a:%3d s:0x%08x", k, a, kbstate);
|
||||
//raw_printf("k:%3d a:'%s' s:0x%08x", k, visctrl((char) a), kbstate);
|
||||
if ( Full() ) return; // Safety
|
||||
key[in] = k;
|
||||
ascii[in] = a;
|
||||
|
||||
@@ -1225,10 +1225,10 @@ void NetHackQtMainWindow::keyPressEvent(QKeyEvent* event)
|
||||
if (message) message->Scroll(0,+1);
|
||||
break;
|
||||
case Qt::Key_Space:
|
||||
if ( flags.rest_on_space ) {
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
//if (flags.rest_on_space) {
|
||||
event->ignore(); // punt to NetHackQtBind::notify()
|
||||
return;
|
||||
//}
|
||||
case Qt::Key_Enter:
|
||||
if ( map )
|
||||
map->clickCursor();
|
||||
|
||||
@@ -75,15 +75,16 @@ void NetHackQtMessageWindow::ClearMessages()
|
||||
list->clear();
|
||||
}
|
||||
|
||||
void NetHackQtMessageWindow::Display(bool block UNUSED)
|
||||
void NetHackQtMessageWindow::Display(bool block)
|
||||
{
|
||||
//
|
||||
// FIXME: support for 'block' is necessary for MSGTYPE=stop
|
||||
//
|
||||
if (changed) {
|
||||
list->repaint();
|
||||
changed=false;
|
||||
}
|
||||
if (block) {
|
||||
// we don't care what the response is here
|
||||
(void) NetHackQtBind::qt_more();
|
||||
}
|
||||
}
|
||||
|
||||
const char * NetHackQtMessageWindow::GetStr(bool init)
|
||||
@@ -113,49 +114,73 @@ void NetHackQtMessageWindow::PutStr(int attr, const QString& text)
|
||||
} else {
|
||||
text2 = text;
|
||||
}
|
||||
|
||||
#if 0
|
||||
QListWidgetItem *item = new QListWidgetItem(text2);
|
||||
|
||||
QFont font = item->font();
|
||||
font.setUnderline(attr == ATR_ULINE);
|
||||
font.setWeight((attr == ATR_BOLD) ? QFont::Bold : QFont::Normal);
|
||||
item->setFont(font);
|
||||
|
||||
if (attr == ATR_DIM || attr == ATR_INVERSE) {
|
||||
QColor fg = item->foreground().color();
|
||||
QColor bg = item->background().color();
|
||||
if (attr == ATR_DIM) {
|
||||
fg.setAlpha(fg.alpha() / 2);
|
||||
new_fgbg = true;
|
||||
if (attr != ATR_NONE) {
|
||||
QListWidgetItem *item = new QListWidgetItem(text2);
|
||||
if (attr != ATR_DIM && attr != ATR_INVERSE) {
|
||||
QFont font = item->font();
|
||||
font.setUnderline(attr == ATR_ULINE);
|
||||
font.setWeight((attr == ATR_BOLD) ? QFont::Bold : QFont::Normal);
|
||||
item->setFont(font);
|
||||
// ATR_BLINK not supported
|
||||
} else {
|
||||
// ATR_DIM or ATR_INVERSE
|
||||
QBrush fg = item->foreground();
|
||||
QBrush bg = item->background();
|
||||
if (fg.color() == bg.color()) { // from menu coloring [AddRow()]
|
||||
// default foreground and background come up the same for
|
||||
// some unknown reason
|
||||
//[pr: both are set to 'Qt::color1' which has same RGB
|
||||
// value as 'Qt::black'; X11 on OSX behaves similarly]
|
||||
if (fg.color() == Qt::color1) {
|
||||
fg = Qt::black;
|
||||
bg = Qt::white;
|
||||
} else {
|
||||
fg = (bg.color() == Qt::white) ? Qt::black : Qt::white;
|
||||
}
|
||||
}
|
||||
if (attr == ATR_DIM) {
|
||||
QColor fg_clr = fg.color();
|
||||
fg_clr.setAlpha(fg_clr.alpha() / 2);
|
||||
item->setFlags(Qt::NoItemFlags);
|
||||
} else if (attr == ATR_INVERSE) {
|
||||
QBrush swapfb;
|
||||
swapfb = fg; fg = bg; bg = swapfb;
|
||||
}
|
||||
item->setForeground(fg);
|
||||
item->setBackground(bg);
|
||||
}
|
||||
if (attr == ATR_INVERSE) {
|
||||
QColor swap;
|
||||
swap = fg; fg = bg; bg = swap;
|
||||
}
|
||||
item->setForeground(fg);
|
||||
item->setBackground(bg);
|
||||
}
|
||||
// ATR_BLINK not supported
|
||||
#endif
|
||||
|
||||
if (list->count() >= (int) ::iflags.msg_history)
|
||||
delete list->item(0);
|
||||
list->addItem(text2);
|
||||
/* assert( list->count() > 0 ); */
|
||||
|
||||
// Force scrollbar to bottom
|
||||
// force scrollbar to bottom;
|
||||
// selects most recent message, which causes it to be highlighted
|
||||
list->setCurrentRow(list->count() - 1);
|
||||
|
||||
if (map)
|
||||
map->putMessage(attr, text2);
|
||||
}
|
||||
|
||||
// append the user's answer to a prompt message
|
||||
// append to the last message; usually the user's answer to a prompt
|
||||
void NetHackQtMessageWindow::AddToStr(const char *answer)
|
||||
{
|
||||
if (list) {
|
||||
QListWidgetItem *item = list->currentItem();
|
||||
int ct = 0;
|
||||
if (!item && (ct = list->count()) > 0) {
|
||||
list->setCurrentRow(ct - 1);
|
||||
item = list->currentItem();
|
||||
}
|
||||
if (item)
|
||||
item->setText(item->text() + QString(" %1").arg(answer));
|
||||
else // just in case...
|
||||
NetHackQtMessageWindow::PutStr(ATR_NONE, answer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user