mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 21:37:52 -07:00
Add hotplug support to DInput and XInput controller backends
This commit is contained in:
parent
92ca6e124e
commit
d26c1ce24d
@ -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();
|
||||||
|
@ -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)))
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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))
|
||||||
|
Loading…
Reference in New Issue
Block a user