From d26c1ce24d9108114d7eab97cfc2ab4f3eea28ee Mon Sep 17 00:00:00 2001 From: Michael M Date: Fri, 10 Nov 2017 12:30:37 -0800 Subject: Add hotplug support to DInput and XInput controller backends --- .../ControllerInterface/Win32/Win32.cpp | 119 ++++++++++++++++++++- 1 file changed, 114 insertions(+), 5 deletions(-) (limited to 'Source/Core/InputCommon/ControllerInterface/Win32/Win32.cpp') diff --git a/Source/Core/InputCommon/ControllerInterface/Win32/Win32.cpp b/Source/Core/InputCommon/ControllerInterface/Win32/Win32.cpp index 2ce583e608..6c78c6b2a0 100644 --- a/Source/Core/InputCommon/ControllerInterface/Win32/Win32.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Win32/Win32.cpp @@ -4,23 +4,132 @@ #include "InputCommon/ControllerInterface/Win32/Win32.h" +#include + +#include + +#include "Common/Event.h" +#include "Common/Logging/Log.h" +#include "Common/ScopeGuard.h" #include "InputCommon/ControllerInterface/DInput/DInput.h" #include "InputCommon/ControllerInterface/XInput/XInput.h" -void ciface::Win32::Init() +constexpr UINT WM_DOLPHIN_STOP = WM_USER; + +static Common::Event s_done_populating; +static std::atomic s_hwnd; +static HWND s_message_window; +static std::thread s_thread; + +static LRESULT CALLBACK WindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) +{ + if (message == WM_INPUT_DEVICE_CHANGE) + { + ciface::DInput::PopulateDevices(s_hwnd); + ciface::XInput::PopulateDevices(); + s_done_populating.Set(); + } + + return DefWindowProc(hwnd, message, wparam, lparam); +} + +void ciface::Win32::Init(void* hwnd) { - // DInput::Init(); + s_hwnd = static_cast(hwnd); XInput::Init(); + + s_thread = std::thread([] { + if (FAILED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED))) + { + ERROR_LOG(SERIALINTERFACE, "CoInitializeEx failed: %i", GetLastError()); + return; + } + Common::ScopeGuard uninit([] { CoUninitialize(); }); + + WNDCLASSEX window_class_info{}; + window_class_info.cbSize = sizeof(window_class_info); + window_class_info.lpfnWndProc = WindowProc; + window_class_info.hInstance = GetModuleHandle(nullptr); + window_class_info.lpszClassName = L"Message"; + + ATOM window_class = RegisterClassEx(&window_class_info); + if (!window_class) + { + NOTICE_LOG(SERIALINTERFACE, "RegisterClassEx failed: %i", GetLastError()); + return; + } + Common::ScopeGuard unregister([&window_class] { + if (!UnregisterClass(MAKEINTATOM(window_class), GetModuleHandle(nullptr))) + ERROR_LOG(SERIALINTERFACE, "UnregisterClass failed: %i", GetLastError()); + }); + + s_message_window = CreateWindowEx(0, L"Message", nullptr, 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, + nullptr, nullptr); + if (!s_message_window) + { + ERROR_LOG(SERIALINTERFACE, "CreateWindowEx failed: %i", GetLastError()); + return; + } + Common::ScopeGuard destroy([] { + if (!DestroyWindow(s_message_window)) + ERROR_LOG(SERIALINTERFACE, "DestroyWindow failed: %i", GetLastError()); + s_message_window = nullptr; + }); + + std::array devices; + // game pad devices + devices[0].usUsagePage = 0x01; + devices[0].usUsage = 0x05; + devices[0].dwFlags = RIDEV_DEVNOTIFY; + devices[0].hwndTarget = s_message_window; + // joystick devices + devices[1].usUsagePage = 0x01; + devices[1].usUsage = 0x04; + devices[1].dwFlags = RIDEV_DEVNOTIFY; + devices[1].hwndTarget = s_message_window; + + if (!RegisterRawInputDevices(devices.data(), static_cast(devices.size()), + static_cast(sizeof(decltype(devices)::value_type)))) + { + ERROR_LOG(SERIALINTERFACE, "RegisterRawInputDevices failed: %i", GetLastError()); + return; + } + + MSG msg; + while (GetMessage(&msg, nullptr, 0, 0) > 0) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + if (msg.message == WM_DOLPHIN_STOP) + break; + } + }); } void ciface::Win32::PopulateDevices(void* hwnd) { - DInput::PopulateDevices(static_cast(hwnd)); - XInput::PopulateDevices(); + if (s_thread.joinable()) + { + s_hwnd = static_cast(hwnd); + s_done_populating.Reset(); + PostMessage(s_message_window, WM_INPUT_DEVICE_CHANGE, 0, 0); + if (!s_done_populating.WaitFor(std::chrono::seconds(10))) + ERROR_LOG(SERIALINTERFACE, "win32 timed out when trying to populate devices"); + } + else + { + ERROR_LOG(SERIALINTERFACE, "win32 asked to populate devices, but device thread isn't running"); + } } void ciface::Win32::DeInit() { - // DInput::DeInit(); + NOTICE_LOG(SERIALINTERFACE, "win32 DeInit"); + if (s_thread.joinable()) + { + PostMessage(s_message_window, WM_DOLPHIN_STOP, 0, 0); + s_thread.join(); + } + XInput::DeInit(); } -- cgit v1.2.3