| 303 | } |
| 304 | |
| 305 | bool HotkeyManager::handleKeybind(int sym, int modifiers) { |
| 306 | // Ensure gamestate is ready |
| 307 | if (!df::global::gview || !df::global::plotinfo) |
| 308 | return false; |
| 309 | |
| 310 | // Get topmost active screen |
| 311 | df::viewscreen *screen = &df::global::gview->view; |
| 312 | while (screen->child) |
| 313 | screen = screen->child; |
| 314 | |
| 315 | // Map keypad return to return |
| 316 | if (sym == SDLK_KP_ENTER) |
| 317 | sym = SDLK_RETURN; |
| 318 | |
| 319 | std::unique_lock<std::mutex> l(lock); |
| 320 | |
| 321 | // If reading input for a keybinding screen, save the input and exit early |
| 322 | if (keybind_save_requested) { |
| 323 | KeySpec spec; |
| 324 | spec.sym = sym; |
| 325 | spec.modifiers = modifiers; |
| 326 | requested_keybind = spec.toString(false); |
| 327 | keybind_save_requested = false; |
| 328 | return true; |
| 329 | } |
| 330 | |
| 331 | if (!bindings.contains(sym)) |
| 332 | return false; |
| 333 | auto& binds = bindings[sym]; |
| 334 | |
| 335 | auto& core = Core::getInstance(); |
| 336 | bool mortal_mode = core.getMortalMode(); |
| 337 | |
| 338 | // Iterate in reverse, prioritizing the last added keybinds |
| 339 | for (const auto& bind : binds | std::views::reverse) { |
| 340 | if (bind.spec.modifiers != modifiers) |
| 341 | continue; |
| 342 | |
| 343 | if (!bind.spec.focus.empty()) { |
| 344 | bool matched = false; |
| 345 | for (const auto& focus : bind.spec.focus) { |
| 346 | if (Gui::matchFocusString(focus)) { |
| 347 | matched = true; |
| 348 | break; |
| 349 | } |
| 350 | } |
| 351 | if (!matched) |
| 352 | continue; |
| 353 | } |
| 354 | |
| 355 | if (!core.getPluginManager()->CanInvokeHotkey(bind.command, screen)) |
| 356 | continue; |
| 357 | |
| 358 | if (mortal_mode && core.isArmokTool(bind.command)) |
| 359 | continue; |
| 360 | |
| 361 | queued_command = bind.cmdline; |
| 362 | hotkey_sig = HotkeySignal::CmdReady; |
no test coverage detected