|
|
|
@ -51,13 +51,20 @@ using namespace DFHack;
|
|
|
|
|
|
|
|
|
|
#include "dfhack/SDL_fakes/events.h"
|
|
|
|
|
|
|
|
|
|
#include "dfhack/df/ui.h"
|
|
|
|
|
#include "dfhack/df/world.h"
|
|
|
|
|
#include "dfhack/df/world_data.h"
|
|
|
|
|
#include "dfhack/df/interface.h"
|
|
|
|
|
#include "dfhack/df/viewscreen_dwarfmodest.h"
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <iomanip>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <fstream>
|
|
|
|
|
#include "tinythread.h"
|
|
|
|
|
using namespace tthread;
|
|
|
|
|
|
|
|
|
|
using namespace tthread;
|
|
|
|
|
using namespace df::enums;
|
|
|
|
|
|
|
|
|
|
struct Core::Cond
|
|
|
|
|
{
|
|
|
|
@ -145,8 +152,17 @@ void fHKthread(void * iodata)
|
|
|
|
|
std::string stuff = core->getHotkeyCmd(); // waits on mutex!
|
|
|
|
|
if(!stuff.empty())
|
|
|
|
|
{
|
|
|
|
|
vector <string> crap;
|
|
|
|
|
command_result cr = plug_mgr->InvokeCommand(stuff, crap, false);
|
|
|
|
|
vector <string> args;
|
|
|
|
|
cheap_tokenise(stuff, args);
|
|
|
|
|
if (args.empty()) {
|
|
|
|
|
core->con.printerr("Empty hotkey command.\n");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string first = args[0];
|
|
|
|
|
args.erase(args.begin());
|
|
|
|
|
command_result cr = plug_mgr->InvokeCommand(first, args, false);
|
|
|
|
|
|
|
|
|
|
if(cr == CR_WOULD_BREAK)
|
|
|
|
|
{
|
|
|
|
|
core->con.printerr("It isn't possible to run an interactive command outside the console.\n");
|
|
|
|
@ -155,53 +171,25 @@ void fHKthread(void * iodata)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A thread function... for the interactive console.
|
|
|
|
|
void fIOthread(void * iodata)
|
|
|
|
|
static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clueless_counter, const string &command)
|
|
|
|
|
{
|
|
|
|
|
IODATA * iod = ((IODATA*) iodata);
|
|
|
|
|
Core * core = iod->core;
|
|
|
|
|
PluginManager * plug_mgr = ((IODATA*) iodata)->plug_mgr;
|
|
|
|
|
CommandHistory main_history;
|
|
|
|
|
main_history.load("dfhack.history");
|
|
|
|
|
Console & con = core->con;
|
|
|
|
|
if(plug_mgr == 0 || core == 0)
|
|
|
|
|
{
|
|
|
|
|
con.printerr("Something horrible happened in Core's constructor...\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
con.print("DFHack is ready. Have a nice day!\n"
|
|
|
|
|
"Type in '?' or 'help' for general help, 'ls' to see all commands.\n");
|
|
|
|
|
int clueless_counter = 0;
|
|
|
|
|
while (true)
|
|
|
|
|
|
|
|
|
|
if (!command.empty())
|
|
|
|
|
{
|
|
|
|
|
string command = "";
|
|
|
|
|
int ret = con.lineedit("[DFHack]# ",command, main_history);
|
|
|
|
|
if(ret == -2)
|
|
|
|
|
{
|
|
|
|
|
cerr << "Console is shutting down properly." << endl;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else if(ret == -1)
|
|
|
|
|
{
|
|
|
|
|
cerr << "Console caught an unspecified error." << endl;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else if(ret)
|
|
|
|
|
{
|
|
|
|
|
// a proper, non-empty command was entered
|
|
|
|
|
main_history.add(command);
|
|
|
|
|
main_history.save("dfhack.history");
|
|
|
|
|
}
|
|
|
|
|
// cut the input into parts
|
|
|
|
|
vector <string> parts;
|
|
|
|
|
cheap_tokenise(command,parts);
|
|
|
|
|
if(parts.size() == 0)
|
|
|
|
|
{
|
|
|
|
|
clueless_counter ++;
|
|
|
|
|
continue;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
string first = parts[0];
|
|
|
|
|
parts.erase(parts.begin());
|
|
|
|
|
|
|
|
|
|
if (first[0] == '#') return;
|
|
|
|
|
|
|
|
|
|
cerr << "Invoking: " << command << endl;
|
|
|
|
|
|
|
|
|
|
// let's see what we actually got
|
|
|
|
@ -220,6 +208,7 @@ void fIOthread(void * iodata)
|
|
|
|
|
" cls - Clear the console.\n"
|
|
|
|
|
" fpause - Force DF to pause.\n"
|
|
|
|
|
" die - Force DF to close immediately\n"
|
|
|
|
|
" keybinding - Modify bindings of commands to keys\n"
|
|
|
|
|
"Plugin management (useful for developers):\n"
|
|
|
|
|
//" belongs COMMAND - Tell which plugin a command belongs to.\n"
|
|
|
|
|
" plug [PLUGIN|v] - List plugin state and description.\n"
|
|
|
|
@ -370,6 +359,40 @@ void fIOthread(void * iodata)
|
|
|
|
|
con.print("%s\n", plug->getName().c_str());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(first == "keybinding")
|
|
|
|
|
{
|
|
|
|
|
if (parts.size() >= 3 && (parts[0] == "set" || parts[0] == "add"))
|
|
|
|
|
{
|
|
|
|
|
std::string keystr = parts[1];
|
|
|
|
|
if (parts[0] == "set")
|
|
|
|
|
core->ClearKeyBindings(keystr);
|
|
|
|
|
for (int i = parts.size()-1; i >= 2; i--)
|
|
|
|
|
{
|
|
|
|
|
if (!core->AddKeyBinding(keystr, parts[i])) {
|
|
|
|
|
con.printerr("Invalid key spec: %s\n", keystr.c_str());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (parts.size() >= 2 && parts[0] == "clear")
|
|
|
|
|
{
|
|
|
|
|
for (unsigned i = 1; i < parts.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
if (!core->ClearKeyBindings(parts[i])) {
|
|
|
|
|
con.printerr("Invalid key spec: %s\n", parts[i].c_str());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
con << "Usage:" << endl
|
|
|
|
|
<< " keybinding clear <key> <key>..." << endl
|
|
|
|
|
<< " keybinding set <key> \"cmdline\" \"cmdline\"..." << endl
|
|
|
|
|
<< " keybinding add <key> \"cmdline\" \"cmdline\"..." << endl
|
|
|
|
|
<< "Later adds, and earlier items within one command have priority." << endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(first == "fpause")
|
|
|
|
|
{
|
|
|
|
|
World * w = core->getWorld();
|
|
|
|
@ -410,6 +433,70 @@ void fIOthread(void * iodata)
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void loadInitFile(Core *core, PluginManager *plug_mgr, string fname)
|
|
|
|
|
{
|
|
|
|
|
ifstream init(fname);
|
|
|
|
|
if (init.bad())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
int tmp = 0;
|
|
|
|
|
string command;
|
|
|
|
|
while (getline(init, command))
|
|
|
|
|
{
|
|
|
|
|
if (!command.empty())
|
|
|
|
|
runInteractiveCommand(core, plug_mgr, tmp, command);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A thread function... for the interactive console.
|
|
|
|
|
void fIOthread(void * iodata)
|
|
|
|
|
{
|
|
|
|
|
IODATA * iod = ((IODATA*) iodata);
|
|
|
|
|
Core * core = iod->core;
|
|
|
|
|
PluginManager * plug_mgr = ((IODATA*) iodata)->plug_mgr;
|
|
|
|
|
|
|
|
|
|
CommandHistory main_history;
|
|
|
|
|
main_history.load("dfhack.history");
|
|
|
|
|
|
|
|
|
|
Console & con = core->con;
|
|
|
|
|
if(plug_mgr == 0 || core == 0)
|
|
|
|
|
{
|
|
|
|
|
con.printerr("Something horrible happened in Core's constructor...\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loadInitFile(core, plug_mgr, "dfhack.init");
|
|
|
|
|
|
|
|
|
|
con.print("DFHack is ready. Have a nice day!\n"
|
|
|
|
|
"Type in '?' or 'help' for general help, 'ls' to see all commands.\n");
|
|
|
|
|
|
|
|
|
|
int clueless_counter = 0;
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
string command = "";
|
|
|
|
|
int ret = con.lineedit("[DFHack]# ",command, main_history);
|
|
|
|
|
if(ret == -2)
|
|
|
|
|
{
|
|
|
|
|
cerr << "Console is shutting down properly." << endl;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else if(ret == -1)
|
|
|
|
|
{
|
|
|
|
|
cerr << "Console caught an unspecified error." << endl;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else if(ret)
|
|
|
|
|
{
|
|
|
|
|
// a proper, non-empty command was entered
|
|
|
|
|
main_history.add(command);
|
|
|
|
|
main_history.save("dfhack.history");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
runInteractiveCommand(core, plug_mgr, clueless_counter, command);
|
|
|
|
|
|
|
|
|
|
if(clueless_counter == 3)
|
|
|
|
|
{
|
|
|
|
|
con.print("Do 'help' or '?' for the list of available commands.\n");
|
|
|
|
@ -434,11 +521,12 @@ Core::Core()
|
|
|
|
|
StackMutex = 0;
|
|
|
|
|
core_cond = 0;
|
|
|
|
|
// set up hotkey capture
|
|
|
|
|
memset(hotkey_states,0,sizeof(hotkey_states));
|
|
|
|
|
hotkey_set = false;
|
|
|
|
|
HotkeyMutex = 0;
|
|
|
|
|
HotkeyCond = 0;
|
|
|
|
|
misc_data_mutex=0;
|
|
|
|
|
last_world_data_ptr = NULL;
|
|
|
|
|
top_viewscreen = NULL;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void Core::fatal (std::string output, bool deactivate)
|
|
|
|
@ -631,8 +719,36 @@ int Core::Update()
|
|
|
|
|
if(errorstate)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
// detect if the game was loaded or unloaded in the meantime
|
|
|
|
|
void *new_wdata = NULL;
|
|
|
|
|
if (df::global::world) {
|
|
|
|
|
df::world_data *wdata = df::global::world->world_data;
|
|
|
|
|
// when the game is unloaded, world_data isn't deleted, but its contents are
|
|
|
|
|
if (wdata && !wdata->sites.empty())
|
|
|
|
|
new_wdata = wdata;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (new_wdata != last_world_data_ptr) {
|
|
|
|
|
last_world_data_ptr = new_wdata;
|
|
|
|
|
plug_mgr->OnStateChange(new_wdata ? SC_GAME_LOADED : SC_GAME_UNLOADED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// detect if the viewscreen changed
|
|
|
|
|
if (df::global::gview)
|
|
|
|
|
{
|
|
|
|
|
df::viewscreen *screen = &df::global::gview->view;
|
|
|
|
|
while (screen->child)
|
|
|
|
|
screen = screen->child;
|
|
|
|
|
if (screen != top_viewscreen)
|
|
|
|
|
{
|
|
|
|
|
top_viewscreen = screen;
|
|
|
|
|
plug_mgr->OnStateChange(SC_VIEWSCREEN_CHANGED);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// notify all the plugins that a game tick is finished
|
|
|
|
|
plug_mgr->OnUpdate();
|
|
|
|
|
|
|
|
|
|
// wake waiting tools
|
|
|
|
|
// do not allow more tools to join in while we process stuff here
|
|
|
|
|
StackMutex->lock();
|
|
|
|
@ -721,39 +837,151 @@ int Core::SDL_Event(SDL::Event* ev, int orig_return)
|
|
|
|
|
if(ev && ev->type == SDL::ET_KEYDOWN || ev->type == SDL::ET_KEYUP)
|
|
|
|
|
{
|
|
|
|
|
SDL::KeyboardEvent * ke = (SDL::KeyboardEvent *)ev;
|
|
|
|
|
bool shift = ke->ksym.mod & SDL::KMOD_SHIFT;
|
|
|
|
|
// consuming F1 .. F8
|
|
|
|
|
int idx = ke->ksym.sym - SDL::K_F1;
|
|
|
|
|
if(idx < 0 || idx > 7)
|
|
|
|
|
return orig_return;
|
|
|
|
|
idx += 8*shift;
|
|
|
|
|
// now we have the real index...
|
|
|
|
|
if(ke->state == SDL::BTN_PRESSED && !hotkey_states[idx])
|
|
|
|
|
|
|
|
|
|
if(ke->state == SDL::BTN_PRESSED && !hotkey_states[ke->ksym.sym])
|
|
|
|
|
{
|
|
|
|
|
hotkey_states[idx] = 1;
|
|
|
|
|
Gui * g = getGui();
|
|
|
|
|
if(g->hotkeys && g->df_interface && g->df_menu_state)
|
|
|
|
|
{
|
|
|
|
|
t_viewscreen * ws = g->GetCurrentScreen();
|
|
|
|
|
// FIXME: put hardcoded values into memory.xml
|
|
|
|
|
if(ws->getClassName() == "viewscreen_dwarfmodest" && *g->df_menu_state == 0x23)
|
|
|
|
|
return orig_return;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
t_hotkey & hotkey = (*g->hotkeys)[idx];
|
|
|
|
|
setHotkeyCmd(hotkey.name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
hotkey_states[ke->ksym.sym] = true;
|
|
|
|
|
|
|
|
|
|
int mod = 0;
|
|
|
|
|
if (ke->ksym.mod & SDL::KMOD_SHIFT) mod |= 1;
|
|
|
|
|
if (ke->ksym.mod & SDL::KMOD_CTRL) mod |= 2;
|
|
|
|
|
if (ke->ksym.mod & SDL::KMOD_ALT) mod |= 4;
|
|
|
|
|
|
|
|
|
|
SelectHotkey(ke->ksym.sym, mod);
|
|
|
|
|
}
|
|
|
|
|
else if(ke->state == SDL::BTN_RELEASED)
|
|
|
|
|
{
|
|
|
|
|
hotkey_states[idx] = 0;
|
|
|
|
|
hotkey_states[ke->ksym.sym] = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return orig_return;
|
|
|
|
|
// do stuff with the events...
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Core::SelectHotkey(int sym, int modifiers)
|
|
|
|
|
{
|
|
|
|
|
// Find the topmost viewscreen
|
|
|
|
|
if (!df::global::gview || !df::global::ui)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
df::viewscreen *screen = &df::global::gview->view;
|
|
|
|
|
while (screen->child)
|
|
|
|
|
screen = screen->child;
|
|
|
|
|
|
|
|
|
|
std::string cmd;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
tthread::lock_guard<tthread::mutex> lock(*HotkeyMutex);
|
|
|
|
|
|
|
|
|
|
// Check the internal keybindings
|
|
|
|
|
std::vector<KeyBinding> &bindings = key_bindings[sym];
|
|
|
|
|
for (int i = bindings.size()-1; i >= 0; --i) {
|
|
|
|
|
if (bindings[i].modifiers != modifiers)
|
|
|
|
|
continue;
|
|
|
|
|
if (!plug_mgr->CanInvokeHotkey(bindings[i].command[0], screen))
|
|
|
|
|
continue;
|
|
|
|
|
cmd = bindings[i].cmdline;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cmd.empty()) {
|
|
|
|
|
// Check the hotkey keybindings
|
|
|
|
|
int idx = sym - SDL::K_F1;
|
|
|
|
|
if(idx >= 0 && idx < 8)
|
|
|
|
|
{
|
|
|
|
|
if (modifiers & 1)
|
|
|
|
|
idx += 8;
|
|
|
|
|
|
|
|
|
|
if (!strict_virtual_cast<df::viewscreen_dwarfmodest>(screen) ||
|
|
|
|
|
df::global::ui->main.mode != ui_sidebar_mode::Hotkeys)
|
|
|
|
|
{
|
|
|
|
|
cmd = df::global::ui->main.hotkeys[idx].name;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cmd.empty()) {
|
|
|
|
|
setHotkeyCmd(cmd);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool parseKeySpec(std::string keyspec, int *psym, int *pmod)
|
|
|
|
|
{
|
|
|
|
|
*pmod = 0;
|
|
|
|
|
|
|
|
|
|
// ugh, ugly
|
|
|
|
|
for (;;) {
|
|
|
|
|
if (keyspec.size() > 6 && keyspec.substr(0, 6) == "Shift-") {
|
|
|
|
|
*pmod |= 1;
|
|
|
|
|
keyspec = keyspec.substr(6);
|
|
|
|
|
} else if (keyspec.size() > 5 && keyspec.substr(0, 5) == "Ctrl-") {
|
|
|
|
|
*pmod |= 2;
|
|
|
|
|
keyspec = keyspec.substr(5);
|
|
|
|
|
} else if (keyspec.size() > 4 && keyspec.substr(0, 4) == "Alt-") {
|
|
|
|
|
*pmod |= 4;
|
|
|
|
|
keyspec = keyspec.substr(4);
|
|
|
|
|
} else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (keyspec.size() == 1 && keyspec[0] >= 'A' && keyspec[0] <= 'Z') {
|
|
|
|
|
*psym = SDL::K_a + (keyspec[0]-'A');
|
|
|
|
|
return true;
|
|
|
|
|
} else if (keyspec.size() == 2 && keyspec[0] == 'F' && keyspec[1] >= '1' && keyspec[1] <= '9') {
|
|
|
|
|
*psym = SDL::K_F1 + (keyspec[1]-'1');
|
|
|
|
|
return true;
|
|
|
|
|
} else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Core::ClearKeyBindings(std::string keyspec)
|
|
|
|
|
{
|
|
|
|
|
int sym, mod;
|
|
|
|
|
if (!parseKeySpec(keyspec, &sym, &mod))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
tthread::lock_guard<tthread::mutex> lock(*HotkeyMutex);
|
|
|
|
|
|
|
|
|
|
std::vector<KeyBinding> &bindings = key_bindings[sym];
|
|
|
|
|
for (int i = bindings.size()-1; i >= 0; --i) {
|
|
|
|
|
if (bindings[i].modifiers == mod)
|
|
|
|
|
bindings.erase(bindings.begin()+i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Core::AddKeyBinding(std::string keyspec, std::string cmdline)
|
|
|
|
|
{
|
|
|
|
|
int sym;
|
|
|
|
|
KeyBinding binding;
|
|
|
|
|
if (!parseKeySpec(keyspec, &sym, &binding.modifiers))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
cheap_tokenise(cmdline, binding.command);
|
|
|
|
|
if (binding.command.empty())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
tthread::lock_guard<tthread::mutex> lock(*HotkeyMutex);
|
|
|
|
|
|
|
|
|
|
binding.cmdline = cmdline;
|
|
|
|
|
key_bindings[sym].push_back(binding);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DFHack::default_hotkey(Core *, df::viewscreen *top)
|
|
|
|
|
{
|
|
|
|
|
// Default hotkey guard function
|
|
|
|
|
for (;top ;top = top->parent)
|
|
|
|
|
if (strict_virtual_cast<df::viewscreen_dwarfmodest>(top))
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////
|
|
|
|
|
// ClassNamCheck
|
|
|
|
|
////////////////
|
|
|
|
|