Antkeeper  0.0.1
action-map.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2023 Christopher J. Howard
3  *
4  * This file is part of Antkeeper source code.
5  *
6  * Antkeeper source code is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Antkeeper source code is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
21 #include <algorithm>
22 #include <cmath>
23 #include <type_traits>
24 #include <utility>
25 
26 namespace input {
27 
29 {
30  if (!m_enabled)
31  {
32  if (m_event_dispatcher)
33  {
34  subscribe();
35  }
36 
37  m_enabled = true;
38  }
39 }
40 
42 {
43  if (m_enabled)
44  {
45  if (m_event_dispatcher)
46  {
47  unsubscribe();
48  }
49 
50  m_enabled = false;
51  }
52 }
53 
55 {
56  for (auto action: m_actions)
57  {
58  action->reset();
59  }
60 }
61 
63 {
64  if (m_event_dispatcher != dispatcher)
65  {
66  if (m_enabled)
67  {
68  if (m_event_dispatcher)
69  {
70  unsubscribe();
71  }
72 
73  m_event_dispatcher = dispatcher;
74 
75  if (m_event_dispatcher)
76  {
77  subscribe();
78  }
79  }
80  else
81  {
82  m_event_dispatcher = dispatcher;
83  }
84  }
85 }
86 
88 {
89  switch (mapping.get_mapping_type())
90  {
93  break;
94 
97  break;
98 
99  case mapping_type::key:
100  add_key_mapping(action, static_cast<const key_mapping&>(mapping));
101  break;
102 
105  break;
106 
109  break;
110 
113  break;
114 
115  default:
116  //std::unreachable();
117  break;
118  }
119 }
120 
122 {
123  m_gamepad_axis_mappings.emplace_back(&action, std::move(mapping));
124  m_actions.emplace(&action);
125 }
126 
128 {
129  m_gamepad_button_mappings.emplace_back(&action, std::move(mapping));
130  m_actions.emplace(&action);
131 }
132 
134 {
135  m_key_mappings.emplace_back(&action, std::move(mapping));
136  m_actions.emplace(&action);
137 }
138 
140 {
141  m_mouse_button_mappings.emplace_back(&action, std::move(mapping));
142  m_actions.emplace(&action);
143 }
144 
146 {
147  m_mouse_motion_mappings.emplace_back(&action, std::move(mapping));
148  m_actions.emplace(&action);
149 }
150 
152 {
153  m_mouse_scroll_mappings.emplace_back(&action, std::move(mapping));
154  m_actions.emplace(&action);
155 }
156 
158 {
159  auto predicate = [&](const auto& tuple) -> bool
160  {
161  return std::get<0>(tuple) == &action;
162  };
163 
164  switch (type)
165  {
167  std::erase_if(m_gamepad_axis_mappings, predicate);
168  break;
169 
171  std::erase_if(m_gamepad_button_mappings, predicate);
172  break;
173 
174  case mapping_type::key:
175  std::erase_if(m_key_mappings, predicate);
176  break;
177 
179  std::erase_if(m_mouse_button_mappings, predicate);
180  break;
181 
183  std::erase_if(m_mouse_motion_mappings, predicate);
184  break;
185 
187  std::erase_if(m_mouse_scroll_mappings, predicate);
188  break;
189 
190  default:
191  //std::unreachable();
192  break;
193  }
194 
195  for (const auto& entry: m_gamepad_axis_mappings)
196  {
197  if (std::get<0>(entry) == &action)
198  {
199  return;
200  }
201  }
202  for (const auto& entry: m_gamepad_button_mappings)
203  {
204  if (std::get<0>(entry) == &action)
205  {
206  return;
207  }
208  }
209  for (const auto& entry: m_key_mappings)
210  {
211  if (std::get<0>(entry) == &action)
212  {
213  return;
214  }
215  }
216  for (const auto& entry: m_mouse_button_mappings)
217  {
218  if (std::get<0>(entry) == &action)
219  {
220  return;
221  }
222  }
223  for (const auto& entry: m_mouse_motion_mappings)
224  {
225  if (std::get<0>(entry) == &action)
226  {
227  return;
228  }
229  }
230  for (const auto& entry: m_mouse_scroll_mappings)
231  {
232  if (std::get<0>(entry) == &action)
233  {
234  return;
235  }
236  }
237 
238  m_actions.erase(&action);
239 }
240 
242 {
243  auto predicate = [&](const auto& tuple) -> bool
244  {
245  return std::get<0>(tuple) == &action;
246  };
247 
248  std::erase_if(m_gamepad_axis_mappings, predicate);
249  std::erase_if(m_gamepad_button_mappings, predicate);
250  std::erase_if(m_key_mappings, predicate);
251  std::erase_if(m_mouse_button_mappings, predicate);
252  std::erase_if(m_mouse_motion_mappings, predicate);
253  std::erase_if(m_mouse_scroll_mappings, predicate);
254 
255  m_actions.erase(&action);
256 }
257 
259 {
260  m_gamepad_axis_mappings.clear();
261  m_gamepad_button_mappings.clear();
262  m_key_mappings.clear();
263  m_mouse_button_mappings.clear();
264  m_mouse_motion_mappings.clear();
265  m_mouse_scroll_mappings.clear();
266 
267  m_actions.clear();
268 }
269 
270 void action_map::handle_gamepad_axis_moved(const gamepad_axis_moved_event& event)
271 {
272  for (const auto& [action, mapping]: m_gamepad_axis_mappings)
273  {
274  if (mapping.axis == event.axis &&
275  (!mapping.gamepad || mapping.gamepad == event.gamepad))
276  {
277  if (std::signbit(event.position) == mapping.direction)
278  {
279  action->evaluate(std::abs(event.position));
280  }
281  else
282  {
283  action->evaluate(0.0f);
284  }
285  }
286  }
287 }
288 
289 void action_map::handle_gamepad_button_pressed(const gamepad_button_pressed_event& event)
290 {
291  for (const auto& [action, mapping]: m_gamepad_button_mappings)
292  {
293  if (mapping.button == event.button &&
294  (!mapping.gamepad || mapping.gamepad == event.gamepad))
295  {
296  action->evaluate(1.0f);
297  }
298  }
299 }
300 
301 void action_map::handle_gamepad_button_released(const gamepad_button_released_event& event)
302 {
303  for (const auto& [action, mapping]: m_gamepad_button_mappings)
304  {
305  if (mapping.button == event.button &&
306  (!mapping.gamepad || mapping.gamepad == event.gamepad))
307  {
308  action->evaluate(0.0f);
309  }
310  }
311 }
312 
313 void action_map::handle_key_pressed(const key_pressed_event& event)
314 {
315  for (const auto& [action, mapping]: m_key_mappings)
316  {
317  if (mapping.scancode == event.scancode &&
318  (!mapping.keyboard || mapping.keyboard == event.keyboard) &&
319  (!mapping.modifiers || (mapping.modifiers & event.modifiers)))
320  {
321  if (!event.repeat)
322  {
323  action->evaluate(1.0f);
324  }
325  else if (mapping.repeat)
326  {
327  action->evaluate(0.0f);
328  action->evaluate(1.0f);
329  }
330  }
331  }
332 }
333 
334 void action_map::handle_key_released(const key_released_event& event)
335 {
336  for (const auto& [action, mapping]: m_key_mappings)
337  {
338  if (mapping.scancode == event.scancode &&
339  (!mapping.keyboard || mapping.keyboard == event.keyboard))
340  {
341  action->evaluate(0.0f);
342  }
343  }
344 }
345 
346 void action_map::handle_mouse_moved(const mouse_moved_event& event)
347 {
348  for (const auto& [action, mapping]: m_mouse_motion_mappings)
349  {
350  if (!mapping.mouse || mapping.mouse == event.mouse)
351  {
352  const float difference = static_cast<float>(event.difference[static_cast<std::underlying_type_t<mouse_motion_axis>>(mapping.axis)]);
353 
354  if (difference && std::signbit(difference) == mapping.direction)
355  {
356  action->evaluate(std::abs(difference));
357  action->evaluate(0.0f);
358  }
359  }
360  }
361 }
362 
363 void action_map::handle_mouse_scrolled(const mouse_scrolled_event& event)
364 {
365  for (const auto& [action, mapping]: m_mouse_scroll_mappings)
366  {
367  if (!mapping.mouse || mapping.mouse == event.mouse)
368  {
369  const auto velocity = event.velocity[static_cast<std::underlying_type_t<mouse_scroll_axis>>(mapping.axis)];
370 
371  if (velocity && std::signbit(velocity) == mapping.direction)
372  {
373  action->evaluate(std::abs(velocity));
374  action->evaluate(0.0f);
375  }
376  }
377  }
378 }
379 
380 void action_map::handle_mouse_button_pressed(const mouse_button_pressed_event& event)
381 {
382  for (const auto& [action, mapping]: m_mouse_button_mappings)
383  {
384  if (mapping.button == event.button &&
385  (!mapping.mouse || mapping.mouse == event.mouse))
386  {
387  action->evaluate(1.0f);
388  }
389  }
390 }
391 
392 void action_map::handle_mouse_button_released(const mouse_button_released_event& event)
393 {
394  for (const auto& [action, mapping]: m_mouse_button_mappings)
395  {
396  if (mapping.button == event.button &&
397  (!mapping.mouse || mapping.mouse == event.mouse))
398  {
399  action->evaluate(0.0f);
400  }
401  }
402 }
403 
404 void action_map::handle_update(const update_event& event)
405 {
406  for (const auto* action: m_actions)
407  {
408  action->update();
409  }
410 }
411 
412 std::vector<gamepad_axis_mapping> action_map::get_gamepad_axis_mappings(const action& action) const
413 {
414  std::vector<gamepad_axis_mapping> mappings;
415 
416  for (const auto& [mapped_action, mapping]: m_gamepad_axis_mappings)
417  {
418  if (mapped_action == &action)
419  {
420  mappings.emplace_back(mapping);
421  }
422  }
423 
424  return mappings;
425 }
426 
427 std::vector<gamepad_button_mapping> action_map::get_gamepad_button_mappings(const action& action) const
428 {
429  std::vector<gamepad_button_mapping> mappings;
430 
431  for (const auto& [mapped_action, mapping]: m_gamepad_button_mappings)
432  {
433  if (mapped_action == &action)
434  {
435  mappings.emplace_back(mapping);
436  }
437  }
438 
439  return mappings;
440 }
441 
442 std::vector<key_mapping> action_map::get_key_mappings(const action& action) const
443 {
444  std::vector<key_mapping> mappings;
445 
446  for (const auto& [mapped_action, mapping]: m_key_mappings)
447  {
448  if (mapped_action == &action)
449  {
450  mappings.emplace_back(mapping);
451  }
452  }
453 
454  return mappings;
455 }
456 
457 std::vector<mouse_button_mapping> action_map::get_mouse_button_mappings(const action& action) const
458 {
459  std::vector<mouse_button_mapping> mappings;
460 
461  for (const auto& [mapped_action, mapping]: m_mouse_button_mappings)
462  {
463  if (mapped_action == &action)
464  {
465  mappings.emplace_back(mapping);
466  }
467  }
468 
469  return mappings;
470 }
471 
472 std::vector<mouse_motion_mapping> action_map::get_mouse_motion_mappings(const action& action) const
473 {
474  std::vector<mouse_motion_mapping> mappings;
475 
476  for (const auto& [mapped_action, mapping]: m_mouse_motion_mappings)
477  {
478  if (mapped_action == &action)
479  {
480  mappings.emplace_back(mapping);
481  }
482  }
483 
484  return mappings;
485 }
486 
487 std::vector<mouse_scroll_mapping> action_map::get_mouse_scroll_mappings(const action& action) const
488 {
489  std::vector<mouse_scroll_mapping> mappings;
490 
491  for (const auto& [mapped_action, mapping]: m_mouse_scroll_mappings)
492  {
493  if (mapped_action == &action)
494  {
495  mappings.emplace_back(mapping);
496  }
497  }
498 
499  return mappings;
500 }
501 
502 void action_map::subscribe()
503 {
504  m_subscriptions.emplace_back(m_event_dispatcher->subscribe<gamepad_axis_moved_event>(std::bind_front(&action_map::handle_gamepad_axis_moved, this)));
505  m_subscriptions.emplace_back(m_event_dispatcher->subscribe<gamepad_button_pressed_event>(std::bind_front(&action_map::handle_gamepad_button_pressed, this)));
506  m_subscriptions.emplace_back(m_event_dispatcher->subscribe<gamepad_button_released_event>(std::bind_front(&action_map::handle_gamepad_button_released, this)));
507  m_subscriptions.emplace_back(m_event_dispatcher->subscribe<key_pressed_event>(std::bind_front(&action_map::handle_key_pressed, this)));
508  m_subscriptions.emplace_back(m_event_dispatcher->subscribe<key_released_event>(std::bind_front(&action_map::handle_key_released, this)));
509  m_subscriptions.emplace_back(m_event_dispatcher->subscribe<mouse_button_pressed_event>(std::bind_front(&action_map::handle_mouse_button_pressed, this)));
510  m_subscriptions.emplace_back(m_event_dispatcher->subscribe<mouse_button_released_event>(std::bind_front(&action_map::handle_mouse_button_released, this)));
511  m_subscriptions.emplace_back(m_event_dispatcher->subscribe<mouse_moved_event>(std::bind_front(&action_map::handle_mouse_moved, this)));
512  m_subscriptions.emplace_back(m_event_dispatcher->subscribe<mouse_scrolled_event>(std::bind_front(&action_map::handle_mouse_scrolled, this)));
513  m_subscriptions.emplace_back(m_event_dispatcher->subscribe<update_event>(std::bind_front(&action_map::handle_update, this)));
514 }
515 
516 void action_map::unsubscribe()
517 {
518  m_subscriptions.clear();
519 }
520 
521 } // namespace input
Forwards messages from publishers to subscribers.
Definition: dispatcher.hpp:37
std::shared_ptr< subscription > subscribe(subscriber< T > &&subscriber)
Subscribes a function object to messages dispatched by this dispatcher.
Definition: dispatcher.hpp:50
void add_gamepad_button_mapping(action &action, gamepad_button_mapping mapping)
Maps input to an action.
Definition: action-map.cpp:127
void disable()
Disables the mapping of input events to actions.
Definition: action-map.cpp:41
std::vector< mouse_motion_mapping > get_mouse_motion_mappings(const action &action) const
Returns all of the mouse motion mappings associated with an action.
Definition: action-map.cpp:472
void remove_mappings()
Unmaps all input from all actions in the action map.
Definition: action-map.cpp:258
std::vector< gamepad_button_mapping > get_gamepad_button_mappings(const action &action) const
Returns all of the gamepad button mappings associated with an action.
Definition: action-map.cpp:427
void add_mouse_button_mapping(action &action, mouse_button_mapping mapping)
Maps input to an action.
Definition: action-map.cpp:139
void add_gamepad_axis_mapping(action &action, gamepad_axis_mapping mapping)
Maps input to an action.
Definition: action-map.cpp:121
void set_event_dispatcher(event::dispatcher *dispatcher)
Sets the event dispatcher from which this action map will receive input events.
Definition: action-map.cpp:62
std::vector< key_mapping > get_key_mappings(const action &action) const
Returns all of the key mappings associated with an action.
Definition: action-map.cpp:442
void reset()
Resets the activation states of each action in the action map.
Definition: action-map.cpp:54
void add_mouse_scroll_mapping(action &action, mouse_scroll_mapping mapping)
Maps input to an action.
Definition: action-map.cpp:151
void add_mouse_motion_mapping(action &action, mouse_motion_mapping mapping)
Maps input to an action.
Definition: action-map.cpp:145
std::vector< mouse_button_mapping > get_mouse_button_mappings(const action &action) const
Returns all of the mouse button mappings associated with an action.
Definition: action-map.cpp:457
void add_key_mapping(action &action, key_mapping mapping)
Maps input to an action.
Definition: action-map.cpp:133
std::vector< mouse_scroll_mapping > get_mouse_scroll_mappings(const action &action) const
Returns all of the mouse scroll associated with an action.
Definition: action-map.cpp:487
std::vector< gamepad_axis_mapping > get_gamepad_axis_mappings(const action &action) const
Returns all of the gamepad axis mappings associated with an action.
Definition: action-map.cpp:412
void enable()
Enables the mapping of input events to actions.
Definition: action-map.cpp:28
void add_mapping(action &action, const mapping &mapping)
Maps input to an action.
Definition: action-map.cpp:87
Evaluates an activation state given input values and publishes events on activation state changes.
Definition: action.hpp:33
void reset() noexcept
Resets the activation state of the action without publishing any events.
Definition: action.cpp:75
void evaluate(float value)
Evaluates the activation state of the action, according to its threshold function and an input value.
Definition: action.cpp:37
Maps a direction along a gamepad axis to a control input value.
Definition: mapping.hpp:61
Maps a gamepad button to a control input value.
Definition: mapping.hpp:95
Maps a keyboard key to a control input value.
Definition: mapping.hpp:125
Abstract base class for input mappings.
Definition: mapping.hpp:43
virtual constexpr mapping_type get_mapping_type() const noexcept=0
Returns the input mapping type.
Maps a mouse button to a control input value.
Definition: mapping.hpp:163
Maps a direction along a mouse motion axis to a control input value.
Definition: mapping.hpp:193
Maps a direction along a mouse scroll axis to a control input value.
Definition: mapping.hpp:227
constexpr int difference(T x, T y) noexcept
Returns the number of differing bits between two values, known as Hamming distance.
Definition: bit-math.hpp:280
Publish-subscribe messaging.
Definition: channel.hpp:32
Input devices, events, and mapping.
mapping_type
Input mapping types.
@ gamepad_axis
Gamepad axis mapping.
@ key
Key mapping.
@ mouse_button
Mouse button mapping.
@ mouse_scroll
Mouse scroll mapping.
@ mouse_motion
Mouse motion mapping.
@ gamepad_button
Gamepad button mapping.
constexpr vector< T, N > abs(const vector< T, N > &x)
Returns the absolute values of each element.
Definition: vector.hpp:985
Text and typography.
Definition: bitmap-font.cpp:24
Event generated when a gamepad axis has been moved.
Event generated when a gamepad button has been pressed.
Event generated when a gamepad button has been released.
Event generated when a keyboard key has been pressed.
Event generated when a keyboard key has been released.
Event generated when a mouse button has been pressed.
Event generated when a mouse button has been released.
Event generated when a mouse has been moved.
Event generated when a mouse has been scrolled.
Event generated after input events are polled.