JeVois  1.18
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
ImGuiBackendMALI.C
Go to the documentation of this file.
1 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2020 by Laurent Itti, the University of Southern
4 // California (USC), and iLab at USC. See http://iLab.usc.edu and http://jevois.org for information about this project.
5 //
6 // This file is part of the JeVois Smart Embedded Machine Vision Toolkit. This program is free software; you can
7 // redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
8 // Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
10 // License for more details. You should have received a copy of the GNU General Public License along with this program;
11 // if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12 //
13 // Contact information: Laurent Itti - 3641 Watt Way, HNB-07A - Los Angeles, CA 90089-2520 - USA.
14 // Tel: +1 213 740 3527 - itti@pollux.usc.edu - http://iLab.usc.edu - http://jevois.org
15 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
16 /*! \file */
17 
18 #ifdef JEVOIS_PLATFORM_PRO
19 
21 #include <jevois/Debug/Log.H>
22 #include <jevois/GPU/OpenGL.H>
23 #include <jevois/Util/Console.H>
24 #include <jevois/Util/Utils.H>
25 
26 #include <imgui_impl_opengl3.h>
27 #include <imgui.h>
28 
29 #include <linux/input.h>
30 #include <linux/kd.h>
31 #include <linux/keyboard.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <cctype>
37 #include <wchar.h>
38 #include <wctype.h>
39 
40 /* Mysteriously missing defines from <linux/input.h>. */
41 #define EV_MAKE 1
42 #define EV_BREAK 0
43 #define EV_REPEAT 2
44 
45 #define NELEMS(x) (sizeof(x) / sizeof((x)[0]))
46 
47 namespace
48 {
49  // ##############################################################################################################
50  static std::string clipboardText;
51 
52  char const * ImGui_ImplMALI_GetClipboardText(void *)
53  { return clipboardText.c_str(); }
54 
55  void ImGui_ImplMALI_SetClipboardText(void *, char const * text)
56  { clipboardText = text; }
57 
58  // Code from https://github.com/bingmann/evdev-keylogger/blob/master/keymap.c
59 
60  /* *******************************************************************************
61  * Copyright (C) 2012 Jason A. Donenfeld <Jason@zx2c4.com>
62  * Copyright (C) 2014 Timo Bingmann <tb@panthema.net>
63  *
64  * This program is free software; you can redistribute it and/or modify it
65  * under the terms of the GNU General Public License as published by the Free
66  * Software Foundation; either version 2 of the License, or (at your option)
67  * any later version.
68  *
69  * This program is distributed in the hope that it will be useful, but WITHOUT
70  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
71  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
72  * more details.
73  *
74  * You should have received a copy of the GNU General Public License along with
75  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
76  * Place, Suite 330, Boston, MA 02111-1307 USA
77  ******************************************************************************/
78 
79  /* c = character key
80  * f = function key
81  * _ = blank/error
82  *
83  * Source: KEY_* defines from <linux/input.h>
84  */
85  static char const char_or_func[] =
86  /* 0123456789ABCDEF */
87  "_fccccccccccccff" /* 0 */
88  "ccccccccccccfFcc" /* 1 */
89  "ccccccccccFccccc" /* 2 */
90  "ccccccFfFfFfffff" /* 3 */
91  "fffffFFfffffffff" /* 4 */
92  "ffff__cff_______" /* 5 */
93  "fFffFfffffffffff" /* 6 */
94  "_______f_____FFF"; /* 7 */
95 
96  int is_char_key(unsigned int code)
97  {
98  if (code >= sizeof(char_or_func)) throw std::range_error("is_char_key: invalid code " + std::to_string(code));
99  return char_or_func[code] == 'c';
100  }
101  /*
102  int is_func_key(unsigned int code)
103  {
104  if (code >= sizeof(char_or_func)) throw std::range_error("is_func_key: invalid code " + std::to_string(code));
105  return char_or_func[code] == 'f' || char_or_func[code] == 'F';
106  }
107  */
108  /*
109  int is_modfunc_key(unsigned int code)
110  {
111  if (code >= sizeof(char_or_func)) throw std::range_error("is_modfunc_key: invalid code " + std::to_string(code));
112  return char_or_func[code] == 'F';
113  }
114  */
115  /*
116  int is_used_key(unsigned int code)
117  {
118  if (code >= sizeof(char_or_func)) throw std::range_error("is_used_key: invalid code " + std::to_string(code));
119  return char_or_func[code] != '_';
120  }
121  */
122 
123  /* Translates character keycodes to continuous array indexes. */
124  int to_char_keys_index(unsigned int keycode)
125  {
126  // keycodes 2-13: US keyboard: 1, 2, ..., 0, -, =
127  if (keycode >= KEY_1 && keycode <= KEY_EQUAL) return keycode - KEY_1;
128  // keycodes 16-27: q, w, ..., [, ]
129  if (keycode >= KEY_Q && keycode <= KEY_RIGHTBRACE) return keycode - KEY_Q + 12;
130  // keycodes 30-41: a, s, ..., ', `
131  if (keycode >= KEY_A && keycode <= KEY_GRAVE) return keycode - KEY_A + 24;
132  // keycodes 43-53: \, z, ..., ., /
133  if (keycode >= KEY_BACKSLASH && keycode <= KEY_SLASH) return keycode - KEY_BACKSLASH + 36;
134  // key right to the left of 'Z' on US layout
135  if (keycode == KEY_102ND) return 47;
136  return -1; // not character keycode
137  }
138 
139  /* Translates function keys keycodes to continuous array indexes. */
140  int to_func_keys_index(unsigned int keycode)
141  {
142  // 1
143  if (keycode == KEY_ESC) return 0;
144  // 14-15
145  if (keycode >= KEY_BACKSPACE && keycode <= KEY_TAB) return keycode - 13;
146  // 28-29
147  if (keycode >= KEY_ENTER && keycode <= KEY_LEFTCTRL) return keycode - 25;
148  // 42
149  if (keycode == KEY_LEFTSHIFT) return keycode - 37;
150  // 54-83
151  if (keycode >= KEY_RIGHTSHIFT && keycode <= KEY_KPDOT) return keycode - 48;
152  // 87-88
153  if (keycode >= KEY_F11 && keycode <= KEY_F12) return keycode - 51;
154  // 96-111
155  if (keycode >= KEY_KPENTER && keycode <= KEY_DELETE) return keycode - 58;
156  // 119
157  if (keycode == KEY_PAUSE) return keycode - 65;
158  // 125-127
159  if (keycode >= KEY_LEFTMETA && keycode <= KEY_COMPOSE) return keycode - 70;
160 
161  return -1; // not function key keycode
162  }
163 
164  /* Determines the system keymap via the dump keys program and some disgusting parsing of it. */
165  void load_system_keymap(std::array<wchar_t, 49> & char_keys, std::array<wchar_t, 49> & shift_keys,
166  std::array<wchar_t, 49> & altgr_keys)
167  {
168  /* HACK: This is obscenely ugly, and we should really just do this in C... */
169  FILE * dumpkeys = popen("/usr/bin/dumpkeys -n | /bin/grep '^\\([[:space:]]shift[[:space:]]\\)*\\([[:space:]]altgr[[:space:]]\\)*keycode' | /bin/sed 's/U+/0x/g' 2>&1", "r");
170  if (!dumpkeys) { LERROR("Cannot run dumpkeys -- NOT UPDATING KEYMAP"); return; }
171 
172  // Here we can get strings like:
173  // keycode x = y
174  // shift keycode x = y
175  // altgr keycode x = y
176  // shift altgr keycode x = y: never happens on my keyboard
177 
178  char_keys.fill(0);
179  shift_keys.fill(0);
180  altgr_keys.fill(0);
181 
182  LINFO("Loading keymap...");
183  char buffer[256]; int n = 0;
184  while (fgets(buffer, sizeof(buffer), dumpkeys))
185  {
186  auto tok = jevois::split(buffer);
187  size_t const ntok = tok.size();
188 
189  if (ntok < 4) continue;
190  if (tok[ntok - 4] != "keycode") continue;
191  unsigned int const keycode = std::stoi(tok[ntok - 3]);
192  if (!is_char_key(keycode)) continue;
193  if (keycode >= sizeof(char_or_func)) continue;
194  bool const shift = (tok[1] == "shift");
195  bool const altgr = (tok[1] == "altgr" || tok[2] == "altgr");
196  std::string const & val = tok[ntok - 1];
197  if (val.empty()) { LERROR("Skipping invalid empty keycode value"); continue; }
198 
199  int index = to_char_keys_index(keycode);
200  wchar_t wch = (wchar_t)jevois::from_string<unsigned int>(val);
201  if (val[0] == '+' && (wch & 0xB00)) wch ^= 0xB00;
202 
203  if (tok[0] == "keycode") { char_keys[index] = wch; ++n; }
204  else
205  {
206  if (shift)
207  {
208  if (wch == L'\0') wch = towupper(char_keys[index]);
209  shift_keys[index] = wch;
210  ++n;
211  }
212  else if (altgr) { altgr_keys[index] = wch; ++n; }
213  }
214  }
215  pclose(dumpkeys);
216  LINFO("Loaded " << n << " keymap entries.");
217  }
218 
219  /* Translates struct input_event *event into the string of size buffer_length
220  * pointed to by buffer. Stores state originating from sequence of event
221  * structs in struc input_event_state *state. Returns the number of bytes
222  * written to buffer. */
223  size_t translate_eventw(input_event const *event, input_event_state *state,
224  wchar_t *wbuffer, size_t wbuffer_len, std::array<wchar_t, 49> const & char_keys,
225  std::array<wchar_t, 49> const & shift_keys, std::array<wchar_t, 49> const & altgr_keys,
226  wchar_t const func_keys[58][8])
227  {
228  wchar_t wch;
229  size_t len = 0;
230 
231  if (event->type != EV_KEY)
232  {
233  // not a keyboard event
234  }
235  else if (event->code >= sizeof(char_or_func))
236  {
237  len += swprintf(&wbuffer[len], wbuffer_len, L"<E-%x>", event->code);
238  }
239  else if (event->value == EV_MAKE || event->value == EV_REPEAT)
240  {
241  if (event->code == KEY_LEFTSHIFT || event->code == KEY_RIGHTSHIFT) state->shift = 1;
242  else if (event->code == KEY_RIGHTALT) state->altgr = 1;
243  else if (event->code == KEY_LEFTALT) state->alt = 1;
244  else if (event->code == KEY_LEFTCTRL || event->code == KEY_RIGHTCTRL) state->ctrl = 1;
245  else if (event->code == KEY_LEFTMETA || event->code == KEY_RIGHTMETA) state->meta = 1;
246 
247  // If ctrl is pressed, do not translate here. Those keys will be probed directly via io.KeysDown, which is set
248  // upstream by our caller:
249  if (state->ctrl) return 0;
250 
251  // For any key that can translate to a meaningful wchar_t, add the translated value to the buffer:
252  if (is_char_key(event->code))
253  {
254  if (state->altgr)
255  {
256  wch = altgr_keys[to_char_keys_index(event->code)];
257  if (wch == L'\0')
258  {
259  if (state->shift) wch = shift_keys[to_char_keys_index(event->code)];
260  else wch = char_keys[to_char_keys_index(event->code)];
261  }
262  }
263  else if (state->shift)
264  {
265  wch = shift_keys[to_char_keys_index(event->code)];
266  if (wch == L'\0') wch = char_keys[to_char_keys_index(event->code)];
267  }
268  else wch = char_keys[to_char_keys_index(event->code)];
269 
270  if (wch != L'\0') len += swprintf(&wbuffer[len], wbuffer_len, L"%lc", wch);
271  }
272 
273  else if (event->code == 57) // special handling of space which is listed as a function key
274  len += swprintf(&wbuffer[len], wbuffer_len, L"%ls", func_keys[to_func_keys_index(event->code)]);
275  /*
276  else if (is_modfunc_key(event->code) && event->code == 57)
277  {
278  if (event->value == EV_MAKE)
279  len += swprintf(&wbuffer[len], wbuffer_len, L"%ls", func_keys[to_func_keys_index(event->code)]);
280  }
281  else if (is_func_key(event->code) && event->code == 57)
282  {
283  len += swprintf(&wbuffer[len], wbuffer_len, L"%ls", func_keys[to_func_keys_index(event->code)]);
284  }
285  else
286  {
287  //len += swprintf(&wbuffer[len], wbuffer_len, L"<E-%x>", event->code);
288  }
289  */
290 
291  }
292  else if (event->value == EV_BREAK)
293  {
294  if (event->code == KEY_LEFTSHIFT || event->code == KEY_RIGHTSHIFT) state->shift = 0;
295  else if (event->code == KEY_RIGHTALT) state->altgr = 0;
296  else if (event->code == KEY_LEFTALT) state->alt = 0;
297  else if (event->code == KEY_LEFTCTRL || event->code == KEY_RIGHTCTRL) state->ctrl = 0;
298  else if (event->code == KEY_LEFTMETA || event->code == KEY_RIGHTMETA) state->meta = 0;
299  }
300 
301  return len;
302  }
303 
304  /* Translates event into multi-byte string description. */
305  size_t translate_event(input_event const *event, input_event_state *state,
306  char *buffer, size_t buffer_len, std::array<wchar_t, 49> const & char_keys,
307  std::array<wchar_t, 49> const & shift_keys, std::array<wchar_t, 49> const & altgr_keys,
308  wchar_t const func_keys[58][8])
309  {
310  wchar_t *wbuffer;
311  size_t wbuffer_len, len;
312 
313  wbuffer = (wchar_t*)buffer;
314  wbuffer_len = buffer_len / sizeof(wchar_t);
315 
316  len = translate_eventw(event, state, wbuffer, wbuffer_len, char_keys, shift_keys, altgr_keys, func_keys);
317 
318  if (!len) *buffer = 0;
319  else wcstombs(buffer, wbuffer, buffer_len);
320  return len;
321  }
322 
323  // We could maybe use that in scanDevices()...
324  // from https://github.com/bingmann/evdev-keylogger/blob/master/logger.c
325  /*
326 #define MAX_PATH 256
327 #define MAX_EVDEV 16
328 
329  int find_default_keyboard_list(char event_device[MAX_EVDEV][MAX_PATH])
330  {
331  FILE *devices;
332  char events[128];
333  char handlers[128];
334  char *event;
335  int evnum = 0, i;
336 
337  devices = fopen("/proc/bus/input/devices", "r");
338  if (!devices) {
339  perror("fopen");
340  return evnum;
341  }
342  while (fgets(events, sizeof(events), devices))
343  {
344  if (strstr(events, "H: Handlers=") == events)
345  strcpy(handlers, events);
346  else if (!strcmp(events, "B: EV=120013\n") && (event = strstr(handlers, "event")))
347  {
348  for (i = 0, event += sizeof("event") - 1; *event && isdigit(*event); ++event, ++i)
349  handlers[i] = *event;
350  handlers[i] = '\0';
351 
352  snprintf(event_device[evnum], sizeof(event_device[evnum]),
353  "/dev/input/event%s", handlers);
354 
355  fprintf(stderr, "listening to keyboard: %s\n", event_device[evnum]);
356 
357  if (++evnum == MAX_EVDEV) break;
358  }
359  }
360  fclose(devices);
361  return evnum;
362  }
363  */
364 
365 } // anonymous namespace
366 
367 // ##############################################################################################################
369  jevois::VideoDisplayBackendMALI(), itsLastNewFrameTime(std::chrono::steady_clock::now()),
370  itsMouseX(0), itsMouseY(0), itsConsoleFd(-1), itsTTY(-1), itsKBmode(0)
371 {
372  for (size_t i = 0; i < NELEMS(itsFd); ++i) itsFd[i] = -1;
373  for (size_t i = 0; i < NELEMS(itsMouseButton); ++i) itsMouseButton[i] = false;
374 
375  load_system_keymap(itsCharKeys, itsShiftKeys, itsAltgrKeys);
376 }
377 
378 // ##############################################################################################################
380 {
381  if (itsInitialized)
382  {
383  ImGui_ImplOpenGL3_Shutdown();
384  ImGui::DestroyContext();
385  }
386 
387  // Close console:
388  if (itsConsoleFd >= 0) { ioctl(itsConsoleFd, KDSETMODE, KD_TEXT); close(itsConsoleFd); }
389 
390  // Unmute keyboard:
391  if (itsTTY >= 0)
392  {
393  try { jevois::unMuteKeyboard(itsTTY, itsKBmode); } catch (...) { jevois::warnAndIgnoreException(); }
394  close(itsTTY);
395  }
396 }
397 
398 // ##############################################################################################################
399 void jevois::ImGuiBackendMALI::init(unsigned short w, unsigned short h, bool fullscreen, float scale, bool conslock)
400 {
401  // Lock the console if desired:
402  if (conslock)
403  {
404  // Open console and tell OS to stop drawing on it:
405  try
406  {
407  itsConsoleFd = jevois::getConsoleFd();
408  ioctl(itsConsoleFd, KDSETMODE, KD_GRAPHICS);
409  }
410  catch (...) { jevois::warnAndIgnoreException(); }
411 
412  // https://unix.stackexchange.com/questions/173712/
413  // best-practice-for-hiding-virtual-console-while-rendering-video-to-framebuffer
414 
415  // Mute keyboard:
416  itsTTY = STDIN_FILENO;
417  try { jevois::muteKeyboard(itsTTY, itsKBmode); }
418  catch (...)
419  {
420  // stdin is not a tty, probably we were launched remotely, so we try to disable the active tty:
421  try { itsTTY = jevois::getActiveTTY(); jevois::muteKeyboard(itsTTY, itsKBmode); }
422  catch (...) { jevois::warnAndIgnoreException(); }
423  }
424  }
425 
426  // Init MALI and OpenGL:
428 
429  // Init our internals:
430  itsWidth = w; itsHeight = h;
431  itsMouseX = w / 2; itsMouseY = h / 2;
432 
433  // Setup Dear ImGui context:
434  IMGUI_CHECKVERSION();
435  ImGui::CreateContext();
436  ImGuiIO & io = ImGui::GetIO();
437  // io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
438  //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
439 
440  // First setup the imgui context:
441  //io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
442  //io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional)
443  //io.BackendPlatformName = "imgui_impl_jevoispro";
444  io.BackendPlatformName = "imgui_impl_sdl";
445 
446  // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
447  io.KeyMap[ImGuiKey_Tab] = KEY_TAB;
448  io.KeyMap[ImGuiKey_LeftArrow] = KEY_LEFT;
449  io.KeyMap[ImGuiKey_RightArrow] = KEY_RIGHT;
450  io.KeyMap[ImGuiKey_UpArrow] = KEY_UP;
451  io.KeyMap[ImGuiKey_DownArrow] = KEY_DOWN;
452  io.KeyMap[ImGuiKey_PageUp] = KEY_PAGEUP;
453  io.KeyMap[ImGuiKey_PageDown] = KEY_PAGEDOWN;
454  io.KeyMap[ImGuiKey_Home] = KEY_HOME;
455  io.KeyMap[ImGuiKey_End] = KEY_END;
456  io.KeyMap[ImGuiKey_Insert] = KEY_INSERT;
457  io.KeyMap[ImGuiKey_Delete] = KEY_DELETE;
458  io.KeyMap[ImGuiKey_Backspace] = KEY_BACKSPACE;
459  io.KeyMap[ImGuiKey_Space] = KEY_SPACE;
460  io.KeyMap[ImGuiKey_Enter] = KEY_ENTER;
461  io.KeyMap[ImGuiKey_Escape] = KEY_ESC;
462  io.KeyMap[ImGuiKey_KeyPadEnter] = KEY_KPENTER;
463  io.KeyMap[ImGuiKey_A] = KEY_A;
464  io.KeyMap[ImGuiKey_C] = KEY_C;
465  io.KeyMap[ImGuiKey_V] = KEY_V;
466  io.KeyMap[ImGuiKey_X] = KEY_X;
467  io.KeyMap[ImGuiKey_Y] = KEY_Y;
468  io.KeyMap[ImGuiKey_Z] = KEY_Z;
469 
470  io.SetClipboardTextFn = ImGui_ImplMALI_SetClipboardText;
471  io.GetClipboardTextFn = ImGui_ImplMALI_GetClipboardText;
472  io.ClipboardUserData = NULL;
473 
474  // Tell imgui to draw a mouse cursor (false at init time):
475  io.MouseDrawCursor = false;
476 
477  // Setup Dear ImGui style:
478  ImGui::StyleColorsDark();
479  //ImGui::StyleColorsClassic();
480 
481  io.FontGlobalScale = scale;
482  ImGui::GetStyle().ScaleAllSizes(scale);
483 
484  // Setup renderer backend:
485  ImGui_ImplOpenGL3_Init("#version 300 es");
486 
487  // Load Fonts
488  // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
489  // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
490  // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
491  // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
492  // - Read 'docs/FONTS.md' for more instructions and details.
493  // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
494  //io.Fonts->AddFontDefault();
495  //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
496  //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
497  //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
498  //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f);
499  //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese());
500  //IM_ASSERT(font != NULL);
501 
502  // Do an initial scan for input event devices:
503  //scanDevices();
504 
505  itsInitialized = true; // will be used to close ImGui if needed
506 }
507 
508 // ##############################################################################################################
509 bool jevois::ImGuiBackendMALI::pollEvents(bool & shouldclose)
510 {
511  ImGuiIO & io = ImGui::GetIO();
512  bool gotsome = false;
513 
514  // Rescan our devices once in a while; could use udev instead
515  static int count = 0;
516  if (++count == 100) { scanDevices(); count = 0; }
517 
518  // Mouse button detection: we want to make sure we do not miss click-then-release within a video frame, while at ths
519  // same time detecting releases from drag:
520  bool mouse_pressed[5] = { false, false, false, false, false };
521  bool mouse_released[5] = { false, false, false, false, false };
522 
523  // Check all our input devices in one select() call:
524  static fd_set rfds; // ready to read
525  static fd_set efds; // has error
526  static struct timeval tv; // timeout
527 
528  FD_ZERO(&rfds); FD_ZERO(&efds);
529  tv.tv_sec = 0; tv.tv_usec = 5;
530  int maxfd = -1;
531 
532  for (size_t i = 0; i < NELEMS(itsFd); ++i)
533  if (itsFd[i] != -1)
534  {
535  FD_SET(itsFd[i], &rfds);
536  FD_SET(itsFd[i], &efds);
537  maxfd = std::max(maxfd, itsFd[i]);
538  }
539  if (maxfd <= 0) return gotsome; // Nothing to check
540 
541  int ret = select(maxfd + 1, &rfds, nullptr, &efds, &tv);
542  if (ret == -1)
543  {
544  LERROR("Select error");
545  if (errno == EINTR) return gotsome; // interrupted; will try again next time...
546  }
547  else if (ret > 0)
548  {
549  for (size_t i = 0; i < NELEMS(itsFd); ++i)
550  {
551  // Only consider the fds that we monitor:
552  if (itsFd[i] == -1) continue;
553 
554  // If error flag is set, terminate this device:
555  if (FD_ISSET(itsFd[i], &efds)) { removeDevice(i); continue; }
556 
557  // If ready to read, let's read:
558  if (FD_ISSET(itsFd[i], &rfds))
559  {
560  static struct input_event events[32];
561  ssize_t len = read(itsFd[i], events, sizeof(events));
562 
563  if (len == -1)
564  {
565  // Drop this device (unplugged?) if true error:
566  if (errno != EAGAIN && errno != EWOULDBLOCK) removeDevice(i);
567  continue;
568  }
569  else if (len > 0)
570  {
571  len /= sizeof(events[0]);
572  gotsome = true; // we received some events, let caller know
573 
574  for (ssize_t j = 0; j < len; ++j)
575  {
576  unsigned int const code = events[j].code;
577  int const val = events[j].value;
578 
579  switch (events[j].type)
580  {
581  // --------------------------------------------------
582  case EV_KEY:
583  // A keyboard or mouse button event: First check for mouse button. To not miss click-then-release within
584  // one video frame, we here just keep track of whether a button press or release event occurred:
585  if (code >= BTN_MOUSE && code < BTN_MOUSE + NELEMS(itsMouseButton))
586  {
587  if (val) mouse_pressed[code - BTN_MOUSE] = true;
588  else mouse_released[code - BTN_MOUSE] = true;
589  continue;
590  }
591 
592  // Now check keyboard keys:
593  if (code == KEY_ESC)
594  {
595  shouldclose = true;
596 
597  // Terminate on ESC
598  /*
599  if (itsConsoleFd >= 0) jevois::unMuteKeyboard(itsTTY, itsKBmode);
600  if (itsTTY >= 0) ioctl(itsConsoleFd, KDSETMODE, KD_TEXT);
601  std::terminate();
602  */
603  }
604 
605  // Any other key: send to ImGui
606  io.KeysDown[code] = (val != 0);
607  if (translate_event(&events[j], &itsInputState, itsInputBuffer, sizeof(itsInputBuffer), itsCharKeys,
608  itsShiftKeys, itsAltgrKeys, itsFuncKeys) > 0)
609  io.AddInputCharactersUTF8(itsInputBuffer);
610 
611  //io.KeyAltgr = (itsInputState.altgr != 0); // not supported by ImGui?
612  io.KeyAlt = (itsInputState.alt != 0);
613  io.KeyShift = (itsInputState.shift != 0);
614  io.KeyCtrl = (itsInputState.ctrl != 0);
615  io.KeySuper = (itsInputState.meta != 0);
616 
617  break;
618 
619  // --------------------------------------------------
620  case EV_ABS:
621  // An absolute position event:
622  switch (code)
623  {
624  case ABS_X: itsMouseX = val; break;
625  case ABS_Y: itsMouseY = val; break;
626  }
627  break;
628 
629  // --------------------------------------------------
630  case EV_REL:
631  // Relative mouse movement:
632  switch (code)
633  {
634  case REL_X:
635  itsMouseX += val;
636  if (itsMouseX < 0) itsMouseX = 0; else if (itsMouseX >= int(itsWidth)) itsMouseX = itsWidth - 1;
637  break;
638 
639  case REL_Y:
640  itsMouseY += val;
641  if (itsMouseY < 0) itsMouseY = 0; else if (itsMouseY >= int(itsHeight)) itsMouseY = itsHeight - 1;
642  break;
643 
644  case REL_WHEEL:
645  io.MouseWheel += val;
646  break;
647 
648  case REL_HWHEEL:
649  io.MouseWheelH += val;
650  break;
651  }
652  break;
653  }
654  }
655  }
656  }
657  }
658  }
659 
660  // Tell imgui about mouse position and buttons now:
661  for (size_t i = 0; i < NELEMS(itsMouseButton); ++i)
662  {
663  // If a button was pressed, say so, unless a button was released and not also pressed:
664  if (mouse_pressed[i]) io.MouseDown[i] = true;
665  else if (mouse_released[i]) io.MouseDown[i] = false;
666  }
667 
668  io.MousePos = ImVec2(float(itsMouseX), float(itsMouseY));
669 
670  return gotsome;
671 }
672 
673 // ##############################################################################################################
675 {
676  // OpenGL new frame:
677  ImGui_ImplOpenGL3_NewFrame(); // initializes ImGui objects on first frame
678  jevois::VideoDisplayBackendMALI::newFrame(); // Clear OpenGL display
679 
680  // Backend new frame:
681  ImGuiIO & io = ImGui::GetIO();
682  if (io.Fonts->IsBuilt() == false) LERROR("Font atlas not built -- IGNORED");
683 
684  // Update imgui displaysize and scale:
685  io.DisplaySize = ImVec2(float(itsWidth), float(itsHeight));
686  if (itsWidth > 0 && itsHeight > 0) io.DisplayFramebufferScale = ImVec2(1.0F, 1.0F);
687 
688  // Update io.DeltaTime with our rendering frame period:
689  auto const now = std::chrono::steady_clock::now();
690  std::chrono::duration<double> const dur = now - itsLastNewFrameTime;
691  io.DeltaTime = dur.count();
692  itsLastNewFrameTime = now;
693 
694  // Imgui new frame:
695  ImGui::NewFrame();
696 }
697 
698 // ##############################################################################################################
700 {
701  ImGui::Render(); // Draw ImGui stuff
702  ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); // Render to OpenGL
703  jevois::VideoDisplayBackendMALI::render(); // OpenGL swapbuffers
704 }
705 
706 // ##############################################################################################################
707 void jevois::ImGuiBackendMALI::addDevice(size_t num, int fd)
708 {
709  if (num >= NELEMS(itsFd)) LFATAL("Invalid device number " << num);
710  if (itsFd[num] != -1) { LERROR("Invalid device number " << num << ": already open -- IGNORED"); return; }
711  itsFd[num] = fd; LINFO("Registered new input device " << fd << ": /dev/input/event" << num);
712 }
713 
714 // ##############################################################################################################
716 {
717  if (num >= NELEMS(itsFd)) LFATAL("Invalid device number " << num);
718  if (itsFd[num] == -1) { LERROR("Invalid device number " << num << ": not open -- IGNORED"); return; }
719  close(itsFd[num]);
720  LINFO("Un-registered input device " << itsFd[num]);
721  itsFd[num] = -1;
722 }
723 
724 // ##############################################################################################################
726 {
727  // FIXME: do not even try to open event devices 0 and 1 as those flow down our framerate...
728  for (size_t i = 2; i < NELEMS(itsFd); ++i)
729  {
730  if (itsFd[i] == -1)
731  {
732  // We do not have this device currently under our watch; see if a new keyboard/mouse was plugged in:
733  std::string const devname = "/dev/input/event" + std::to_string(i);
734 
735  int fd = open(devname.c_str(), O_RDONLY | O_NONBLOCK);
736  if (fd > 0) { if (jevois::isInputDevice(fd)) addDevice(i, fd); else close(fd); }
737  }
738  else if (jevois::isInputDevice(itsFd[i]) == false)
739  removeDevice(i); // We had this device, but it likely got unplugged:
740  }
741 }
742 
743 #endif // JEVOIS_PLATFORM_PRO
jevois::VideoDisplayBackend::newFrame
virtual void newFrame()
Start a new frame and clear the window/framebuffer.
Definition: VideoDisplayBackend.C:224
jevois::VideoDisplayBackend::render
virtual void render()
Render the VideoDisplay graphics.
Definition: VideoDisplayBackend.C:237
input_event_state::ctrl
int ctrl
Definition: ImGuiBackendMALI.H:40
jevois::ImGuiBackendMALI::itsFd
int itsFd[32]
Definition: ImGuiBackendMALI.H:114
jevois::unMuteKeyboard
void unMuteKeyboard(int tty, int kb_mode)
Restore the keyboard mode for given tty.
Definition: Console.C:114
jevois::split
std::vector< std::string > split(std::string const &input, std::string const &regex="\\s+")
Split string into vector of tokens using a regex to specify what to split on; default regex splits by...
Definition: Utils.C:258
jevois::ImGuiBackendMALI::scanDevices
void scanDevices()
Definition: ImGuiBackendMALI.C:725
jevois::ImGuiBackendMALI::render
virtual void render() override
Render the ImGui graphics.
Definition: ImGuiBackendMALI.C:699
EV_REPEAT
#define EV_REPEAT
Definition: ImGuiBackendMALI.C:43
jevois::VideoDisplayBackendMALI::init
virtual void init(unsigned short w, unsigned short h, bool fullscreen=false) override
Initialize the underlying engine that will process events, create windows, etc.
Definition: VideoDisplayBackendMALI.C:35
jevois::VideoDisplayBackendMALI
Backend for VideoDisplay on JeVois-Pro host using MALI.
Definition: VideoDisplayBackendMALI.H:29
jevois::ImGuiBackendMALI::pollEvents
virtual bool pollEvents(bool &shouldclose) override
Poll events such as mouse movements, buttons, keyboard, joystick, and pass to ImGui.
Definition: ImGuiBackendMALI.C:509
jevois::ImGuiBackendMALI::addDevice
void addDevice(size_t num, int fd)
Definition: ImGuiBackendMALI.C:707
Console.H
LERROR
#define LERROR(msg)
Convenience macro for users to print out console or syslog messages, ERROR level.
Definition: Log.H:211
OpenGL.H
jevois::getActiveTTY
int getActiveTTY()
Get current active tty.
Definition: Console.C:121
jevois
Definition: Concepts.dox:1
F
float F
Definition: GUIhelper.C:1968
input_event_state::shift
int shift
Definition: ImGuiBackendMALI.H:39
Log.H
jevois::isInputDevice
bool isInputDevice(int fd)
Indicate whether this fd (which should be from /dev/input/eventX) is keyboard, mouse,...
Definition: Console.C:36
EV_MAKE
#define EV_MAKE
Definition: ImGuiBackendMALI.C:41
jevois::ImGuiBackendMALI::itsAltgrKeys
std::array< wchar_t, 49 > itsAltgrKeys
Definition: ImGuiBackendMALI.H:127
input_event_state::alt
int alt
Definition: ImGuiBackendMALI.H:38
jevois::warnAndIgnoreException
std::string warnAndIgnoreException(std::string const &prefix="")
Convenience function to catch an exception, issue some LERROR (depending on type),...
Definition: Log.C:236
jevois::muteKeyboard
void muteKeyboard(int tty, int &kb_mode)
Prevent keystrokes from reaching the tty.
Definition: Console.C:103
L
#define L(i, x, y, z)
Definition: GUIhelper.C:1969
LFATAL
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
EV_BREAK
#define EV_BREAK
Definition: ImGuiBackendMALI.C:42
jevois::ImGuiBackendMALI::itsShiftKeys
std::array< wchar_t, 49 > itsShiftKeys
Definition: ImGuiBackendMALI.H:126
jevois::ImGuiBackendMALI::removeDevice
void removeDevice(size_t num)
Definition: ImGuiBackendMALI.C:715
jevois::ImGuiBackendMALI::init
virtual void init(unsigned short w, unsigned short h, bool fullscreen=false, float scale=1.0f, bool conslock=true)
Initialize the underlying engine that will process events, create windows, etc.
Definition: ImGuiBackendMALI.C:399
jevois::to_string
std::string to_string(T const &val)
Convert from type to string.
NELEMS
#define NELEMS(x)
Definition: ImGuiBackendMALI.C:45
input_event_state::altgr
int altgr
Definition: ImGuiBackendMALI.H:37
jevois::ImGuiBackendMALI::ImGuiBackendMALI
ImGuiBackendMALI()
Constructor.
Definition: ImGuiBackendMALI.C:368
jevois::ImGuiBackendMALI::~ImGuiBackendMALI
virtual ~ImGuiBackendMALI()
Virtual destructor for safe inheritance, free resources.
Definition: ImGuiBackendMALI.C:379
Utils.H
jevois::ImGuiBackendMALI::itsCharKeys
std::array< wchar_t, 49 > itsCharKeys
Definition: ImGuiBackendMALI.H:125
h
int h
Definition: GUIhelper.C:1968
input_event_state::meta
int meta
Definition: ImGuiBackendMALI.H:41
input_event_state
Helper struct to key track of key states.
Definition: ImGuiBackendMALI.H:35
jevois::getConsoleFd
int getConsoleFd()
Get a file descriptor to the console.
Definition: Console.C:85
jevois::ImGuiBackendMALI::itsMouseButton
bool itsMouseButton[5]
Definition: ImGuiBackendMALI.H:119
jevois::ImGuiBackendMALI::newFrame
virtual void newFrame() override
Start a new frame and clear the window/framebuffer.
Definition: ImGuiBackendMALI.C:674
ImGuiBackendMALI.H
LINFO
#define LINFO(msg)
Convenience macro for users to print out console or syslog messages, INFO level.
Definition: Log.H:194