Raspberry Pi Weighting Control System
This project serves as a simple weighting control system, that was realized as a Bachelor Thesis
event_handler.cpp
Go to the documentation of this file.
1 #include <spdlog/spdlog.h>
2 #include "event_handler.h"
3 #include "app_workspace.h"
4 #include "gui_input.h"
5 #include "gui_button.h"
6 #include "gui_combobox.h"
7 #include "localisation.h"
8 #include "imgui.h"
9 #include "imgui_internal.h"
10 
11 #include "gui_direct.h"
12 #include "gui_label.h"
13 #include "gui_observer.h"
14 #include "gui_selectable.h"
15 
16 namespace event_handler {
17 
18 template<typename Base, typename T>
19 inline bool instanceof(const T *ptr) {
20  return dynamic_cast<const Base*>(ptr) != nullptr;
21 }
22 
23 /*********************************************************************************************************************/
24 /* Global variables */
25 /*********************************************************************************************************************/
26 
27  /*
28  bool lshift_flag = false;
29  bool capslock_flag = false;
30  */
31 
32  // SPI keyboard representation (its actually keypad)
33  // # (0x2000) is ONLY USED as "enter" confirm/ select button
34  /******************************************************************************/
35  /* */
36  /* / ---------- \ / ---------- \ / ---------- \ / ---------- \ */
37  /* | 1 = 0x0008 | | 2 = 0x0004 | | 3 = 0x0002 | | A = 0x0001 | */
38  /* \ ---------- / \ ---------- / \ ---------- / \ ---------- / */
39  /* */
40  /* / ---------- \ / ---------- \ / ---------- \ / ---------- \ */
41  /* | 4 = 0x0080 | | 5 = 0x0040 | | 6 = 0x0020 | | B = 0x0010 | */
42  /* \ ---------- / \ ---------- / \ ---------- / \ ---------- / */
43  /* */
44  /* / ---------- \ / ---------- \ / ---------- \ / ---------- \ */
45  /* | 7 = 0x0800 | | 8 = 0x0400 | | 9 = 0x0200 | | C = 0x0100 | */
46  /* \ ---------- / \ ---------- / \ ---------- / \ ---------- / */
47  /* */
48  /* / ---------- \ / ---------- \ / ---------- \ / ---------- \ */
49  /* | * = 0x8000 | | 0 = 0x4000 | | # = 0x2000 | | D = 0x1000 | */
50  /* \ ---------- / \ ---------- / \ ---------- / \ ---------- / */
51  /* */
52  /******************************************************************************/
53 
58  std::map<uint16_t, SDL_Scancode> navigation_keymap {
59  {0x0008, SDL_SCANCODE_1},
60  {0x0004, SDL_SCANCODE_2},
61  {0x0002, SDL_SCANCODE_3},
62  {0x0001, SDL_SCANCODE_UP}, // use A as UP arrow
63  //
64  {0x0080, SDL_SCANCODE_4},
65  {0x0040, SDL_SCANCODE_5},
66  {0x0020, SDL_SCANCODE_6},
67  {0x0010, SDL_SCANCODE_LEFT}, // use B as LEFT arrow (possibly bad contact, doesn't always work)
68  //
69  {0x0800, SDL_SCANCODE_7},
70  {0x0400, SDL_SCANCODE_8},
71  {0x0200, SDL_SCANCODE_9},
72  {0x0100, SDL_SCANCODE_RIGHT}, // use C as RIGHT arrow (possibly bad contact, doesn't always work)
73  //
74  //{0x8000, SDL_SCANCODE_KP_MULTIPLY},
75  {0x4000, SDL_SCANCODE_BACKSPACE},
76  //{0x2000, SDL_SCANCODE_RETURN}, // use # as RETURN - needs to be same for all maps, maybe take out?
77  {0x1000, SDL_SCANCODE_DOWN} // use D as DOWN arrow
78  };
79 
84  std::map<uint16_t, SDL_Scancode> int_in_keymap {
85  {0x0008, SDL_SCANCODE_1},
86  {0x0004, SDL_SCANCODE_2},
87  {0x0002, SDL_SCANCODE_3},
88  {0x0001, SDL_SCANCODE_BACKSPACE}, // use A as BACKSPACE (wanted this on B, but B isn't working properly)
89  //
90  {0x0080, SDL_SCANCODE_4},
91  {0x0040, SDL_SCANCODE_5},
92  {0x0020, SDL_SCANCODE_6},
93  {0x0010, SDL_SCANCODE_MINUS},
94  //
95  {0x0800, SDL_SCANCODE_7},
96  {0x0400, SDL_SCANCODE_8},
97  {0x0200, SDL_SCANCODE_9},
98  //{0x0100, SDL_SCANCODE_C},
99  //
100  //{0x8000, SDL_SCANCODE_KP_MULTIPLY},
101  {0x4000, SDL_SCANCODE_0},
102  //{0x2000, SDL_SCANCODE_RETURN}, // use # as RETURN - needs to be same for all maps, maybe take out?
103  //{0x1000, SDL_SCANCODE_D}
104  };
105 
110  std::map<uint16_t, SDL_Scancode> float_in_keymap {
111  {0x0008, SDL_SCANCODE_1},
112  {0x0004, SDL_SCANCODE_2},
113  {0x0002, SDL_SCANCODE_3},
114  {0x0001, SDL_SCANCODE_BACKSPACE}, // use A as BACKSPACE (wanted this on B, but B isn't working properly)
115  //
116  {0x0080, SDL_SCANCODE_4},
117  {0x0040, SDL_SCANCODE_5},
118  {0x0020, SDL_SCANCODE_6},
119  {0x0010, SDL_SCANCODE_MINUS},
120  //
121  {0x0800, SDL_SCANCODE_7},
122  {0x0400, SDL_SCANCODE_8},
123  {0x0200, SDL_SCANCODE_9},
124  //{0x0100, SDL_SCANCODE_C},
125  //
126  {0x8000, SDL_SCANCODE_DECIMALSEPARATOR}, // uses * as decimal separator
127  {0x4000, SDL_SCANCODE_0},
128  //{0x2000, SDL_SCANCODE_RETURN}, // use # as RETURN - needs to be same for all maps, maybe take out?
129  //{0x1000, SDL_SCANCODE_D}
130  };
131 
138  std::map<uint16_t, std::vector<SDL_Scancode>> text_in_keymap {
139  {0x0008, {SDL_SCANCODE_1}},
140  {0x0004, {SDL_SCANCODE_2, SDL_SCANCODE_A, SDL_SCANCODE_B, SDL_SCANCODE_C}},
141  {0x0002, {SDL_SCANCODE_3, SDL_SCANCODE_D, SDL_SCANCODE_E, SDL_SCANCODE_F}},
142  {0x0001, {SDL_SCANCODE_BACKSPACE}}, // same as above
143  //
144  {0x0080, {SDL_SCANCODE_4, SDL_SCANCODE_G, SDL_SCANCODE_H, SDL_SCANCODE_I}},
145  {0x0040, {SDL_SCANCODE_5, SDL_SCANCODE_J, SDL_SCANCODE_K, SDL_SCANCODE_L}},
146  {0x0020, {SDL_SCANCODE_6, SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_O}},
147  {0x0010, {SDL_SCANCODE_LSHIFT}},
148  //
149  {0x0800, {SDL_SCANCODE_7, SDL_SCANCODE_P, SDL_SCANCODE_Q, SDL_SCANCODE_R, SDL_SCANCODE_S}},
150  {0x0400, {SDL_SCANCODE_8, SDL_SCANCODE_T, SDL_SCANCODE_U, SDL_SCANCODE_V}},
151  {0x0200, {SDL_SCANCODE_9, SDL_SCANCODE_W, SDL_SCANCODE_X, SDL_SCANCODE_Y, SDL_SCANCODE_Z}},
152  {0x0100, {SDL_SCANCODE_CAPSLOCK}},
153  //
154  {0x8000, {SDL_SCANCODE_KP_MULTIPLY, SDL_SCANCODE_DECIMALSEPARATOR, SDL_SCANCODE_MINUS}},
155  {0x4000, {SDL_SCANCODE_0}},
156  //{0x2000, {SDL_SCANCODE_KP_HASH}}, // MUSTNT USER THIS!!!
157  //{0x1000, {SDL_SCANCODE_D}}
158  };
159 
161  std::map<SDL_Scancode, const char*> custom_keymap {
162  {SDL_SCANCODE_KP_MULTIPLY, "*"},
163  {SDL_SCANCODE_DECIMALSEPARATOR, "."},
164  {SDL_SCANCODE_MINUS, "-"}
165  };
166 
168  std::map<SDL_Scancode, const char*> custom_shift_keymap {
169  {SDL_SCANCODE_MINUS, "_"}
170  };
171 
172 /*********************************************************************************************************************/
173 /* Local function definitions */
174 /*********************************************************************************************************************/
175 
184  SDL_Scancode determine_scancode(uint16_t keycode, uint8_t cycle = 0);
192  SDL_Event create_key_event(SDL_Scancode scancode, Uint16 mod = 0);
199  SDL_Event create_textinput_event(SDL_Scancode scancode);
207  void push_event_to_sdl(SDL_Event* event, bool replace_cur_char = false);
208 
212  bool can_switch_modes();
216  int handle_return_event();
220  int handle_nav_event(kb_event* raw_event);
224  int handle_input_event(kb_event* raw_event);
225 
226 /*********************************************************************************************************************/
227 /* Implementation of header function definitions */
228 /*********************************************************************************************************************/
229 
230  // FIXME - move those to Global variables section
231  static kb_event* last_raw_event = nullptr;
232  static uint8_t key_cycle = 0;
233  // this flag is needed because ImGui can't tell, that an combobox is focused (expanded)
234  static bool combo_expanded = false;
235 
242  int handle_raw_event(kb_event* raw_event) {
243  if (app_workspace::get_instance()->hx_measuring || app_workspace::get_instance()->get_kb_testing()) {
244  if (raw_event->scancode == 0x2000 &&
245  !(raw_event->flags & KB_FLAGS_REPEATING) && (raw_event->flags & KB_FLAGS_KEY_DOWN))
246  {
247  if (app_workspace::get_instance()->hx_measuring) {
248  app_workspace::get_instance()->hx711_controller->stop_reading();
249  app_workspace::get_instance()->hx_continuous = false;
250  app_workspace::get_instance()->hx_not_finished_yet = true;
251  app_workspace::get_instance()->refresh_current_screen = true;
252  spdlog::info("event_handler.cpp - Recieved event to stop continuous measuring. Stopping...");
253  } else {
254  app_workspace::get_instance()->set_kb_testing(false);
255  app_workspace::get_instance()->get_scr_mgr()->set_selected_screen(ADMIN_SELECT_SCREEN);
256  app_workspace::get_instance()->get_scr_mgr()->refresh_screen_elements(ADMIN_SELECT_SCREEN);
257  }
258  return 0;
259  }
260 
261  spdlog::debug("event_handler.cpp - Not handling keyboard events because a hx measuring is ongoing.");
262  return 0;
263  }
264 
265  if (raw_event->flags & KB_FLAGS_TEST) {
266  return 0;
267  }
268 
269  std::unique_ptr<app_workspace_ns::kb_input_state> &kbins = app_workspace::get_instance()->kb_in_state;
270 
271  spdlog::debug("Handling raw event: {0}", raw_event->scancode);
272 
273  // handling '#' used as return - always the same action (considered key exception, doesn't go to lookup maps)
274  // only handle on first event KEY DOWN
275  if (raw_event->scancode == 0x2000 &&
276  !(raw_event->flags & KB_FLAGS_REPEATING) &&
277  (raw_event->flags & KB_FLAGS_KEY_DOWN))
278  {
279  return handle_return_event();
280  }
281 
282  if (raw_event->flags & KB_FLAGS_KEY_DOWN) { // handling KEY_DOWN events
283  if (!(raw_event->flags & KB_FLAGS_REPEATING)) { // only first event (not continous hold events)
284  if (kbins->current_mode == app_workspace_ns::kb_input_mode::NAVIGATION)
285  return handle_nav_event(raw_event);
286 
287  if (kbins->current_mode == app_workspace_ns::kb_input_mode::INPUT)
288  return handle_input_event(raw_event);
289  }
290  }
291 
292  return 1;
293  }
294 
295  /*
296  Return 0 if only custom handling of the event is enough. 1 if the event is to be forwarded to ImGui.
297  */
298  int handle_sdl_event(const SDL_Event* event) {
299  screen_manager *scr_mgr = app_workspace::get_instance()->get_scr_mgr();
300  // reference to KeyBoardINputState (kbins)
301  std::unique_ptr<app_workspace_ns::kb_input_state> &kbins = app_workspace::get_instance()->kb_in_state;
302 
303  // if no item is focused, arrow down focuses "second" element and arrow up focuses "first" element
304  // this masks arrow down to arrow up in case when no item is focused resulting in first element being focused
305  // FIXME - potential bug: arrow down KEYUP event is still processed, this triggers on KEYDOWN and
306  // doesn't trigger again on KEYUP - event seq: UP - KEYDOWN, UP - KEYUP, DOWN - KEYUP
307  if ((event->type == SDL_KEYDOWN || event->type == SDL_KEYUP) &&
308  (event->key.keysym.scancode == SDL_SCANCODE_DOWN || event->key.keysym.scancode == SDL_SCANCODE_UP) &&
309  (!ImGui::IsAnyItemFocused() || (!scr_mgr->focused && !combo_expanded)))
310  {
311  // need to consume event and reque new one, only changing current event doesn't work
312  spdlog::debug("event_handler.cpp - masking arrow key DOWN as UP {0}", combo_expanded);
313  //SDL_Event e = create_key_event(SDL_SCANCODE_UP);
314  // "fix" - after changing screens focus is lost, but focused item count is remembered and incorrect item is selected
315  // PAGE_UP selects first item
316  SDL_Event e = create_key_event(SDL_SCANCODE_PAGEUP);
317  push_event_to_sdl(&e, false);
318 
319  return 0;
320  }
321 
322  // handling only KEYDOWN events
323  if (event->type == SDL_KEYDOWN) {
324  spdlog::debug("event_handler.cpp - KEYDOWN scancode: {0}", event->key.keysym.scancode);
325 
326 // raspbian should be defined on rpi through cmake option
327 #ifndef __RASPBIAN__
328  if (event->key.keysym.scancode == SDL_SCANCODE_RALT) {
330  return 0;
331  }
332 
333  if (kbins->current_mode == app_workspace_ns::kb_input_mode::NAVIGATION &&
334  event->key.keysym.scancode == SDL_SCANCODE_BACKSPACE)
335  {
336  scr_mgr->pop_screen_from_previous();
337  return 0;
338  }
339 #endif
340 
341  // handle 1-9 while kb_input_mode is NAVIGATION (0 isn't included)
342  // need this for navigation on desktop, but for rpi moved to handle_nav_event() and never comes here
343  if (kbins->current_mode == app_workspace_ns::kb_input_mode::NAVIGATION &&
344  event->key.keysym.scancode >= SDL_SCANCODE_1 &&
345  event->key.keysym.scancode < SDL_SCANCODE_0) {
346  // screen number is only allowed in range 1-9 (0 reserved for error)
347  uint8_t screen_no = strtol(SDL_GetKeyName(event->key.keysym.sym), NULL, 10);
348 
349  if (screen_no > scr_mgr->get_screen_count()) {
350  spdlog::debug("event_handler.cpp - Requested screen ({0}) doesn't exist", screen_no);
351  return 0;
352  }
353 
354  scr_mgr->set_selected_screen(screen_no);
355 
356  return 0;
357  }
358 
359  // handle other keys (than 1-9) while in NAVIGATION mode
360  if (kbins->current_mode == app_workspace_ns::kb_input_mode::NAVIGATION) {
361  return 1;
362  }
363 
364  // handle any keypress (exc. #) while kb_input_mode is INPUT
365  if (kbins->current_mode == app_workspace_ns::kb_input_mode::INPUT) {
366  }
367  }
368 
369  if (event->type == SDL_KEYUP) {
370  spdlog::debug("event_handler.cpp - KEYUP scancode: {0}", event->key.keysym.scancode);
371  return 1; // DO NOT SEND KEYUP TO IMGUI - this is to prevent unwanted actions on desktop (spi keyboard only sends keydown events)
372  }
373 
374  return 1;
375  }
376 
377 /*********************************************************************************************************************/
378 /* Implementation of local function definitions */
379 /*********************************************************************************************************************/
380 
381  SDL_Scancode determine_scancode(uint16_t keycode, uint8_t cycle) {
382  std::unique_ptr<app_workspace_ns::kb_input_state> &kbins = app_workspace::get_instance()->kb_in_state;
383 
384  try {
385  if (kbins->current_mode == app_workspace_ns::kb_input_mode::NAVIGATION) {
386  return navigation_keymap.at(keycode);
387  } else if (kbins->current_mode == app_workspace_ns::kb_input_mode::INPUT) {
388  switch (kbins->current_type) {
390  return float_in_keymap.at(keycode);
391  } break;
393  return int_in_keymap.at(keycode);
394  } break;
396  std::vector<SDL_Scancode> opts = text_in_keymap.at(keycode);
397  return opts[cycle % opts.size()];
398  } break;
400  default:
401  spdlog::error("keyboard.cpp - This kb_input_type has no keymap");
402  }
403  } else {
404  spdlog::error("keyboard.cpp - Unknown kb_input_mode");
405  }
406  } catch (const std::out_of_range& ex) {
407  /* logging disabled, because of many key options, that are not specified in maps
408  spdlog::error("keyboard.cpp - Failed to find event for pressed key.");
409  spdlog::error("\t\t\t - 1) Key doesn't have event in selected input mode and type");
410  spdlog::error("\t\t\t - 2) Multiple keys pressed at once (unrecognized key value)");
411  */
412  return SDL_SCANCODE_CLEAR;
413  }
414 
415  return SDL_SCANCODE_CLEAR;
416  }
417 
418  SDL_Event create_key_event(SDL_Scancode scancode, Uint16 mod) {
419  SDL_Event event;
420 
421  event.type = SDL_KEYDOWN;
422  event.key.timestamp = SDL_GetTicks();
423  event.key.keysym.scancode = scancode;
424  event.key.keysym.sym = SDL_GetKeyFromScancode(scancode);
425  event.key.keysym.mod = mod;
426  event.key.state = 1;
427 
428  return event;
429  }
430 
431  SDL_Event create_textinput_event(SDL_Scancode scancode) {
432  SDL_Event txt_in;
433  txt_in.type = SDL_TEXTINPUT;
434 
435  try {
436  const char* txt;
437 
438  if (app_workspace::get_instance()->lshift_flag) {
439  try {
440  txt = custom_shift_keymap.at(scancode);
441  strcpy(txt_in.text.text, txt);
442  return txt_in;
443  } catch (std::out_of_range &ex) {
444  // no action, maybe log debug - if scancode isn't in shift_keymap, try looking in normal keymap
445  }
446  }
447 
448  txt = custom_keymap.at(scancode);
449 
450  // if the code is in cutom keymaps, use that instead of general SDL
451  strcpy(txt_in.text.text, txt);
452  return txt_in;
453  } catch (std::out_of_range &ex) {
454  spdlog::debug("event_handler.cpp - Custom keymaps are missing request scancode ({0})", scancode);
455  }
456 
457  strcpy(txt_in.text.text, SDL_GetScancodeName(scancode));
458  // if scancode is a letter between A-Z and capslock is off, make lower case
459  if (scancode >= SDL_SCANCODE_A && scancode <= SDL_SCANCODE_Z && !app_workspace::get_instance()->capslock_flag) {
460  for (size_t i = 0; i < strlen(txt_in.text.text); i++) {
461  txt_in.text.text[i] = std::tolower(txt_in.text.text[i]);
462  }
463  }
464 
465  return txt_in;
466  }
467 
468  void push_event_to_sdl(SDL_Event* event, bool replace_cur_char) {
469  std::unique_ptr<app_workspace_ns::kb_input_state> &kbins = app_workspace::get_instance()->kb_in_state;
470 
471  spdlog::debug("event_handler.cpp - Pushing key event with scancode: {0} - {1}", event->key.keysym.scancode,
472  SDL_GetKeyName(SDL_GetKeyFromScancode(event->key.keysym.scancode)));
473 
474  // remove character so new one replaces it
475  if (replace_cur_char &&
476  kbins->current_mode == app_workspace_ns::kb_input_mode::INPUT &&
477  kbins->current_type == app_workspace_ns::kb_input_type::TEXT &&
478  event->key.keysym.scancode != SDL_SCANCODE_RETURN)
479  {
480  SDL_Event backspace = create_key_event(SDL_SCANCODE_BACKSPACE, 0);
481  SDL_PushEvent(&backspace);
482 
483  backspace.type = SDL_KEYUP;
484  SDL_PushEvent(&backspace);
485  }
486 
487  SDL_PushEvent(event); // pushes KEY_DOWN event
488 
489  if (kbins->current_mode == app_workspace_ns::kb_input_mode::INPUT &&
490  event->key.keysym.scancode != SDL_SCANCODE_RETURN &&
491  event->key.keysym.scancode != SDL_SCANCODE_BACKSPACE) // backspace during input MUSTN'T send textinput event
492  {
493  SDL_Event txt_input = create_textinput_event(event->key.keysym.scancode);
494  SDL_PushEvent(&txt_input); // pushes TEXTINPUT_EVENT of pressed key
495  }
496 
497  event->type = SDL_KEYUP;
498  event->key.state = 0;
499  SDL_PushEvent(event); // pushes KEY_UP event
500  }
501 
503  screen_manager *scr_mgr = app_workspace::get_instance()->get_scr_mgr();
504  std::unique_ptr<app_workspace_ns::kb_input_state> &kbins = app_workspace::get_instance()->kb_in_state;
505 
506  if (kbins->current_mode != app_workspace_ns::kb_input_mode::NAVIGATION)
507  return false;
508 
509  if (scr_mgr->activated)
510  return false;
511 
512  // for now only gui_input can be activated
513  if (!instanceof<gui_input>(scr_mgr->focused))
514  return false;
515 
516  return true;
517  }
518 
526  // for debug purposes
527  if (instanceof<gui_button>(element)) {
528  spdlog::debug("event_handler.cpp - action of focused element BUTTON");
529  } else if (instanceof<gui_combobox>(element)) {
530  spdlog::debug("event_handler.cpp - action of focused element COMBOBOX");
531  } else if (instanceof<gui_direct>(element)) {
532  spdlog::debug("event_handler.cpp - action of focused element DIRECT");
533  } else if (instanceof<gui_label>(element)) {
534  spdlog::debug("event_handler.cpp - action of focused element LABEL");
535  } else if (instanceof<gui_observer>(element)) {
536  spdlog::debug("event_handler.cpp - action of focused element OBSERVER");
537  } else if (instanceof<gui_selectable>(element)) {
538  spdlog::debug("event_handler.cpp - action of focused element SELECTABLE");
539  }else {
540  spdlog::debug("event_handler.cpp - action of focused element UNKNOWN");
541  }
542 
543  // handling button action - it needs to send TEXT_INPUT event as well
544  if (instanceof<gui_button>(element) || instanceof<gui_combobox>(element) ||
545  instanceof<gui_selectable>(element) || combo_expanded)
546  {
547  // on normal keyboard button callback is activated with "spacebar"
548  // events are: KEYDOWN - SPACE, TEXTINPUT - SPACE, KEYUP - SPACE
549  // only TEXTINPUT - SPACE might be enough to invoke callback (not tested!!!)
550 
551  spdlog::debug("event_handler.cpp - creating action event");
552 
553  SDL_Event ret = create_key_event(SDL_SCANCODE_SPACE, 0);
554  SDL_PushEvent(&ret);
555 
556  SDL_Event txt_input = create_textinput_event(ret.key.keysym.scancode);
557  SDL_PushEvent(&txt_input); // pushes TEXTINPUT_EVENT of pressed key
558 
559  ret.type = SDL_KEYUP;
560  ret.key.state = 0;
561  SDL_PushEvent(&ret); // pushes KEY_UP event
562 
563  if (instanceof<gui_combobox>(element) || combo_expanded) {
564  combo_expanded = !combo_expanded; // if activating/ deactivating combo set this flag
565  spdlog::debug("event_handler.cpp - setting combo_expanded flag to {0}", combo_expanded);
566  }
567 
568  return 1;
569  }
570 
571  return 0;
572  }
573 
580  screen_manager *scr_mgr = app_workspace::get_instance()->get_scr_mgr();
582  SDL_Event event;
583 
584  /* how this works: if in navigation - enter on input enters input mode
585  - enter on combobox/ button "activate event"
586  if in input mode - enter exits input mode
587  */
588 
589 
590  spdlog::debug("event_handler.cpp - handling RETURN raw event");
591 
593  spdlog::debug("event_handler.cpp - switching input mode from INPUT to NAVIGATION");
594 
595  event = create_key_event(SDL_SCANCODE_RETURN, 0);
596  push_event_to_sdl(&event, false);
598 
599  // disable flags when exiting input mode (not necessary, does the same on input)
600  app_workspace::get_instance()->lshift_flag = false;
601  app_workspace::get_instance()->capslock_flag = false;
602 
603  return 1;
604  }
605 
606  // see can_switch_modes for condition to enter INPUT mode
607  if (can_switch_modes()) {
608  spdlog::debug("event_handler.cpp - switching input mode from NAVIGATION to INPUT");
609 
610  // disable flags when entering input mode
611  app_workspace::get_instance()->lshift_flag = false;
612  app_workspace::get_instance()->capslock_flag = false;
613 
614  event = create_key_event(SDL_SCANCODE_RETURN, 0);
615  push_event_to_sdl(&event, false);
616 
618  // can_switch_modes() checks that focused is instanceof gui_input
619  kbins->current_type = ((gui_input*) scr_mgr->focused)->get_in_type();
620  spdlog::debug("event_handler.cpp - input type: {0}", kbins->current_type);
621 
622  return 1;
623  }
624 
625  // when combo is expanded, an element IS focused, but it ISN'T any element from screen definition
626  if (scr_mgr->focused || combo_expanded) {
627  spdlog::debug("event_handler.cpp - calling focused element action");
628  return focused_element_action(scr_mgr->focused);
629  }
630 
631  spdlog::debug("event_handler.cpp - handling RETURN raw event with no action");
632  return 0;
633  }
634 
635  int handle_nav_event(kb_event* raw_event) {
636  screen_manager *scr_mgr = app_workspace::get_instance()->get_scr_mgr();
637  SDL_Event event;
638  SDL_Scancode sc;
639 
640  // custom event handling here, doesn't need to be sent tro SDL
641 
642  sc = determine_scancode(raw_event->scancode);
643  if (sc == SDL_SCANCODE_CLEAR)
644  return 0;
645 
646  // 1-9 isn't send to SDL
647  if (sc >= SDL_SCANCODE_1 && sc < SDL_SCANCODE_0) {
648  uint8_t screen_no = strtol(SDL_GetScancodeName(sc), NULL, 10);
649  scr_mgr->set_selected_screen(screen_no);
650  return 1;
651  }
652 
653  // 0 sends backspace - serves to go to previous screen
654  if (sc == SDL_SCANCODE_BACKSPACE) {
655  scr_mgr->pop_screen_from_previous();
656  return 1; // not needed to send backspace during navigation to sdl (would be handled twice)
657  }
658 
659  // create SDL event for keys other than 1-9: eg.: "arrow" UP, DOWN for nav
660  event = create_key_event(sc);
661  push_event_to_sdl(&event);
662 
663  return 1;
664  }
665 
666  int handle_input_event(kb_event* raw_event) {
667  std::unique_ptr<app_workspace_ns::kb_input_state> &kbins = app_workspace::get_instance()->kb_in_state;
668  SDL_Event event;
669  SDL_Scancode sc;
670 
671  // custom event handling here, doesn't need to be sent tro SDL
672 
673  // if input is text, determine if the key_should be cycled (e.g.: 2 = A/B/C)
674  if (kbins->current_type == app_workspace_ns::kb_input_type::TEXT) {
675  uint32_t delta_t = UINT32_MAX;
676  // time since last event in us
677  if (last_raw_event != nullptr) {
678  delta_t = (raw_event->timestamp.tv_sec - last_raw_event->timestamp.tv_sec) * 1000000 +
679  (raw_event->timestamp.tv_usec - last_raw_event->timestamp.tv_usec);
680  if (last_raw_event->scancode == raw_event->scancode && delta_t < 2000000) //
681  key_cycle++;
682  else
683  key_cycle = 0;
684  } else {
685  key_cycle = 0;
686  }
687 
688  last_raw_event = raw_event;
689  }
690 
691  sc = determine_scancode(raw_event->scancode, key_cycle);
692  if (sc == SDL_SCANCODE_CLEAR)
693  return 0;
694 
695  // those flags could be handled in key_cycle branch which would be a bit more efficient, but this is more readable
696  // DO NOT create event for mod keys
697  if (sc == SDL_SCANCODE_LSHIFT) {
698  app_workspace::get_instance()->lshift_flag = !app_workspace::get_instance()->lshift_flag;
699  return 1;
700  }
701  if (sc == SDL_SCANCODE_CAPSLOCK) {
702  app_workspace::get_instance()->capslock_flag = !app_workspace::get_instance()->capslock_flag;
703  return 1;
704  }
705 
706  // doesn't do anything
707  Uint16 mod = 0;
708  if (app_workspace::get_instance()->lshift_flag) mod |= KMOD_LSHIFT;
709  if (app_workspace::get_instance()->capslock_flag) mod |= KMOD_CAPS;
710 
711  event = create_key_event(sc, mod);
712  push_event_to_sdl(&event, key_cycle > 0);
713 
714  return 1;
715  }
716 
718  app_workspace *app_wrk = app_workspace::get_instance().get();
719  user_workspace *usr_wrk = app_wrk->get_userspace();
720 
721  // if the rfid is the same as logged in user
722  if (usr_wrk->compare_rfid_serials(&(event->tag)) == 0) { // 0 means equal
723  spdlog::info("event_handler.cpp - RFID event handler: user with this RFID is already logged");
724  // dont go to err screen (other than log, no action)
725  return 1;
726  }
727 
728  // currently logged user is pacient (0) and rfid doesnt match -> new user is trying to log in (regardless of new user role)
729  if (usr_wrk->get_role() == 0) {
730  spdlog::error("event_handler.cpp - RFID event handler: ");
731  // TODO/ FIXME - maybe instead of error and manual log in, automatically log over
732  app_wrk->set_err_screen_and_activate(get_localized_text("ERR_USER_LOGIN"),
733  get_localized_text("ERR_USER_ALREADY_LOGGED"));
734  return 1;
735  }
736 
737  if (usr_wrk->get_role() == 2 && app_wrk->get_scr_mgr()->get_selected_screen() == 6 &&
738  app_wrk->s5_screen_6_indicator == 1) // if user is admin, active screen is 6 and content on screen is user addition
739  {
740  app_wrk->s6_rfid_indicator = true;
741  app_wrk->refresh_current_screen = true;
742  rfid_reader::rfid_tag* tag_tmp = (rfid_reader::rfid_tag*) calloc(1, sizeof(rfid_reader::rfid_tag));
743  memcpy(tag_tmp, &(event->tag), sizeof(rfid_reader::rfid_tag));
744  app_wrk->s6_rfid_tag.reset(tag_tmp);
745  return 0;
746  }
747 
748  // this is reached only if role > 0 (employee/ admin). assuming the login is for subuser. subuser already exists, no place to log in new user
749  if (usr_wrk->has_subuser()) {
750  spdlog::error("event_handler.cpp - RFID event handler: already have user and subuser.");
751  // can move to error screen with this, but rfid is usually loaded several times with one "press" on the reader
752  // app_wrk->set_err_screen_and_activate(get_localized_text("ERR_USERS_FULL"),
753  // get_localized_text("ERR_USERS_FULL_DESC"));
754  return 1;
755  }
756 
757  app_wrk->log_in_user_rfid(&(event->tag), true);
758 
759  return 0;
760  }
761 
763  if (app_workspace::get_instance()->hx_measuring) {
764  spdlog::debug("event_handler.cpp - Not handling keyboard events, because a hx measuring is ongoing.");
765  return 1;
766  }
767 
768  spdlog::info("event_handler.cpp - handling RFID event");
769  app_workspace *app_wrk = app_workspace::get_instance().get();
770 
771  printf("RFID serial: ");
772  for (int i = 0; i < event->tag.serial_size; i++) {
773  printf("%02x(%d) ", event->tag.serial[i], event->tag.serial[i]);
774  }
775  printf("\n");
776 
777  if (!(event->flags & RFID_FLAGS_REPEATING)) {
778  if (app_wrk->has_user()) {
779  return rfid_action_user_logged(event);
780  } else {
781  if (app_wrk->get_scr_mgr()->get_selected_screen() == 1) {
782  app_wrk->log_in_user_rfid(&(event->tag), false);
783  } else {
784  spdlog::error("event_handler.cpp - No user logged in. RFID login only possible from screen 1.");
785  }
786  }
787  }
788 
789  return 0;
790  }
791 }
One of the most importat classes in the whole project. Holds variables that define the state of the a...
static std::unique_ptr< app_workspace > & get_instance()
Get the instance app_workspace which is a singleton.
user_workspace * get_userspace()
void set_err_screen_and_activate(const char *title, const char *label)
Set the err screen and activate the screen on next frame.
bool s6_rfid_indicator
int s5_screen_6_indicator
int log_in_user_rfid(rfid_reader::rfid_tag *tag, bool loading_sub=false)
Logs in user through scanned RFID tag. Initializes user_workspace if loading_sub is false.
bool has_user()
Checks if user is logged in. This is determined by user_space being initialized and having the user_l...
screen_manager * get_scr_mgr()
Get the screen manager instance. This instance is kind of singleton. Is initalized only once on start...
std::unique_ptr< rfid_reader::rfid_tag > s6_rfid_tag
bool refresh_current_screen
When this is set to true, elements of current screen will be reinitialized for next frame.
Paren class for other gui elements.
Definition: gui_element.h:8
This is a wrapper for various ImGui input types.
Definition: gui_input.h:24
gui_element * focused
void pop_screen_from_previous()
void set_selected_screen(uint8_t screen)
uint8_t get_screen_count()
gui_element * activated
uint8_t get_selected_screen()
Container that servers for storing users data and manipulating them.
int compare_rfid_serials(rfid_reader::rfid_tag *tag)
@ KB_FLAGS_REPEATING
Definition: keyboard.h:14
@ KB_FLAGS_TEST
Definition: keyboard.h:15
@ KB_FLAGS_KEY_DOWN
Definition: keyboard.h:13
const char * get_localized_text(const char *key)
Get the localized text object.
int handle_rfid_event(rfid_event *event)
Function handles RFID event.
bool can_switch_modes()
Determine if keyboard can be switched to INPUT mode.
bool instanceof(const T *ptr)
int handle_return_event()
Handles return event (NAVIGATION/INPUT mode switch, BUTTON/ COMBOOX... activation)
std::map< uint16_t, SDL_Scancode > navigation_keymap
Map for translating SPI keyboard scancodes keys to SDL scancodes for navigation.
std::map< SDL_Scancode, const char * > custom_shift_keymap
std::map< uint16_t, SDL_Scancode > int_in_keymap
int input map that is used for mapping spi keys to sdl scancodes
int handle_nav_event(kb_event *raw_event)
Handle event as navigation event.
void push_event_to_sdl(SDL_Event *event, bool replace_cur_char=false)
Pushes event to SDL queue. This event then can be propagated to ImGui. If @replace_cur_char is set,...
SDL_Event create_key_event(SDL_Scancode scancode, Uint16 mod=0)
Creates an SDL_Event simulating keyboard press from SDL_Scancode.
int rfid_action_user_logged(rfid_event *event)
std::map< uint16_t, SDL_Scancode > float_in_keymap
float input map that is used for mapping spi keys to sdl scancodes
int handle_input_event(kb_event *raw_event)
Handle event as input event.
SDL_Scancode determine_scancode(uint16_t keycode, uint8_t cycle=0)
Determines SDL_Scancode from SPI keyboard keycode and cycle. Cycle is used to cycle keys during text ...
int focused_element_action(gui_element *element)
std::map< uint16_t, std::vector< SDL_Scancode > > text_in_keymap
Text input keymap. Holds what scancodes are cycled for corresponding key. First value should correspo...
int handle_raw_event(kb_event *raw_event)
Offers option for cutom event handling. If not desired only created SDL event.
SDL_Event create_textinput_event(SDL_Scancode scancode)
Creates an SDL_Event for input.
int handle_sdl_event(const SDL_Event *event)
Handles SDL2 library event.
std::map< SDL_Scancode, const char * > custom_keymap
@ RFID_FLAGS_REPEATING
Definition: rfid_reader.h:93
#define ADMIN_SELECT_SCREEN
Structure holding the values of keyboard input mode and input type.
Definition: app_workspace.h:96
Structure of SPI keyboard event, Contains pressed scancode, flags and timestamp of event creation.
Definition: keyboard.h:23
struct timeval timestamp
Definition: keyboard.h:26
uint8_t flags
Definition: keyboard.h:25
uint16_t scancode
Definition: keyboard.h:24
Structure used for RFID event. This structure contains read tag and flags of the event.
Definition: rfid_reader.h:100
uint8_t flags
Definition: rfid_reader.h:102
rfid_reader::rfid_tag tag
Definition: rfid_reader.h:101
RFID tag structure. Stores data read from SPI.
Definition: rfid_reader.h:12
uint8_t serial[10]
Definition: rfid_reader.h:14