-
Notifications
You must be signed in to change notification settings - Fork 2
/
librawinput.h
234 lines (197 loc) · 8.88 KB
/
librawinput.h
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/// @file
/// @brief librawinput
/// @author ttsuki
/// @date 2019.10.23 original
/// @date 2021.11.18 v2
/// @date 2022.05.22 v3
/// @date 2023.01.19 v3.1
// Licensed under the MIT License.
// Copyright (c) 2019-2023 ttsuki All rights reserved.
#pragma once
#include <Windows.h>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <chrono>
#include <string>
#include <array>
#include <vector>
#include <optional>
#include <bitset>
#include <functional>
namespace ttsuki::librawinput
{
/// Microsecond clock
using TIMESTAMP = int64_t;
static inline TIMESTAMP Clock()
{
using namespace std::chrono;
static const high_resolution_clock::time_point ClockStart = high_resolution_clock::now();
return static_cast<TIMESTAMP>(duration_cast<microseconds>(high_resolution_clock::now() - ClockStart).count());
}
}
namespace ttsuki::librawinput
{
/// Fixed memory buffer vector template.
template <class T, size_t TCapacity>
class ARRAY
{
std::array<T, TCapacity> values{};
size_t count{};
public:
[[nodiscard]] const T* data() const { return values.data(); }
[[nodiscard]] size_t size() const { return count; }
[[nodiscard]] size_t capacity() const { return TCapacity; }
[[nodiscard]] bool empty() const { return size() == 0; }
[[nodiscard]] const T& operator [](size_t i) const { return values[i]; }
[[nodiscard]] auto begin() const { return values.begin(); }
[[nodiscard]] auto end() const { return values.begin() + count; }
void clear() { count = 0; }
void push_back(T item) { if (size() < capacity()) values[count++] = item; }
};
}
namespace ttsuki::librawinput
{
enum struct RawInputDeviceType : uint32_t
{
None = 0x00,
Mouse = 0x01,
Keyboard = 0x02,
Joystick = 0x04,
GamePad = 0x08,
Other = 0x80000000,
ALL = ~0u,
};
struct RawInputDeviceDescription
{
HANDLE Handle{};
RawInputDeviceType Type{};
std::wstring Path{};
std::wstring ManufactureName{};
std::wstring ProductName{};
std::wstring SerialNumber{};
};
/// Starts listening raw input events.
/// @param target_device_types target devices (bitwise or-ed)
/// @returns devices
std::vector<RawInputDeviceDescription> GetRawInputDeviceList(RawInputDeviceType target_device_types);
struct KeyboardEvent;
struct MouseEvent;
struct HidEvent;
struct JoystickHidEvent;
using RawInputEventCallback = std::function<void(const RAWINPUT* input, TIMESTAMP timestamp)>;
using KeyboardEventCallback = std::function<void(const KeyboardEvent&)>;
using MouseEventCallback = std::function<void(const MouseEvent&)>;
using HidEventCallback = std::function<void(const HidEvent&)>;
using JoystickHidEventCallback = std::function<void(const JoystickHidEvent&)>;
struct RawInputCallbacks
{
RawInputEventCallback RawInputEventCallback{};
KeyboardEventCallback KeyboardEventCallback{};
MouseEventCallback MouseEventCallback{};
HidEventCallback HidEventCallback{};
JoystickHidEventCallback JoystickHidEventCallback{};
};
/// Starts listening raw input events.
/// @param target_device_types target devices (bitwise or-ed)
/// @param callbacks event callbacks
/// @returns listener handle
std::shared_ptr<void> StartRawInput(RawInputDeviceType target_device_types, RawInputCallbacks callbacks);
struct KeyboardEvent
{
/// Constructs KeyboardEvent from RAWINPUT.
[[nodiscard]] static KeyboardEvent Parse(const RAWINPUT* input, TIMESTAMP timestamp);
HANDLE Device;
TIMESTAMP Timestamp;
RAWKEYBOARD RawKeyboard;
[[nodiscard]] double ElapsedTimeSec() const { return static_cast<double>(Clock() - Timestamp) / 1000000.0; }
[[nodiscard]] uint16_t VirtualKeyCode() const { return static_cast<uint16_t>(RawKeyboard.VKey); }
[[nodiscard]] bool KeyIsDown() const { return (RawKeyboard.Flags & RI_KEY_BREAK) == 0; }
};
struct MouseEvent
{
/// Constructs MouseEvent from RAWINPUT.
[[nodiscard]] static MouseEvent Parse(const RAWINPUT* input, TIMESTAMP timestamp);
HANDLE Device;
TIMESTAMP Timestamp;
RAWMOUSE RawMouse;
enum struct ButtonIndex : uint32_t
{
Button1 = RI_MOUSE_LEFT_BUTTON_DOWN,
Button2 = RI_MOUSE_RIGHT_BUTTON_DOWN,
Button3 = RI_MOUSE_MIDDLE_BUTTON_DOWN,
Button4 = RI_MOUSE_BUTTON_4_DOWN,
Button5 = RI_MOUSE_BUTTON_5_DOWN,
LeftButton = Button1,
RightButton = Button2,
MiddleButton = Button3,
ButtonDownMask = Button1 | Button2 | Button3 | Button4 | Button5,
};
[[nodiscard]] double ElapsedTimeSec() const { return static_cast<double>(Clock() - Timestamp) / 1000000.0; }
[[nodiscard]] int LastX() const { return RawMouse.lLastX; }
[[nodiscard]] int LastY() const { return RawMouse.lLastY; }
[[nodiscard]] bool LastXYIsAbsolute() const { return RawMouse.usFlags & MOUSE_MOVE_ABSOLUTE; }
[[nodiscard]] int WheelDelta() const { return (RawMouse.usButtonFlags & RI_MOUSE_WHEEL) ? static_cast<int16_t>(RawMouse.usButtonData) : 0; }
[[nodiscard]] ButtonIndex PressedButtons() const { return static_cast<ButtonIndex>(RawMouse.usButtonFlags & static_cast<uint32_t>(ButtonIndex::ButtonDownMask)); }
[[nodiscard]] ButtonIndex ReleasedButtons() const { return static_cast<ButtonIndex>(RawMouse.usButtonFlags >> 1 & static_cast<uint32_t>(ButtonIndex::ButtonDownMask)); }
[[nodiscard]] bool ButtonIsDown(ButtonIndex b) const { return (static_cast<uint32_t>(PressedButtons()) & static_cast<uint32_t>(b)) != 0; }
[[nodiscard]] bool ButtonIsUp(ButtonIndex b) const { return (static_cast<uint32_t>(ReleasedButtons()) & static_cast<uint32_t>(b)) != 0; }
};
struct HidDeviceCaps;
struct HidEvent
{
/// Constructs HidEvent from RAWINPUT.
[[nodiscard]] static HidEvent Parse(const RAWINPUT* input, TIMESTAMP timestamp, const HidDeviceCaps* caps);
HANDLE Device;
TIMESTAMP Timestamp;
RAWHID RawHid;
const HidDeviceCaps* Caps;
struct ValueInput
{
uint16_t Page;
uint16_t Usage;
int32_t Value;
int32_t MinValue;
int32_t MaxValue;
};
struct ButtonInput
{
uint16_t Page;
uint16_t FirstUsage;
uint16_t LastUsage;
uint16_t ButtonCount;
uint64_t ButtonStatuses;
};
static inline constexpr size_t kMaxCountOfValues = 16;
static inline constexpr size_t kMaxCountOfButtonPages = 16;
static inline constexpr size_t kMaxCountOfButtonsPerPage = 64;
ARRAY<ValueInput, kMaxCountOfValues> Values;
ARRAY<ButtonInput, kMaxCountOfButtonPages> Buttons;
[[nodiscard]] double ElapsedTimeSec() const { return static_cast<double>(Clock() - Timestamp) / 1000000.0; }
};
struct JoystickHidEvent
{
/// Constructs JoystickHidEvent from HidEvent.
[[nodiscard]] static JoystickHidEvent FromHidEvent(const HidEvent& e);
HANDLE Device;
TIMESTAMP Timestamp;
std::optional<float> X, Y, Z;
std::optional<float> RotX, RotY, RotZ;
std::optional<float> Slider0, Slider1, Slider2, Slider3;
std::optional<float> HatSwitch0, HatSwitch1;
std::optional<float> HatSwitch0X, HatSwitch0Y;
std::optional<float> HatSwitch1X, HatSwitch1Y;
uint32_t ButtonCount;
std::bitset<64> Buttons;
[[nodiscard]] double ElapsedTimeSec() const { return static_cast<double>(Clock() - Timestamp) / 1000000.0; }
};
static inline std::underlying_type_t<RawInputDeviceType> operator +(RawInputDeviceType a) { return static_cast<std::underlying_type_t<RawInputDeviceType>>(a); }
static inline bool operator !(RawInputDeviceType a) { return !+a; }
static inline RawInputDeviceType operator ~(RawInputDeviceType a) { return static_cast<RawInputDeviceType>(~+a); }
static inline RawInputDeviceType operator |(RawInputDeviceType a, RawInputDeviceType b) { return static_cast<RawInputDeviceType>(+a | +b); }
static inline RawInputDeviceType operator &(RawInputDeviceType a, RawInputDeviceType b) { return static_cast<RawInputDeviceType>(+a & +b); }
static inline RawInputDeviceType operator ^(RawInputDeviceType a, RawInputDeviceType b) { return static_cast<RawInputDeviceType>(+a ^ +b); }
static inline RawInputDeviceType& operator |=(RawInputDeviceType& a, RawInputDeviceType b) { return a = a | b; }
static inline RawInputDeviceType& operator &=(RawInputDeviceType& a, RawInputDeviceType b) { return a = a & b; }
static inline RawInputDeviceType& operator ^=(RawInputDeviceType& a, RawInputDeviceType b) { return a = a ^ b; }
}