// Copyright 2017 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "InputCommon/ControllerInterface/Win32/Win32.h" #include #include // must be before hidclass #include #include #include #include "Common/Flag.h" #include "Common/Logging/Log.h" #include "InputCommon/ControllerInterface/DInput/DInput.h" #include "InputCommon/ControllerInterface/WGInput/WGInput.h" #include "InputCommon/ControllerInterface/XInput/XInput.h" #pragma comment(lib, "OneCoreUAP.Lib") // Dolphin's render window static HWND s_hwnd; static std::mutex s_populate_mutex; // TODO is this really needed? static Common::Flag s_first_populate_devices_asked; static HCMNOTIFICATION s_notify_handle; _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 (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. if (s_first_populate_devices_asked.IsSet()) { std::lock_guard lk_population(s_populate_mutex); // TODO: we could easily use the message passed alongside this event, which tells // whether a device was added or removed, to avoid removing old, still connected, devices g_controller_interface.PlatformPopulateDevices([] { ciface::DInput::PopulateDevices(s_hwnd); ciface::XInput::PopulateDevices(); }); } } return ERROR_SUCCESS; } void ciface::Win32::Init(void* hwnd) { s_hwnd = static_cast(hwnd); XInput::Init(); WGInput::Init(); 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) { s_hwnd = static_cast(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) { s_hwnd = static_cast(hwnd); std::lock_guard lk_population(s_populate_mutex); ciface::DInput::ChangeWindow(s_hwnd); } void ciface::Win32::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(); }