|
From: <gi...@cr...> - 2025-12-02 02:40:15
|
via fc0747a1318971587091bcd2982e28368b67a057 (commit)
from 8a2acc8fe4edb7d8d02b253b7d5b91ab6a2eb48b (commit)
-----------------------------------------------------------------------
commit fc0747a1318971587091bcd2982e28368b67a057
Author: Isaac Clancy <ik...@ya...>
Date: Mon Nov 24 19:33:21 2025 +1300
Fix resizing the console in webtiles
When playing webtiles on a console, the game has to wait for input from
the console as well as from the websocket connection (to handle tiles
spectators joining). We were doing this by using `select` on stdin and
the websocket, however, we wouldn't stop waiting when a window resize
signal was triggered. To fix this, use `pselect` and handle the `EINTR`
result it returns to stop waiting and handle resize signals when they
arrive.
-----------------------------------------------------------------------
Summary of changes:
crawl-ref/source/libunix.cc | 130 +++++++++++++++++++++++---------------------
crawl-ref/source/tileweb.cc | 111 ++++++++++++++++++++++++-------------
crawl-ref/source/tileweb.h | 12 ++--
3 files changed, 145 insertions(+), 108 deletions(-)
diff --git a/crawl-ref/source/libunix.cc b/crawl-ref/source/libunix.cc
index f1f4d688ba..2f841a3e26 100644
--- a/crawl-ref/source/libunix.cc
+++ b/crawl-ref/source/libunix.cc
@@ -504,6 +504,52 @@ static int proc_mouse_event(int c, const MEVENT *me)
static int pending = 0;
+static bool _curses_fetch_pending_key(bool blocking)
+{
+ if (pending)
+ return true;
+
+ wint_t c;
+ int i;
+ if (blocking)
+ i = get_wch(&c);
+ else
+ {
+ nodelay(stdscr, TRUE);
+ // apparently some need this to guarantee non-blocking -- bwr
+ timeout(0);
+ i = get_wch(&c);
+ nodelay(stdscr, FALSE);
+ }
+
+ switch (i)
+ {
+ case ERR:
+ // getch() returns -1 on EOF, convert that into an Escape. Evil hack,
+ // but the alternative is to explicitly check for -1 everywhere where
+ // we might otherwise spin in a tight keyboard input loop.
+ // XXX: this doesn't work with `blocking` false, because we can't tell
+ // a timeout from an error...
+ if (!blocking)
+ return false;
+ pending = ESCAPE;
+ return true;
+ case OK:
+ // a normal (printable) key
+ pending = c;
+ return true;
+ case KEY_CODE_YES:
+ default:
+ pending = -c;
+ return true;
+ }
+}
+
+static bool _curses_has_key()
+{
+ return _curses_fetch_pending_key(false);
+}
+
static int _get_key_from_curses()
{
#ifdef WATCHDOG
@@ -512,38 +558,19 @@ static int _get_key_from_curses()
watchdog();
#endif
- if (pending)
- {
- int c = pending;
- pending = 0;
- return c;
- }
-
- wint_t c;
-
#ifdef USE_TILE_WEB
refresh();
-
tiles.redraw();
- tiles.await_input(c, true);
+ wint_t c = tiles.await_input(&_curses_has_key);
if (c != 0)
return c;
#endif
- switch (get_wch(&c))
- {
- case ERR:
- // getch() returns -1 on EOF, convert that into an Escape. Evil hack,
- // but the alternative is to explicitly check for -1 everywhere where
- // we might otherwise spin in a tight keyboard input loop.
- return ESCAPE;
- case OK:
- // a normal (printable) key
- return c;
- }
-
- return -c;
+ _curses_fetch_pending_key(true);
+ int result = pending;
+ pending = 0;
+ return result;
}
#if defined(KEY_RESIZE) || defined(USE_UNIX_SIGNALS)
@@ -574,10 +601,8 @@ static int _headless_getchk()
#ifdef USE_TILE_WEB
- wint_t c;
tiles.redraw();
- tiles.await_input(c, true);
-
+ wint_t c = tiles.await_input([]() { return false; });
if (c != 0)
return c;
#endif
@@ -591,11 +616,9 @@ static int _headless_getch_ck()
do
{
c = _headless_getchk();
- // TODO: release?
- // XX this should possibly sleep
- } while (
- ((c == CK_MOUSE_MOVE || c == CK_MOUSE_CLICK)
- && !crawl_state.mouse_enabled));
+ }
+ while ((c == CK_MOUSE_MOVE || c == CK_MOUSE_CLICK)
+ && !crawl_state.mouse_enabled);
return c;
}
@@ -1818,21 +1841,19 @@ void delay(unsigned int time)
static bool _headless_kbhit()
{
- // TODO: ??
if (pending)
return true;
#ifdef USE_TILE_WEB
- wint_t c;
- bool result = tiles.await_input(c, false);
-
- if (result && c != 0)
+ wint_t c = tiles.try_await_input();
+ if (c != 0)
+ {
pending = c;
+ return true;
+ }
+#endif
- return result;
-#else
return false;
-#endif
}
/* This is Juho Snellman's modified kbhit, to work with macros */
@@ -1844,32 +1865,17 @@ bool kbhit()
if (pending)
return true;
- wint_t c;
-#ifndef USE_TILE_WEB
- int i;
-
- nodelay(stdscr, TRUE);
- timeout(0); // apparently some need this to guarantee non-blocking -- bwr
- i = get_wch(&c);
- nodelay(stdscr, FALSE);
+ if (_curses_has_key())
+ return true;
- switch (i)
+#ifdef USE_TILE_WEB
+ wint_t c = tiles.try_await_input();
+ if (c != 0)
{
- case OK:
pending = c;
return true;
- case KEY_CODE_YES:
- pending = -c;
- return true;
- default:
- return false;
}
-#else
- bool result = tiles.await_input(c, false);
-
- if (result && c != 0)
- pending = c;
-
- return result;
#endif
+
+ return false;
}
diff --git a/crawl-ref/source/tileweb.cc b/crawl-ref/source/tileweb.cc
index 67b55fa69e..9edd28bf0d 100644
--- a/crawl-ref/source/tileweb.cc
+++ b/crawl-ref/source/tileweb.cc
@@ -7,6 +7,7 @@
#include <cerrno>
#include <cstdarg>
+#include <signal.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
@@ -610,61 +611,95 @@ wint_t TilesFramework::_handle_control_message(sockaddr_un addr, string data)
return c;
}
-bool TilesFramework::await_input(wint_t& c, bool block)
+wint_t TilesFramework::try_await_input()
{
+ if (m_sock_name.empty())
+ return 0;
+
+ fd_set fds;
+ int result;
+ while (true)
+ {
+ FD_ZERO(&fds);
+ FD_SET(m_sock, &fds);
+
+ timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ result = select(m_sock + 1, &fds, nullptr, nullptr, &timeout);
+ if (result == -1 && errno == EINTR)
+ continue;
+
+ if (result <= 0)
+ return 0;
+
+ wint_t c = _receive_control_message();
+ if (c != 0)
+ return c;
+ }
+}
+
+struct save_signal_mask
+{
+ save_signal_mask()
+ {
+ sigprocmask(SIG_SETMASK, nullptr, &old);
+ }
+
+ ~save_signal_mask()
+ {
+ sigprocmask(SIG_SETMASK, &old, nullptr);
+ }
+
+ sigset_t old;
+};
+
+wint_t TilesFramework::await_input(bool(*has_console_input)())
+{
+ if (m_sock_name.empty())
+ return 0;
+
int result;
fd_set fds;
- int maxfd = m_sock_name.empty() ? STDIN_FILENO : m_sock;
+ int maxfd = m_sock;
+
+ save_signal_mask saved_sig_mask;
+ sigset_t signals_to_wait_for;
+ sigemptyset(&signals_to_wait_for);
+ sigaddset(&signals_to_wait_for, SIGWINCH);
+ sigprocmask(SIG_BLOCK, &signals_to_wait_for, nullptr);
while (true)
{
- do
- {
- FD_ZERO(&fds);
- FD_SET(STDIN_FILENO, &fds);
- if (!m_sock_name.empty())
- FD_SET(m_sock, &fds);
-
- if (block)
- {
- tiles.flush_messages();
- result = select(maxfd + 1, &fds, nullptr, nullptr, nullptr);
- }
- else
- {
- timeval timeout;
- timeout.tv_sec = 0;
- timeout.tv_usec = 0;
+ FD_ZERO(&fds);
+ FD_SET(STDIN_FILENO, &fds);
+ FD_SET(m_sock, &fds);
- result = select(maxfd + 1, &fds, nullptr, nullptr, &timeout);
- }
- }
- while (result == -1 && errno == EINTR);
+ tiles.flush_messages();
- if (result == 0)
- return false;
- else if (result > 0)
+ if (has_console_input())
+ return 0;
+ result = pselect(maxfd + 1, &fds, nullptr, nullptr, nullptr,
+ &saved_sig_mask.old);
+ if (has_console_input())
+ return 0;
+ if (result == -1 && errno == EINTR || result == 0)
+ continue;
+ if (result > 0)
{
- if (!m_sock_name.empty() && FD_ISSET(m_sock, &fds))
+ if (FD_ISSET(m_sock, &fds))
{
- c = _receive_control_message();
-
+ wint_t c = _receive_control_message();
if (c != 0)
- return true;
- }
-
- if (FD_ISSET(STDIN_FILENO, &fds))
- {
- c = 0;
- return true;
+ return c;
}
}
else if (errno == EBADF)
{
// This probably means that stdin got closed because of a
// SIGHUP. We'll just return.
- c = 0;
- return false;
+ return 0;
}
else
die("select error: %s", strerror(errno));
diff --git a/crawl-ref/source/tileweb.h b/crawl-ref/source/tileweb.h
index 0d69aa0bf1..54f2c70838 100644
--- a/crawl-ref/source/tileweb.h
+++ b/crawl-ref/source/tileweb.h
@@ -169,6 +169,7 @@ public:
bool has_receivers() { return !m_dest_addrs.empty(); }
bool is_controlled_from_web() { return m_controlled_from_web; }
+ wint_t try_await_input();
/* Webtiles can receive input both via stdin, and on the
socket. Also, while waiting for input, it should be
able to handle other control messages (for example,
@@ -176,15 +177,10 @@ public:
This function waits until input is available either via
stdin or from a control message. If the input came from
- a control message, it will be written into c; otherwise,
- it still has to be read from stdin.
-
- If block is false, await_input will immediately return,
- even if no input is available. The return value indicates
- whether input can be read from stdin; c will be non-zero
- if input came via a control message.
+ a control message, it will be returned; otherwise, zero
+ will be returned and it still has to be read from stdin.
*/
- bool await_input(wint_t& c, bool block);
+ wint_t await_input(bool(*has_console_input)());
void check_for_control_messages();
--
Dungeon Crawl Stone Soup
|