diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp index afdf768435..c4ef52a2a9 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp @@ -37,6 +37,10 @@ ControllerInterface g_controller_interface; +// We need to save which input channel we are in by thread, so we can access the correct input +// update values in different threads by input channel. We start from InputChannel::Host on all +// threads as hotkeys are updated from a worker thread, but UI can read from the main thread. This +// will never interfere with game threads. static thread_local ciface::InputChannel tls_input_channel = ciface::InputChannel::Host; void ControllerInterface::Initialize(const WindowSystemInfo& wsi) @@ -272,7 +276,11 @@ void ControllerInterface::UpdateInput() { std::lock_guard lk(m_devices_mutex, std::adopt_lock); for (const auto& d : m_devices) + { + // Theoretically we could avoid updating input on devices that don't have any references to + // them, but in practice a few devices types could break in different ways, so we don't d->UpdateInput(); + } } } diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h index 12d17614da..024548b713 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h @@ -67,6 +67,9 @@ public: void Shutdown(); void AddDevice(std::shared_ptr device); void RemoveDevice(std::function callback); + // This is mandatory to use on device populations functions that can be called concurrently by + // more than one thread, or that are called by a single other thread. + // Without this, our devices list might end up in a mixed state. void PlatformPopulateDevices(std::function callback); bool IsInit() const { return m_is_init; } void UpdateInput(); diff --git a/Source/Core/InputCommon/ControllerInterface/CoreDevice.h b/Source/Core/InputCommon/ControllerInterface/CoreDevice.h index c368578949..1994cb402c 100644 --- a/Source/Core/InputCommon/ControllerInterface/CoreDevice.h +++ b/Source/Core/InputCommon/ControllerInterface/CoreDevice.h @@ -227,6 +227,7 @@ public: std::chrono::milliseconds maximum_wait) const; protected: + // Exclusively needed when reading/writing "m_devices" mutable std::recursive_mutex m_devices_mutex; std::vector> m_devices; }; diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp b/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp index ebe2e84286..bc1820b8ad 100644 --- a/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp @@ -91,6 +91,11 @@ KeyboardMouse::~KeyboardMouse() { s_keyboard_mouse_exists = false; + // Independently of the order in which we do these, if we put a breakpoint on Unacquire() (or in + // any place in the call stack before this), when refreshing devices from the UI, on the second + // attempt, it will get stuck in an infinite (while) loop inside dinput8.dll. Given that it can't + // be otherwise be reproduced (not even with sleeps), we can just ignore the problem. + // kb m_kb_device->Unacquire(); m_kb_device->Release(); diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.h b/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.h index ceda59d02c..3bd073a6f1 100644 --- a/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.h +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.h @@ -34,6 +34,7 @@ private: RelativeMouseState relative_mouse; }; + // Keyboard key class Key : public Input { public: @@ -46,6 +47,7 @@ private: const u8 m_index; }; + // Mouse button class Button : public Input { public: @@ -58,6 +60,7 @@ private: const u8 m_index; }; + // Mouse movement offset axis. Includes mouse wheel class Axis : public Input { public: @@ -72,6 +75,7 @@ private: const u8 m_index; }; + // Mouse from window center class Cursor : public Input { public: