MayaFlux 0.2.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;
12}
13
14namespace MayaFlux::Core {
15
16/**
17 * @class InputManager
18 * @brief Manages input processing thread and node dispatch
19 *
20 * InputManager is the core processing entity for input. It:
21 * - Owns the input processing thread
22 * - Maintains device→node routing table
23 * - Receives InputValues from backends via thread-safe queue
24 * - Dispatches input to registered nodes by calling process_input()
25 *
26 * Threading model:
27 * - Backends push to queue from their threads (thread-safe)
28 * - Single processing thread dispatches to nodes
29 * - Node callbacks fire on the processing thread
30 *
31 * Owned by InputSubsystem, which handles lifecycle coordination.
32 */
33class MAYAFLUX_API InputManager {
34public:
37
38 // Non-copyable, non-movable
39 InputManager(const InputManager&) = delete;
43
44 // ─────────────────────────────────────────────────────────────────────
45 // Lifecycle
46 // ─────────────────────────────────────────────────────────────────────
47
48 /**
49 * @brief Start the processing thread
50 */
51 void start();
52
53 /**
54 * @brief Stop the processing thread
55 *
56 * Waits for thread to finish processing current queue before returning.
57 */
58 void stop();
59
60 /**
61 * @brief Check if processing thread is running
62 */
63 [[nodiscard]] bool is_running() const { return m_running.load(); }
64
65 // ─────────────────────────────────────────────────────────────────────
66 // Input Enqueueing (called by backends)
67 // ─────────────────────────────────────────────────────────────────────
68
69 /**
70 * @brief Enqueue an input value for processing
71 * @param value The input value from a backend
72 *
73 * Thread-safe. Called from backend threads.
74 * Wakes processing thread if sleeping.
75 */
76 void enqueue(const InputValue& value);
77
78 /**
79 * @brief Enqueue multiple input values
80 * @param values Vector of input values
81 */
82 void enqueue_batch(const std::vector<InputValue>& values);
83
84 // ─────────────────────────────────────────────────────────────────────
85 // Node Registration
86 // ─────────────────────────────────────────────────────────────────────
87
88 /**
89 * @brief Register a node to receive input
90 * @param node The InputNode to register
91 * @param binding Specifies what input the node wants
92 *
93 * Thread-safe. Can be called while processing is active.
94 */
95 void register_node(
96 const std::shared_ptr<Nodes::Input::InputNode>& node,
97 InputBinding binding);
98
99 /**
100 * @brief Unregister a node
101 * @param node The node to unregister
102 *
103 * Removes the node from all bindings.
104 */
105 void unregister_node(const std::shared_ptr<Nodes::Input::InputNode>& node);
106
107 /**
108 * @brief Unregister all nodes
109 */
110 void unregister_all_nodes();
111
112 /**
113 * @brief Get count of registered nodes
114 */
115 [[nodiscard]] size_t get_registered_node_count() const;
116
117 // ─────────────────────────────────────────────────────────────────────
118 // Statistics
119 // ─────────────────────────────────────────────────────────────────────
120
121 /**
122 * @brief Get number of events processed since start
123 */
124 [[nodiscard]] uint64_t get_events_processed() const
125 {
126 return m_events_processed.load();
127 }
128
129 /**
130 * @brief Get current queue depth
131 */
132 [[nodiscard]] size_t get_queue_depth() const;
133
134private:
135 // ─────────────────────────────────────────────────────────────────────
136 // Processing Thread
137 // ─────────────────────────────────────────────────────────────────────
138
139 void processing_loop();
140 void dispatch_to_nodes(const InputValue& value);
141 bool matches_binding(const InputValue& value, const InputBinding& binding) const;
142 std::optional<InputBinding> resolve_vid_pid(const InputBinding& binding, const std::vector<InputDeviceInfo>& devices) const;
143
145 std::atomic<bool> m_running { false };
146 std::atomic<bool> m_stop_requested { false };
147
148 // ─────────────────────────────────────────────────────────────────────
149 // Input Queue (lock-free)
150 // ─────────────────────────────────────────────────────────────────────
151
152 std::atomic<bool> m_queue_notify { false };
153 static constexpr size_t MAX_QUEUE_SIZE = 4096;
155
156 // ─────────────────────────────────────────────────────────────────────
157 // Node Registry
158 // ─────────────────────────────────────────────────────────────────────
159
161 std::weak_ptr<Nodes::Input::InputNode> node;
163 };
164 using RegistrationList = std::vector<NodeRegistration>;
165
166 std::vector<std::shared_ptr<Nodes::Input::InputNode>> m_tracked_nodes; ///< To keep nodes alive
167
169
170#ifdef MAYAFLUX_PLATFORM_MACOS
171 // Apple's broken LLVM doesn't support std::atomic<std::shared_ptr<T>>
172 std::atomic<const RegistrationList*> m_registrations { nullptr };
173
174 // Hazard pointers for safe lock-free reads on macOS
175 static constexpr size_t MAX_READERS = 16;
176 mutable std::array<std::atomic<const RegistrationList*>, MAX_READERS> m_hazard_ptrs;
177 mutable std::atomic<size_t> m_hazard_counter { 0 };
178
179 void retire_list(const RegistrationList* list);
180#else
181 // Proper C++20 atomic shared_ptr on real toolchains
182 std::atomic<std::shared_ptr<const RegistrationList>> m_registrations;
183#endif
184
185 Registry::Service::InputService* m_input_service { nullptr };
186
187 // ─────────────────────────────────────────────────────────────────────
188 // Statistics
189 // ─────────────────────────────────────────────────────────────────────
190
191 std::atomic<uint64_t> m_events_processed { 0 };
192};
193
194} // 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.
Backend input device service interface.