From d0c28d3f50ba9178462681114dcaef74addc15b1 Mon Sep 17 00:00:00 2001 From: lethosor Date: Fri, 1 Jan 2016 11:15:29 -0500 Subject: [PATCH] Prevent plugins with active viewscreens from being unloaded This requires plugins to pass plugin_self to Screen::show(), but avoids the need to implement special checks in plugin_onstatechange for the SC_BEGIN_UNLOAD event. --- NEWS.rst | 2 ++ library/PluginManager.cpp | 7 +++++++ library/include/modules/Screen.h | 6 +++++- library/modules/Screen.cpp | 26 +++++++++++++++++++++++++- plugins/autochop.cpp | 4 ++-- plugins/buildingplan.cpp | 2 +- plugins/command-prompt.cpp | 4 +--- plugins/dwarfmonitor.cpp | 8 ++++---- plugins/embark-tools.cpp | 16 ++++------------ plugins/hotkeys.cpp | 2 +- plugins/manipulator.cpp | 10 +++++----- plugins/stocks.cpp | 12 ++++-------- 12 files changed, 61 insertions(+), 38 deletions(-) diff --git a/NEWS.rst b/NEWS.rst index f42a52d5a..17ea64e38 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -41,6 +41,8 @@ Internals ./dfhack +devel/print-args example "Dwarf Fortress.exe" +devel/print-args example +- Prevented plugins with active viewscreens from being unloaded and causing a crash + New Scripts ----------- - `devel/save-version`: Displays DF version information about the current save diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index 2dad07797..9711e2e4d 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -24,6 +24,7 @@ distribution. #include "modules/EventManager.h" #include "modules/Filesystem.h" +#include "modules/Screen.h" #include "Internal.h" #include "Core.h" #include "MemAccess.h" @@ -385,6 +386,12 @@ bool Plugin::unload(color_ostream &con) // if we are actually loaded if(state == PS_LOADED) { + if (Screen::hasActiveScreens(this)) + { + con.printerr("Cannot unload plugin %s: has active viewscreens\n", name.c_str()); + access->unlock(); + return false; + } EventManager::unregisterAll(this); // notify the plugin about an attempt to shutdown if (plugin_onstatechange && diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h index 37dc9808c..2c4a29062 100644 --- a/library/include/modules/Screen.h +++ b/library/include/modules/Screen.h @@ -54,6 +54,7 @@ namespace df namespace DFHack { class Core; + class Plugin; typedef std::set interface_key_set; @@ -208,9 +209,12 @@ namespace DFHack DFHACK_EXPORT bool findGraphicsTile(const std::string &page, int x, int y, int *ptile, int *pgs = NULL); // Push and remove viewscreens - DFHACK_EXPORT bool show(df::viewscreen *screen, df::viewscreen *before = NULL); + DFHACK_EXPORT bool show(df::viewscreen *screen, df::viewscreen *before = NULL, Plugin *p = NULL); + inline bool show(df::viewscreen *screen, Plugin *p) + { return show(screen, NULL, p); } DFHACK_EXPORT void dismiss(df::viewscreen *screen, bool to_first = false); DFHACK_EXPORT bool isDismissed(df::viewscreen *screen); + DFHACK_EXPORT bool hasActiveScreens(Plugin *p); /// Retrieve the string representation of the bound key. DFHACK_EXPORT std::string getKeyDisplay(df::interface_key key); diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 0f79e1a46..71103ffda 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -282,7 +282,9 @@ bool Screen::findGraphicsTile(const std::string &pagename, int x, int y, int *pt return false; } -bool Screen::show(df::viewscreen *screen, df::viewscreen *before) +static std::map plugin_screens; + +bool Screen::show(df::viewscreen *screen, df::viewscreen *before, Plugin *plugin) { CHECK_NULL_POINTER(screen); CHECK_INVALID_ARGUMENT(!screen->parent && !screen->child); @@ -306,6 +308,9 @@ bool Screen::show(df::viewscreen *screen, df::viewscreen *before) if (dfhack_viewscreen::is_instance(screen)) static_cast(screen)->onShow(); + if (plugin) + plugin_screens[screen] = plugin; + return true; } @@ -313,6 +318,10 @@ void Screen::dismiss(df::viewscreen *screen, bool to_first) { CHECK_NULL_POINTER(screen); + auto it = plugin_screens.find(screen); + if (it != plugin_screens.end()) + plugin_screens.erase(it); + if (screen->breakdown_level != interface_breakdown_types::NONE) return; @@ -332,6 +341,21 @@ bool Screen::isDismissed(df::viewscreen *screen) return screen->breakdown_level != interface_breakdown_types::NONE; } +bool Screen::hasActiveScreens(Plugin *plugin) +{ + if (plugin_screens.empty()) + return false; + df::viewscreen *screen = &gview->view; + while (screen) + { + auto it = plugin_screens.find(screen); + if (it != plugin_screens.end() && it->second == plugin) + return true; + screen = screen->child; + } + return false; +} + #ifdef _LINUX // Link to the libgraphics class directly: class DFHACK_EXPORT enabler_inputst { diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index f7ba3d300..cbb5db0b3 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -600,7 +600,7 @@ struct autochop_hook : public df::viewscreen_dwarfmodest if (isInDesignationMenu() && input->count(interface_key::CUSTOM_C)) { sendKey(interface_key::LEAVESCREEN); - Screen::show(new ViewscreenAutochop()); + Screen::show(new ViewscreenAutochop(), plugin_self); } else { @@ -643,7 +643,7 @@ command_result df_autochop (color_ostream &out, vector & parameters) return CR_WRONG_USAGE; } if (Maps::IsValid()) - Screen::show(new ViewscreenAutochop()); + Screen::show(new ViewscreenAutochop(), plugin_self); return CR_OK; } diff --git a/plugins/buildingplan.cpp b/plugins/buildingplan.cpp index ddc804897..6f833ba6d 100644 --- a/plugins/buildingplan.cpp +++ b/plugins/buildingplan.cpp @@ -161,7 +161,7 @@ struct buildingplan_hook : public df::viewscreen_dwarfmodest } else if (input->count(interface_key::CUSTOM_SHIFT_M)) { - Screen::show(new ViewscreenChooseMaterial(planner.getDefaultItemFilterForType(type))); + Screen::show(new ViewscreenChooseMaterial(planner.getDefaultItemFilterForType(type)), plugin_self); } else if (input->count(interface_key::CUSTOM_SHIFT_Q)) { diff --git a/plugins/command-prompt.cpp b/plugins/command-prompt.cpp index dd3b59398..042d5f642 100644 --- a/plugins/command-prompt.cpp +++ b/plugins/command-prompt.cpp @@ -318,7 +318,7 @@ command_result show_prompt(color_ostream &out, std::vector & param std::string params; for(size_t i=0;icount(interface_key::CUSTOM_SHIFT_Z)) { @@ -1665,7 +1665,7 @@ private: static void open_stats_srceen() { - Screen::show(new ViewscreenFortStats()); + Screen::show(new ViewscreenFortStats(), plugin_self); } static void add_work_history(df::unit *unit, activity_type type) @@ -1915,12 +1915,12 @@ static command_result dwarfmonitor_cmd(color_ostream &out, vector & par else if (cmd == 's' || cmd == 'S') { if(Maps::IsValid()) - Screen::show(new ViewscreenFortStats()); + Screen::show(new ViewscreenFortStats(), plugin_self); } else if (cmd == 'p' || cmd == 'P') { if(Maps::IsValid()) - Screen::show(new ViewscreenPreferences()); + Screen::show(new ViewscreenPreferences(), plugin_self); } else if (cmd == 'r' || cmd == 'R') { diff --git a/plugins/embark-tools.cpp b/plugins/embark-tools.cpp index 7fa9cafca..d2bdf4909 100644 --- a/plugins/embark-tools.cpp +++ b/plugins/embark-tools.cpp @@ -21,6 +21,9 @@ using namespace DFHack; using df::global::enabler; using df::global::gps; +DFHACK_PLUGIN("embark-tools"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + #define FOR_ITER_TOOLS(iter) for(auto iter = tools.begin(); iter != tools.end(); iter++) void update_embark_sidebar (df::viewscreen_choose_start_sitest * screen) @@ -684,7 +687,7 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest void display_settings() { - Screen::show(new embark_tools_settings); + Screen::show(new embark_tools_settings, plugin_self); } inline bool is_valid_page() @@ -734,9 +737,6 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, feed); IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, render); -DFHACK_PLUGIN("embark-tools"); -DFHACK_PLUGIN_IS_ENABLED(is_enabled); - command_result embark_tools_cmd (color_ostream &out, std::vector & parameters); DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) @@ -783,14 +783,6 @@ DFhackCExport command_result plugin_enable (color_ostream &out, bool enable) DFhackCExport command_result plugin_onstatechange (color_ostream &out, state_change_event evt) { - if (evt == SC_BEGIN_UNLOAD) - { - if (Gui::getCurFocus() == "dfhack/embark-tools/options") - { - out.printerr("Settings screen active.\n"); - return CR_FAILURE; - } - } return CR_OK; } diff --git a/plugins/hotkeys.cpp b/plugins/hotkeys.cpp index 45ca07cbd..d7139dd96 100644 --- a/plugins/hotkeys.cpp +++ b/plugins/hotkeys.cpp @@ -319,7 +319,7 @@ static command_result hotkeys_cmd(color_ostream &out, vector & paramete if (Gui::getFocusString(top_screen) != "dfhack/viewscreen_hotkeys") { find_active_keybindings(top_screen); - Screen::show(new ViewscreenHotkeys(top_screen)); + Screen::show(new ViewscreenHotkeys(top_screen), plugin_self); } } } diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index a3f0f8ef8..e2a4e54a0 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -1773,14 +1773,14 @@ void viewscreen_unitlaborsst::feed(set *events) if (events->count(interface_key::CUSTOM_B)) { - Screen::show(new viewscreen_unitbatchopst(units, true, &do_refresh_names)); + Screen::show(new viewscreen_unitbatchopst(units, true, &do_refresh_names), plugin_self); } if (events->count(interface_key::CUSTOM_E)) { vector tmp; tmp.push_back(cur); - Screen::show(new viewscreen_unitbatchopst(tmp, false, &do_refresh_names)); + Screen::show(new viewscreen_unitbatchopst(tmp, false, &do_refresh_names), plugin_self); } if (events->count(interface_key::CUSTOM_P)) @@ -1791,11 +1791,11 @@ void viewscreen_unitlaborsst::feed(set *events) has_selected = true; if (has_selected) { - Screen::show(new viewscreen_unitprofessionset(units, true)); + Screen::show(new viewscreen_unitprofessionset(units, true), plugin_self); } else { vector tmp; tmp.push_back(cur); - Screen::show(new viewscreen_unitprofessionset(tmp, false)); + Screen::show(new viewscreen_unitprofessionset(tmp, false), plugin_self); } } @@ -2144,7 +2144,7 @@ struct unitlist_hook : df::viewscreen_unitlistst { if (units[page].size()) { - Screen::show(new viewscreen_unitlaborsst(units[page], cursor_pos[page])); + Screen::show(new viewscreen_unitlaborsst(units[page], cursor_pos[page]), plugin_self); return; } } diff --git a/plugins/stocks.cpp b/plugins/stocks.cpp index 85fedb9f2..8565c703f 100644 --- a/plugins/stocks.cpp +++ b/plugins/stocks.cpp @@ -771,7 +771,7 @@ public: } else if (input->count(interface_key::HELP)) { - Screen::show(new search_help); + Screen::show(new search_help, plugin_self); } bool key_processed = false; @@ -1425,7 +1425,7 @@ struct stocks_hook : public df::viewscreen_storesst if (input->count(interface_key::CUSTOM_E)) { Screen::dismiss(this); - Screen::show(new ViewscreenStocks()); + Screen::show(new ViewscreenStocks(), plugin_self); return; } INTERPOSE_NEXT(feed)(input); @@ -1457,7 +1457,7 @@ struct stocks_stockpile_hook : public df::viewscreen_dwarfmodest if (input->count(interface_key::CUSTOM_I)) { - Screen::show(new ViewscreenStocks(sp)); + Screen::show(new ViewscreenStocks(sp), plugin_self); return true; } @@ -1531,7 +1531,7 @@ static command_result stocks_cmd(color_ostream &out, vector & parameter } else if (toLower(parameters[0])[0] == 's') { - Screen::show(new ViewscreenStocks()); + Screen::show(new ViewscreenStocks(), plugin_self); return CR_OK; } } @@ -1557,10 +1557,6 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan case SC_MAP_LOADED: ViewscreenStocks::reset(); break; - case SC_BEGIN_UNLOAD: - if (Gui::getCurFocus().find("dfhack/stocks") == 0) - return CR_FAILURE; - break; default: break; }