MayaFlux 0.2.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
8
10
11namespace MayaFlux::Core {
12
14 : m_config(config)
15 , m_tokens {
16 .Buffer = Buffers::ProcessingToken::INPUT_BACKEND,
17 .Node = Nodes::ProcessingToken::EVENT_RATE,
18 .Task = Vruta::ProcessingToken::EVENT_DRIVEN
19 }
20{
21}
22
27
29{
30 // Input subsystem doesn't register timing callbacks like audio/graphics.
31 // Backends push to InputManager's queue, which has its own thread.
32}
33
35{
37 "Initializing Input Subsystem...");
38
39 m_handle = &handle;
40
41 if (m_config.hid.enabled) {
43 }
44 if (m_config.midi.enabled) {
46 }
47 if (m_config.osc.enabled) {
49 }
52 }
53
55
56 m_ready.store(true);
57
59 "Input Subsystem initialized with {} backend(s)", m_backends.size());
60}
61
63{
64 auto& registry = Registry::BackendRegistry::instance();
65
66 auto input_service = std::make_shared<Registry::Service::InputService>();
67
68 input_service->get_all_devices = [this]() {
69 return get_all_devices();
70 };
71
72 input_service->open_device = [this](InputType type, uint32_t id) {
73 return open_device(type, id);
74 };
75
76 input_service->close_device = [this](InputType type, uint32_t id) {
77 close_device(type, id);
78 };
79
80 m_input_service = input_service;
81
82 registry.register_service<Registry::Service::InputService>(
83 [input_service]() -> void* {
84 return input_service.get();
85 });
86}
87
89{
90 if (!m_ready.load()) {
92 "Cannot start InputSubsystem: not initialized");
93 return;
94 }
95
96 if (m_running.load()) {
97 return;
98 }
99
101
102 {
103 std::shared_lock lock(m_backends_mutex);
104 for (auto& [type, backend] : m_backends) {
105 backend->start();
106 }
107 }
108
109 m_running.store(true);
110
112 "Input Subsystem started");
113}
114
116{
117 if (!m_running.load())
118 return;
119
120 {
121 std::shared_lock lock(m_backends_mutex);
122 for (auto& [type, backend] : m_backends) {
123 backend->stop();
124 }
125 }
126
127 m_running.store(false);
128}
129
131{
132 if (m_running.load())
133 return;
134
135 {
136 std::shared_lock lock(m_backends_mutex);
137 for (auto& [type, backend] : m_backends) {
138 backend->start();
139 }
140 }
141
142 m_running.store(true);
143}
144
146{
147 if (!m_running.load())
148 return;
149
150 {
151 std::shared_lock lock(m_backends_mutex);
152 for (auto& [type, backend] : m_backends) {
153 backend->stop();
154 }
155 }
156
158
159 m_running.store(false);
160
162 "Input Subsystem stopped");
163}
164
166{
167 if (!m_ready.load())
168 return;
169
170 stop();
171
172 {
173 std::unique_lock lock(m_backends_mutex);
174 for (auto& [type, backend] : m_backends) {
175 backend->shutdown();
176 }
177 m_backends.clear();
178 }
179
181
182 auto& registry = Registry::BackendRegistry::instance();
183 registry.unregister_service<Registry::Service::InputService>();
184 m_input_service.reset();
185
186 m_ready.store(false);
187
189 "Input Subsystem shutdown complete");
190}
191
192// ─────────────────────────────────────────────────────────────────────────────
193// Backend Management
194// ─────────────────────────────────────────────────────────────────────────────
195
196bool InputSubsystem::add_backend(std::unique_ptr<IInputBackend> backend)
197{
198 if (!backend)
199 return false;
200
201 InputType type = backend->get_type();
202
203 std::unique_lock lock(m_backends_mutex);
204
205 if (m_backends.find(type) != m_backends.end()) {
207 "Backend type {} already registered", static_cast<int>(type));
208 return false;
209 }
210
211 if (!backend->initialize()) {
213 "Failed to initialize backend: {}", backend->get_name());
214 return false;
215 }
216
217 wire_backend_to_manager(backend.get());
218
219 m_backends[type] = std::move(backend);
220
222 "Added input backend: {}", m_backends[type]->get_name());
223
224 return true;
225}
226
228{
229 std::shared_lock lock(m_backends_mutex);
230 auto it = m_backends.find(type);
231 return (it != m_backends.end()) ? it->second.get() : nullptr;
232}
233
234std::vector<IInputBackend*> InputSubsystem::get_backends() const
235{
236 std::shared_lock lock(m_backends_mutex);
237 std::vector<IInputBackend*> result;
238 result.reserve(m_backends.size());
239 for (const auto& [type, backend] : m_backends) {
240 result.push_back(backend.get());
241 }
242 return result;
243}
244
245// ─────────────────────────────────────────────────────────────────────────────
246// Device Management
247// ─────────────────────────────────────────────────────────────────────────────
248
249std::vector<InputDeviceInfo> InputSubsystem::get_all_devices() const
250{
251 std::shared_lock lock(m_backends_mutex);
252 std::vector<InputDeviceInfo> result;
253 for (const auto& [type, backend] : m_backends) {
254 auto devices = backend->get_devices();
255 result.insert(result.end(), devices.begin(), devices.end());
256 }
257 return result;
258}
259
260bool InputSubsystem::open_device(InputType backend_type, uint32_t device_id)
261{
262 std::shared_lock lock(m_backends_mutex);
263 auto it = m_backends.find(backend_type);
264 if (it == m_backends.end()) {
266 "Backend not found for device open request");
267 return false;
268 }
269 return it->second->open_device(device_id);
270}
271
272void InputSubsystem::close_device(InputType backend_type, uint32_t device_id)
273{
274 std::shared_lock lock(m_backends_mutex);
275 auto it = m_backends.find(backend_type);
276 if (it != m_backends.end()) {
277 it->second->close_device(device_id);
278 }
279}
280
281// ─────────────────────────────────────────────────────────────────────────────
282// Private: Backend Initialization
283// ─────────────────────────────────────────────────────────────────────────────
284
286{
287 backend->set_input_callback([this](const InputValue& value) {
289 });
290
291 backend->set_device_callback([](const InputDeviceInfo& info, bool connected) {
293 "Device {}: {} ({})",
294 connected ? "connected" : "disconnected",
295 info.name, static_cast<int>(info.backend_type));
296 });
297}
298
300{
301 HIDBackend::Config hid_config;
302 hid_config.filters = m_config.hid.filters;
307
308 auto hid = std::make_unique<HIDBackend>(hid_config);
309
310 if (add_backend(std::move(hid))) {
311 if (m_config.hid.auto_open) {
312 auto* backend = dynamic_cast<HIDBackend*>(get_backend(InputType::HID));
313 for (const auto& dev : backend->get_devices()) {
314 backend->open_device(dev.id);
315 }
316 }
317 }
318}
319
321{
322 MIDIBackend::Config midi_config;
329
330 auto midi = std::make_unique<MIDIBackend>(midi_config);
331
332 if (add_backend(std::move(midi))) {
334 auto* backend = dynamic_cast<MIDIBackend*>(get_backend(InputType::MIDI));
335 for (const auto& dev : backend->get_devices()) {
336 if (dev.is_input) {
337 backend->open_device(dev.id);
338 }
339 }
340 }
341 }
342}
343
349
355
356[[nodiscard]] std::vector<InputDeviceInfo> InputSubsystem::get_hid_devices() const
357{
358 std::shared_lock lock(m_backends_mutex);
359 auto it = m_backends.find(InputType::HID);
360 return (it != m_backends.end()) ? it->second->get_devices() : std::vector<InputDeviceInfo> {};
361}
362
363[[nodiscard]] std::vector<InputDeviceInfo> InputSubsystem::get_midi_devices() const
364{
365 std::shared_lock lock(m_backends_mutex);
366 auto it = m_backends.find(InputType::MIDI);
367 return (it != m_backends.end()) ? it->second->get_devices() : std::vector<InputDeviceInfo> {};
368}
369
370[[nodiscard]] std::optional<InputDeviceInfo> InputSubsystem::get_device_info(
371 InputType backend_type,
372 uint32_t device_id) const
373{
374 std::shared_lock lock(m_backends_mutex);
375 auto it = m_backends.find(backend_type);
376 if (it == m_backends.end())
377 return std::nullopt;
378
379 auto devices = it->second->get_devices();
380 for (const auto& dev : devices) {
381 if (dev.id == device_id) {
382 return dev;
383 }
384 }
385 return std::nullopt;
386}
387
388[[nodiscard]] std::optional<InputDeviceInfo> InputSubsystem::find_hid_device(
389 uint16_t vendor_id,
390 uint16_t product_id) const
391{
392 auto devices = get_hid_devices();
393 for (const auto& dev : devices) {
394 if (dev.vendor_id == vendor_id && dev.product_id == product_id) {
395 return dev;
396 }
397 }
398 return std::nullopt;
399}
400
401} // namespace MayaFlux::Core
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
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
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 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.
void start() override
Start the subsystem's processing/event loops.
IInputBackend * get_backend(InputType type) const
Get a backend by type.
bool open_device(uint32_t device_id) override
Open a device for input.
RtMidi-based MIDI input backend.
Unified interface combining buffer and node processing for subsystems.
static BackendRegistry & instance()
Get the global registry instance.
InputType
Input backend type enumeration.
@ HID
Generic HID devices (game controllers, custom hardware)
@ MIDI
MIDI controllers and instruments.
@ InputSubsystem
Input subsystem operations (device management, event dispatch)
@ Core
Core engine, backend, subsystems.
HIDBackendInfo hid
HID backend configuration.
MIDIBackendInfo midi
MIDI backend configuration.
OSCBackendInfo 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.
std::vector< std::string > input_port_filters
std::vector< std::string > output_port_filters
bool enabled
Enable Serial backend.
Backend input device service interface.