JeVois  1.21
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Loading...
Searching...
No Matches
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
47namespace
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// ##############################################################################################################
399void 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// ##############################################################################################################
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) 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// ##############################################################################################################
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
int h
Definition GUIhelper.C:2491
#define L(i, x, y, z)
Definition GUIhelper.C:2492
#define NELEMS(x)
#define EV_BREAK
#define EV_REPEAT
#define EV_MAKE
std::array< wchar_t, 49 > itsShiftKeys
void addDevice(size_t num, int fd)
void removeDevice(size_t num)
std::array< wchar_t, 49 > itsCharKeys
virtual bool pollEvents(bool &shouldclose) override
Poll events such as mouse movements, buttons, keyboard, joystick, and pass to ImGui.
virtual ~ImGuiBackendMALI()
Virtual destructor for safe inheritance, free resources.
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.
virtual void render() override
Render the ImGui graphics.
virtual void newFrame() override
Start a new frame and clear the window/framebuffer.
std::array< wchar_t, 49 > itsAltgrKeys
Backend for VideoDisplay on JeVois-Pro host using MALI.
virtual void init(unsigned short w, unsigned short h, bool fullscreen=false) override
Initialize the underlying engine that will process events, create windows, etc.
virtual void newFrame()
Start a new frame and clear the window/framebuffer.
virtual void render()
Render the VideoDisplay graphics.
void muteKeyboard(int tty, int &kb_mode)
Prevent keystrokes from reaching the tty.
Definition Console.C:103
void unMuteKeyboard(int tty, int kb_mode)
Restore the keyboard mode for given tty.
Definition Console.C:114
int getActiveTTY()
Get current active tty.
Definition Console.C:121
int getConsoleFd()
Get a file descriptor to the console.
Definition Console.C:85
bool isInputDevice(int fd)
Indicate whether this fd (which should be from /dev/input/eventX) is keyboard, mouse,...
Definition Console.C:36
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
Definition Log.H:230
std::string warnAndIgnoreException(std::string const &prefix="")
Convenience function to catch an exception, issue some LERROR (depending on type),...
Definition Log.C:236
#define LERROR(msg)
Convenience macro for users to print out console or syslog messages, ERROR level.
Definition Log.H:211
#define LINFO(msg)
Convenience macro for users to print out console or syslog messages, INFO level.
Definition Log.H:194
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:270
Main namespace for all JeVois classes and functions.
Definition Concepts.dox:2