diff options
| author | Admiral H. Curtiss <pikachu025@gmail.com> | 2024-01-17 12:39:45 +0100 |
|---|---|---|
| committer | Admiral H. Curtiss <pikachu025@gmail.com> | 2024-01-18 00:30:45 +0100 |
| commit | 83d4b692b8bdde2b56fea18937d74451b8b1b974 (patch) | |
| tree | 389fdf315ad2152279f5be08deb03e2094a04713 /Source/Core/InputCommon/ControllerInterface | |
| parent | 12318f921f66bd1d6482814802c847f5ed1f9979 (diff) | |
InputCommon/WGInput: Handle add/remove events on separate thread to prevent deadlocks.
In particular this is triggered when running Dolphin with the Steam overlay.
Diffstat (limited to 'Source/Core/InputCommon/ControllerInterface')
| -rw-r--r-- | Source/Core/InputCommon/ControllerInterface/WGInput/WGInput.cpp | 61 |
1 files changed, 56 insertions, 5 deletions
diff --git a/Source/Core/InputCommon/ControllerInterface/WGInput/WGInput.cpp b/Source/Core/InputCommon/ControllerInterface/WGInput/WGInput.cpp index 14c1072992..2fa3fc2e77 100644 --- a/Source/Core/InputCommon/ControllerInterface/WGInput/WGInput.cpp +++ b/Source/Core/InputCommon/ControllerInterface/WGInput/WGInput.cpp @@ -6,6 +6,7 @@ #include <array> #include <map> #include <string_view> +#include <utility> // For CoGetApartmentType #include <objbase.h> @@ -25,7 +26,9 @@ #include "Common/HRWrap.h" #include "Common/Logging/Log.h" +#include "Common/ScopeGuard.h" #include "Common/StringUtil.h" +#include "Common/WorkQueueThread.h" #include "InputCommon/ControllerInterface/ControllerInterface.h" namespace WGI = winrt::Windows::Gaming::Input; @@ -605,8 +608,21 @@ private: ControlState m_battery_level = 0; }; +enum class AddRemoveEventType +{ + AddOrReplace, + Remove, +}; + +struct AddRemoveEvent +{ + AddRemoveEventType type; + WGI::RawGameController raw_game_controller; +}; + static thread_local bool s_initialized_winrt; static winrt::event_token s_event_added, s_event_removed; +static Common::WorkQueueThread<AddRemoveEvent> s_device_add_remove_queue; static bool COMIsInitialized() { @@ -668,6 +684,36 @@ static void RemoveDevice(const WGI::RawGameController& raw_game_controller) // (H is the lambda) #pragma warning(disable : 4265) +static void HandleAddRemoveEvent(AddRemoveEvent evt) +{ + try + { + winrt::init_apartment(); + } + catch (const winrt::hresult_error& ex) + { + ERROR_LOG_FMT(CONTROLLERINTERFACE, + "WGInput: Failed to CoInitialize for add/remove controller event: {}", + WStringToUTF8(ex.message())); + return; + } + Common::ScopeGuard coinit_guard([] { winrt::uninit_apartment(); }); + + switch (evt.type) + { + case AddRemoveEventType::AddOrReplace: + RemoveDevice(evt.raw_game_controller); + AddDevice(evt.raw_game_controller); + break; + case AddRemoveEventType::Remove: + RemoveDevice(evt.raw_game_controller); + break; + default: + ERROR_LOG_FMT(CONTROLLERINTERFACE, "WGInput: Invalid add/remove controller event: {}", + std::to_underlying(evt.type)); + } +} + void Init() { if (!COMIsInitialized()) @@ -678,18 +724,21 @@ void Init() s_initialized_winrt = true; } + s_device_add_remove_queue.Reset("WGInput Add/Remove Device Thread", HandleAddRemoveEvent); + try { // These events will be invoked from WGI-managed threadpool. s_event_added = WGI::RawGameController::RawGameControllerAdded( - [](auto&&, const WGI::RawGameController raw_game_controller) { - RemoveDevice(raw_game_controller); - AddDevice(raw_game_controller); + [](auto&&, WGI::RawGameController raw_game_controller) { + s_device_add_remove_queue.EmplaceItem( + AddRemoveEvent{AddRemoveEventType::AddOrReplace, std::move(raw_game_controller)}); }); s_event_removed = WGI::RawGameController::RawGameControllerRemoved( - [](auto&&, const WGI::RawGameController raw_game_controller) { - RemoveDevice(raw_game_controller); + [](auto&&, WGI::RawGameController raw_game_controller) { + s_device_add_remove_queue.EmplaceItem( + AddRemoveEvent{AddRemoveEventType::Remove, std::move(raw_game_controller)}); }); } catch (winrt::hresult_error) @@ -702,6 +751,8 @@ void Init() void DeInit() { + s_device_add_remove_queue.Shutdown(); + WGI::RawGameController::RawGameControllerAdded(s_event_added); WGI::RawGameController::RawGameControllerRemoved(s_event_removed); |
