JeVois  1.22
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 ImGuiPlatformIO & platform_io = ImGui::GetPlatformIO();
489 platform_io.Platform_SetClipboardTextFn = ImGui_ImplMALI_SetClipboardText;
490 platform_io.Platform_GetClipboardTextFn = ImGui_ImplMALI_GetClipboardText;
491 platform_io.Platform_ClipboardUserData = nullptr;
492 platform_io.Platform_SetImeDataFn = ImGui_ImplMALI_PlatformSetImeData;
493
494 // Set platform dependent data in viewport
495 static int windowID = 0; // Only one window when using MALI fbdev
496 ImGuiViewport* main_viewport = ImGui::GetMainViewport();
497 main_viewport->PlatformHandle = (void*)(intptr_t)windowID;
498 main_viewport->PlatformHandleRaw = nullptr;
499
500 // Tell imgui to draw a mouse cursor (false at init time):
501 io.MouseDrawCursor = false;
502
503 // Setup Dear ImGui style:
504 ImGui::StyleColorsDark();
505 io.FontGlobalScale = scale;
506 ImGui::GetStyle().ScaleAllSizes(scale);
507
508 // Setup renderer backend:
509 ImGui_ImplOpenGL3_Init("#version 300 es");
510
511 // Load Fonts
512 // - 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.
513 // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
514 // - 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).
515 // - 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.
516 // - Read 'docs/FONTS.md' for more instructions and details.
517 // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
518 //io.Fonts->AddFontDefault();
519 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
520 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
521 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
522 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f);
523 //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese());
524 //IM_ASSERT(font != NULL);
525
526 // Do an initial scan for input event devices:
527 //scanDevices();
528
529 // Weird ID conflict with column separators in GUIhelper::drawParameters() reported by ImGui, disable the warning:
530 io.ConfigDebugHighlightIdConflicts = false;
531
532 itsInitialized = true; // will be used to close ImGui if needed
533}
534
535// ##############################################################################################################
537{
538 ImGuiIO & io = ImGui::GetIO();
539 bool gotsome = false;
540
541 // Rescan our devices once in a while; could use udev instead
542 static int count = 0;
543 if (++count == 100) { scanDevices(); count = 0; }
544
545 // Mouse button detection: we want to make sure we do not miss click-then-release within a video frame, while at ths
546 // same time detecting releases from drag:
547 bool mouse_pressed[5] = { false, false, false, false, false };
548 bool mouse_released[5] = { false, false, false, false, false };
549
550 // Check all our input devices in one select() call:
551 static fd_set rfds; // ready to read
552 static fd_set efds; // has error
553 static struct timeval tv; // timeout
554
555 FD_ZERO(&rfds); FD_ZERO(&efds);
556 tv.tv_sec = 0; tv.tv_usec = 5;
557 int maxfd = -1;
558
559 for (size_t i = 0; i < NELEMS(itsFd); ++i)
560 if (itsFd[i] != -1)
561 {
562 FD_SET(itsFd[i], &rfds);
563 FD_SET(itsFd[i], &efds);
564 maxfd = std::max(maxfd, itsFd[i]);
565 }
566 if (maxfd <= 0) return gotsome; // Nothing to check
567
568 int ret = select(maxfd + 1, &rfds, nullptr, &efds, &tv);
569 if (ret == -1)
570 {
571 LERROR("Select error");
572 if (errno == EINTR) return gotsome; // interrupted; will try again next time...
573 }
574 else if (ret > 0)
575 {
576 for (size_t i = 0; i < NELEMS(itsFd); ++i)
577 {
578 // Only consider the fds that we monitor:
579 if (itsFd[i] == -1) continue;
580
581 // If error flag is set, terminate this device:
582 if (FD_ISSET(itsFd[i], &efds)) { removeDevice(i); continue; }
583
584 // If ready to read, let's read:
585 if (FD_ISSET(itsFd[i], &rfds))
586 {
587 static struct input_event events[32];
588 ssize_t len = read(itsFd[i], events, sizeof(events));
589
590 if (len == -1)
591 {
592 // Drop this device (unplugged?) if true error:
593 if (errno != EAGAIN) removeDevice(i);
594 continue;
595 }
596 else if (len > 0)
597 {
598 len /= sizeof(events[0]);
599 gotsome = true; // we received some events, let caller know
600
601 for (ssize_t j = 0; j < len; ++j)
602 {
603 unsigned int const code = events[j].code;
604 int const val = events[j].value;
605
606 switch (events[j].type)
607 {
608 // --------------------------------------------------
609 case EV_KEY:
610 // A keyboard or mouse button event: First check for mouse button. To not miss click-then-release within
611 // one video frame, we here just keep track of whether a button press or release event occurred:
612 if (code >= BTN_MOUSE && code < BTN_MOUSE + NELEMS(itsMouseButton))
613 {
614 if (val) mouse_pressed[code - BTN_MOUSE] = true;
615 else mouse_released[code - BTN_MOUSE] = true;
616 continue;
617 }
618
619 // Now check keyboard keys:
620 if (code == KEY_ESC)
621 {
622 shouldclose = true;
623
624 // Terminate on ESC
625 /*
626 if (itsConsoleFd >= 0) jevois::unMuteKeyboard(itsTTY, itsKBmode);
627 if (itsTTY >= 0) ioctl(itsConsoleFd, KDSETMODE, KD_TEXT);
628 std::terminate();
629 */
630 }
631
632 // Any other key: send to ImGui
633 io.KeysDown[code] = (val != 0);
634 if (translate_event(&events[j], &itsInputState, itsInputBuffer, sizeof(itsInputBuffer), itsCharKeys,
635 itsShiftKeys, itsAltgrKeys, itsFuncKeys) > 0)
636 io.AddInputCharactersUTF8(itsInputBuffer);
637
638 //io.KeyAltgr = (itsInputState.altgr != 0); // not supported by ImGui?
639 io.KeyAlt = (itsInputState.alt != 0);
640 io.KeyShift = (itsInputState.shift != 0);
641 io.KeyCtrl = (itsInputState.ctrl != 0);
642 io.KeySuper = (itsInputState.meta != 0);
643
644 break;
645
646 // --------------------------------------------------
647 case EV_ABS:
648 // An absolute position event:
649 switch (code)
650 {
651 case ABS_X: itsMouseX = val; break;
652 case ABS_Y: itsMouseY = val; break;
653 }
654 break;
655
656 // --------------------------------------------------
657 case EV_REL:
658 // Relative mouse movement:
659 switch (code)
660 {
661 case REL_X:
662 itsMouseX += val;
663 if (itsMouseX < 0) itsMouseX = 0; else if (itsMouseX >= int(itsWidth)) itsMouseX = itsWidth - 1;
664 break;
665
666 case REL_Y:
667 itsMouseY += val;
668 if (itsMouseY < 0) itsMouseY = 0; else if (itsMouseY >= int(itsHeight)) itsMouseY = itsHeight - 1;
669 break;
670
671 case REL_WHEEL:
672 io.MouseWheel += val;
673 break;
674
675 case REL_HWHEEL:
676 io.MouseWheelH += val;
677 break;
678 }
679 break;
680 }
681 }
682 }
683 }
684 }
685 }
686
687 // Tell imgui about mouse position and buttons now:
688 for (size_t i = 0; i < NELEMS(itsMouseButton); ++i)
689 {
690 // If a button was pressed, say so, unless a button was released and not also pressed:
691 if (mouse_pressed[i]) io.MouseDown[i] = true;
692 else if (mouse_released[i]) io.MouseDown[i] = false;
693 }
694
695 io.MousePos = ImVec2(float(itsMouseX), float(itsMouseY));
696
697 return gotsome;
698}
699
700// ##############################################################################################################
702{
703 // OpenGL new frame:
704 ImGui_ImplOpenGL3_NewFrame(); // initializes ImGui objects on first frame
705 jevois::VideoDisplayBackendMALI::newFrame(); // Clear OpenGL display
706
707 // Backend new frame:
708 ImGuiIO & io = ImGui::GetIO();
709 if (io.Fonts->IsBuilt() == false) LERROR("Font atlas not built -- IGNORED");
710
711 // Update imgui displaysize and scale:
712 io.DisplaySize = ImVec2(float(itsWidth), float(itsHeight));
713 if (itsWidth > 0 && itsHeight > 0) io.DisplayFramebufferScale = ImVec2(1.0F, 1.0F);
714
715 // Update io.DeltaTime with our rendering frame period:
716 auto const now = std::chrono::steady_clock::now();
717 std::chrono::duration<double> const dur = now - itsLastNewFrameTime;
718 io.DeltaTime = dur.count();
719 itsLastNewFrameTime = now;
720
721 // Imgui new frame:
722 ImGui::NewFrame();
723}
724
725// ##############################################################################################################
727{
728 ImGui::Render(); // Draw ImGui stuff
729 ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); // Render to OpenGL
730 jevois::VideoDisplayBackendMALI::render(); // OpenGL swapbuffers
731}
732
733// ##############################################################################################################
735{
736 if (num >= NELEMS(itsFd)) LFATAL("Invalid device number " << num);
737 if (itsFd[num] != -1) { LERROR("Invalid device number " << num << ": already open -- IGNORED"); return; }
738 itsFd[num] = fd; LINFO("Registered new input device " << fd << ": /dev/input/event" << num);
739}
740
741// ##############################################################################################################
743{
744 if (num >= NELEMS(itsFd)) LFATAL("Invalid device number " << num);
745 if (itsFd[num] == -1) { LERROR("Invalid device number " << num << ": not open -- IGNORED"); return; }
746 close(itsFd[num]);
747 LINFO("Un-registered input device " << itsFd[num]);
748 itsFd[num] = -1;
749}
750
751// ##############################################################################################################
753{
754 // FIXME: do not even try to open event devices 0 and 1 as those flow down our framerate...
755 for (size_t i = 2; i < NELEMS(itsFd); ++i)
756 {
757 if (itsFd[i] == -1)
758 {
759 // We do not have this device currently under our watch; see if a new keyboard/mouse was plugged in:
760 std::string const devname = "/dev/input/event" + std::to_string(i);
761
762 int fd = open(devname.c_str(), O_RDONLY | O_NONBLOCK);
763 if (fd > 0) { if (jevois::isInputDevice(fd)) addDevice(i, fd); else close(fd); }
764 }
765 else if (jevois::isInputDevice(itsFd[i]) == false)
766 removeDevice(i); // We had this device, but it likely got unplugged:
767 }
768}
769
770#endif // JEVOIS_PLATFORM_PRO
int h
Definition GUIhelper.C:2520
#define L(i, x, y, z)
Definition GUIhelper.C:2521
#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