-
Notifications
You must be signed in to change notification settings - Fork 0
/
Device.h
284 lines (240 loc) · 9.27 KB
/
Device.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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
/** @file
@brief Header
@date 2015
@author
Sensics, Inc.
<http://sensics.com/osvr>
*/
// Copyright 2015 Sensics, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef INCLUDED_Device_h_GUID_A5AB4F26_B2C8_4F8F_5375_91F421C72017
#define INCLUDED_Device_h_GUID_A5AB4F26_B2C8_4F8F_5375_91F421C72017
// Internal Includes
#include "HandleError.h"
// Library/third-party includes
#include <hidapi.h>
// Standard includes
#include <stdexcept>
#include <vector>
#include <string>
#include <memory>
#include <cstddef> // for std::size_t
namespace hidapi {
namespace detail {
/// Deleter functor for a HIDAPI device
class DeviceDeleter {
public:
void operator()(hid_device *dev) {
if (dev) {
hid_close(dev);
}
}
};
using DeviceUniquePtr = std::unique_ptr<hid_device, detail::DeviceDeleter>;
using DeviceSharedPtr = std::shared_ptr<hid_device>;
} // namespace detail
using DataByte = unsigned char;
using DataVector = std::vector<DataByte>;
/// @name Non-throwing data types and methods
/// @brief Use to interrogate the results of non-throwing calls.
/// @{
using DataResult = std::pair<DataVector, const wchar_t *>;
inline bool had_error(DataResult const &result) {
return nullptr != result.second;
}
inline const wchar_t *get_error(DataResult const &result) {
return result.second;
}
inline DataVector const &get_data(DataResult const &result) {
return result.first;
}
inline DataVector &get_data(DataResult &result) { return result.first; }
inline DataVector &&get_data(DataResult &&result) {
return std::move(result.first);
}
/// @}
static const std::size_t DEFAULT_MAX_LENGTH = 512;
/// CRTP base class for device objects: provides common functionality for
/// C++-wrapped HIDAPI devices.
template <typename Derived> class DeviceBase {
public:
/// Checks for validity of the object.
explicit operator bool() const { return (nullptr != get_()); }
/// Accessor for the raw HIDAPI opaque object, for functions that aren't
/// wrapped.
hid_device *get() const { return get_(); }
/// @name Non-throwing methods
/// @brief In case of error, these methods simply return an empty buffer
/// with the error as the second return value (second member in the
/// pair/tuple). Free functions above can interrogate that result.
/// @{
/// Reads a HID report, if available.
///
/// If the device has more than one report type, the first byte will be the
/// report type.
///
/// An empty result means nothing available. Errors will cause the second
/// value returned (the wchar_t pointer) to be non-null and refer to an
/// error message.
DataResult read(std::size_t maxLength = DEFAULT_MAX_LENGTH) {
auto data = DataVector(maxLength);
auto result = hid_read(get(), data.data(), maxLength);
return handle_buffer(std::move(data), result);
}
/// Writes an output HID report
///
/// The first byte of data[] has to contain Report ID. Hence the report
/// size will contain one additional byte with Report ID. If your report
/// is 8 bytes, then you must pass 9 bytes - Report ID plus 8 bytes of
/// report data.
/// Returns the number of bytes written or -1 if failed
///
int write(const unsigned char* data, std::size_t length) {
auto result = hid_write(get(), data, length);
return result;
}
/// Gets a HID feature report.
///
/// The supplied report ID will be the first byte of the returned data
/// vector.
DataVector get_feature_report(unsigned char reportId,
std::size_t maxLength = DEFAULT_MAX_LENGTH) {
auto data = DataVector(maxLength + 1);
data[0] = reportId;
auto result = hid_getFeatureReport(get(), data.data(), maxLength);
return handle_buffer(std::move(data), result);
}
/// @}
/// @name Throwing methods
/// @brief In case of error, these methods throw an error. In case of no
/// error (and nothing to read is considered no error) they return only the
/// buffer.
/// @{
/// Reads a HID report, if available.
///
/// @sa DeviceBase::read()
DataResult read_throwing(std::size_t maxLength = DEFAULT_MAX_LENGTH) {
auto data = DataVector(maxLength);
auto result = hid_read(get(), data.data(), maxLength);
return handle_buffer_and_throw(std::move(data), result);
}
/// Gets a HID feature report.
///
/// @sa DeviceBase::get_feature_report()
DataVector
get_feature_report_throwing(unsigned char reportId,
std::size_t maxLength = DEFAULT_MAX_LENGTH) {
auto data = DataVector(maxLength + 1);
data[0] = reportId;
auto result = hid_getFeatureReport(get(), data.data(), maxLength);
return handle_buffer_and_throw(std::move(data), result);
}
/// @}
using derived_type = Derived;
private:
const wchar_t *handle_buffer_base(DataVector &data, int callResult) {
const wchar_t *ret = nullptr;
if (callResult < 0) {
ret = detail::handle_error(*get());
} else {
data.resize(callResult);
}
return ret;
}
DataResult handle_buffer(DataVector &&data, int callResult) {
const wchar_t *ret = handle_buffer_base(data, callResult);
if (callResult < 0) {
data.clear();
}
return std::make_pair(std::move(data), ret);
}
DataVector handle_buffer_and_throw(DataVector &&data, int callResult) {
auto errMsg = handle_buffer_base(data, callResult);
if (callResult < 0) {
detail::handle_error_throwing(errMsg);
}
return std::move(data);
}
/// Function used by CTRP to get HIDAPI opaque pointer from derived class.
hid_device *get_() const {
return static_cast<derived_type const *>(this)->get();
}
};
/// A wrapper class for a HID device with ownership semantics equal to those of
/// std::unique_ptr
///
/// Most functionality provided by hidapi::DeviceBase
class UniqueDevice : public DeviceBase<UniqueDevice> {
public:
using base = DeviceBase<UniqueDevice>;
/// Default, empty constructor. Not very useful on its own, mostly for move
/// assignment.
UniqueDevice() : base() {}
/// Constructor opening the first device with the given VID and PID and
/// optionally matching the serial number. Wraps `hid_open()`
UniqueDevice(unsigned short vid, unsigned short pid,
const wchar_t *serial_number = nullptr)
: base(), dev_(hid_open(vid, pid, serial_number)) {}
/// Constructor from platform-specific device path, often found through
/// enumeration. Wraps `hid_open_path()`
UniqueDevice(const char *path) : base(), dev_(hid_open_path(path)) {}
/// @overload
UniqueDevice(std::string const &path) : UniqueDevice(path.c_str()) {}
/// Accessor for the raw HIDAPI opaque object, for functions that aren't
/// wrapped.
hid_device *get() const { return dev_.get(); }
/// Move constructor
UniqueDevice(UniqueDevice &&other) : dev_(std::move(other.dev_)) {}
/// Move assignment
UniqueDevice &operator=(UniqueDevice &&other) {
if (&other == this) {
return *this;
}
dev_ = std::move(other.dev_);
return *this;
}
/// Not copy constructible
UniqueDevice(UniqueDevice const &) = delete;
/// Not copy assignable
UniqueDevice &operator=(UniqueDevice const &) = delete;
private:
detail::DeviceUniquePtr dev_;
};
/// A wrapper class for a HID device with ownership semantics equal to those of
/// std::shared_ptr
class SharedDevice : public DeviceBase<SharedDevice> {
public:
using base = DeviceBase<SharedDevice>;
/// Default, empty constructor. Not very useful aside from assignment.
SharedDevice() : base() {}
/// Constructor opening the first device with the given VID and PID and
/// optionally matching the serial number. Wraps `hid_open()`
SharedDevice(unsigned short vid, unsigned short pid,
const wchar_t *serial_number = nullptr)
: base(),
dev_(hid_open(vid, pid, serial_number), detail::DeviceDeleter()) {}
/// Constructor from platform-specific device path, often found through
/// enumeration. Wraps `hid_open_path()`
SharedDevice(const char *path)
: base(), dev_(hid_open_path(path), detail::DeviceDeleter()) {}
/// @overload
SharedDevice(std::string const &path) : SharedDevice(path.c_str()) {}
/// Accessor for the raw HIDAPI opaque object, for functions that aren't
/// wrapped.
hid_device *get() const { return dev_.get(); }
private:
detail::DeviceSharedPtr dev_;
};
} // namespace hidapi
#endif // INCLUDED_Device_h_GUID_A5AB4F26_B2C8_4F8F_5375_91F421C72017