MayaFlux 0.4.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
193{
194 while (!m_running.load(std::memory_order_acquire))
195 std::this_thread::yield();
196}
197
198// ─────────────────────────────────────────────────────────────────────────────
199// Backend Management
200// ─────────────────────────────────────────────────────────────────────────────
201
202bool InputSubsystem::add_backend(std::unique_ptr<IInputBackend> backend)
203{
204 if (!backend)
205 return false;
206
207 InputType type = backend->get_type();
208
209 std::unique_lock lock(m_backends_mutex);
210
211 if (m_backends.find(type) != m_backends.end()) {
213 "Backend type {} already registered", static_cast<int>(type));
214 return false;
215 }
216
217 if (!backend->initialize()) {
219 "Failed to initialize backend: {}", backend->get_name());
220 return false;
221 }
222
223 wire_backend_to_manager(backend.get());
224
225 m_backends[type] = std::move(backend);
226
228 "Added input backend: {}", m_backends[type]->get_name());
229
230 return true;
231}
232
234{
235 std::shared_lock lock(m_backends_mutex);
236 auto it = m_backends.find(type);
237 return (it != m_backends.end()) ? it->second.get() : nullptr;
238}
239
240std::vector<IInputBackend*> InputSubsystem::get_backends() const
241{
242 std::shared_lock lock(m_backends_mutex);
243 std::vector<IInputBackend*> result;
244 result.reserve(m_backends.size());
245 for (const auto& [type, backend] : m_backends) {
246 result.push_back(backend.get());
247 }
248 return result;
249}
250
251// ─────────────────────────────────────────────────────────────────────────────
252// Device Management
253// ─────────────────────────────────────────────────────────────────────────────
254
255std::vector<InputDeviceInfo> InputSubsystem::get_all_devices() const
256{
257 std::shared_lock lock(m_backends_mutex);
258 std::vector<InputDeviceInfo> result;
259 for (const auto& [type, backend] : m_backends) {
260 auto devices = backend->get_devices();
261 result.insert(result.end(), devices.begin(), devices.end());
262 }
263 return result;
264}
265
266bool InputSubsystem::open_device(InputType backend_type, uint32_t device_id)
267{
268 std::shared_lock lock(m_backends_mutex);
269 auto it = m_backends.find(backend_type);
270 if (it == m_backends.end()) {
272 "Backend not found for device open request");
273 return false;
274 }
275 return it->second->open_device(device_id);
276}
277
278void InputSubsystem::close_device(InputType backend_type, uint32_t device_id)
279{
280 std::shared_lock lock(m_backends_mutex);
281 auto it = m_backends.find(backend_type);
282 if (it != m_backends.end()) {
283 it->second->close_device(device_id);
284 }
285}
286
287// ─────────────────────────────────────────────────────────────────────────────
288// Private: Backend Initialization
289// ─────────────────────────────────────────────────────────────────────────────
290
292{
293 backend->set_input_callback([this](const InputValue& value) {
295 });
296
297 backend->set_device_callback([](const InputDeviceInfo& info, bool connected) {
299 "Device {}: {} ({})",
300 connected ? "connected" : "disconnected",
301 info.name, static_cast<int>(info.backend_type));
302 });
303}
304
306{
307 HIDBackend::Config hid_config;
308 hid_config.filters = m_config.hid.filters;
313
314 auto hid = std::make_unique<HIDBackend>(hid_config);
315
316 if (add_backend(std::move(hid))) {
317 if (m_config.hid.auto_open) {
318 auto* backend = dynamic_cast<HIDBackend*>(get_backend(InputType::HID));
319 for (const auto& dev : backend->get_devices()) {
320 backend->open_device(dev.id);
321 }
322 }
323 }
324}
325
327{
328 MIDIBackend::Config midi_config;
329 midi_config.input_port_filters = m_config.midi.input_port_filters;
330 midi_config.auto_open_inputs = m_config.midi.auto_open_inputs;
331 midi_config.virtual_port_name = m_config.midi.virtual_port_name;
332
333#if !defined(MAYAFLUX_PLATFORM_WINDOWS)
334 midi_config.output_port_filters = m_config.midi.output_port_filters;
335 midi_config.auto_open_outputs = m_config.midi.auto_open_outputs;
336 midi_config.enable_virtual_port = m_config.midi.enable_virtual_port;
337#endif
338
339 auto midi = std::make_unique<MIDIBackend>(midi_config);
340
341 if (add_backend(std::move(midi))) {
343 auto* backend = dynamic_cast<MIDIBackend*>(get_backend(InputType::MIDI));
344 for (const auto& dev : backend->get_devices()) {
345 if (dev.is_input) {
346 backend->open_device(dev.id);
347 }
348 }
349 }
350 }
351}
357
358[[nodiscard]] std::vector<InputDeviceInfo> InputSubsystem::get_hid_devices() const
359{
360 std::shared_lock lock(m_backends_mutex);
361 auto it = m_backends.find(InputType::HID);
362 return (it != m_backends.end()) ? it->second->get_devices() : std::vector<InputDeviceInfo> {};
363}
364
365[[nodiscard]] std::vector<InputDeviceInfo> InputSubsystem::get_midi_devices() const
366{
367 std::shared_lock lock(m_backends_mutex);
368 auto it = m_backends.find(InputType::MIDI);
369 return (it != m_backends.end()) ? it->second->get_devices() : std::vector<InputDeviceInfo> {};
370}
371
372[[nodiscard]] std::optional<InputDeviceInfo> InputSubsystem::get_device_info(
373 InputType backend_type,
374 uint32_t device_id) const
375{
376 std::shared_lock lock(m_backends_mutex);
377 auto it = m_backends.find(backend_type);
378 if (it == m_backends.end())
379 return std::nullopt;
380
381 auto devices = it->second->get_devices();
382 for (const auto& dev : devices) {
383 if (dev.id == device_id) {
384 return dev;
385 }
386 }
387 return std::nullopt;
388}
389
390[[nodiscard]] std::optional<InputDeviceInfo> InputSubsystem::find_hid_device(
391 uint16_t vendor_id,
392 uint16_t product_id) const
393{
394 auto devices = get_hid_devices();
395 for (const auto& dev : devices) {
396 if (dev.vendor_id == vendor_id && dev.product_id == product_id) {
397 return dev;
398 }
399 }
400 return std::nullopt;
401}
402
403} // 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
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.
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.
@ 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.
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.
Backend input device service interface.