1616#include < SDL_keycode.h>
1717
1818using namespace DFHack ;
19+ using Hotkey::KeySpec;
20+ using Hotkey::KeyBinding;
1921
2022enum HotkeySignal {
2123 None = 0 ,
2224 CmdReady,
2325 Shutdown,
2426};
2527
26- bool operator ==(const HotkeyManager:: KeySpec& a, const HotkeyManager:: KeySpec& b) {
28+ bool operator ==(const KeySpec& a, const KeySpec& b) {
2729 return a.modifiers == b.modifiers && a.sym == b.sym &&
2830 a.focus .size () == b.focus .size () &&
2931 std::equal (a.focus .begin (), a.focus .end (), b.focus .begin ());
3032}
3133
3234// Equality operator for key bindings
33- bool operator ==(const HotkeyManager:: KeyBinding& a, const HotkeyManager:: KeyBinding& b) {
35+ bool operator ==(const KeyBinding& a, const KeyBinding& b) {
3436 return a.spec == b.spec &&
3537 a.command == b.command &&
3638 a.cmdline == b.cmdline ;
3739}
3840
41+ std::string Hotkey::keyspec_to_string (const KeySpec &spec, bool include_focus) {
42+ std::string sym;
43+ if (spec.modifiers & DFH_MOD_CTRL) sym += " Ctrl-" ;
44+ if (spec.modifiers & DFH_MOD_ALT) sym += " Alt-" ;
45+ if (spec.modifiers & DFH_MOD_SHIFT) sym += " Shift-" ;
46+
47+ std::string key_name;
48+ if (spec.sym < 0 ) {
49+ key_name = " MOUSE" + std::to_string (-spec.sym );
50+ } else {
51+ key_name = DFSDL::DFSDL_GetKeyName (spec.sym );
52+ }
53+ sym += key_name;
54+
55+ if (include_focus && !spec.focus .empty ()) {
56+ sym += " @" ;
57+ bool first = true ;
58+ for (const auto & focus : spec.focus ) {
59+ if (first) {
60+ first = false ;
61+ sym += focus;
62+ } else {
63+ sym += " |" + focus;
64+ }
65+ }
66+ }
67+
68+ return sym;
69+ }
70+
3971// Hotkeys actions are executed from an external thread to avoid deadlocks
4072// that may occur if running commands from the render or simulation threads.
4173void HotkeyManager::hotkey_thread_fn () {
@@ -64,7 +96,7 @@ void HotkeyManager::hotkey_thread_fn() {
6496 }
6597}
6698
67- std::optional<HotkeyManager:: KeySpec> HotkeyManager::parseKeySpec (std::string spec) {
99+ std::optional<KeySpec> HotkeyManager::parseKeySpec (std::string spec) {
68100 KeySpec out;
69101
70102 // Determine focus string, if present
@@ -138,16 +170,16 @@ bool HotkeyManager::addKeybind(std::string keyspec, std::string cmd) {
138170 return this ->addKeybind (spec_opt.value (), cmd);
139171}
140172
141- bool HotkeyManager::clearKeybind (const KeySpec& spec, bool any_focus) {
173+ bool HotkeyManager::clearKeybind (const KeySpec& spec, bool any_focus, std::string cmdline ) {
142174 std::lock_guard<std::mutex> l (lock);
143175 if (!bindings.contains (spec.sym ))
144176 return false ;
145177 auto & binds = bindings[spec.sym ];
146178
147- auto new_end = std::remove_if (binds.begin (), binds.end (), [any_focus, spec](const auto & v) {
148- return any_focus
179+ auto new_end = std::remove_if (binds.begin (), binds.end (), [any_focus, spec, &cmdline ](const auto & v) {
180+ return ( any_focus
149181 ? v.spec .sym == spec.sym && v.spec .modifiers == spec.modifiers
150- : v.spec == spec;
182+ : v.spec == spec) && (cmdline. empty () || v. cmdline == cmdline) ;
151183 });
152184 if (new_end == binds.end ())
153185 return false ; // No bindings removed
@@ -156,11 +188,11 @@ bool HotkeyManager::clearKeybind(const KeySpec& spec, bool any_focus) {
156188 return true ;
157189}
158190
159- bool HotkeyManager::clearKeybind (std::string keyspec, bool any_focus) {
191+ bool HotkeyManager::clearKeybind (std::string keyspec, bool any_focus, std::string cmdline ) {
160192 std::optional<KeySpec> spec_opt = parseKeySpec (keyspec);
161193 if (!spec_opt.has_value ())
162194 return false ;
163- return this ->clearKeybind (spec_opt.value (), any_focus);
195+ return this ->clearKeybind (spec_opt.value (), any_focus, cmdline );
164196}
165197
166198std::vector<std::string> HotkeyManager::listKeybinds (const KeySpec& spec) {
@@ -200,7 +232,7 @@ std::vector<std::string> HotkeyManager::listKeybinds(std::string keyspec) {
200232 return this ->listKeybinds (spec_opt.value ());
201233}
202234
203- std::vector<HotkeyManager:: KeyBinding> HotkeyManager::listActiveKeybinds () {
235+ std::vector<KeyBinding> HotkeyManager::listActiveKeybinds () {
204236 std::vector<KeyBinding> out;
205237
206238 for (const auto & [_, bind_set] : bindings) {
@@ -223,6 +255,17 @@ std::vector<HotkeyManager::KeyBinding> HotkeyManager::listActiveKeybinds() {
223255 return out;
224256}
225257
258+ std::vector<KeyBinding> HotkeyManager::listAllKeybinds () {
259+ std::vector<KeyBinding> out;
260+
261+ for (const auto & [_, bind_set] : bindings) {
262+ for (const auto & bind : bind_set) {
263+ out.emplace_back (bind);
264+ }
265+ }
266+ return out;
267+ }
268+
226269bool HotkeyManager::handleKeybind (int sym, int modifiers) {
227270 // Ensure gamestate is ready
228271 if (!df::global::gview || !df::global::plotinfo)
@@ -237,6 +280,17 @@ bool HotkeyManager::handleKeybind(int sym, int modifiers) {
237280 sym = SDLK_RETURN;
238281
239282 std::unique_lock<std::mutex> l (lock);
283+
284+ // If reading input for a keybinding screen, save the input and exit early
285+ if (keybind_save_requested) {
286+ KeySpec spec;
287+ spec.sym = sym;
288+ spec.modifiers = modifiers;
289+ requested_keybind = Hotkey::keyspec_to_string (spec);
290+ keybind_save_requested = false ;
291+ return true ;
292+ }
293+
240294 if (!bindings.contains (sym))
241295 return false ;
242296 auto & binds = bindings[sym];
@@ -286,6 +340,16 @@ void HotkeyManager::setHotkeyCommand(std::string cmd) {
286340 cond.notify_all ();
287341}
288342
343+ void HotkeyManager::requestKeybindInput () {
344+ std::lock_guard<std::mutex> l (lock);
345+ keybind_save_requested = true ;
346+ requested_keybind = " " ;
347+ }
348+
349+ std::string HotkeyManager::readKeybindInput () {
350+ std::lock_guard<std::mutex> l (lock);
351+ return requested_keybind;
352+ }
289353
290354void HotkeyManager::handleKeybindingCommand (color_ostream &con, const std::vector<std::string>& parts) {
291355 if (parts.size () >= 3 && (parts[0 ] == " set" || parts[0 ] == " add" )) {
0 commit comments