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 // Statistics
120 // ─────────────────────────────────────────────────────────────────────
121
122 /**
123 * @brief Get number of events processed since start
124 */
125 [[nodiscard]] uint64_t get_events_processed() const
126 {
127 return m_events_processed.load();
128 }
129
130 /**
131 * @brief Get current queue depth
132 */
133 [[nodiscard]] size_t get_queue_depth() const;
134
135 /**
136 * @brief Setup OSC bridge if enabled in config
137 */
138 void setup_osc_bridge(const OSCConfigInfo& osc_config);
139
140private:
141 // ─────────────────────────────────────────────────────────────────────
142 // Processing Thread
143 // ─────────────────────────────────────────────────────────────────────
144
145 void processing_loop();
146 void dispatch_to_nodes(const InputValue& value);
147 bool matches_binding(const InputValue& value, const InputBinding& binding) const;
148 std::optional<InputBinding> resolve_vid_pid(const InputBinding& binding, const std::vector<InputDeviceInfo>& devices) const;
149
151 std::atomic<bool> m_running { false };
152 std::atomic<bool> m_stop_requested { false };
153
154 // ─────────────────────────────────────────────────────────────────────
155 // Input Queue (lock-free)
156 // ─────────────────────────────────────────────────────────────────────
157
158 std::atomic<bool> m_queue_notify { false };
159 static constexpr size_t MAX_QUEUE_SIZE = 4096;
161
162 // ─────────────────────────────────────────────────────────────────────
163 // Node Registry
164 // ─────────────────────────────────────────────────────────────────────
165
167 std::weak_ptr<Nodes::Input::InputNode> node;
169 };
170 using RegistrationList = std::vector<NodeRegistration>;
171
172 std::vector<std::shared_ptr<Nodes::Input::InputNode>> m_tracked_nodes; ///< To keep nodes alive
173
175
176#ifdef MAYAFLUX_PLATFORM_MACOS
177 // Apple's broken LLVM doesn't support std::atomic<std::shared_ptr<T>>
178 std::atomic<const RegistrationList*> m_registrations { nullptr };
179
180 // Hazard pointers for safe lock-free reads on macOS
181 static constexpr size_t MAX_READERS = 16;
182 mutable std::array<std::atomic<const RegistrationList*>, MAX_READERS> m_hazard_ptrs;
183 mutable std::atomic<size_t> m_hazard_counter { 0 };
184
185 void retire_list(const RegistrationList* list);
186#else
187 // Proper C++20 atomic shared_ptr on real toolchains
188 std::atomic<std::shared_ptr<const RegistrationList>> m_registrations;
189#endif
190
191 Registry::Service::InputService* m_input_service { nullptr };
192 Registry::Service::NetworkService* m_network_service { nullptr };
193
194 void teardown_osc_bridge();
195
196 uint64_t m_osc_endpoint_id { 0 };
197
198 // ─────────────────────────────────────────────────────────────────────
199 // Statistics
200 // ─────────────────────────────────────────────────────────────────────
201
202 std::atomic<uint64_t> m_events_processed { 0 };
203};
204
205} // 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:124
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:85
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.