@@ -38,39 +38,38 @@ bool operator==(const KeyBinding& a, const KeyBinding& b) {
3838 a.cmdline == b.cmdline ;
3939}
4040
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_SUPER) sym += " Super-" ;
46- if (spec. modifiers & DFH_MOD_SHIFT) sym += " Shift-" ;
41+ std::string KeySpec::toString ( bool include_focus) const {
42+ std::string out ;
43+ if (modifiers & DFH_MOD_CTRL) out += " Ctrl-" ;
44+ if (modifiers & DFH_MOD_ALT) out += " Alt-" ;
45+ if (modifiers & DFH_MOD_SUPER) out += " Super-" ;
46+ if (modifiers & DFH_MOD_SHIFT) out += " Shift-" ;
4747
4848 std::string key_name;
49- if (spec. sym < 0 ) {
50- key_name = " MOUSE" + std::to_string (-spec. sym );
49+ if (this -> sym < 0 ) {
50+ key_name = " MOUSE" + std::to_string (-this -> sym );
5151 } else {
52- key_name = DFSDL::DFSDL_GetKeyName (spec. sym );
52+ key_name = DFSDL::DFSDL_GetKeyName (this -> sym );
5353 }
54- sym += key_name;
54+ out += key_name;
5555
56- if (include_focus && !spec. focus .empty ()) {
57- sym += " @" ;
56+ if (include_focus && !this -> focus .empty ()) {
57+ out += " @" ;
5858 bool first = true ;
59- for (const auto & focus : spec. focus ) {
59+ for (const auto & fc : this -> focus ) {
6060 if (first) {
6161 first = false ;
62- sym += focus ;
62+ out += fc ;
6363 } else {
64- sym += " |" + focus ;
64+ out += " |" + fc ;
6565 }
6666 }
6767 }
6868
69- return sym ;
69+ return out ;
7070}
7171
72-
73- std::optional<KeySpec> Hotkey::parseKeySpec (std::string spec, std::string* err) {
72+ std::optional<KeySpec> KeySpec::parse (std::string spec, std::string* err) {
7473 KeySpec out;
7574
7675 // Determine focus string, if present
@@ -126,6 +125,23 @@ std::optional<KeySpec> Hotkey::parseKeySpec(std::string spec, std::string* err)
126125 return std::nullopt ;
127126}
128127
128+ bool KeySpec::isDisruptive () const {
129+ // Miscellaneous essential keys
130+ const std::string essential_key_set = " \r\x1B\b\t -=[]\\ ;',./" ;
131+
132+ // Letters A-Z, 0-9, and other special keys such as return/escape, and other general typing keys
133+ bool is_essential_key = (this ->sym >= SDLK_a && this ->sym <= SDLK_z)
134+ || (this ->sym >= SDLK_0 && this ->sym <= SDLK_9)
135+ || essential_key_set.find (this ->sym ) != std::string::npos
136+ || (this ->sym >= SDLK_LEFT && this ->sym <= SDLK_UP);
137+
138+ // Essential keys are safe, so long as they have a modifier that isn't Shift
139+ if (is_essential_key && !(this ->modifiers & ~DFH_MOD_SHIFT))
140+ return true ;
141+
142+ return false ;
143+ }
144+
129145// Hotkeys actions are executed from an external thread to avoid deadlocks
130146// that may occur if running commands from the render or simulation threads.
131147void HotkeyManager::hotkey_thread_fn () {
@@ -179,7 +195,7 @@ bool HotkeyManager::addKeybind(KeySpec spec, std::string cmd) {
179195}
180196
181197bool HotkeyManager::addKeybind (std::string keyspec, std::string cmd) {
182- std::optional<KeySpec> spec_opt = Hotkey::parseKeySpec (keyspec);
198+ std::optional<KeySpec> spec_opt = KeySpec::parse (keyspec);
183199 if (!spec_opt.has_value ())
184200 return false ;
185201 return this ->addKeybind (spec_opt.value (), cmd);
@@ -205,7 +221,7 @@ bool HotkeyManager::removeKeybind(const KeySpec& spec, bool match_focus, std::st
205221}
206222
207223bool HotkeyManager::removeKeybind (std::string keyspec, bool match_focus, std::string_view cmdline) {
208- std::optional<KeySpec> spec_opt = Hotkey::parseKeySpec (keyspec);
224+ std::optional<KeySpec> spec_opt = KeySpec::parse (keyspec);
209225 if (!spec_opt.has_value ())
210226 return false ;
211227 return this ->removeKeybind (spec_opt.value (), match_focus, cmdline);
@@ -243,7 +259,7 @@ std::vector<std::string> HotkeyManager::listKeybinds(const KeySpec& spec) {
243259
244260std::vector<std::string> HotkeyManager::listKeybinds (std::string keyspec) {
245261 std::lock_guard<std::mutex> l (lock);
246- std::optional<KeySpec> spec_opt = Hotkey::parseKeySpec (keyspec);
262+ std::optional<KeySpec> spec_opt = KeySpec::parse (keyspec);
247263 if (!spec_opt.has_value ())
248264 return {};
249265 return this ->listKeybinds (spec_opt.value ());
@@ -305,7 +321,7 @@ bool HotkeyManager::handleKeybind(int sym, int modifiers) {
305321 KeySpec spec;
306322 spec.sym = sym;
307323 spec.modifiers = modifiers;
308- requested_keybind = Hotkey::keyspec_to_string ( spec);
324+ requested_keybind = spec. toString ( false );
309325 keybind_save_requested = false ;
310326 return true ;
311327 }
@@ -377,7 +393,7 @@ void HotkeyManager::handleKeybindingCommand(color_ostream &con, const std::vecto
377393 if (parts[0 ] == " set" )
378394 removeKeybind (keystr);
379395 for (const auto & part : parts | std::views::drop (2 ) | std::views::reverse) {
380- auto spec = Hotkey::parseKeySpec (keystr, &parse_error);
396+ auto spec = KeySpec::parse (keystr, &parse_error);
381397 if (!spec.has_value ()) {
382398 con.printerr (" %s\n " , parse_error.c_str ());
383399 break ;
@@ -390,7 +406,7 @@ void HotkeyManager::handleKeybindingCommand(color_ostream &con, const std::vecto
390406 }
391407 else if (parts.size () >= 2 && parts[0 ] == " clear" ) {
392408 for (const auto & part : parts | std::views::drop (1 )) {
393- auto spec = Hotkey::parseKeySpec (part, &parse_error);
409+ auto spec = KeySpec::parse (part, &parse_error);
394410 if (!spec.has_value ()) {
395411 con.printerr (" %s\n " , parse_error.c_str ());
396412 }
@@ -401,7 +417,7 @@ void HotkeyManager::handleKeybindingCommand(color_ostream &con, const std::vecto
401417 }
402418 }
403419 else if (parts.size () == 2 && parts[0 ] == " list" ) {
404- auto spec = Hotkey::parseKeySpec (parts[1 ], &parse_error);
420+ auto spec = KeySpec::parse (parts[1 ], &parse_error);
405421 if (!spec.has_value ()) {
406422 con.printerr (" %s\n " , parse_error.c_str ());
407423 return ;
@@ -419,7 +435,7 @@ void HotkeyManager::handleKeybindingCommand(color_ostream &con, const std::vecto
419435 << " keybinding set <key>[@context] \" cmdline\" \" cmdline\" ..." << std::endl
420436 << " keybinding add <key>[@context] \" cmdline\" \" cmdline\" ..." << std::endl
421437 << " Later adds, and earlier items within one command have priority." << std::endl
422- << " Supported keys: [Ctrl-][Alt-][Shift-](A-Z, 0-9, F1-F12, `, or Enter )." << std::endl
438+ << " Supported keys: [Ctrl-][Alt-][Super-][ Shift-](A-Z, 0-9, F1-F12, `, etc. )." << std::endl
423439 << " Context may be used to limit the scope of the binding, by" << std::endl
424440 << " requiring the current context to have a certain prefix." << std::endl
425441 << " Current UI context is: " << std::endl
0 commit comments