From c3bdd05d2a7d5c0a5b513481ab929cef66e5249b Mon Sep 17 00:00:00 2001 From: Dentomologist Date: Sun, 26 May 2024 16:50:12 -0700 Subject: [PATCH] TAS Input: Enable hotkeys and controller input when Input has focus Enable emulator hotkeys and controller input (when that option is enabled) when a TAS Input window has focus, as if it was the render window instead. This allows TASers to use frame advance and the like without having to switch the focused window or disabling Hotkeys Require Window Focus which also picks up keypresses while other apps are active. Cursor updates are disabled when the TAS Input window has focus, as otherwise the Wii IR widget (and anything else controlled by the mouse) becomes unusable. The cursor continues to work normally when the render window has focus. --- Source/Android/jni/MainAndroid.cpp | 5 +++++ Source/Core/Core/Core.cpp | 3 ++- Source/Core/Core/Host.h | 1 + Source/Core/DolphinNoGUI/MainNoGUI.cpp | 5 +++++ Source/Core/DolphinQt/Host.cpp | 15 +++++++++++++++ Source/Core/DolphinQt/Host.h | 3 +++ Source/Core/DolphinQt/TAS/TASInputWindow.cpp | 16 ++++++++++++++++ Source/Core/DolphinQt/TAS/TASInputWindow.h | 3 +++ Source/Core/DolphinTool/ToolHeadlessPlatform.cpp | 5 +++++ .../DInput/DInputKeyboardMouse.cpp | 12 +++++++++++- .../Quartz/QuartzKeyboardAndMouse.mm | 10 ++++++++-- .../ControllerInterface/Xlib/XInput2.cpp | 13 ++++++++++--- Source/DSPTool/StubHost.cpp | 4 ++++ Source/UnitTests/StubHost.cpp | 4 ++++ 14 files changed, 92 insertions(+), 7 deletions(-) diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index 9fcf1b33a3..cb1a252d3c 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -173,6 +173,11 @@ bool Host_RendererIsFullscreen() return false; } +bool Host_TASInputHasFocus() +{ + return false; +} + void Host_YieldToUI() { } diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 5c60b3b02f..bae5b15258 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -1073,7 +1073,8 @@ void UpdateInputGate(bool require_focus, bool require_full_focus) { // If the user accepts background input, controls should pass even if an on screen interface is on const bool focus_passes = - !require_focus || (Host_RendererHasFocus() && !Host_UIBlocksControllerState()); + !require_focus || + ((Host_RendererHasFocus() || Host_TASInputHasFocus()) && !Host_UIBlocksControllerState()); // Ignore full focus if we don't require basic focus const bool full_focus_passes = !require_focus || !require_full_focus || (focus_passes && Host_RendererHasFullFocus()); diff --git a/Source/Core/Core/Host.h b/Source/Core/Core/Host.h index 8d22e9d59a..66a5e2d78e 100644 --- a/Source/Core/Core/Host.h +++ b/Source/Core/Core/Host.h @@ -53,6 +53,7 @@ bool Host_UIBlocksControllerState(); bool Host_RendererHasFocus(); bool Host_RendererHasFullFocus(); bool Host_RendererIsFullscreen(); +bool Host_TASInputHasFocus(); void Host_Message(HostMessageID id); void Host_PPCSymbolsChanged(); diff --git a/Source/Core/DolphinNoGUI/MainNoGUI.cpp b/Source/Core/DolphinNoGUI/MainNoGUI.cpp index 6cbb2750d2..40145e6e4c 100644 --- a/Source/Core/DolphinNoGUI/MainNoGUI.cpp +++ b/Source/Core/DolphinNoGUI/MainNoGUI.cpp @@ -111,6 +111,11 @@ bool Host_RendererIsFullscreen() return s_platform->IsWindowFullscreen(); } +bool Host_TASInputHasFocus() +{ + return false; +} + void Host_YieldToUI() { } diff --git a/Source/Core/DolphinQt/Host.cpp b/Source/Core/DolphinQt/Host.cpp index fe339364ab..bb912a7ab2 100644 --- a/Source/Core/DolphinQt/Host.cpp +++ b/Source/Core/DolphinQt/Host.cpp @@ -162,6 +162,11 @@ bool Host::GetGBAFocus() #endif } +bool Host::GetTASInputFocus() const +{ + return m_tas_input_focus; +} + bool Host::GetRenderFullscreen() { return m_render_fullscreen; @@ -177,6 +182,11 @@ void Host::SetRenderFullscreen(bool fullscreen) } } +void Host::SetTASInputFocus(const bool focus) +{ + m_tas_input_focus = focus; +} + void Host::ResizeSurface(int new_width, int new_height) { if (g_presenter) @@ -228,6 +238,11 @@ bool Host_RendererIsFullscreen() return Host::GetInstance()->GetRenderFullscreen(); } +bool Host_TASInputHasFocus() +{ + return Host::GetInstance()->GetTASInputFocus(); +} + void Host_YieldToUI() { qApp->processEvents(QEventLoop::ExcludeUserInputEvents); diff --git a/Source/Core/DolphinQt/Host.h b/Source/Core/DolphinQt/Host.h index 645c4bd54d..b7f21190da 100644 --- a/Source/Core/DolphinQt/Host.h +++ b/Source/Core/DolphinQt/Host.h @@ -25,12 +25,14 @@ public: bool GetRenderFullFocus(); bool GetRenderFullscreen(); bool GetGBAFocus(); + bool GetTASInputFocus() const; void SetMainWindowHandle(void* handle); void SetRenderHandle(void* handle); void SetRenderFocus(bool focus); void SetRenderFullFocus(bool focus); void SetRenderFullscreen(bool fullscreen); + void SetTASInputFocus(bool focus); void ResizeSurface(int new_width, int new_height); signals: @@ -49,4 +51,5 @@ private: std::atomic m_render_focus{false}; std::atomic m_render_full_focus{false}; std::atomic m_render_fullscreen{false}; + std::atomic m_tas_input_focus{false}; }; diff --git a/Source/Core/DolphinQt/TAS/TASInputWindow.cpp b/Source/Core/DolphinQt/TAS/TASInputWindow.cpp index 604a564991..d3dc078375 100644 --- a/Source/Core/DolphinQt/TAS/TASInputWindow.cpp +++ b/Source/Core/DolphinQt/TAS/TASInputWindow.cpp @@ -6,7 +6,9 @@ #include #include +#include #include +#include #include #include #include @@ -17,6 +19,7 @@ #include "Common/CommonTypes.h" +#include "DolphinQt/Host.h" #include "DolphinQt/QtUtils/AspectRatioWidget.h" #include "DolphinQt/QtUtils/QueueOnObject.h" #include "DolphinQt/Resources.h" @@ -268,3 +271,16 @@ std::optional TASInputWindow::GetSpinBox(TASSpinBox* spin, int zer return (spin->GetValue() - zero) / scale; } + +void TASInputWindow::changeEvent(QEvent* const event) +{ + if (event->type() == QEvent::ActivationChange) + { + const bool active_window_is_tas_input = + qobject_cast(QApplication::activeWindow()) != nullptr; + + // Switching between TAS Input windows will call SetTASInputFocus(true) twice, but that's fine. + Host::GetInstance()->SetTASInputFocus(active_window_is_tas_input); + } + QDialog::changeEvent(event); +} diff --git a/Source/Core/DolphinQt/TAS/TASInputWindow.h b/Source/Core/DolphinQt/TAS/TASInputWindow.h index 34cc843633..924e53ac87 100644 --- a/Source/Core/DolphinQt/TAS/TASInputWindow.h +++ b/Source/Core/DolphinQt/TAS/TASInputWindow.h @@ -18,6 +18,7 @@ class QBoxLayout; class QCheckBox; class QDialog; +class QEvent; class QGroupBox; class QSpinBox; class QString; @@ -68,6 +69,8 @@ protected: QKeySequence shortcut_key_sequence, Qt::Orientation orientation, QWidget* shortcut_widget); + void changeEvent(QEvent* event) override; + QGroupBox* m_settings_box; QCheckBox* m_use_controller; QSpinBox* m_turbo_press_frames; diff --git a/Source/Core/DolphinTool/ToolHeadlessPlatform.cpp b/Source/Core/DolphinTool/ToolHeadlessPlatform.cpp index 8c1772406f..0b04cbfa19 100644 --- a/Source/Core/DolphinTool/ToolHeadlessPlatform.cpp +++ b/Source/Core/DolphinTool/ToolHeadlessPlatform.cpp @@ -84,6 +84,11 @@ bool Host_RendererIsFullscreen() return false; } +bool Host_TASInputHasFocus() +{ + return false; +} + void Host_YieldToUI() { } diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp b/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp index 3c6a82dcd8..8d7f7c7ada 100644 --- a/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp @@ -179,7 +179,8 @@ void KeyboardMouse::UpdateCursorInput() const auto win_height = std::max(rect.bottom - rect.top, 1l); POINT point = {}; - if (g_controller_interface.IsMouseCenteringRequested() && Host_RendererHasFocus()) + if (g_controller_interface.IsMouseCenteringRequested() && + (Host_RendererHasFocus() || Host_TASInputHasFocus())) { point.x = win_width / 2; point.y = win_height / 2; @@ -189,6 +190,15 @@ void KeyboardMouse::UpdateCursorInput() SetCursorPos(screen_point.x, screen_point.y); g_controller_interface.SetMouseCenteringRequested(false); } + else if (Host_TASInputHasFocus()) + { + // When a TAS Input window has focus and "Enable Controller Input" is checked most types of + // input should be read normally as if the render window had focus instead. The cursor is an + // exception, as otherwise using the mouse to set any control in the TAS Input window will also + // update the Wii IR value (or any other input controlled by the cursor). + + return; + } else { GetCursorPos(&point); diff --git a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm index 55f212900d..9a8ddfc6df 100644 --- a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm +++ b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm @@ -244,7 +244,8 @@ Core::DeviceRemoval KeyboardAndMouse::UpdateInput() const double window_width = std::max(bounds.size.width, 1.0); const double window_height = std::max(bounds.size.height, 1.0); - if (g_controller_interface.IsMouseCenteringRequested() && Host_RendererHasFocus()) + if (g_controller_interface.IsMouseCenteringRequested() && + (Host_RendererHasFocus() || Host_TASInputHasFocus())) { m_cursor.x = 0; m_cursor.y = 0; @@ -258,8 +259,13 @@ Core::DeviceRemoval KeyboardAndMouse::UpdateInput() g_controller_interface.SetMouseCenteringRequested(false); } - else + else if (!Host_TASInputHasFocus()) { + // When a TAS Input window has focus and "Enable Controller Input" is checked most types of + // input should be read normally as if the render window had focus instead. The cursor is an + // exception, as otherwise using the mouse to set any control in the TAS Input window will also + // update the Wii IR value (or any other input controlled by the cursor). + NSPoint loc = [NSEvent mouseLocation]; const auto window_scale = g_controller_interface.GetWindowInputScale(); diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp index e4b717e3af..057d774c5b 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp @@ -391,9 +391,16 @@ Core::DeviceRemoval KeyboardMouse::UpdateInput() m_state.axis.z += delta_z; m_state.axis.z /= SCROLL_AXIS_DECAY; - const bool should_center_mouse = - g_controller_interface.IsMouseCenteringRequested() && Host_RendererHasFocus(); - if (update_mouse || should_center_mouse) + const bool should_center_mouse = g_controller_interface.IsMouseCenteringRequested() && + (Host_RendererHasFocus() || Host_TASInputHasFocus()); + + // When a TAS Input window has focus and "Enable Controller Input" is checked most types of + // input should be read normally as if the render window had focus instead. The cursor is an + // exception, as otherwise using the mouse to set any control in the TAS Input window will also + // update the Wii IR value (or any other input controlled by the cursor). + const bool should_update_mouse = update_mouse && !Host_TASInputHasFocus(); + + if (should_update_mouse || should_center_mouse) UpdateCursor(should_center_mouse); if (update_keyboard) diff --git a/Source/DSPTool/StubHost.cpp b/Source/DSPTool/StubHost.cpp index d32346a457..e270acb23a 100644 --- a/Source/DSPTool/StubHost.cpp +++ b/Source/DSPTool/StubHost.cpp @@ -59,6 +59,10 @@ bool Host_RendererIsFullscreen() { return false; } +bool Host_TASInputHasFocus() +{ + return false; +} void Host_YieldToUI() { } diff --git a/Source/UnitTests/StubHost.cpp b/Source/UnitTests/StubHost.cpp index 47200bd40c..dc7dfe2277 100644 --- a/Source/UnitTests/StubHost.cpp +++ b/Source/UnitTests/StubHost.cpp @@ -63,6 +63,10 @@ bool Host_RendererIsFullscreen() { return false; } +bool Host_TASInputHasFocus() +{ + return false; +} void Host_YieldToUI() { }