diff --git a/docs/Core.rst b/docs/Core.rst index 763858b61..ac17401dc 100644 --- a/docs/Core.rst +++ b/docs/Core.rst @@ -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. diff --git a/docs/builtins/keybinding.rst b/docs/builtins/keybinding.rst index c9665a048..c6553e48c 100644 --- a/docs/builtins/keybinding.rst +++ b/docs/builtins/keybinding.rst @@ -33,6 +33,11 @@ The ```` 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 diff --git a/docs/changelog.txt b/docs/changelog.txt index fef273c64..3ff8ad217 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -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 diff --git a/library/Core.cpp b/library/Core.cpp index e4713c07f..0a1c19351 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -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 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; } diff --git a/library/include/Core.h b/library/include/Core.h index dc2408ae9..180b5cc7b 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -250,7 +250,6 @@ namespace DFHack int8_t modstate; std::map > key_bindings; - std::map hotkey_states; std::string hotkey_cmd; enum hotkey_set_t { NO, diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 8ea5e9dac..860b56cc1 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -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