Merge pull request #746 from lethosor/gui-hooks

Support for hooking into GUI-related functions
develop
Lethosor 2015-11-16 21:38:07 -05:00
commit 140da9ff37
11 changed files with 262 additions and 69 deletions

@ -119,6 +119,7 @@ include/modules/Units.h
include/modules/Engravings.h
include/modules/EventManager.h
include/modules/Gui.h
include/modules/GuiHooks.h
include/modules/Items.h
include/modules/Job.h
include/modules/kitchen.h

@ -353,7 +353,6 @@ bool Plugin::load(color_ostream &con)
plugin_enable = (command_result (*)(color_ostream &,bool)) LookupPlugin(plug, "plugin_enable");
plugin_is_enabled = (bool*) LookupPlugin(plug, "plugin_is_enabled");
plugin_eval_ruby = (command_result (*)(color_ostream &, const char*)) LookupPlugin(plug, "plugin_eval_ruby");
plugin_get_exports = (PluginExports* (*)(void)) LookupPlugin(plug, "plugin_get_exports");
index_lua(plug);
plugin_lib = plug;
commands.clear();
@ -611,16 +610,6 @@ Plugin::plugin_state Plugin::getState() const
return state;
}
PluginExports *Plugin::getExports()
{
if (!plugin_get_exports)
return NULL;
PluginExports *exports = plugin_get_exports();
if (!exports->bind(plugin_lib))
return NULL;
return exports;
};
void Plugin::index_lua(DFLibrary *lib)
{
if (auto cmdlist = (CommandReg*)LookupPlugin(lib, "plugin_lua_commands"))
@ -793,19 +782,6 @@ void Plugin::push_function(lua_State *state, LuaFunction *fn)
lua_pushcclosure(state, lua_fun_wrapper, 4);
}
bool PluginExports::bind(DFLibrary *lib)
{
for (auto it = bindings.begin(); it != bindings.end(); ++it)
{
std::string name = it->first;
void** dest = it->second;
*dest = LookupPlugin(lib, name.c_str());
if (!*dest)
return false;
}
return true;
}
PluginManager::PluginManager(Core * core) : core(core)
{
plugin_mutex = new recursive_mutex();
@ -960,16 +936,6 @@ Plugin *PluginManager::getPluginByCommand(const std::string &command)
return NULL;
}
void *PluginManager::getPluginExports(const std::string &name)
{
Plugin *plug = getPluginByName(name);
if (!plug)
return NULL;
if (plug->getState() != Plugin::plugin_state::PS_LOADED)
return NULL;
return plug->getExports();
}
// FIXME: handle name collisions...
command_result PluginManager::InvokeCommand(color_ostream &out, const std::string & command, std::vector <std::string> & parameters)
{

@ -50,7 +50,6 @@ namespace df
namespace DFHack
{
class Core;
class PluginExports;
class PluginManager;
class virtual_identity;
class RPCService;
@ -170,7 +169,6 @@ namespace DFHack
command_result invoke(color_ostream &out, const std::string & command, std::vector <std::string> & parameters);
bool can_invoke_hotkey(const std::string & command, df::viewscreen *top );
plugin_state getState () const;
PluginExports *getExports();
RPCService *rpc_connect(color_ostream &out);
@ -237,16 +235,7 @@ namespace DFHack
command_result (*plugin_enable)(color_ostream &, bool);
RPCService* (*plugin_rpcconnect)(color_ostream &);
command_result (*plugin_eval_ruby)(color_ostream &, const char*);
PluginExports* (*plugin_get_exports)(void);
};
class DFHACK_EXPORT PluginExports {
protected:
friend class Plugin;
std::map<std::string, void**> bindings;
bool bind(DFLibrary* lib);
};
#define PLUGIN_EXPORT_BIND(sym) bindings.insert(std::pair<std::string, void**>(#sym, (void**)&this->sym))
#define PLUGIN_EXPORT_BINDN(sym, name) bindings.insert(std::pair<std::string, void**>(name, (void**)&this->sym))
class DFHACK_EXPORT PluginManager
{
// PRIVATE METHODS
@ -275,7 +264,6 @@ namespace DFHack
Plugin *getPluginByName (const std::string &name) { return (*this)[name]; }
Plugin *getPluginByCommand (const std::string &command);
void *getPluginExports(const std::string &name);
command_result InvokeCommand(color_ostream &out, const std::string & command, std::vector <std::string> & parameters);
bool CanInvokeHotkey(const std::string &command, df::viewscreen *top);
Plugin* operator[] (const std::string name);
@ -324,17 +312,6 @@ namespace DFHack
DFhackDataExport bool plugin_is_enabled = false; \
bool &varname = plugin_is_enabled;
#define DFHACK_PLUGIN_EXPORTS(clsname) \
DFhackCExport PluginExports* plugin_get_exports() \
{ \
static clsname* instance = NULL; \
if (!instance) \
instance = new clsname; \
return (PluginExports*)instance; \
}
#define GET_PLUGIN_EXPORTS(plugname, clsname) \
(clsname*)DFHack::Core::getInstance().getPluginManager()->getPluginExports(plugname)
#define DFHACK_PLUGIN_LUA_COMMANDS \
DFhackCExport const DFHack::CommandReg plugin_lua_commands[] =
#define DFHACK_PLUGIN_LUA_FUNCTIONS \

@ -38,6 +38,8 @@ distribution.
#include "df/announcement_flags.h"
#include "df/unit_report_type.h"
#include "modules/GuiHooks.h"
namespace df {
struct viewscreen;
struct job;
@ -150,6 +152,10 @@ namespace DFHack
DFHACK_EXPORT bool setDesignationCoords (const int32_t x, const int32_t y, const int32_t z);
DFHACK_EXPORT bool getMousePos (int32_t & x, int32_t & y);
// The distance from the z-level of the tile at map coordinates (x, y) to the closest ground z-level below
// Defaults to 0, unless overriden by plugins
DFHACK_EXPORT int getDepthAt (int32_t x, int32_t y);
/*
* Gui screens
*/
@ -183,5 +189,10 @@ namespace DFHack
DFHACK_EXPORT bool getMenuWidth(uint8_t & menu_width, uint8_t & area_map_width);
DFHACK_EXPORT bool setMenuWidth(const uint8_t menu_width, const uint8_t area_map_width);
namespace Hooks {
GUI_HOOK_DECLARE(depth_at, int, (int32_t x, int32_t y));
GUI_HOOK_DECLARE(dwarfmode_view_dims, DwarfmodeDims, ());
}
}
}

@ -0,0 +1,71 @@
#pragma once
#include <vector>
namespace DFHack {
#define GUI_HOOK_DECLARE(name, rtype, args) DFHACK_EXPORT extern DFHack::GuiHooks::Hook<rtype args> name
#define GUI_HOOK_DEFINE(name, base_func) DFHack::GuiHooks::Hook<decltype(base_func)> name(base_func)
#define GUI_HOOK_TOP(name) name.top()
#define GUI_HOOK_CALLBACK(hook, name, callback) DFHack::GuiHooks::Hook<decltype(callback)>::Callback name(&hook, callback)
namespace GuiHooks {
template <typename T_func>
class Hook {
typedef Hook<T_func> T_hook;
friend class Callback;
T_func *base_func;
std::vector<T_func*> funcs;
void add(T_func *func)
{
if (std::find(funcs.begin(), funcs.end(), func) == funcs.end())
funcs.push_back(func);
}
void remove(T_func *func)
{
auto it = std::find(funcs.begin(), funcs.end(), func);
if (it != funcs.end())
funcs.erase(it);
}
public:
Hook(T_func* base) : base_func(base)
{ }
T_func* top()
{
return funcs.empty() ? base_func : funcs[funcs.size() - 1];
}
T_func* next(T_func* cur)
{
if (funcs.size())
{
auto it = std::find(funcs.begin(), funcs.end(), cur);
if (it != funcs.end() && it != funcs.begin())
return *(it - 1);
}
return base_func;
}
class Callback {
T_func *func;
T_hook *hook;
bool enabled;
public:
Callback(T_hook *hook, T_func *func) : hook(hook), func(func)
{ }
~Callback()
{
disable();
}
inline T_func *next() { return hook->next(func); }
void apply (bool enable)
{
if (enable)
hook->add(func);
else
hook->remove(func);
enabled = enable;
}
inline void enable() { apply(true); }
inline void disable() { apply(false); }
inline bool is_enabled() { return enabled; }
inline void toggle() { apply(!enabled); }
};
};
}
}

@ -36,6 +36,8 @@ distribution.
#include "df/graphic.h"
#include "df/viewscreen.h"
#include "modules/GuiHooks.h"
namespace df
{
struct job;
@ -288,6 +290,11 @@ namespace DFHack
private:
void do_paint_string(const std::string &str, const Pen &pen);
};
namespace Hooks {
GUI_HOOK_DECLARE(set_tile, void, (const Pen &pen, int x, int y, bool map));
}
}
class DFHACK_EXPORT dfhack_viewscreen : public df::viewscreen {

@ -1358,9 +1358,9 @@ df::coord Gui::getCursorPos()
return df::coord(cursor->x, cursor->y, cursor->z);
}
Gui::DwarfmodeDims Gui::getDwarfmodeViewDims()
Gui::DwarfmodeDims getDwarfmodeViewDims_default()
{
DwarfmodeDims dims;
Gui::DwarfmodeDims dims;
auto ws = Screen::getWindowSize();
dims.y1 = 1;
@ -1403,6 +1403,12 @@ Gui::DwarfmodeDims Gui::getDwarfmodeViewDims()
return dims;
}
GUI_HOOK_DEFINE(Gui::Hooks::dwarfmode_view_dims, getDwarfmodeViewDims_default);
Gui::DwarfmodeDims Gui::getDwarfmodeViewDims()
{
return GUI_HOOK_TOP(Gui::Hooks::dwarfmode_view_dims)();
}
void Gui::resetDwarfmodeView(bool pause)
{
using df::global::cursor;
@ -1524,6 +1530,17 @@ bool Gui::getMousePos (int32_t & x, int32_t & y)
return (x == -1) ? false : true;
}
int getDepthAt_default (int32_t x, int32_t y)
{
return 0;
}
GUI_HOOK_DEFINE(Gui::Hooks::depth_at, getDepthAt_default);
int Gui::getDepthAt (int32_t x, int32_t y)
{
return GUI_HOOK_TOP(Gui::Hooks::depth_at)(x, y);
}
bool Gui::getWindowSize (int32_t &width, int32_t &height)
{
if (gps) {

@ -32,6 +32,7 @@ distribution.
using namespace std;
#include "modules/Screen.h"
#include "modules/GuiHooks.h"
#include "MemAccess.h"
#include "VersionInfo.h"
#include "Types.h"
@ -93,8 +94,9 @@ bool Screen::inGraphicsMode()
return init && init->display.flag.is_set(init_display_flags::USE_GRAPHICS);
}
static void doSetTile(const Pen &pen, int index)
static void doSetTile_default(const Pen &pen, int x, int y, bool map)
{
int index = ((x * gps->dimy) + y);
auto screen = gps->screen + index*4;
screen[0] = uint8_t(pen.ch);
screen[1] = uint8_t(pen.fg) & 15;
@ -107,6 +109,12 @@ static void doSetTile(const Pen &pen, int index)
gps->screentexpos_cbr[index] = pen.tile_bg;
}
GUI_HOOK_DEFINE(Screen::Hooks::set_tile, doSetTile_default);
static void doSetTile(const Pen &pen, int x, int y, bool map)
{
GUI_HOOK_TOP(Screen::Hooks::set_tile)(pen, x, y, map);
}
bool Screen::paintTile(const Pen &pen, int x, int y)
{
if (!gps || !pen.valid()) return false;
@ -114,7 +122,7 @@ bool Screen::paintTile(const Pen &pen, int x, int y)
auto dim = getWindowSize();
if (x < 0 || x >= dim.x || y < 0 || y >= dim.y) return false;
doSetTile(pen, x*dim.y + y);
doSetTile(pen, x, y, false);
return true;
}
@ -188,10 +196,8 @@ bool Screen::fillRect(const Pen &pen, int x1, int y1, int x2, int y2)
for (int x = x1; x <= x2; x++)
{
int index = x*dim.y;
for (int y = y1; y <= y2; y++)
doSetTile(pen, index+y);
doSetTile(pen, x, y, false);
}
return true;
@ -208,13 +214,13 @@ bool Screen::drawBorder(const std::string &title)
for (int x = 0; x < dim.x; x++)
{
doSetTile(border, x * dim.y + 0);
doSetTile(border, x * dim.y + dim.y - 1);
doSetTile(border, x, 0, false);
doSetTile(border, x, dim.y - 1, false);
}
for (int y = 0; y < dim.y; y++)
{
doSetTile(border, 0 * dim.y + y);
doSetTile(border, (dim.x - 1) * dim.y + y);
doSetTile(border, 0, y, false);
doSetTile(border, dim.x - 1, y, false);
}
paintString(signature, dim.x-8, dim.y-1, "DFHack");

@ -5,6 +5,7 @@ endif()
ADD_DEFINITIONS(-DDEV_PLUGIN)
#DFHACK_PLUGIN(autolabor2 autolabor2.cpp)
DFHACK_PLUGIN(buildprobe buildprobe.cpp)
DFHACK_PLUGIN(color-dfhack-text color-dfhack-text.cpp)
DFHACK_PLUGIN(counters counters.cpp)
DFHACK_PLUGIN(dumpmats dumpmats.cpp)
DFHACK_PLUGIN(eventExample eventExample.cpp)

@ -0,0 +1,136 @@
#include "Console.h"
#include "Core.h"
#include "DataDefs.h"
#include "Export.h"
#include "PluginManager.h"
#include "modules/Gui.h"
#include "modules/Screen.h"
using namespace DFHack;
DFHACK_PLUGIN("color-dfhack-text");
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
REQUIRE_GLOBAL(gps);
struct {
bool flicker;
uint8_t color;
short tick;
} config;
void color_text_tile(const Screen::Pen &pen, int x, int y, bool map);
GUI_HOOK_CALLBACK(Screen::Hooks::set_tile, color_text_hook, color_text_tile);
void color_text_tile(const Screen::Pen &pen, int x, int y, bool map)
{
Screen::Pen pen2 = pen;
uint8_t color = config.flicker ? config.tick % 8 : config.color;
pen2.fg = color;
pen2.bg = color;
pen2.bold = true;
return color_text_hook.next()(pen2, x, y, map);
}
void aaaaa_set_tile(const Screen::Pen &pen, int x, int y, bool map);
GUI_HOOK_CALLBACK(Screen::Hooks::set_tile, aaaaa_set_tile_hook, aaaaa_set_tile);
void aaaaa_set_tile(const Screen::Pen &pen, int x, int y, bool map)
{
Screen::Pen pen2 = pen;
if ((pen.ch >= 'A' && pen.ch <= 'Z') || (pen.ch >= '0' && pen.ch <= '9'))
pen2.ch = 'A';
else if (pen.ch >= 'a' && pen.ch <= 'z')
pen2.ch = 'a';
aaaaa_set_tile_hook.next()(pen2, x, y, map);
}
void shift_set_tile(const Screen::Pen &pen, int x, int y, bool map);
GUI_HOOK_CALLBACK(Screen::Hooks::set_tile, shift_set_tile_hook, shift_set_tile);
void shift_set_tile(const Screen::Pen &pen, int x, int y, bool map)
{
x = (x + 1) % gps->dimx;
shift_set_tile_hook.next()(pen, x, y, map);
}
DFhackCExport command_result plugin_enable (color_ostream &out, bool enable)
{
color_text_hook.apply(enable);
if (!enable)
{
shift_set_tile_hook.disable();
aaaaa_set_tile_hook.disable();
}
is_enabled = enable;
return CR_OK;
}
command_result color(color_ostream &out, std::vector<std::string> &params)
{
if (params.empty())
return plugin_enable(out, true);
for (auto it = params.begin(); it != params.end(); ++it)
{
std::string p = toLower(*it);
if (!p.size())
continue;
#define CHECK_COLOR(color_name) else if (p == toLower(std::string(#color_name))) \
{ config.flicker = false; config.color = COLOR_##color_name % 8; plugin_enable(out, true); }
CHECK_COLOR(RED)
CHECK_COLOR(GREEN)
CHECK_COLOR(BLUE)
CHECK_COLOR(YELLOW)
CHECK_COLOR(BROWN)
CHECK_COLOR(CYAN)
CHECK_COLOR(MAGENTA)
CHECK_COLOR(WHITE)
CHECK_COLOR(GREY)
CHECK_COLOR(BLACK)
#undef CHECK_COLOR
else if (p == "flicker")
{
config.flicker = true;
plugin_enable(out, true);
}
else if (p.size() >= 3 && p.substr(0, 3) == "aaa")
{
aaaaa_set_tile_hook.toggle();
}
else if (p == "shift")
{
shift_set_tile_hook.toggle();
}
else if (p == "disable")
{
plugin_enable(out, false);
}
else if (p != "enable")
{
out.printerr("Unrecognized option: %s\n", p.c_str());
return CR_WRONG_USAGE;
}
}
return CR_OK;
}
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
{
commands.push_back(PluginCommand(
"color-dfhack-text",
"color <color>|flicker|enable|disable|shift|aaaaa",
color,
false
));
config.flicker = false;
config.color = COLOR_RED;
return CR_OK;
}
DFhackCExport command_result plugin_shutdown (color_ostream &out)
{
return CR_OK;
}
DFhackCExport command_result plugin_onupdate (color_ostream &out)
{
++config.tick;
return CR_OK;
}

@ -65,7 +65,7 @@ static df::coord get_mouse_pos(int32_t &mx, int32_t &my)
pos.x = vx + mx - 1;
pos.y = vy + my - 1;
pos.z = vz;
pos.z = vz - Gui::getDepthAt(pos.x, pos.y);
return pos;
}