Add hotplug support to DInput and XInput controller backends

This commit is contained in:
Michael M 2017-11-10 12:30:37 -08:00 committed by Jordan Woyak
parent 92ca6e124e
commit d26c1ce24d
5 changed files with 123 additions and 10 deletions

View File

@ -46,7 +46,7 @@ void ControllerInterface::Initialize(const WindowSystemInfo& wsi)
m_is_populating_devices = true; m_is_populating_devices = true;
#ifdef CIFACE_USE_WIN32 #ifdef CIFACE_USE_WIN32
ciface::Win32::Init(); ciface::Win32::Init(wsi.render_surface);
#endif #endif
#ifdef CIFACE_USE_XLIB #ifdef CIFACE_USE_XLIB
// nothing needed // nothing needed
@ -154,7 +154,7 @@ void ControllerInterface::Shutdown()
ciface::Win32::DeInit(); ciface::Win32::DeInit();
#endif #endif
#ifdef CIFACE_USE_XLIB #ifdef CIFACE_USE_XLIB
// nothing needed // nothing needed
#endif #endif
#ifdef CIFACE_USE_OSX #ifdef CIFACE_USE_OSX
ciface::OSX::DeInit(); ciface::OSX::DeInit();

View File

@ -46,6 +46,8 @@ std::string GetDeviceName(const LPDIRECTINPUTDEVICE8 device)
void PopulateDevices(HWND hwnd) void PopulateDevices(HWND hwnd)
{ {
g_controller_interface.RemoveDevice([](const auto* dev) { return dev->GetSource() == "DInput"; });
IDirectInput8* idi8; IDirectInput8* idi8;
if (FAILED(DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, if (FAILED(DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8,
(LPVOID*)&idi8, nullptr))) (LPVOID*)&idi8, nullptr)))

View File

@ -4,23 +4,132 @@
#include "InputCommon/ControllerInterface/Win32/Win32.h" #include "InputCommon/ControllerInterface/Win32/Win32.h"
#include <windows.h>
#include <thread>
#include "Common/Event.h"
#include "Common/Logging/Log.h"
#include "Common/ScopeGuard.h"
#include "InputCommon/ControllerInterface/DInput/DInput.h" #include "InputCommon/ControllerInterface/DInput/DInput.h"
#include "InputCommon/ControllerInterface/XInput/XInput.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<HWND> s_hwnd;
static HWND s_message_window;
static std::thread s_thread;
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{ {
// DInput::Init(); 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)
{
s_hwnd = static_cast<HWND>(hwnd);
XInput::Init(); 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<RAWINPUTDEVICE, 2> 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<UINT>(devices.size()),
static_cast<UINT>(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) void ciface::Win32::PopulateDevices(void* hwnd)
{ {
DInput::PopulateDevices(static_cast<HWND>(hwnd)); if (s_thread.joinable())
XInput::PopulateDevices(); {
s_hwnd = static_cast<HWND>(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() 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(); XInput::DeInit();
} }

View File

@ -8,8 +8,8 @@ namespace ciface
{ {
namespace Win32 namespace Win32
{ {
void Init(); void Init(void* hwnd);
void PopulateDevices(void* hwnd); void PopulateDevices(void* hwnd);
void DeInit(); void DeInit();
} } // namespace Win32
} } // namespace ciface

View File

@ -92,6 +92,8 @@ void PopulateDevices()
if (!hXInput) if (!hXInput)
return; return;
g_controller_interface.RemoveDevice([](const auto* dev) { return dev->GetSource() == "XInput"; });
XINPUT_CAPABILITIES caps; XINPUT_CAPABILITIES caps;
for (int i = 0; i != 4; ++i) for (int i = 0; i != 4; ++i)
if (ERROR_SUCCESS == PXInputGetCapabilities(i, 0, &caps)) if (ERROR_SUCCESS == PXInputGetCapabilities(i, 0, &caps))