Qt: Clear all keyboard bind states when focus is lost

This commit is contained in:
Stenzek 2023-03-16 21:25:45 +10:00
parent 3673827363
commit d7372d2442
3 changed files with 85 additions and 11 deletions

View File

@ -675,17 +675,22 @@ void MainWindow::onRunningGameChanged(const QString& filename, const QString& ga
void MainWindow::onApplicationStateChanged(Qt::ApplicationState state)
{
if (!s_system_valid || !g_settings.pause_on_focus_loss)
if (!s_system_valid)
return;
const bool focus_loss = (state != Qt::ApplicationActive);
if (focus_loss)
{
if (!m_was_paused_by_focus_loss && !s_system_paused)
if (g_settings.pause_on_focus_loss && !m_was_paused_by_focus_loss && !s_system_paused)
{
g_emu_thread->setSystemPaused(true);
m_was_paused_by_focus_loss = true;
}
// Clear the state of all keyboard binds.
// That way, if we had a key held down, and lost focus, the bind won't be stuck enabled because we never
// got the key release message, because it happened in another window which "stole" the event.
InputManager::ClearBindStateFromSource(InputManager::MakeHostKeyboardKey(0));
}
else
{
@ -1728,7 +1733,8 @@ void MainWindow::updateEmulationActions(bool starting, bool running, bool cheevo
if (!g_gdb_server->isListening() && g_settings.debugging.enable_gdb_server && starting)
{
QMetaObject::invokeMethod(g_gdb_server, "start", Qt::QueuedConnection, Q_ARG(quint16, g_settings.debugging.gdb_server_port));
QMetaObject::invokeMethod(g_gdb_server, "start", Qt::QueuedConnection,
Q_ARG(quint16, g_settings.debugging.gdb_server_port));
}
else if (g_gdb_server->isListening() && !running)
{
@ -2753,14 +2759,14 @@ void MainWindow::onToolsOpenDataDirectoryTriggered()
void MainWindow::onSettingsTriggeredFromToolbar()
{
if (s_system_valid)
{
m_settings_toolbar_menu->exec(QCursor::pos());
}
else
{
doSettings();
}
if (s_system_valid)
{
m_settings_toolbar_menu->exec(QCursor::pos());
}
else
{
doSettings();
}
}
void MainWindow::checkForUpdates(bool display_message)

View File

@ -915,6 +915,71 @@ bool InputManager::InvokeEvents(InputBindingKey key, float value, GenericInputBi
return true;
}
void InputManager::ClearBindStateFromSource(InputBindingKey key)
{
// Why are we doing it this way? Because any of the bindings could cause a reload and invalidate our iterators :(.
// Axis handlers should be fine, so we'll do those as a first pass.
for (const auto& [match_key, binding] : s_binding_map)
{
if (key.source_type != match_key.source_type || key.source_subtype != match_key.source_subtype ||
key.source_index != match_key.source_index || !IsAxisHandler(binding->handler))
{
continue;
}
for (u32 i = 0; i < binding->num_keys; i++)
{
if (binding->keys[i].MaskDirection() != match_key)
continue;
std::get<InputAxisEventHandler>(binding->handler)(0.0f);
break;
}
}
// Now go through the button handlers, and pick them off.
bool matched;
do
{
matched = false;
for (const auto& [match_key, binding] : s_binding_map)
{
if (key.source_type != match_key.source_type || key.source_subtype != match_key.source_subtype ||
key.source_index != match_key.source_index || IsAxisHandler(binding->handler))
{
continue;
}
for (u32 i = 0; i < binding->num_keys; i++)
{
if (binding->keys[i].MaskDirection() != match_key)
continue;
// Skip if we weren't pressed.
const u8 bit = static_cast<u8>(1) << i;
if ((binding->current_mask & bit) == 0)
continue;
// Only fire handler if we're changing from active state.
const u8 current_mask = binding->current_mask;
binding->current_mask &= ~bit;
if (current_mask == binding->full_mask)
{
std::get<InputButtonEventHandler>(binding->handler)(0.0f);
matched = true;
break;
}
}
// Need to start again, might've reloaded.
if (matched)
break;
}
} while (matched);
}
bool InputManager::PreprocessEvent(InputBindingKey key, float value, GenericInputBinding generic_key)
{
// does imgui want the event?

View File

@ -284,6 +284,9 @@ void AddVibrationBinding(u32 pad_index, const InputBindingKey* motor_0_binding,
/// Returns true if anything was bound to this key, otherwise false.
bool InvokeEvents(InputBindingKey key, float value, GenericInputBinding generic_key = GenericInputBinding::Unknown);
/// Clears internal state for any binds with a matching source/index.
void ClearBindStateFromSource(InputBindingKey key);
/// Sets a hook which can be used to intercept events before they're processed by the normal bindings.
/// This is typically used when binding new controls to detect what gets pressed.
void SetHook(InputInterceptHook::Callback callback);