diff --git a/docs/changelog.txt b/docs/changelog.txt index 2d21901ca3..39232d081a 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -62,6 +62,7 @@ Template for new versions: ## Fixes - Fixed ``scriptmanager.getModStatePath()`` causing scripts to crash when called - `prospect`: don't warn about a missing magma sea in the pre-embark estimate when the world was generated without one +- Core: Windows console will no longer hang on close (on some builds of Windows) ## Misc Improvements - `overlay`: errors raised by a bad C function result count or corrupted Lua call stack are now reported as Lua errors instead of crashing the game diff --git a/library/Console-windows.cpp b/library/Console-windows.cpp index 7dce13d56d..bb6b1fc8fb 100644 --- a/library/Console-windows.cpp +++ b/library/Console-windows.cpp @@ -73,6 +73,7 @@ namespace DFHack rawmode = 0; console_in = 0; console_out = 0; + exit_event = 0; ConsoleWindow = 0; default_attributes = 0; state = con_unclaimed; @@ -281,16 +282,21 @@ namespace DFHack inf.dwCursorPosition.X = (SHORT)plen; SetConsoleCursorPosition(console_out, inf.dwCursorPosition); + // exit_event comes first so shutdown wins if input is also + // pending. The console input handle is signaled whenever its + // input queue is non-empty, so the read below cannot block. + const HANDLE wait_handles[] = { exit_event, console_in }; while (1) { INPUT_RECORD rec; DWORD count; lock->unlock(); - if (ReadConsoleInputA(console_in, &rec, 1, &count) == 0) { - lock->lock(); + DWORD wait_result = WaitForMultipleObjects(2, wait_handles, FALSE, INFINITE); + lock->lock(); + if (wait_result != WAIT_OBJECT_0 + 1 + || ReadConsoleInputA(console_in, &rec, 1, &count) == 0) { return Console::SHUTDOWN; } - lock->lock(); if (rec.EventType != KEY_EVENT || !rec.Event.KeyEvent.bKeyDown) continue; switch (rec.Event.KeyEvent.wVirtualKeyCode) @@ -396,6 +402,7 @@ namespace DFHack int rawmode; HANDLE console_in; HANDLE console_out; + HANDLE exit_event; HWND ConsoleWindow; HWND MainWindow; WORD default_attributes; @@ -479,6 +486,9 @@ bool Console::init(bool) d->console_owned = AllocConsole() != FALSE; d->ConsoleWindow = GetConsoleWindow(); wlock = new std::recursive_mutex(); + d->exit_event = CreateEventW(NULL, TRUE, FALSE, NULL); + if (!d->exit_event) + return false; if (d->console_owned) { HMENU hm = GetSystemMenu(d->ConsoleWindow,false); @@ -537,6 +547,10 @@ bool Console::shutdown(void) { assert(inited); std::lock_guard lock{*wlock}; + // Wake any thread waiting on console input so it leaves prompt_loop + // before we detach or reconfigure the console below. + if (d->exit_event) + SetEvent(d->exit_event); if (d->console_owned) { FreeConsole();