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
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
===================
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
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
the first applicable one. Later ``add`` commands, and earlier entries within one
``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
- ``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

@ -2356,11 +2356,21 @@ bool Core::DFH_ncurses_key(int key)
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
bool Core::DFH_SDL_Event(SDL_Event* ev)
{
static std::map<int, bool> hotkey_states;
// do NOT process events before we are ready.
if(!started || !ev)
if (!started || !ev)
return false;
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) {
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;
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;
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;
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;
SelectHotkey(ke.keysym.sym, modstate);
// the check against hotkey_states[sym] ensures we only process keybindings once per keypress
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;
}

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

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