MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
InputManager.hpp
Go to the documentation of this file.
1#pragma once
2
5
7class InputNode;
8}
9
11struct InputService;
12struct NetworkService;
13}
14
15namespace MayaFlux::Core {
16
17/**
18 * @class InputManager
19 * @brief Manages input processing thread and node dispatch
20 *
21 * InputManager is the core processing entity for input. It:
22 * - Owns the input processing thread
23 * - Maintains device→node routing table
24 * - Receives InputValues from backends via thread-safe queue
25 * - Dispatches input to registered nodes by calling process_input()
26 *
27 * Threading model:
28 * - Backends push to queue from their threads (thread-safe)
29 * - Single processing thread dispatches to nodes
30 * - Node callbacks fire on the processing thread
31 *
32 * Owned by InputSubsystem, which handles lifecycle coordination.
33 */
34class MAYAFLUX_API InputManager {
35public:
38
39 // Non-copyable, non-movable
40 InputManager(const InputManager&) = delete;
44
45 // ─────────────────────────────────────────────────────────────────────
46 // Lifecycle
47 // ─────────────────────────────────────────────────────────────────────
48
49 /**
50 * @brief Start the processing thread
51 */
52 void start();
53
54 /**
55 * @brief Stop the processing thread
56 *
57 * Waits for thread to finish processing current queue before returning.
58 */
59 void stop();
60
61 /**
62 * @brief Check if processing thread is running
63 */
64 [[nodiscard]] bool is_running() const { return m_running.load(); }
65
66 // ─────────────────────────────────────────────────────────────────────
67 // Input Enqueueing (called by backends)
68 // ─────────────────────────────────────────────────────────────────────
69
70 /**
71 * @brief Enqueue an input value for processing
72 * @param value The input value from a backend
73 *
74 * Thread-safe. Called from backend threads.
75 * Wakes processing thread if sleeping.
76 */
77 void enqueue(const InputValue& value);
78
79 /**
80 * @brief Enqueue multiple input values
81 * @param values Vector of input values
82 */
83 void enqueue_batch(const std::vector<InputValue>& values);
84
85 // ─────────────────────────────────────────────────────────────────────
86 // Node Registration
87 // ─────────────────────────────────────────────────────────────────────
88
89 /**
90 * @brief Register a node to receive input
91 * @param node The InputNode to register
92 * @param binding Specifies what input the node wants
93 *
94 * Thread-safe. Can be called while processing is active.
95 */
96 void register_node(
97 const std::shared_ptr<Nodes::Input::InputNode>& node,
98 InputBinding binding);
99
100 /**
101 * @brief Unregister a node
102 * @param node The node to unregister
103 *
104 * Removes the node from all bindings.
105 */
106 void unregister_node(const std::shared_ptr<Nodes::Input::InputNode>& node);
107
108 /**
109 * @brief Unregister all nodes
110 */
111 void unregister_all_nodes();
112
113 /**
114 * @brief Get count of registered nodes
115 */
116 [[nodiscard]] size_t get_registered_node_count() const;
117
118 /**
119 * @brief Returns a snapshot of all currently tracked input nodes.
120 * @return Vector of active InputNode instances.
121 */
122 [[nodiscard]] std::vector<std::shared_ptr<Nodes::Input::InputNode>> get_nodes() const;
123
124 // ─────────────────────────────────────────────────────────────────────
125 // Statistics
126 // ─────────────────────────────────────────────────────────────────────
127
128 /**
129 * @brief Get number of events processed since start
130 */
131 [[nodiscard]] uint64_t get_events_processed() const
132 {
133 return m_events_processed.load();
134 }
135
136 /**
137 * @brief Get current queue depth
138 */
139 [[nodiscard]] size_t get_queue_depth() const;
140
141 /**
142 * @brief Setup OSC bridge if enabled in config
143 */
144 void setup_osc_bridge(const OSCConfigInfo& osc_config);
145
146private:
147 // ─────────────────────────────────────────────────────────────────────
148 // Processing Thread
149 // ─────────────────────────────────────────────────────────────────────
150
151 void processing_loop();
152 void dispatch_to_nodes(const InputValue& value);
153 bool matches_binding(const InputValue& value, const InputBinding& binding) const;
154 std::optional<InputBinding> resolve_vid_pid(const InputBinding& binding, const std::vector<InputDeviceInfo>& devices) const;
155
157 std::atomic<bool> m_running { false };
158 std::atomic<bool> m_stop_requested { false };
159
160 // ─────────────────────────────────────────────────────────────────────
161 // Input Queue (lock-free)
162 // ─────────────────────────────────────────────────────────────────────
163
164 std::atomic<bool> m_queue_notify { false };
165 static constexpr size_t MAX_QUEUE_SIZE = 4096;
167
168 // ─────────────────────────────────────────────────────────────────────
169 // Node Registry
170 // ─────────────────────────────────────────────────────────────────────
171
173 std::weak_ptr<Nodes::Input::InputNode> node;
175 };
176 using RegistrationList = std::vector<NodeRegistration>;
177
178 std::vector<std::shared_ptr<Nodes::Input::InputNode>> m_tracked_nodes; ///< To keep nodes alive
179
180 mutable std::mutex m_registry_mutex;
181
182#ifdef MAYAFLUX_PLATFORM_MACOS
183 // Apple's broken LLVM doesn't support std::atomic<std::shared_ptr<T>>
184 std::atomic<const RegistrationList*> m_registrations { nullptr };
185
186 // Hazard pointers for safe lock-free reads on macOS
187 static constexpr size_t MAX_READERS = 16;
188 mutable std::array<std::atomic<const RegistrationList*>, MAX_READERS> m_hazard_ptrs;
189 mutable std::atomic<size_t> m_hazard_counter { 0 };
190
191 void retire_list(const RegistrationList* list);
192#else
193 // Proper C++20 atomic shared_ptr on real toolchains
194 std::atomic<std::shared_ptr<const RegistrationList>> m_registrations;
195#endif
196
197 Registry::Service::InputService* m_input_service { nullptr };
198 Registry::Service::NetworkService* m_network_service { nullptr };
199
200 void teardown_osc_bridge();
201
202 uint64_t m_osc_endpoint_id { 0 };
203
204 // ─────────────────────────────────────────────────────────────────────
205 // Statistics
206 // ─────────────────────────────────────────────────────────────────────
207
208 std::atomic<uint64_t> m_events_processed { 0 };
209};
210
211} // namespace MayaFlux::Core
std::vector< NodeRegistration > RegistrationList
uint64_t get_events_processed() const
Get number of events processed since start.
InputManager(const InputManager &)=delete
std::atomic< std::shared_ptr< const RegistrationList > > m_registrations
InputManager(InputManager &&)=delete
Memory::LockFreeQueue< InputValue, MAX_QUEUE_SIZE > m_queue
bool is_running() const
Check if processing thread is running.
std::vector< std::shared_ptr< Nodes::Input::InputNode > > m_tracked_nodes
To keep nodes alive.
InputManager & operator=(const InputManager &)=delete
InputManager & operator=(InputManager &&)=delete
Manages input processing thread and node dispatch.
Policy-driven unified circular buffer implementation.
void register_node(const std::shared_ptr< Nodes::Node > &node, const Nodes::ProcessingToken &token, uint32_t channel)
Definition Graph.cpp:123
void unregister_node(const std::shared_ptr< Nodes::Node > &node, const Nodes::ProcessingToken &token, uint32_t channel)
Removes a node from the root node of specified channels.
Definition Graph.cpp:84
Specifies what input an InputNode wants to receive.
std::weak_ptr< Nodes::Input::InputNode > node
Generic input value container.
OSC backend configuration.
Backend input device service interface.
Backend network transport service interface.