Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Template for new versions:

## Fixes
- Fix broken weather lookup in ``World::ReadCurrentWeather``
- Fixed a possible hang or assertion failure when pressing a hotkey while a DFHack GUI window was open but unfocused
- `3dveins`: fix failure on embarks that cross midmap tiles
- `aquifer`: make ``--skip-top`` and top-relative ``--levels`` take effect for the ``drain``, ``convert``, and ``add`` actions instead of being silently ignored
- `autodump`: ``destroy`` no longer leaves the contents of destroyed containers in limbo or crashes when destroying unit-held items
Expand Down
3 changes: 3 additions & 0 deletions library/include/Core.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ namespace DFHack

/// check if the activity lock is owned by this thread
bool isSuspended(void);
/// check if this is DF's render thread, which can never acquire the
/// activity lock and therefore can never call into Lua
bool isRenderThread() { return df_render_thread == std::this_thread::get_id(); }
/// Is everything OK?
bool isValid(void) { return !errorstate; }

Expand Down
56 changes: 42 additions & 14 deletions library/modules/Gui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ distribution.
#include <string>
#include <vector>
#include <map>
#include <optional>

using std::string;
using std::vector;
Expand Down Expand Up @@ -1065,6 +1066,33 @@ bool Gui::any_job_hotkey(df::viewscreen *top)
|| workshop_job_hotkey(top);
}

// Resolves the selection reported by a DFHack viewscreen, if it can be
// queried safely on this thread. Returns the selection (possibly null) when
// the question is answered, or std::nullopt when the caller should continue
// to the native UI checks.
//
// getSelected*() on a lua-backed screen enters Lua through CoreSuspender,
// which asserts or deadlocks on the render thread, where hotkey guards run.
// A focused lua screen owns the selection, so we must report none; a
// defocused lua screen is transparent to the UI below, so the native checks
// still apply.
template<typename R>
static std::optional<R> dfscreen_selection(df::viewscreen *top,
R (dfhack_viewscreen::*method)())
{
auto dfscreen = dfhack_viewscreen::try_cast(top);
if (!dfscreen)
return std::nullopt;

if (!dfscreen->is_lua_screen() || !Core::getInstance().isRenderThread())
return (dfscreen->*method)();

if (dfscreen->isFocused())
return nullptr;

return std::nullopt;
}

df::job *Gui::getAnyWorkshopJob(df::viewscreen *top) {
auto bld = getAnyBuilding(top);
if (!bld)
Expand All @@ -1084,8 +1112,8 @@ df::job *Gui::getSelectedWorkshopJob(color_ostream &out, bool quiet) {
}

df::job *Gui::getAnyJob(df::viewscreen *top) {
if (auto dfscreen = dfhack_viewscreen::try_cast(top))
return dfscreen->getSelectedJob();
if (auto sel = dfscreen_selection(top, &dfhack_viewscreen::getSelectedJob))
return *sel;

if (matchFocusString("dwarfmode/Info/JOBS")) {
auto &cri_job = game->main_interface.info.jobs.cri_job;
Expand Down Expand Up @@ -1124,8 +1152,8 @@ df::job *Gui::getSelectedJob(color_ostream &out, bool quiet)

df::unit *Gui::getAnyUnit(df::viewscreen *top)
{
if (auto dfscreen = dfhack_viewscreen::try_cast(top))
return dfscreen->getSelectedUnit();
if (auto sel = dfscreen_selection(top, &dfhack_viewscreen::getSelectedUnit))
return *sel;

if (game->main_interface.view_sheets.open
&& game->main_interface.view_sheets.active_sheet == view_sheet_type::UNIT)
Expand Down Expand Up @@ -1417,8 +1445,8 @@ df::item *Gui::getAnyItem(df::viewscreen *top)
{
using df::global::game;

if (auto dfscreen = dfhack_viewscreen::try_cast(top))
return dfscreen->getSelectedItem();
if (auto sel = dfscreen_selection(top, &dfhack_viewscreen::getSelectedItem))
return *sel;

if (game->main_interface.view_sheets.open
&& game->main_interface.view_sheets.active_sheet == view_sheet_type::ITEM)
Expand Down Expand Up @@ -1568,8 +1596,8 @@ bool Gui::any_stockpile_hotkey(df::viewscreen* top)
}

df::building_stockpilest* Gui::getAnyStockpile(df::viewscreen* top) {
if (auto dfscreen = dfhack_viewscreen::try_cast(top))
return dfscreen->getSelectedStockpile();
if (auto sel = dfscreen_selection(top, &dfhack_viewscreen::getSelectedStockpile))
return *sel;

if (game->main_interface.bottom_mode_selected == main_bottom_mode_type::STOCKPILE)
return game->main_interface.stockpile.cur_bld;
Expand All @@ -1591,8 +1619,8 @@ bool Gui::any_civzone_hotkey(df::viewscreen* top) {
}

df::building_civzonest *Gui::getAnyCivZone(df::viewscreen* top) {
if (auto dfscreen = dfhack_viewscreen::try_cast(top))
return dfscreen->getSelectedCivZone();
if (auto sel = dfscreen_selection(top, &dfhack_viewscreen::getSelectedCivZone))
return *sel;

if (game->main_interface.bottom_mode_selected == main_bottom_mode_type::ZONE)
return game->main_interface.civzone.cur_bld;
Expand All @@ -1611,8 +1639,8 @@ df::building_civzonest *Gui::getSelectedCivZone(color_ostream &out, bool quiet)

df::building *Gui::getAnyBuilding(df::viewscreen *top)
{
if (auto dfscreen = dfhack_viewscreen::try_cast(top))
return dfscreen->getSelectedBuilding();
if (auto sel = dfscreen_selection(top, &dfhack_viewscreen::getSelectedBuilding))
return *sel;

if (game->main_interface.view_sheets.open
&& game->main_interface.view_sheets.active_sheet == view_sheet_type::BUILDING)
Expand Down Expand Up @@ -1686,8 +1714,8 @@ df::plant *Gui::getAnyPlant(df::viewscreen *top)
{
using df::global::cursor;

if (auto dfscreen = dfhack_viewscreen::try_cast(top))
return dfscreen->getSelectedPlant();
if (auto sel = dfscreen_selection(top, &dfhack_viewscreen::getSelectedPlant))
return *sel;

if (Gui::dwarfmode_hotkey(top) && has_cursor())
{
Expand Down
Loading