Merge pull request #3543 from myk002/myk_keybindings

suppress SDL events if a DFHack keybinding is matched
develop
Myk 2023-07-10 11:25:41 -07:00 committed by GitHub
commit c734a58d9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 67 additions and 10 deletions

@ -377,6 +377,26 @@ Other (non-DFHack-specific) variables that affect DFHack:
sensitive), ``DF2CONSOLE()`` will produce UTF-8-encoded text. Note that this sensitive), ``DF2CONSOLE()`` will produce UTF-8-encoded text. Note that this
should be the case in most UTF-8-capable \*nix terminal emulators already. should be the case in most UTF-8-capable \*nix terminal emulators already.
Core preferences
================
There are a few settings that can be changed dynamically via
`gui/control-panel` to affect runtime behavior. You can also toggle these from
the commandline using the `lua` command, e.g.
``lua dfhack.HIDE_ARMOK_TOOLS=true`` or by editing the generated
``dfhack-config/init/dfhack.control-panel-preferences.init`` file and
restarting DF.
- ``dfhack.HIDE_CONSOLE_ON_STARTUP``: Whether to hide the external DFHack
terminal window on startup. This, of course, is not useful to change
dynamically. You'll have to use `gui/control-panel` or edit the init file
directly and restart DF for it to have an effect.
- ``dfhack.HIDE_ARMOK_TOOLS``: Whether to hide "armok" tools in command lists.
- ``dfhack.SUPPRESS_DUPLICATE_KEYBOARD_EVENTS``: Whether to prevent DFHack
keybindings from producing DF key events.
Miscellaneous notes Miscellaneous notes
=================== ===================
This section is for odd but important notes that don't fit anywhere else. This section is for odd but important notes that don't fit anywhere else.

@ -33,6 +33,11 @@ The ``<key>`` parameter above has the following **case-sensitive** syntax::
where the ``KEY`` part can be any recognized key and :kbd:`[`:kbd:`]` denote where the ``KEY`` part can be any recognized key and :kbd:`[`:kbd:`]` denote
optional parts. optional parts.
DFHack commands can advertise the contexts in which they can be usefully run.
For example, a command that acts on a selected unit can tell `keybinding` that
it is not "applicable" in the current context if a unit is not actively
selected.
When multiple commands are bound to the same key combination, DFHack selects When multiple commands are bound to the same key combination, DFHack selects
the first applicable one. Later ``add`` commands, and earlier entries within one the first applicable one. Later ``add`` commands, and earlier entries within one
``add`` command have priority. Commands that are not specifically intended for ``add`` command have priority. Commands that are not specifically intended for

@ -41,6 +41,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
## Misc Improvements ## Misc Improvements
- ``widgets.EditField``: DFHack edit fields now support cut/copy/paste with the system clipboard with Ctrl-X/Ctrl-C/Ctrl-V - ``widgets.EditField``: DFHack edit fields now support cut/copy/paste with the system clipboard with Ctrl-X/Ctrl-C/Ctrl-V
- Suppress DF keyboard events when a DFHack keybinding is matched. This prevents, for example, a backtick from appearing in a textbox as text when you launch `gui/launcher` from the backtick keybinding.
## Documentation ## Documentation

