2#include <ossia/detail/hash_map.hpp>
4#include <ossia/detail/timer.hpp>
5#include <ossia/network/context.hpp>
6#include <ossia/protocols/joystick/game_controller_protocol.hpp>
7#include <ossia/protocols/joystick/joystick_protocol.hpp>
9#if __has_include(<SDL2/SDL.h>)
15#include <ossia/detail/fmt.hpp>
20struct sdl_joystick_context
22 sdl_joystick_context()
24 SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS,
"1");
26 SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,
"1");
28 if(
int ret = SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); ret < 0)
29 throw std::runtime_error(fmt::format(
"SDL Init failure: {}", SDL_GetError()));
34 SDL_InitSubSystem(SDL_INIT_HAPTIC);
35 SDL_InitSubSystem(SDL_INIT_SENSOR);
37 SDL_JoystickEventState(SDL_ENABLE);
38 SDL_GameControllerEventState(SDL_ENABLE);
41 static sdl_joystick_context& instance()
43 static sdl_joystick_context instance{};
47 ~sdl_joystick_context()
51 ev.type = SDL_FIRSTEVENT;
58class joystick_protocol_manager
61 static joystick_protocol_manager& instance()
63 static joystick_protocol_manager instance{};
67 joystick_protocol_manager() { sdl_joystick_context::instance(); }
69 ~joystick_protocol_manager() { SDL_Quit(); }
71 bool joystick_is_registered(
const SDL_JoystickID joystick_id)
73 return m_joystick_protocols.find(joystick_id) != m_joystick_protocols.end();
76 void register_protocol(
auto& protocol)
78 const SDL_JoystickID joystick_id = protocol.m_joystick_id;
80 if(joystick_is_registered(joystick_id))
81 throw std::runtime_error(
"A protocol is already registered for this joystick");
84 std::lock_guard<std::mutex> _{m_joystick_protocols_mutex};
85 m_joystick_protocols[joystick_id] = &protocol;
89 void unregister_protocol(
auto& protocol)
91 const SDL_JoystickID joystick_id = protocol.m_joystick_id;
93 if(!joystick_is_registered(joystick_id))
94 throw std::runtime_error(
95 "Cannot unregister a protocol that haven't been registered");
98 std::lock_guard<std::mutex> _{m_joystick_protocols_mutex};
99 m_joystick_protocols.erase(joystick_id);
103 template <
typename T>
104 T* get_protocol_by_id(
const SDL_JoystickID
id)
106 std::lock_guard<std::mutex> _{m_joystick_protocols_mutex};
107 auto it = m_joystick_protocols.find(
id);
108 if(it != m_joystick_protocols.end())
110 proto& p = it->second;
111 if(
auto res = ossia::get_if<T*>(&p); res && *res)
117 using proto = ossia::variant<joystick_protocol*, game_controller_protocol*>;
118 ossia::hash_map<SDL_JoystickID, proto> m_joystick_protocols;
119 std::mutex m_joystick_protocols_mutex;
124struct joystick_event_processor
128 explicit timer_context(boost::asio::io_context& ctx)
134 timer_context(timer_context&&) =
default;
135 timer_context& operator=(timer_context&&) =
default;
137 boost::asio::io_context* context{};
142 static inline std::atomic_int instance_count = 0;
143 joystick_event_processor(joystick_protocol_manager& manager)
148 ~joystick_event_processor() =
default;
150 static joystick_event_processor& instance(joystick_protocol_manager& manager)
152 static joystick_event_processor instance{manager};
156 void register_context(boost::asio::io_context& ctx)
158 for(
auto& tm : this->m_timers)
160 if(tm.context == &ctx)
167 m_timers.emplace_back(ctx);
169 if(instance_count > 0)
171 auto& tm = m_timers.back();
176 void start(ossia::timer& timer)
178 using namespace std::literals;
179 timer.set_delay(4ms);
180 timer.start([
this] { this->process_events(); });
183 void unregister_context(boost::asio::io_context& ctx)
185 for(
auto it = m_timers.begin(); it != m_timers.end();)
188 if(tm.context == &ctx)
193 it = m_timers.erase(it);
202 void start_event_loop()
204 if(instance_count++ > 0)
207 for(
auto& tm : m_timers)
211 void stop_event_loop()
213 if(--instance_count > 0)
216 using namespace std::literals;
219 ev.type = SDL_FIRSTEVENT;
222 for(
auto& tm : m_timers)
229 proto->m_device->apply_incoming_message({*proto, 0}, *param, std::move(val));
232 void push_axis(
const SDL_JoyAxisEvent& ev)
234 if(
auto p = m_manager.get_protocol_by_id<joystick_protocol>(ev.which))
236 const float res = (ev.value + .5f) / (0x7FFF + .5f);
237 push(p, p->m_axis_parameters[ev.axis], res);
241 void push_axis(
const SDL_ControllerAxisEvent& ev)
243 if(
auto p = m_manager.get_protocol_by_id<game_controller_protocol>(ev.which))
245 const float res = (ev.value + .5f) / (0x7FFF + .5f);
246 push(p, p->m_axis_parameters[ev.axis], res);
250 void push_button(
const SDL_JoyButtonEvent& ev)
252 if(
auto p = m_manager.get_protocol_by_id<joystick_protocol>(ev.which))
254 push(p, p->m_button_parameters[ev.button],
bool(ev.state == SDL_PRESSED));
258 void push_button(
const SDL_ControllerButtonEvent& ev)
260 if(
auto p = m_manager.get_protocol_by_id<game_controller_protocol>(ev.which))
262 push(p, p->m_button_parameters[ev.button],
bool(ev.state == SDL_PRESSED));
266 void push_sensor(
const SDL_ControllerSensorEvent& ev)
268 if(
auto p = m_manager.get_protocol_by_id<game_controller_protocol>(ev.which))
271 p, p->m_sensor_parameters[ev.sensor],
272 ossia::vec3f{ev.data[0], ev.data[1], ev.data[2]});
276 void push_touchpad(
const SDL_ControllerTouchpadEvent& ev)
278 if(
auto p = m_manager.get_protocol_by_id<game_controller_protocol>(ev.which))
280 auto it = p->m_touchpads.find(ev.touchpad);
281 if(it == p->m_touchpads.end())
284 auto& touchpad = it->second;
285 if(ev.finger >= 0 && ev.finger < touchpad.fingers.size())
287 auto& finger = touchpad.fingers[ev.finger];
288 push(p, finger.x, ev.x);
289 push(p, finger.y, ev.y);
290 push(p, finger.pressure, ev.pressure);
295 void push_hat(
const SDL_JoyHatEvent& ev)
297 if(
auto p = m_manager.get_protocol_by_id<joystick_protocol>(ev.which))
299 const uint8_t v = ev.value;
300 float x = 0.0f, y = 0.0f;
304 else if(v & SDL_HAT_RIGHT)
309 else if(v & SDL_HAT_DOWN)
312 push(p, p->m_hat_parameters[ev.hat], std::array<float, 2>{x, y});
316 void process_event(
const SDL_Event& ev)
320 case SDL_JOYAXISMOTION:
324 case SDL_JOYBUTTONDOWN:
325 case SDL_JOYBUTTONUP:
326 push_button(ev.jbutton);
329 case SDL_JOYHATMOTION:
333 case SDL_CONTROLLERAXISMOTION:
336 case SDL_CONTROLLERBUTTONDOWN:
337 case SDL_CONTROLLERBUTTONUP:
338 push_button(ev.cbutton);
340 case SDL_CONTROLLERTOUCHPADDOWN:
341 case SDL_CONTROLLERTOUCHPADMOTION:
342 case SDL_CONTROLLERTOUCHPADUP:
343 push_touchpad(ev.ctouchpad);
345 case SDL_CONTROLLERSENSORUPDATE:
346 push_sensor(ev.csensor);
356 void process_events()
360 int max_event_count = 20;
361 while(SDL_PollEvent(&ev) && max_event_count-- > 0)
367 joystick_protocol_manager& m_manager;
368 std::vector<timer_context> m_timers;
The parameter_base class.
Definition ossia/network/base/parameter.hpp:48
The value class.
Definition value.hpp:173