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