15 : m_config(
std::move(config))
30 "HIDBackend already initialized");
34 if (hid_init() != 0) {
36 "Failed to initialize HIDAPI");
41 "HIDBackend initialized (HIDAPI version: {})",
get_version());
53 "Cannot start HIDBackend: not initialized");
59 "HIDBackend already running");
87 "HIDBackend stopped");
102 hid_close(state->handle);
103 state->handle =
nullptr;
114 "HIDBackend shutdown complete");
121 std::vector<InputDeviceInfo> result;
125 result.push_back(ext_info);
139 std::unordered_set<std::string> previous_paths;
141 previous_paths.insert(info.path);
144 std::unordered_set<std::string> current_paths;
146 hid_device_info* devs = hid_enumerate(0x0, 0x0);
147 hid_device_info* cur = devs;
152 cur->usage_page, cur->usage)) {
158 std::string path(cur->path);
159 current_paths.insert(path);
161 bool is_new = (previous_paths.find(path) == previous_paths.end());
171 info.
usage = cur->usage;
177 if (cur->manufacturer_string) {
178 std::wstring ws(cur->manufacturer_string);
180 std::ranges::transform(ws, info.
manufacturer.begin(), [](
wchar_t c) { return static_cast<char>(c); });
182 if (cur->product_string) {
183 std::wstring ws(cur->product_string);
184 info.
name.resize(ws.length());
185 std::ranges::transform(ws, info.
name.begin(), [](
wchar_t c) { return static_cast<char>(c); });
187 info.
name =
"HID Device " + std::to_string(cur->vendor_id) +
":" + std::to_string(cur->product_id);
189 if (cur->serial_number) {
190 std::wstring ws(cur->serial_number);
192 std::ranges::transform(ws, info.
serial_number.begin(), [](
wchar_t c) { return static_cast<char>(c); });
199 "HID device found: {} (VID:{:04X} PID:{:04X})",
207 hid_free_enumeration(devs);
210 if (current_paths.find(it->second.path) == current_paths.end()) {
212 "HID device disconnected: {}", it->second.name);
216 if (open_it->second->handle) {
217 hid_close(open_it->second->handle);
238 "HID device {} already open", device_id);
245 "HID device {} not found", device_id);
249 hid_device* handle = hid_open_path(it->second.path.c_str());
252 "Failed to open HID device {}: {}", device_id, it->second.name);
256 hid_set_nonblocking(handle, 1);
260 state.
info = it->second;
264 auto state_ptr = std::make_shared<HIDDeviceState>();
265 state_ptr->handle = handle;
266 state_ptr->info = it->second;
268 state_ptr->active.store(
true);
273 "Opened HID device {}: {}", device_id, it->second.name);
287 it->second->active.store(
false);
288 if (it->second->handle) {
289 hid_close(it->second->handle);
293 "Closed HID device {}: {}", device_id, it->second->info.name);
308 std::vector<uint32_t> result;
312 result.push_back(
id);
332 const struct hid_api_version* ver = hid_version();
334 return std::to_string(ver->major) +
"." + std::to_string(ver->minor) +
"." + std::to_string(ver->patch);
366 return hid_send_feature_report(it->second->handle, data.data(), data.size());
376 buffer[0] = report_id;
377 return hid_get_feature_report(it->second->handle, buffer.data(), buffer.size());
387 return hid_write(it->second->handle, data.data(), data.size());
400 if (state->active.load() && state->handle) {
406 std::this_thread::sleep_for(std::chrono::milliseconds(1));
412 int bytes_read = hid_read_timeout(
416 if (bytes_read > 0) {
417 std::span<const uint8_t> report(state.
read_buffer.data(), bytes_read);
420 }
else if (bytes_read < 0) {
422 "HID read error on device {}", device_id);
423 state.
active.store(
false);
429 uint16_t usage_page, uint16_t usage)
const
433 return f.matches(vid, pid, usage_page, usage);
440 if (info.path == path) {
467 value.
data = std::vector<uint8_t>(report.begin(), report.end());
469 std::chrono::steady_clock::now().time_since_epoch().count());
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
InputCallback m_input_callback
void close_device(uint32_t device_id) override
Close a previously opened device.
std::vector< InputDeviceInfo > get_devices() const override
Get list of available devices.
bool open_device(uint32_t device_id) override
Open a device for input.
uint32_t m_next_device_id
void notify_input(const InputValue &value)
void clear_device_filters()
Clear all device filters.
InputValue parse_hid_report(uint32_t device_id, std::span< const uint8_t > report)
int send_feature_report(uint32_t device_id, std::span< const uint8_t > data)
Send a feature report to a device.
void start() override
Start listening for input events.
std::atomic< bool > m_running
bool matches_any_filter(uint16_t vid, uint16_t pid, uint16_t usage_page, uint16_t usage) const
int write(uint32_t device_id, std::span< const uint8_t > data)
Send an output report to a device.
bool is_device_open(uint32_t device_id) const override
Check if a device is currently open.
std::mutex m_callback_mutex
void set_input_callback(InputCallback callback) override
Register callback for input values.
std::thread m_poll_thread
std::atomic< bool > m_initialized
void set_device_callback(DeviceCallback callback) override
Register callback for device connect/disconnect events.
std::atomic< bool > m_stop_requested
void poll_device(uint32_t device_id, HIDDeviceState &state)
void add_device_filter(const HIDDeviceFilter &filter)
Add a device filter for enumeration.
std::optional< HIDDeviceInfoExt > get_device_info_ext(uint32_t device_id) const
Get extended HID device info.
std::mutex m_devices_mutex
void shutdown() override
Shutdown and release all resources.
DeviceCallback m_device_callback
uint32_t find_or_assign_device_id(const std::string &path)
std::unordered_map< uint32_t, HIDDeviceInfoExt > m_enumerated_devices
void stop() override
Stop listening for input events.
void notify_device_change(const InputDeviceInfo &info, bool connected)
std::string get_version() const override
Get backend version string.
int get_feature_report(uint32_t device_id, uint8_t report_id, std::span< uint8_t > buffer)
Get a feature report from a device.
bool initialize() override
Initialize the input backend.
std::vector< uint32_t > get_open_devices() const override
Get list of currently open device IDs.
size_t refresh_devices() override
Refresh the device list.
std::unordered_map< uint32_t, std::shared_ptr< HIDDeviceState > > m_open_devices
HIDAPI-based HID input backend.
@ HID
Generic HID devices (game controllers, custom hardware)
std::function< void(const InputValue &)> InputCallback
Callback signature for input events.
std::function< void(const InputDeviceInfo &, bool connected)> DeviceCallback
Callback signature for device connection/disconnection events.
@ InputBackend
Input device backend (HID, MIDI, OSC)
@ Core
Core engine, backend, subsystems.
int poll_timeout_ms
Timeout for hid_read_timeout.
size_t read_buffer_size
Per-device read buffer size.
std::vector< HIDDeviceFilter > filters
Device filters (empty = all devices)
Configuration for HID backend.
Filter for HID device enumeration.
uint16_t release_number
Device release number.
uint16_t usage_page
HID usage page.
std::string path
Platform-specific device path.
int interface_number
USB interface number (-1 if unknown)
Extended HID device information.
std::vector< uint8_t > read_buffer
std::atomic< bool > active
Internal state for an open HID device.