@ -2356,11 +2356,21 @@ bool Core::DFH_ncurses_key(int key)
return ncurses_wgetch(key, dummy); return ncurses_wgetch(key, dummy);
} }
static bool getSuppressDuplicateKeyboardEvents() {
auto L = Lua::Core::State;
color_ostream_proxy out(Core::getInstance().getConsole());
Lua::StackUnwinder top(L);
return DFHack::Lua::PushModulePublic(out, L, "dfhack", "SUPPRESS_DUPLICATE_KEYBOARD_EVENTS") &&
lua_toboolean(L, -1);
}
// returns true if the event is handled // returns true if the event is handled
bool Core::DFH_SDL_Event(SDL_Event* ev) bool Core::DFH_SDL_Event(SDL_Event* ev)
{ {
static std::map<int, bool> hotkey_states;
// do NOT process events before we are ready. // do NOT process events before we are ready.
if(!started || !ev) if (!started || !ev)
return false; return false;
if (ev->type == SDL_WINDOWEVENT && ev->window.event == SDL_WINDOWEVENT_FOCUS_GAINED) { if (ev->type == SDL_WINDOWEVENT && ev->window.event == SDL_WINDOWEVENT_FOCUS_GAINED) {
@ -2372,23 +2382,40 @@ bool Core::DFH_SDL_Event(SDL_Event* ev)
if (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) { if (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) {
auto &ke = ev->key; auto &ke = ev->key;
auto &sym = ke.keysym.sym;
if (ke.keysym.sym == SDLK_LSHIFT || ke.keysym.sym == SDLK_RSHIFT) if (sym == SDLK_LSHIFT || sym == SDLK_RSHIFT)
modstate = (ev->type == SDL_KEYDOWN) ? modstate | DFH_MOD_SHIFT : modstate & ~DFH_MOD_SHIFT; modstate = (ev->type == SDL_KEYDOWN) ? modstate | DFH_MOD_SHIFT : modstate & ~DFH_MOD_SHIFT;
else if (ke.keysym.sym == SDLK_LCTRL || ke.keysym.sym == SDLK_RCTRL) else if (sym == SDLK_LCTRL || sym == SDLK_RCTRL)
modstate = (ev->type == SDL_KEYDOWN) ? modstate | DFH_MOD_CTRL : modstate & ~DFH_MOD_CTRL; modstate = (ev->type == SDL_KEYDOWN) ? modstate | DFH_MOD_CTRL : modstate & ~DFH_MOD_CTRL;
else if (ke.keysym.sym == SDLK_LALT || ke.keysym.sym == SDLK_RALT) else if (sym == SDLK_LALT || sym == SDLK_RALT)
modstate = (ev->type == SDL_KEYDOWN) ? modstate | DFH_MOD_ALT : modstate & ~DFH_MOD_ALT; modstate = (ev->type == SDL_KEYDOWN) ? modstate | DFH_MOD_ALT : modstate & ~DFH_MOD_ALT;
else if (ke.state == SDL_PRESSED && !hotkey_states[ke.keysym.sym]) else if (ke.state == SDL_PRESSED && !hotkey_states[sym])
{ {
hotkey_states[ke.keysym.sym] = true; // the check against hotkey_states[sym] ensures we only process keybindings once per keypress
SelectHotkey(ke.keysym.sym, modstate); DEBUG(keybinding).print("key down: sym=%d (%c)\n", sym, sym);
bool handled = SelectHotkey(sym, modstate);
if (handled) {
DEBUG(keybinding).print("inhibiting SDL key down event\n");
hotkey_states[sym] = true;
return getSuppressDuplicateKeyboardEvents();
}
} }
else if(ke.state == SDL_RELEASED) else if (ke.state == SDL_RELEASED)
{ {
hotkey_states[ke.keysym.sym] = false; DEBUG(keybinding).print("key up: sym=%d (%c)\n", sym, sym);
hotkey_states[sym] = false;
} }
} }
else if (ev->type == SDL_TEXTINPUT) {
auto &te = ev->text;
DEBUG(keybinding).print("text input: '%s'\n", te.text);
if (strlen(te.text) == 1 && hotkey_states[te.text[0]]) {
DEBUG(keybinding).print("inhibiting SDL text event\n");
return true;
}
}
return false; return false;
} }

@ -250,7 +250,6 @@ namespace DFHack
int8_t modstate; int8_t modstate;
std::map<int, std::vector<KeyBinding> > key_bindings; std::map<int, std::vector<KeyBinding> > key_bindings;
std::map<int, bool> hotkey_states;
std::string hotkey_cmd; std::string hotkey_cmd;
enum hotkey_set_t { enum hotkey_set_t {
NO, NO,

@ -63,6 +63,11 @@ function dfhack.getHideArmokTools()
return dfhack.HIDE_ARMOK_TOOLS return dfhack.HIDE_ARMOK_TOOLS
end end
dfhack.SUPPRESS_DUPLICATE_KEYBOARD_EVENTS = true
function dfhack.getSuppressDuplicateKeyboardEvents()
return dfhack.SUPPRESS_DUPLICATE_KEYBOARD_EVENTS
end
-- Error handling -- Error handling
safecall = dfhack.safecall safecall = dfhack.safecall