File: Win32.cpp

package info (click to toggle)
dolphin-emu 2503%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 111,624 kB
  • sloc: cpp: 787,747; ansic: 217,914; xml: 31,400; python: 4,226; yacc: 3,985; javascript: 2,430; makefile: 777; asm: 726; sh: 281; pascal: 257; perl: 97; objc: 75
file content (131 lines) | stat: -rw-r--r-- 3,958 bytes parent folder | download | duplicates (3)
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(&notify_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