ST_engine  0.3-ALPHA
input_manager.cpp
1 /* This file is part of the "ST" project.
2  * You may use, distribute or modify this code under the terms
3  * of the GNU General Public License version 2.
4  * See LICENCE.txt in the root directory of the project.
5  *
6  * Author: Maxim Atanasov
7  * E-mail: maxim.atanasov@protonmail.com
8  */
9 
10 #include <input_manager.hpp>
11 #include <SDL.h>
12 #include <string>
13 
14 static bool singleton_initialized = false;
15 
16 input_manager::~input_manager() {
17  singleton_initialized = false;
18 }
19 
25 input_manager::input_manager(task_manager &gTask_manager, message_bus &gMessageBus) : gMessage_bus(gMessageBus),
26  gTask_manager(gTask_manager) {
27 
28  if (singleton_initialized) {
29  throw std::runtime_error("The input manager cannot be initialized more than once!");
30  } else {
31  singleton_initialized = true;
32  }
33 
34  if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) < 0) {
35  gMessage_bus.send_msg(
36  new message(LOG_ERROR, make_data<std::string>("Could not initialize gamepad subsystem!")));
37  }
38 
39  //Initialize controls
40  int32_t length = 0;
41  keyboard_sdl_raw = reinterpret_cast<bool *>(const_cast<uint8_t *>(SDL_GetKeyboardState(&length)));
42  controls.buttons = static_cast<bool *>(malloc(static_cast<size_t>(length)));
43  controls_prev_frame.buttons = static_cast<bool *>(malloc(static_cast<size_t>(length)));
44  SDL_PollEvent(&event);
45 
46  //Subscribe to the messages we need
47  gMessage_bus.subscribe(VIRTUAL_SCREEN_COORDINATES, &msg_sub);
48  gMessage_bus.subscribe(REAL_SCREEN_COORDINATES, &msg_sub);
49  gMessage_bus.subscribe(START_TEXT_INPUT, &msg_sub);
50  gMessage_bus.subscribe(STOP_TEXT_INPUT, &msg_sub);
51  gMessage_bus.subscribe(CLEAR_TEXT_STREAM, &msg_sub);
52  gMessage_bus.subscribe(REGISTER_KEY, &msg_sub);
53  gMessage_bus.subscribe(UNREGISTER_KEY, &msg_sub);
54  gMessage_bus.subscribe(CONTROLLER_RUMBLE, &msg_sub);
55  gMessage_bus.subscribe(SET_LEFT_JOYSTICK_HORIZONTAL_THRESHOLD, &msg_sub);
56  gMessage_bus.subscribe(SET_LEFT_JOYSTICK_VERTICAL_THRESHOLD, &msg_sub);
57  gMessage_bus.subscribe(SET_RIGHT_JOYSTICK_HORIZONTAL_THRESHOLD, &msg_sub);
58  gMessage_bus.subscribe(SET_RIGHT_JOYSTICK_VERTICAL_THRESHOLD, &msg_sub);
59  gMessage_bus.subscribe(SET_LEFT_TRIGGER_THRESHOLD, &msg_sub);
60  gMessage_bus.subscribe(SET_RIGHT_TRIGGER_THRESHOLD, &msg_sub);
61 }
62 
63 #ifndef _MSC_VER
69 void input_manager::update_task(void* mngr){
70  auto self = static_cast<input_manager*>(mngr);
71  self->handle_messages();
72  self->take_input();
73 }
74 
79  gTask_manager.start_task_lockfree(new ST::task(update_task, this, nullptr));
80 }
81 #else
82 
86 void input_manager::update() {
87  handle_messages();
88  take_input();
89 }
90 
91 #endif
92 
96 void input_manager::take_input() {
97  while (SDL_PollEvent(&event) != 0) {
98  if (event.type == SDL_QUIT) {
99  gMessage_bus.send_msg(new message(END_GAME));
100  }
101  if (event.type == SDL_MOUSEWHEEL) {
102  controls.mouse_scroll += event.wheel.y;
103  if (controls.mouse_scroll < 0) {
104  controls.mouse_scroll = 0;
105  }
106  gMessage_bus.send_msg(new message(MOUSE_SCROLL, controls.mouse_scroll));
107  }
108  if (event.type == SDL_TEXTINPUT) {
109  if (text_input) {
110  composition += event.edit.text;
111  //Drop the tilde when closing the console, otherwise it just stays there
112  while (!composition.empty() && composition.at(composition.size() - 1) == CONSOLE_TOGGLE_KEY) {
113  composition.pop_back();
114  }
115  gMessage_bus.send_msg(new message(TEXT_STREAM, make_data(composition)));
116  }
117  }
118  if (event.type == SDL_KEYDOWN) {
119  if (event.key.keysym.sym == SDLK_BACKSPACE) {
120  if (composition.length() > 0) {
121  composition.pop_back();
122  gMessage_bus.send_msg(new message(TEXT_STREAM, make_data(composition)));
123  }
124  }
125  }
126  if (event.type == SDL_MOUSEMOTION) {
127  SDL_GetMouseState(&controls.mouse_x, &controls.mouse_y);
128  controls.mouse_x = static_cast<int>(static_cast<float>(controls.mouse_x) * ratio_w);
129  controls.mouse_y = static_cast<int>(static_cast<float>(controls.mouse_y) * ratio_h);
130  }
131  if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
132  r_width = event.window.data1;
133  r_height = event.window.data2;
134  ratio_w = static_cast<float>(v_width) / static_cast<float>(r_width);
135  ratio_h = static_cast<float>(v_height) / static_cast<float>(r_height);
136  }
137  if (event.cdevice.type == SDL_CONTROLLERDEVICEADDED) {
138  SDL_GameController *controller = SDL_GameControllerOpen(static_cast<int>(controllers.size()));
139  controllers.emplace_back(controller);
140  gMessage_bus.send_msg(new message(LOG_INFO, make_data<std::string>(
141  "Found a controller: " + std::string(SDL_GameControllerName(controller)))));
142  SDL_Haptic *haptic = SDL_HapticOpen(static_cast<int32_t>(controllers.size() - 1));
143  if (haptic != nullptr) {
144  if (SDL_HapticRumbleInit(haptic) < 0) {
145  gMessage_bus.send_msg(new message(LOG_INFO, make_data<std::string>(
146  "Unable to initialize rumble for controller " +
147  std::string(SDL_GameControllerName(controller)))));
148  } else {
149  gMessage_bus.send_msg(new message(LOG_INFO, make_data<std::string>(
150  "The controller \"" + std::string(SDL_GameControllerName(controller)) +
151  "\" supports haptic feedback")));
152  controllers_haptic.emplace_back(haptic);
153  }
154  } else {
155  gMessage_bus.send_msg(new message(LOG_INFO, make_data<std::string>(
156  "The controller \"" + std::string(SDL_GameControllerName(controller)) +
157  "\" does not support haptic feedback")));
158  }
159  }
160  if (event.cdevice.type == SDL_CONTROLLERDEVICEREMOVED) {
161  uint8_t number = 0;
162  for (uint8_t i = 0; i < controllers.size() && i < 8; i++) {
163  if (SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controllers.at(i))) == event.cdevice.which) {
164  number = i;
165  }
166  }
167  SDL_GameControllerClose(controllers.at(number));
168  controllers.erase(controllers.begin() + number);
169  if (number < controllers_haptic.size()) {
170  SDL_HapticClose(controllers_haptic.at(number));
171  controllers_haptic.erase(controllers_haptic.begin() + number);
172  }
173  gMessage_bus.send_msg(new message(LOG_INFO, make_data<std::string>(
174  "Controller " + std::to_string(number + 1) + " disconnected")));
175  }
176  }
177 
178  memcpy(controls_prev_frame.buttons, controls.buttons, 512);
179  memcpy(controls.buttons, keyboard_sdl_raw, 512);
180  take_controller_input();
181  take_mouse_input();
182 
183  //check if any of the registered keys is pressed and send a message if so
184  for (auto i: registered_keys) {
185  if (key_event(i.first, ST::key_event::PRESS)) {
186  gMessage_bus.send_msg(new message(KEY_PRESSED, static_cast<uint8_t>(i.first)));
187  }
188  if (key_event(i.first, ST::key_event::HOLD)) {
189  gMessage_bus.send_msg(new message(KEY_HELD, static_cast<uint8_t>(i.first)));
190  }
191  if (key_event(i.first, ST::key_event::RELEASE)) {
192  gMessage_bus.send_msg(new message(KEY_RELEASED, static_cast<uint8_t>(i.first)));
193  }
194  }
195 }
196 
197 void input_manager::take_mouse_input() {
198  //Collect mouse input
199  uint32_t mouse_state = SDL_GetMouseState(nullptr, nullptr);
200  controls.buttons[1] = mouse_state & SDL_BUTTON(SDL_BUTTON_LEFT);
201  controls.buttons[2] = mouse_state & SDL_BUTTON(SDL_BUTTON_MIDDLE);
202  controls.buttons[3] = mouse_state & SDL_BUTTON(SDL_BUTTON_RIGHT);
203 
204  //only send mouse coordinates if they change
205  if (controls.mouse_x != controls_prev_frame.mouse_x) {
206  gMessage_bus.send_msg(new message(MOUSE_X, static_cast<uint32_t>(controls.mouse_x)));
207  controls_prev_frame.mouse_x = controls.mouse_x;
208  }
209  if (controls.mouse_y != controls_prev_frame.mouse_y) {
210  gMessage_bus.send_msg(new message(MOUSE_Y, static_cast<uint32_t>(controls.mouse_y)));
211  controls_prev_frame.mouse_y = controls.mouse_y;
212  }
213 }
214 
220 void input_manager::take_controller_input() {
221  controller_analog_inputs_prev_frame = controller_analog_inputs;
222  for (SDL_GameController *c: controllers) {
223  controls.buttons[static_cast<uint8_t>(ST::key::CONTROLLER_BUTTON_A)] = SDL_GameControllerGetButton(c,
224  SDL_CONTROLLER_BUTTON_A);
225  controls.buttons[static_cast<uint8_t>(ST::key::CONTROLLER_BUTTON_B)] = SDL_GameControllerGetButton(c,
226  SDL_CONTROLLER_BUTTON_B);
227  controls.buttons[static_cast<uint8_t>(ST::key::CONTROLLER_BUTTON_X)] = SDL_GameControllerGetButton(c,
228  SDL_CONTROLLER_BUTTON_X);
229  controls.buttons[static_cast<uint8_t>(ST::key::CONTROLLER_BUTTON_Y)] = SDL_GameControllerGetButton(c,
230  SDL_CONTROLLER_BUTTON_Y);
231  controls.buttons[static_cast<uint8_t>(ST::key::CONTROLLER_BUTTON_DPAD_UP)] = SDL_GameControllerGetButton(c,
232  SDL_CONTROLLER_BUTTON_DPAD_UP);
233  controls.buttons[static_cast<uint8_t>(ST::key::CONTROLLER_BUTTON_DPAD_DOWN)] = SDL_GameControllerGetButton(c,
234  SDL_CONTROLLER_BUTTON_DPAD_DOWN);
235  controls.buttons[static_cast<uint8_t>(ST::key::CONTROLLER_BUTTON_DPAD_LEFT)] = SDL_GameControllerGetButton(c,
236  SDL_CONTROLLER_BUTTON_DPAD_LEFT);
237  controls.buttons[static_cast<uint8_t>(ST::key::CONTROLLER_BUTTON_DPAD_RIGHT)] = SDL_GameControllerGetButton(c,
238  SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
239  controls.buttons[static_cast<uint8_t>(ST::key::CONTROLLER_BUTTON_LEFTSTICK)] = SDL_GameControllerGetButton(c,
240  SDL_CONTROLLER_BUTTON_LEFTSTICK);
241  controls.buttons[static_cast<uint8_t>(ST::key::CONTROLLER_BUTTON_RIGHTSTICK)] = SDL_GameControllerGetButton(c,
242  SDL_CONTROLLER_BUTTON_RIGHTSTICK);
243  controls.buttons[static_cast<uint8_t>(ST::key::CONTROLLER_BUTTON_LEFTSHOULDER)] = SDL_GameControllerGetButton(c,
244  SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
245  controls.buttons[static_cast<uint8_t>(ST::key::CONTROLLER_BUTTON_RIGHTSHOULDER)] = SDL_GameControllerGetButton(
246  c, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
247  controls.buttons[static_cast<uint8_t>(ST::key::CONTROLLER_BUTTON_START)] = SDL_GameControllerGetButton(c,
248  SDL_CONTROLLER_BUTTON_START);
249  controls.buttons[static_cast<uint8_t>(ST::key::CONTROLLER_BUTTON_SELECT)] = SDL_GameControllerGetButton(c,
250  SDL_CONTROLLER_BUTTON_BACK);
251 
252  controller_analog_inputs.left_trigger = SDL_GameControllerGetAxis(c, SDL_CONTROLLER_AXIS_TRIGGERLEFT);
253  controller_analog_inputs.right_trigger = SDL_GameControllerGetAxis(c, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
254  controller_analog_inputs.right_stick_horizontal = SDL_GameControllerGetAxis(c, SDL_CONTROLLER_AXIS_RIGHTX);
255  controller_analog_inputs.right_stick_vertical = SDL_GameControllerGetAxis(c, SDL_CONTROLLER_AXIS_RIGHTY);
256  controller_analog_inputs.left_stick_horizontal = SDL_GameControllerGetAxis(c, SDL_CONTROLLER_AXIS_LEFTX);
257  controller_analog_inputs.left_stick_vertical = SDL_GameControllerGetAxis(c, SDL_CONTROLLER_AXIS_LEFTY);
258 
259  controls.buttons[static_cast<uint8_t>(ST::key::CONTROLLER_BUTTON_LEFT_TRIGGER)] =
260  controller_analog_inputs.left_trigger > 0;
261  controls.buttons[static_cast<uint8_t>(ST::key::CONTROLLER_BUTTON_RIGHT_TRIGGER)] =
262  controller_analog_inputs.right_trigger > 0;
263  }
264  //only send controller axis values if they change and the values exceed the thresholds
265 
266  //Branch-less checks
267  controller_analog_inputs.left_trigger = (controller_analog_inputs.left_trigger >= left_trigger_threshold) *
268  controller_analog_inputs.left_trigger; // NOLINT(cppcoreguidelines-narrowing-conversions)
269  controller_analog_inputs.right_trigger = (controller_analog_inputs.right_trigger >= right_trigger_threshold) *
270  controller_analog_inputs.right_trigger; // NOLINT(cppcoreguidelines-narrowing-conversions)
271 
272  if (controller_analog_inputs.left_trigger != controller_analog_inputs_prev_frame.left_trigger) {
273  gMessage_bus.send_msg(new message(LEFT_TRIGGER, controller_analog_inputs.left_trigger));
274  }
275 
276  if (controller_analog_inputs.right_trigger != controller_analog_inputs_prev_frame.right_trigger) {
277  gMessage_bus.send_msg(new message(RIGHT_TRIGGER, controller_analog_inputs.right_trigger));
278  }
279 
280  //Branch-less check
281  controller_analog_inputs.left_stick_vertical = (controller_analog_inputs.left_stick_vertical >
282  left_stick_vertical_threshold // NOLINT(cppcoreguidelines-narrowing-conversions)
283  || controller_analog_inputs.left_stick_vertical <
284  -left_stick_vertical_threshold) *
285  controller_analog_inputs.left_stick_vertical;
286 
287  //Branch-less check
288  controller_analog_inputs.left_stick_horizontal = (controller_analog_inputs.left_stick_horizontal >
289  left_stick_horizontal_threshold // NOLINT(cppcoreguidelines-narrowing-conversions)
290  || controller_analog_inputs.left_stick_horizontal <
291  -left_stick_horizontal_threshold) *
292  controller_analog_inputs.left_stick_horizontal;
293 
294  //Branch-less check
295  controller_analog_inputs.right_stick_horizontal = (controller_analog_inputs.right_stick_horizontal >
296  right_stick_horizontal_threshold // NOLINT(cppcoreguidelines-narrowing-conversions)
297  || controller_analog_inputs.right_stick_horizontal <
298  -right_stick_horizontal_threshold) *
299  controller_analog_inputs.right_stick_horizontal;
300 
301  //Branch-less check
302  controller_analog_inputs.right_stick_vertical = (controller_analog_inputs.right_stick_vertical >
303  right_stick_vertical_threshold // NOLINT(cppcoreguidelines-narrowing-conversions)
304  || controller_analog_inputs.right_stick_vertical <
305  -right_stick_vertical_threshold) *
306  controller_analog_inputs.right_stick_vertical;
307 
308  if (controller_analog_inputs.left_stick_vertical != controller_analog_inputs_prev_frame.left_stick_vertical) {
309  gMessage_bus.send_msg(new message(LEFT_STICK_VERTICAL, controller_analog_inputs.left_stick_vertical));
310  }
311 
312  if (controller_analog_inputs.left_stick_horizontal != controller_analog_inputs_prev_frame.left_stick_horizontal) {
313  gMessage_bus.send_msg(new message(LEFT_STICK_HORIZONTAL, controller_analog_inputs.left_stick_horizontal));
314  }
315 
316  if (controller_analog_inputs.right_stick_vertical != controller_analog_inputs_prev_frame.right_stick_vertical) {
317  gMessage_bus.send_msg(new message(RIGHT_STICK_VERTICAL, controller_analog_inputs.right_stick_vertical));
318  }
319 
320  if (controller_analog_inputs.right_stick_horizontal != controller_analog_inputs_prev_frame.right_stick_horizontal) {
321  gMessage_bus.send_msg(new message(RIGHT_STICK_HORIZONTAL, controller_analog_inputs.right_stick_horizontal));
322  }
323 }
324 
329 void input_manager::handle_messages() {
330  message *temp = msg_sub.get_next_message();
331  while (temp != nullptr) {
332  switch (temp->msg_name) {
333  case VIRTUAL_SCREEN_COORDINATES: {
334  auto data = temp->base_data0;
335  v_width = data & 0xffffU;
336  v_height = (data >> 16U) & 0xffffU;
337  ratio_w = static_cast<float>(v_width) / static_cast<float>(r_width);
338  ratio_h = static_cast<float>(v_height) / static_cast<float>(r_height);
339  break;
340  }
341  case REAL_SCREEN_COORDINATES: {
342  auto data = temp->base_data0;
343  r_width = data & 0xffffU;
344  r_height = (data >> 16U) & 0xffffU;
345  ratio_w = static_cast<float>(v_width) / static_cast<float>(r_width);
346  ratio_h = static_cast<float>(v_height) / static_cast<float>(r_height);
347  break;
348  }
349  case START_TEXT_INPUT:
350  text_input = true;
351  break;
352  case STOP_TEXT_INPUT:
353  text_input = false;
354  break;
355  case CLEAR_TEXT_STREAM:
356  composition.clear();
357  break;
358  case REGISTER_KEY: {
359  auto key_val = static_cast<ST::key>(temp->base_data0);
360  ++registered_keys[key_val];
361  break;
362  }
363  case UNREGISTER_KEY: {
364  auto key_val = static_cast<ST::key>(temp->base_data0);
365  if (registered_keys[key_val] > 1) {
366  --registered_keys[key_val];
367  } else if (registered_keys[key_val] == 1) {
368  registered_keys.erase(key_val);
369  }
370  break;
371  }
372  case CONTROLLER_RUMBLE: {
373  if (!controllers.empty() && !controllers_haptic.empty()) {
374  float strength = *reinterpret_cast<float *>(&temp->base_data0);
375  uint16_t duration = temp->base_data1;
376  for (SDL_Haptic *haptic: controllers_haptic) {
377  SDL_HapticRumblePlay(haptic, strength, duration);
378  }
379  }
380  break;
381  }
382  default: //Otherwise we process any threshold messages
383  uint8_t left_stick_hor_check = (temp->msg_name == SET_LEFT_JOYSTICK_HORIZONTAL_THRESHOLD);
384  this->left_stick_horizontal_threshold = left_stick_hor_check * static_cast<int16_t>(temp->base_data0) +
385  // NOLINT(cppcoreguidelines-narrowing-conversions)
386  !left_stick_hor_check * this->left_stick_horizontal_threshold;
387 
388  uint8_t left_stick_ver_check = (temp->msg_name == SET_LEFT_JOYSTICK_VERTICAL_THRESHOLD);
389  this->left_stick_vertical_threshold = left_stick_ver_check * static_cast<int16_t>(temp->base_data0) +
390  // NOLINT(cppcoreguidelines-narrowing-conversions)
391  !left_stick_ver_check * this->left_stick_vertical_threshold;
392 
393  uint8_t right_stick_hor_check = (temp->msg_name == SET_RIGHT_JOYSTICK_HORIZONTAL_THRESHOLD);
394  this->right_stick_horizontal_threshold =
395  right_stick_hor_check * static_cast<int16_t>(temp->base_data0) +
396  // NOLINT(cppcoreguidelines-narrowing-conversions)
397  !right_stick_hor_check * this->right_stick_horizontal_threshold;
398 
399  uint8_t right_stick_ver_check = (temp->msg_name == SET_RIGHT_JOYSTICK_VERTICAL_THRESHOLD);
400  this->right_stick_vertical_threshold = right_stick_ver_check * static_cast<int16_t>(temp->base_data0) +
401  // NOLINT(cppcoreguidelines-narrowing-conversions)
402  !right_stick_ver_check * this->right_stick_vertical_threshold;
403 
404  uint8_t right_trigger_check = (temp->msg_name == SET_RIGHT_TRIGGER_THRESHOLD);
405  this->right_trigger_threshold = right_trigger_check * static_cast<int16_t>(temp->base_data0) +
406  // NOLINT(cppcoreguidelines-narrowing-conversions)
407  !right_trigger_check * this->right_trigger_threshold;
408 
409  uint8_t left_trigger_check = (temp->msg_name == SET_LEFT_TRIGGER_THRESHOLD);
410  this->left_trigger_threshold = left_trigger_check * static_cast<int16_t>(temp->base_data0) +
411  // NOLINT(cppcoreguidelines-narrowing-conversions)
412  !left_trigger_check * this->left_trigger_threshold;
413  break;
414  }
415  delete temp;
416  temp = msg_sub.get_next_message();
417  }
418 }
419 
427 bool input_manager::key_event(ST::key arg, ST::key_event k_event) const {
428  auto key = static_cast<uint8_t>(arg);
429  return (controls.buttons[key] ^ (k_event == ST::key_event::RELEASE))
430  && (controls_prev_frame.buttons[key] ^ (k_event == ST::key_event::PRESS));
431 }
An object representing a task to be run by the task manager.
Definition: task.hpp:24
This object is responsible for taking input.
input_manager(task_manager &gTask_manager, message_bus &gMessageBus)
The central messaging system of the engine. All subsystem make extensive use of it.
Definition: message_bus.hpp:29
void subscribe(uint8_t msg, subscriber *sub)
Definition: message_bus.cpp:71
void send_msg(message *msg)
Definition: message_bus.cpp:19
A message object passed around in the message bus. Holds anything created with make_data<>().
Definition: message.hpp:21
message * get_next_message()
Definition: subscriber.hpp:39
The Task Manager handles all things multi-threaded in the engine.
void start_task_lockfree(ST::task *arg)