From fff9072b0736d94d81648856179d953f5c2061f8 Mon Sep 17 00:00:00 2001 From: lethosor Date: Fri, 13 Nov 2015 20:10:29 -0500 Subject: [PATCH] Add initial support for hooking into Gui-related functions --- library/CMakeLists.txt | 1 + library/include/modules/GuiHooks.h | 67 ++++++++++++++++++++ library/include/modules/Screen.h | 7 +++ library/modules/Screen.cpp | 24 +++++--- plugins/devel/CMakeLists.txt | 1 + plugins/devel/color-dfhack-text.cpp | 96 +++++++++++++++++++++++++++++ 6 files changed, 187 insertions(+), 9 deletions(-) create mode 100644 library/include/modules/GuiHooks.h create mode 100644 plugins/devel/color-dfhack-text.cpp diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 3cd415780..206badeb0 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -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 diff --git a/library/include/modules/GuiHooks.h b/library/include/modules/GuiHooks.h new file mode 100644 index 000000000..c9a68d92f --- /dev/null +++ b/library/include/modules/GuiHooks.h @@ -0,0 +1,67 @@ +#pragma once +#include +namespace DFHack { + #define GUI_HOOK_DECLARE(name, rtype, args) DFHACK_EXPORT extern DFHack::GuiHooks::Hook name + #define GUI_HOOK_DEFINE(name, base_func) DFHack::GuiHooks::Hook name(base_func) + #define GUI_HOOK_TOP(name) name.top() + #define GUI_HOOK_CALLBACK(hook, name, callback) DFHack::GuiHooks::Hook::Callback name(&hook, callback) + namespace GuiHooks { + template + class Hook { + typedef Hook T_hook; + friend class Callback; + T_func *base_func; + std::vector 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; + 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); + } + inline void enable() { apply(true); } + inline void disable() { apply(false); } + }; + }; + } +} diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h index ab032c4e3..389becbfe 100644 --- a/library/include/modules/Screen.h +++ b/library/include/modules/Screen.h @@ -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 { diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 517414766..2d9408aeb 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -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 real_doSetTile(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, real_doSetTile); +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, dim.y + y, false); } paintString(signature, dim.x-8, dim.y-1, "DFHack"); diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index 79bf55ff6..1f3ff6f75 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -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) diff --git a/plugins/devel/color-dfhack-text.cpp b/plugins/devel/color-dfhack-text.cpp new file mode 100644 index 000000000..45147fba6 --- /dev/null +++ b/plugins/devel/color-dfhack-text.cpp @@ -0,0 +1,96 @@ +#include "Console.h" +#include "Core.h" +#include "DataDefs.h" +#include "Export.h" +#include "PluginManager.h" + +#include "modules/Screen.h" + +using namespace DFHack; + +DFHACK_PLUGIN("color-dfhack-text"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + +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); +} + +DFhackCExport command_result plugin_enable (color_ostream &out, bool enable) +{ + color_text_hook.apply(enable); + is_enabled = enable; + return CR_OK; +} + +command_result color(color_ostream &out, std::vector ¶ms) +{ + plugin_enable(out, true); + std::string p0 = toLower(params[0]); +#define CHECK_COLOR(color_name) else if (p0 == toLower(std::string(#color_name))) \ + { config.flicker = false; config.color = COLOR_##color_name % 8; } + if (params.empty()) + return CR_OK; + 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 (p0 == "flicker") + { + config.flicker = true; + } + else if (p0 == "disable") + { + plugin_enable(out, false); + } + else if (p0 != "enable") + { + out.printerr("Unrecognized option: %s\n", p0.c_str()); + return CR_WRONG_USAGE; + } + return CR_OK; +} + +DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) +{ + commands.push_back(PluginCommand( + "color-dfhack-text", + "color |flicker|enable|disable", + 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; +}