1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
// Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "InputCommon/ControllerInterface/Win32/Win32.h"
#include <Windows.h>
#include <cfgmgr32.h>
// must be before hidclass
#include <initguid.h>
#include <hidclass.h>
#include <mutex>
#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")
static std::mutex s_populate_mutex;
// TODO is this really needed?
static Common::Flag s_first_populate_devices_asked;
static HCMNOTIFICATION s_notify_handle;
namespace ciface::Win32
{
class InputBackend final : public ciface::InputBackend
{
public:
InputBackend(ControllerInterface* controller_interface);
~InputBackend();
void PopulateDevices() override;
void HandleWindowChange() override;
HWND GetHWND();
};
} // namespace ciface::Win32
_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(
static_cast<ciface::Win32::InputBackend*>(Context)->GetHWND());
ciface::XInput::PopulateDevices();
});
}
}
return ERROR_SUCCESS;
}
namespace ciface::Win32
{
std::unique_ptr<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface)
{
return std::make_unique<InputBackend>(controller_interface);
}
HWND InputBackend::GetHWND()
{
return static_cast<HWND>(GetControllerInterface().GetWindowSystemInfo().render_window);
}
InputBackend::InputBackend(ControllerInterface* controller_interface)
: ciface::InputBackend(controller_interface)
{
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, this, OnDevicesChanged, &s_notify_handle);
if (cfg_rv != CR_SUCCESS)
{
ERROR_LOG_FMT(CONTROLLERINTERFACE, "CM_Register_Notification failed: {:x}", cfg_rv);
}
}
void InputBackend::PopulateDevices()
{
std::lock_guard lk_population(s_populate_mutex);
s_first_populate_devices_asked.Set();
ciface::DInput::PopulateDevices(GetHWND());
ciface::XInput::PopulateDevices();
ciface::WGInput::PopulateDevices();
}
void InputBackend::HandleWindowChange()
{
std::lock_guard lk_population(s_populate_mutex);
ciface::DInput::ChangeWindow(GetHWND());
}
InputBackend::~InputBackend()
{
s_first_populate_devices_asked.Clear();
DInput::DeInit();
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();
}
} // namespace ciface::Win32
|