mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-03-12 22:56:52 +01:00
ciface/win32: use CM_Register_Notification instead of wnd msgs
This commit is contained in:
parent
b6ac63dc47
commit
76d2e47444
@ -3,35 +3,38 @@
|
||||
|
||||
#include "InputCommon/ControllerInterface/Win32/Win32.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <Windows.h>
|
||||
#include <cfgmgr32.h>
|
||||
// must be before hidclass
|
||||
#include <initguid.h>
|
||||
|
||||
#include <hidclass.h>
|
||||
|
||||
#include <array>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include "Common/Flag.h"
|
||||
#include "Common/HRWrap.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/ScopeGuard.h"
|
||||
#include "Common/Thread.h"
|
||||
#include "InputCommon/ControllerInterface/DInput/DInput.h"
|
||||
#include "InputCommon/ControllerInterface/WGInput/WGInput.h"
|
||||
#include "InputCommon/ControllerInterface/XInput/XInput.h"
|
||||
|
||||
constexpr UINT WM_DOLPHIN_STOP = WM_USER;
|
||||
#pragma comment(lib, "OneCoreUAP.Lib")
|
||||
|
||||
// Dolphin's render window
|
||||
static HWND s_hwnd;
|
||||
// Windows messaging window (hidden)
|
||||
static HWND s_message_window;
|
||||
static std::thread s_thread;
|
||||
static std::mutex s_populate_mutex;
|
||||
// TODO is this really needed?
|
||||
static Common::Flag s_first_populate_devices_asked;
|
||||
static HCMNOTIFICATION s_notify_handle;
|
||||
|
||||
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
|
||||
_Pre_satisfies_(EventDataSize >= sizeof(CM_NOTIFY_EVENT_DATA)) static DWORD CALLBACK
|
||||
OnDevicesChanged(_In_ HCMNOTIFICATION hNotify, _In_opt_ PVOID Context,
|
||||
_In_ CM_NOTIFY_ACTION Action,
|
||||
_In_reads_bytes_(EventDataSize) PCM_NOTIFY_EVENT_DATA EventData,
|
||||
_In_ DWORD EventDataSize)
|
||||
{
|
||||
if (message == WM_INPUT_DEVICE_CHANGE)
|
||||
if (Action == CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL ||
|
||||
Action == CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL)
|
||||
{
|
||||
// Windows automatically sends this message before we ask for it and before we are "ready" to
|
||||
// listen for it.
|
||||
@ -47,143 +50,60 @@ static LRESULT CALLBACK WindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARA
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return DefWindowProc(hwnd, message, wparam, lparam);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
void ciface::Win32::Init(void* hwnd)
|
||||
{
|
||||
s_hwnd = static_cast<HWND>(hwnd);
|
||||
|
||||
XInput::Init();
|
||||
WGInput::Init();
|
||||
|
||||
std::promise<HWND> message_window_promise;
|
||||
|
||||
s_thread = std::thread([&message_window_promise] {
|
||||
Common::SetCurrentThreadName("ciface::Win32 Message Loop");
|
||||
|
||||
HWND message_window = nullptr;
|
||||
Common::ScopeGuard promise_guard([&] { message_window_promise.set_value(message_window); });
|
||||
|
||||
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "CoInitializeEx failed: {}", Common::HRWrap(hr));
|
||||
return;
|
||||
}
|
||||
Common::ScopeGuard uninit([] { CoUninitialize(); });
|
||||
|
||||
const auto window_name = TEXT("DolphinWin32ControllerInterface");
|
||||
|
||||
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 = window_name;
|
||||
|
||||
ATOM window_class = RegisterClassEx(&window_class_info);
|
||||
if (!window_class)
|
||||
{
|
||||
NOTICE_LOG_FMT(CONTROLLERINTERFACE, "RegisterClassEx failed: {}",
|
||||
Common::HRWrap(GetLastError()));
|
||||
return;
|
||||
}
|
||||
Common::ScopeGuard unregister([&window_class] {
|
||||
if (!UnregisterClass(MAKEINTATOM(window_class), GetModuleHandle(nullptr)))
|
||||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "UnregisterClass failed: {}",
|
||||
Common::HRWrap(GetLastError()));
|
||||
});
|
||||
|
||||
message_window = CreateWindowEx(0, window_name, nullptr, 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr,
|
||||
nullptr, nullptr);
|
||||
promise_guard.Exit();
|
||||
if (!message_window)
|
||||
{
|
||||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "CreateWindowEx failed: {}",
|
||||
Common::HRWrap(GetLastError()));
|
||||
return;
|
||||
}
|
||||
Common::ScopeGuard destroy([&] {
|
||||
if (!DestroyWindow(message_window))
|
||||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "DestroyWindow failed: {}",
|
||||
Common::HRWrap(GetLastError()));
|
||||
});
|
||||
|
||||
std::array<RAWINPUTDEVICE, 2> devices;
|
||||
// game pad devices
|
||||
devices[0].usUsagePage = 0x01;
|
||||
devices[0].usUsage = 0x05;
|
||||
devices[0].dwFlags = RIDEV_DEVNOTIFY;
|
||||
devices[0].hwndTarget = message_window;
|
||||
// joystick devices
|
||||
devices[1].usUsagePage = 0x01;
|
||||
devices[1].usUsage = 0x04;
|
||||
devices[1].dwFlags = RIDEV_DEVNOTIFY;
|
||||
devices[1].hwndTarget = message_window;
|
||||
|
||||
if (!RegisterRawInputDevices(devices.data(), static_cast<UINT>(devices.size()),
|
||||
static_cast<UINT>(sizeof(decltype(devices)::value_type))))
|
||||
{
|
||||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "RegisterRawInputDevices failed: {}", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
MSG msg;
|
||||
while (GetMessage(&msg, nullptr, 0, 0) > 0)
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
if (msg.message == WM_DOLPHIN_STOP)
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
s_message_window = message_window_promise.get_future().get();
|
||||
CM_NOTIFY_FILTER notify_filter{.cbSize = sizeof(notify_filter),
|
||||
.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE,
|
||||
.u{.DeviceInterface{.ClassGuid = GUID_DEVINTERFACE_HID}}};
|
||||
const CONFIGRET cfg_rv =
|
||||
CM_Register_Notification(¬ify_filter, nullptr, OnDevicesChanged, &s_notify_handle);
|
||||
if (cfg_rv != CR_SUCCESS)
|
||||
{
|
||||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "CM_Register_Notification failed: {:x}", cfg_rv);
|
||||
}
|
||||
}
|
||||
|
||||
void ciface::Win32::PopulateDevices(void* hwnd)
|
||||
{
|
||||
if (s_thread.joinable())
|
||||
{
|
||||
s_hwnd = static_cast<HWND>(hwnd);
|
||||
// To avoid blocking this thread until the async population has finished, directly do it here
|
||||
// (we need the DInput Keyboard and Mouse "default" device to always be added without any wait).
|
||||
std::lock_guard lk_population(s_populate_mutex);
|
||||
s_first_populate_devices_asked.Set();
|
||||
ciface::DInput::PopulateDevices(s_hwnd);
|
||||
ciface::XInput::PopulateDevices();
|
||||
ciface::WGInput::PopulateDevices();
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG_FMT(CONTROLLERINTERFACE,
|
||||
"win32 asked to populate devices, but device thread isn't running");
|
||||
}
|
||||
s_hwnd = static_cast<HWND>(hwnd);
|
||||
std::lock_guard lk_population(s_populate_mutex);
|
||||
s_first_populate_devices_asked.Set();
|
||||
ciface::DInput::PopulateDevices(s_hwnd);
|
||||
ciface::XInput::PopulateDevices();
|
||||
ciface::WGInput::PopulateDevices();
|
||||
}
|
||||
|
||||
void ciface::Win32::ChangeWindow(void* hwnd)
|
||||
{
|
||||
if (s_thread.joinable()) // "Has init?"
|
||||
{
|
||||
s_hwnd = static_cast<HWND>(hwnd);
|
||||
std::lock_guard lk_population(s_populate_mutex);
|
||||
ciface::DInput::ChangeWindow(s_hwnd);
|
||||
}
|
||||
s_hwnd = static_cast<HWND>(hwnd);
|
||||
std::lock_guard lk_population(s_populate_mutex);
|
||||
ciface::DInput::ChangeWindow(s_hwnd);
|
||||
}
|
||||
|
||||
void ciface::Win32::DeInit()
|
||||
{
|
||||
NOTICE_LOG_FMT(CONTROLLERINTERFACE, "win32 DeInit");
|
||||
if (s_thread.joinable())
|
||||
{
|
||||
PostMessage(s_message_window, WM_DOLPHIN_STOP, 0, 0);
|
||||
s_thread.join();
|
||||
s_message_window = nullptr;
|
||||
s_first_populate_devices_asked.Clear();
|
||||
DInput::DeInit();
|
||||
}
|
||||
s_first_populate_devices_asked.Clear();
|
||||
DInput::DeInit();
|
||||
s_hwnd = nullptr;
|
||||
|
||||
if (s_notify_handle)
|
||||
{
|
||||
const CONFIGRET cfg_rv = CM_Unregister_Notification(s_notify_handle);
|
||||
if (cfg_rv != CR_SUCCESS)
|
||||
{
|
||||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "CM_Unregister_Notification failed: {:x}", cfg_rv);
|
||||
}
|
||||
s_notify_handle = nullptr;
|
||||
}
|
||||
|
||||
XInput::DeInit();
|
||||
WGInput::DeInit();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user