implement basic overlay event logic

develop
myk002 2022-10-31 12:22:00 -07:00
parent d7fba5c720
commit bd318b7a21
No known key found for this signature in database
GPG Key ID: 8A39CA0FA0C16E78
2 changed files with 150 additions and 1 deletions

@ -1,3 +1,83 @@
local _ENV = mkmodule('plugins.overlay') local _ENV = mkmodule('plugins.overlay')
local widgets = require('gui.widgets')
local widget_db = {} -- map of widget name to state
local active_hotspot_widgets = {} -- map of widget names to the db entry
local active_viewscreen_widgets = {} -- map of vs_name to map of w.names -> db
local active_triggered_screen = nil
function reload()
widget_db = {}
active_hotspot_widgets = {}
active_viewscreen_widgets = {}
active_triggered_screen = nil
end
-- reduces the next call by a small random amount to introduce jitter into the
-- widget processing timings
local function do_update(db_entry, now_ms, vs)
if db_entry.next_update_ms > now_ms then return end
local w = db_entry.widget
local freq_ms = w.overlay_onupdate_max_freq_seconds * 1000
local jitter = math.rand(0, freq_ms // 8) -- up to ~12% jitter
db_entry.next_update_ms = now_ms + freq_ms - jitter
if w:overlay_onupdate(vs) then
active_triggered_screen = w:overlay_trigger()
if active_triggered_screen then return true end
end
end
function update_hotspot_widgets()
if active_triggered_screen then
if active_triggered_screen:isActive() then return end
active_triggered_screen = nil
end
local now_ms = dfhack.getTickCount()
for _,db_entry in pairs(active_hotspot_widgets) do
if do_update(db_entry, now_ms) then return end
end
end
function update_viewscreen_widgets(vs_name, vs)
local vs_widgets = active_viewscreen_widgets[vs_name]
if not vs_widgets then return end
local now_ms = dfhack.getTickCount()
for _,db_entry in pairs(vs_widgets) do
if do_update(db_entry, now_ms, vs) then return end
end
end
function feed_viewscreen_widgets(vs_name, keys)
local vs_widgets = active_viewscreen_widgets[vs_name]
if not vs_widgets then return false end
for _,db_entry in pairs(vs_widgets) do
if db_entry.widget:onInput(keys) then return true end
end
return false
end
function render_viewscreen_widgets(vs_name)
local vs_widgets = active_viewscreen_widgets[vs_name]
if not vs_widgets then return false end
local dc = Painter.new()
for _,db_entry in pairs(vs_widgets) do
db_entry.widget:render(dc)
end
end
-- called when the DF window is resized
function reposition_widgets()
local w, h = dscreen.getWindowSize()
local vr = ViewRect{rect=mkdims_wh(0, 0, w, h)}
for _,db_entry in pairs(widget_db) do
db_entry.widget:updateLayout(vr)
end
end
OverlayWidget = defclass(OverlayWidget, widgets.Widget)
OverlayWidget.ATTRS{
overlay_onupdate_max_freq_seconds=5,
}
return _ENV return _ENV

@ -88,6 +88,8 @@
#include "PluginManager.h" #include "PluginManager.h"
#include "VTableInterpose.h" #include "VTableInterpose.h"
#include "modules/Screen.h"
using namespace DFHack; using namespace DFHack;
DFHACK_PLUGIN("overlay"); DFHACK_PLUGIN("overlay");
@ -98,18 +100,76 @@ namespace DFHack {
DBG_DECLARE(overlay, event, DebugCategory::LINFO); DBG_DECLARE(overlay, event, DebugCategory::LINFO);
} }
static df::coord2d screenSize;
template<typename FA, typename FR>
static void call_overlay_lua(const char *fn_name, int nargs, int nres,
FA && args_lambda,
FR && res_lambda) {
DEBUG(event).print("calling overlay lua function: '%s'\n", fn_name);
CoreSuspender guard;
auto L = Lua::Core::State;
Lua::StackUnwinder top(L);
color_ostream &out = Core::getInstance().getConsole();
if (!lua_checkstack(L, 1 + nargs) ||
!Lua::PushModulePublic(
out, L, "plugins.overlay", fn_name)) {
out.printerr("Failed to load overlay Lua code\n");
return;
}
std::forward<FA&&>(args_lambda)(L);
if (!Lua::SafeCall(out, L, nargs, nres))
out.printerr("Failed Lua call to '%s'\n", fn_name);
std::forward<FR&&>(res_lambda)(L);
}
static auto DEFAULT_LAMBDA = [](lua_State *){};
template<typename FA>
static void call_overlay_lua(const char *fn_name, int nargs, int nres,
FA && args_lambda) {
call_overlay_lua(fn_name, nargs, nres, args_lambda, DEFAULT_LAMBDA);
}
static void call_overlay_lua(const char *fn_name) {
call_overlay_lua(fn_name, 0, 0, DEFAULT_LAMBDA, DEFAULT_LAMBDA);
}
template<class T> template<class T>
struct viewscreen_overlay : T { struct viewscreen_overlay : T {
typedef T interpose_base; typedef T interpose_base;
DEFINE_VMETHOD_INTERPOSE(void, logic, ()) { DEFINE_VMETHOD_INTERPOSE(void, logic, ()) {
INTERPOSE_NEXT(logic)(); INTERPOSE_NEXT(logic)();
call_overlay_lua("update_viewscreen_widgets", 2, 0, [&](lua_State *L) {
Lua::Push(L, T::_identity.getName());
Lua::Push(L, this);
});
} }
DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set<df::interface_key> *input)) { DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set<df::interface_key> *input)) {
bool input_is_handled = false;
call_overlay_lua("feed_viewscreen_widgets", 2, 1,
[&](lua_State *L) {
Lua::Push(L, T::_identity.getName());
Lua::PushInterfaceKeys(L, *input);
}, [&](lua_State *L) {
input_is_handled = lua_toboolean(L, -1);
});
if (!input_is_handled)
INTERPOSE_NEXT(feed)(input); INTERPOSE_NEXT(feed)(input);
} }
DEFINE_VMETHOD_INTERPOSE(void, render, ()) { DEFINE_VMETHOD_INTERPOSE(void, render, ()) {
INTERPOSE_NEXT(render)(); INTERPOSE_NEXT(render)();
call_overlay_lua("render_viewscreen_widgets", 2, 0, [&](lua_State *L) {
Lua::Push(L, T::_identity.getName());
Lua::Push(L, this);
});
} }
}; };
@ -320,6 +380,9 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector <Plugin
"Manage onscreen widgets.", "Manage onscreen widgets.",
overlay_cmd)); overlay_cmd));
screenSize = Screen::getWindowSize();
call_overlay_lua("reload");
return plugin_enable(out, true); return plugin_enable(out, true);
} }
@ -328,5 +391,11 @@ DFhackCExport command_result plugin_shutdown(color_ostream &out) {
} }
DFhackCExport command_result plugin_onupdate (color_ostream &out) { DFhackCExport command_result plugin_onupdate (color_ostream &out) {
df::coord2d newScreenSize = Screen::getWindowSize();
if (newScreenSize != screenSize) {
call_overlay_lua("reposition_widgets");
screenSize = newScreenSize;
}
call_overlay_lua("update_hotspot_widgets");
return CR_OK; return CR_OK;
} }