MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
InputSubsystem.cpp
Go to the documentation of this file.
1#include "InputSubsystem.hpp"
2
5
9
11
12namespace MayaFlux::Core {
13
15 : m_config(config)
16 , m_tokens {
17 .Buffer = Buffers::ProcessingToken::INPUT_BACKEND,
18 .Node = Nodes::ProcessingToken::EVENT_RATE,
19 .Task = Vruta::ProcessingToken::EVENT_DRIVEN
20 }
21{
22}
23
28
30{
31 // Input subsystem doesn't register timing callbacks like audio/graphics.
32 // Backends push to InputManager's queue, which has its own thread.
33}
34
36{
38 "Initializing Input Subsystem...");
39
40 m_handle = &handle;
41
42 if (m_config.hid.enabled) {
44 }
45 if (m_config.midi.enabled) {
47 }
48 if (m_config.osc.enabled) {
50 }
53 }
54
57 }
58
60
61 m_ready.store(true);
62
64 "Input Subsystem initialized with {} backend(s)", m_backends.size());
65}
66
68{
69 auto& registry = Registry::BackendRegistry::instance();
70
71 auto input_service = std::make_shared<Registry::Service::InputService>();
72
73 input_service->get_all_devices = [this]() {
74 return get_all_devices();
75 };
76
77 input_service->open_device = [this](InputType type, uint32_t id) {
78 return open_device(type, id);
79 };
80
81 input_service->close_device = [this](InputType type, uint32_t id) {
82 close_device(type, id);
83 };
84
85 m_input_service = input_service;
86
87 registry.register_service<Registry::Service::InputService>(
88 [input_service]() -> void* {
89 return input_service.get();
90 });
91}
92
94{
95 if (!m_ready.load()) {
97 "Cannot start InputSubsystem: not initialized");
98 return;
99 }
100
101 if (m_running.load()) {
102 return;
103 }
104
106
107 {
108 std::shared_lock lock(m_backends_mutex);
109 for (auto& [type, backend] : m_backends) {
110 backend->start();
111 }
112 }
113
114 m_running.store(true);
115
117 "Input Subsystem started");
118}
119
121{
122 if (!m_running.load())
123 return;
124
125 {
126 std::shared_lock lock(m_backends_mutex);
127 for (auto& [type, backend] : m_backends) {
128 backend->stop();
129 }
130 }
131
132 m_running.store(false);
133}
134
136{
137 if (m_running.load())
138 return;
139
140 {
141 std::shared_lock lock(m_backends_mutex);
142 for (auto& [type, backend] : m_backends) {
143 backend->start();
144 }
145 }
146
147 m_running.store(true);
148}
149
151{
152 if (!m_running.load())
153 return;
154
155 {
156 std::shared_lock lock(m_backends_mutex);
157 for (auto& [type, backend] : m_backends) {
158 backend->stop();
159 }
160 }
161
163
164 m_running.store(false);
165
167 "Input Subsystem stopped");
168}
169
171{
172 if (!m_ready.load())
173 return;
174
175 stop();
176
177 {
178 std::unique_lock lock(m_backends_mutex);
179 for (auto& [type, backend] : m_backends) {
180 backend->shutdown();
181 }
182 m_backends.clear();
183 }
184
186
187 auto& registry = Registry::BackendRegistry::instance();
188 registry.unregister_service<Registry::Service::InputService>();
189 m_input_service.reset();
190
191 m_ready.store(false);
192
194 "Input Subsystem shutdown complete");
195}
196
198{
199 while (!m_running.load(std::memory_order_acquire))
200 std::this_thread::yield();
201}
202
203// ─────────────────────────────────────────────────────────────────────────────
204// Backend Management
205// ─────────────────────────────────────────────────────────────────────────────
206
207bool InputSubsystem::add_backend(std::unique_ptr<IInputBackend> backend)
208{
209 if (!backend)
210 return false;
211
212 InputType type = backend->get_type();
213
214 std::unique_lock lock(m_backends_mutex);
215
216 if (m_backends.find(type) != m_backends.end()) {
218 "Backend type {} already registered", static_cast<int>(type));
219 return false;
220 }
221
222 if (!backend->initialize()) {
224 "Failed to initialize backend: {}", backend->get_name());
225 return false;
226 }
227
228 wire_backend_to_manager(backend.get());
229
230 m_backends[type] = std::move(backend);
231
233 "Added input backend: {}", m_backends[type]->get_name());
234
235 return true;
236}
237
239{
240 std::shared_lock lock(m_backends_mutex);
241 auto it = m_backends.find(type);
242 return (it != m_backends.end()) ? it->second.get() : nullptr;
243}
244
245std::vector<IInputBackend*> InputSubsystem::get_backends() const
246{
247 std::shared_lock lock(m_backends_mutex);
248 std::vector<IInputBackend*> result;
249 result.reserve(m_backends.size());
250 for (const auto& [type, backend] : m_backends) {
251 result.push_back(backend.get());
252 }
253 return result;
254}
255
256// ─────────────────────────────────────────────────────────────────────────────
257// Device Management
258// ─────────────────────────────────────────────────────────────────────────────
259
260std::vector<InputDeviceInfo> InputSubsystem::get_all_devices() const
261{
262 std::shared_lock lock(m_backends_mutex);
263 std::vector<InputDeviceInfo> result;
264 for (const auto& [type, backend] : m_backends) {
265 auto devices = backend->get_devices();
266 result.insert(result.end(), devices.begin(), devices.end());
267 }
268 return result;
269}
270
271bool InputSubsystem::open_device(InputType backend_type, uint32_t device_id)
272{
273 std::shared_lock lock(m_backends_mutex);
274 auto it = m_backends.find(backend_type);
275 if (it == m_backends.end()) {
277 "Backend not found for device open request");
278 return false;
279 }
280 return it->second->open_device(device_id);
281}
282
283void InputSubsystem::close_device(InputType backend_type, uint32_t device_id)
284{
285 std::shared_lock lock(m_backends_mutex);
286 auto it = m_backends.find(backend_type);
287 if (it != m_backends.end()) {
288 it->second->close_device(device_id);
289 }
290}
291
292// ─────────────────────────────────────────────────────────────────────────────
293// Private: Backend Initialization
294// ─────────────────────────────────────────────────────────────────────────────
295
297{
298 backend->set_input_callback([this](const InputValue& value) {
300 });
301
302 backend->set_device_callback([](const InputDeviceInfo& info, bool connected) {
304 "Device {}: {} ({})",
305 connected ? "connected" : "disconnected",
306 info.name, static_cast<int>(info.backend_type));
307 });
308}
309
311{
312 HIDBackend::Config hid_config;
313 hid_config.filters = m_config.hid.filters;
318
319 auto hid = std::make_unique<HIDBackend>(hid_config);
320
321 if (add_backend(std::move(hid))) {
322 if (m_config.hid.auto_open) {
323 auto* backend = dynamic_cast<HIDBackend*>(get_backend(InputType::HID));
324 for (const auto& dev : backend->get_devices()) {
325 backend->open_device(dev.id);
326 }
327 }
328 }
329}
330
332{
333 MIDIBackend::Config midi_config;
334 midi_config.input_port_filters = m_config.midi.input_port_filters;
335 midi_config.auto_open_inputs = m_config.midi.auto_open_inputs;
336 midi_config.virtual_port_name = m_config.midi.virtual_port_name;
337
338#if !defined(MAYAFLUX_PLATFORM_WINDOWS)
339 midi_config.output_port_filters = m_config.midi.output_port_filters;
340 midi_config.auto_open_outputs = m_config.midi.auto_open_outputs;
341 midi_config.enable_virtual_port = m_config.midi.enable_virtual_port;
342#endif
343
344 auto midi = std::make_unique<MIDIBackend>(midi_config);
345
346 if (add_backend(std::move(midi))) {
348 auto* backend = dynamic_cast<MIDIBackend*>(get_backend(InputType::MIDI));
349 for (const auto& dev : backend->get_devices()) {
350 if (dev.is_input) {
351 backend->open_device(dev.id);
352 }
353 }
354 }
355 }
356}
362
364{
365 TabletBackend::Config tablet_config;
369
370 add_backend(std::make_unique<TabletBackend>(tablet_config));
371}
372
373[[nodiscard]] std::vector<InputDeviceInfo> InputSubsystem::get_hid_devices() const
374{
375 std::shared_lock lock(m_backends_mutex);
376 auto it = m_backends.find(InputType::HID);
377 return (it != m_backends.end()) ? it->second->get_devices() : std::vector<InputDeviceInfo> {};
378}
379
380[[nodiscard]] std::vector<InputDeviceInfo> InputSubsystem::get_midi_devices() const
381{
382 std::shared_lock lock(m_backends_mutex);
383 auto it = m_backends.find(InputType::MIDI);
384 return (it != m_backends.end()) ? it->second->get_devices() : std::vector<InputDeviceInfo> {};
385}
386
387[[nodiscard]] std::vector<InputDeviceInfo> InputSubsystem::get_tablet_devices() const
388{
389 std::shared_lock lock(m_backends_mutex);
390 auto it = m_backends.find(InputType::TABLET);
391 return (it != m_backends.end()) ? it->second->get_devices() : std::vector<InputDeviceInfo> {};
392}
393
394[[nodiscard]] std::optional<InputDeviceInfo> InputSubsystem::get_device_info(
395 InputType backend_type,
396 uint32_t device_id) const
397{
398 std::shared_lock lock(m_backends_mutex);
399 auto it = m_backends.find(backend_type);
400 if (it == m_backends.end())
401 return std::nullopt;
402
403 auto devices = it->second->get_devices();
404 for (const auto& dev : devices) {
405 if (dev.id == device_id) {
406 return dev;
407 }
408 }
409 return std::nullopt;
410}
411
412[[nodiscard]] std::optional<InputDeviceInfo> InputSubsystem::find_hid_device(
413 uint16_t vendor_id,
414 uint16_t product_id) const
415{
416 auto devices = get_hid_devices();
417 for (const auto& dev : devices) {
418 if (dev.vendor_id == vendor_id && dev.product_id == product_id) {
419 return dev;
420 }
421 }
422 return std::nullopt;
423}
424
425} // namespace MayaFlux::Core
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
vk::PhysicalDeviceType type
Definition VKDevice.cpp:146
float value
bool open_device(uint32_t device_id) override
Open a device for input.
HIDAPI-based HID input backend.
virtual void set_device_callback(DeviceCallback callback)=0
Register callback for device connect/disconnect events.
virtual void set_input_callback(InputCallback callback)=0
Register callback for input values.
Abstract interface for input device backends.
void unregister()
unregister all nodes from InputManager
void enqueue_input(const InputValue &value)
enqueue input value to InputManager
void setup_osc_bridge(const OSCConfigInfo &config)
enqueue batch of input values to InputManager
std::optional< InputDeviceInfo > get_device_info(InputType backend_type, uint32_t device_id) const
Get device info by backend type and device ID.
std::vector< InputDeviceInfo > get_all_devices() const
Get all available input devices across all backends.
void pause() override
Pause the subsystem's processing/event loops.
void close_device(InputType backend_type, uint32_t device_id)
Close a device.
InputSubsystem(GlobalInputConfig &config)
bool add_backend(std::unique_ptr< IInputBackend > backend)
Add a custom input backend.
std::shared_ptr< Registry::Service::InputService > m_input_service
SubsystemProcessingHandle * m_handle
void shutdown() override
Shutdown and cleanup subsystem resources.
bool open_device(InputType backend_type, uint32_t device_id)
Open a device.
std::vector< InputDeviceInfo > get_midi_devices() const
Get all MIDI devices.
std::vector< InputDeviceInfo > get_hid_devices() const
Get all HID devices.
std::vector< IInputBackend * > get_backends() const
Get all active backends.
std::unordered_map< InputType, std::unique_ptr< IInputBackend > > m_backends
std::optional< InputDeviceInfo > find_hid_device(uint16_t vendor_id, uint16_t product_id) const
Find HID device by vendor/product ID.
void wait_until_running() override
Block until the subsystem's processing loop is confirmed live.
void wire_backend_to_manager(IInputBackend *backend)
void register_callbacks() override
Register callback hooks for this domain.
void initialize(SubsystemProcessingHandle &handle) override
Initialize with a handle provided by SubsystemManager.
void resume() override
Resume the subsystem's processing/event loops.
void stop() override
Stop the subsystem's processing/event loops.
std::vector< InputDeviceInfo > get_tablet_devices() const
Get all tablet devices.
void start() override
Start the subsystem's processing/event loops.
IInputBackend * get_backend(InputType type) const
Get a backend by type.
Unified interface combining buffer and node processing for subsystems.
static BackendRegistry & instance()
Get the global registry instance.
InputType
Input backend type enumeration.
@ TABLET
Digitizers and styluses (pressure, tilt, rotation)
@ HID
Generic HID devices (game controllers, custom hardware)
@ MIDI
MIDI controllers and instruments.
CoreMidiBackend MIDIBackend
@ InputSubsystem
Input subsystem operations (device management, event dispatch)
@ Core
Core engine, backend, subsystems.
HIDBackendInfo hid
HID backend configuration.
MIDIBackendInfo midi
MIDI backend configuration.
TabletBackendInfo tablet
Tablet backend configuration.
OSCConfigInfo osc
OSC backend configuration.
SerialBackendInfo serial
Serial backend configuration.
Configuration for the InputSubsystem.
size_t read_buffer_size
Per-device read buffer size.
int poll_timeout_ms
Polling timeout in milliseconds.
uint32_t reconnect_interval_ms
Reconnection attempt interval.
bool auto_open
Auto-open matching devices on start.
std::vector< HIDDeviceFilter > filters
Device filters (empty = all devices)
bool auto_reconnect
Auto-reconnect disconnected devices.
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)
uint32_t reconnect_interval_ms
Reconnection attempt interval.
bool auto_reconnect
Auto-reopen disconnected devices.
Configuration for HID backend.
std::string name
Human-readable device name.
InputType backend_type
Which backend manages this device.
Information about a connected input device.
Generic input value container.
bool auto_open_outputs
Auto-open all MIDI output ports.
bool enable_virtual_port
Create a virtual MIDI port.
std::vector< std::string > input_port_filters
Filter input ports by name substring.
std::vector< std::string > output_port_filters
Filter output ports by name substring.
bool auto_open_inputs
Auto-open all MIDI input ports.
std::string virtual_port_name
Name for virtual port.
bool enabled
Enable OSC backend.
bool enabled
Enable Serial backend.
int poll_timeout_ms
Timeout for hid_read_timeout.
size_t read_buffer_size
Per-device read buffer size.
bool split_eraser
Report the eraser end as its own tool.
bool enabled
Enable tablet backend.
bool split_eraser
Report the eraser end as its own tool.
int poll_timeout_ms
Timeout for hid_read_timeout.
size_t read_buffer_size
Per-device read buffer.
Configuration for the tablet backend.
Backend input device service interface.