FabGL
ESP32 Display Controller and Graphics Library
keyboard.cpp
1 /*
2  Created by Fabrizio Di Vittorio (fdivitto2013@gmail.com) - <http://www.fabgl.com>
3  Copyright (c) 2019-2021 Fabrizio Di Vittorio.
4  All rights reserved.
5 
6 
7 * Please contact fdivitto2013@gmail.com if you need a commercial license.
8 
9 
10 * This library and related software is available under GPL v3. Feel free to use FabGL in free software and hardware:
11 
12  FabGL is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  FabGL is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with FabGL. If not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 
27 
28 #include <string.h>
29 
30 #include "freertos/FreeRTOS.h"
31 #include "freertos/task.h"
32 #include "freertos/timers.h"
33 #include "freertos/queue.h"
34 
35 #include "keyboard.h"
36 
37 
38 #pragma GCC optimize ("O2")
39 
40 
41 namespace fabgl {
42 
43 
44 
45 
47 
48 
49 
50 Keyboard::Keyboard()
51  : m_keyboardAvailable(false),
52  m_scancodeSet(2),
53  m_lastDeadKey(VK_NONE),
54  m_codepage(nullptr)
55 {
56 }
57 
58 
59 Keyboard::~Keyboard()
60 {
61  PS2DeviceLock lock(this);
62  if (m_SCodeToVKConverterTask)
63  vTaskDelete(m_SCodeToVKConverterTask);
64  if (m_virtualKeyQueue)
65  vQueueDelete(m_virtualKeyQueue);
66 }
67 
68 
69 void Keyboard::begin(bool generateVirtualKeys, bool createVKQueue, int PS2Port)
70 {
71  PS2Device::begin(PS2Port);
72 
73  m_CTRL = false;
74  m_LALT = false;
75  m_RALT = false;
76  m_SHIFT = false;
77  m_CAPSLOCK = false;
78  m_GUI = false;
79  m_NUMLOCK = false;
80  m_SCROLLLOCK = false;
81 
82  m_numLockLED = false;
83  m_capsLockLED = false;
84  m_scrollLockLED = false;
85 
86  m_SCodeToVKConverterTask = nullptr;
87  m_virtualKeyQueue = nullptr;
88 
89  m_uiApp = nullptr;
90 
91  reset();
92 
93  if (generateVirtualKeys || createVKQueue) {
94  if (createVKQueue)
95  m_virtualKeyQueue = xQueueCreate(FABGLIB_KEYBOARD_VIRTUALKEY_QUEUE_SIZE, sizeof(VirtualKeyItem));
96  xTaskCreate(&SCodeToVKConverterTask, "", Keyboard::scancodeToVirtualKeyTaskStackSize, this, FABGLIB_SCODETOVK_TASK_PRIORITY, &m_SCodeToVKConverterTask);
97  }
98 }
99 
100 
101 void Keyboard::begin(gpio_num_t clkGPIO, gpio_num_t dataGPIO, bool generateVirtualKeys, bool createVKQueue)
102 {
103  PS2Controller::begin(clkGPIO, dataGPIO);
104  PS2Controller::setKeyboard(this);
105  begin(generateVirtualKeys, createVKQueue, 0);
106 }
107 
108 
109 // reset keyboard, set scancode 2 and US layout
111 {
112  memset(m_VKMap, 0, sizeof(m_VKMap));
113 
114  // sets default layout
115  setLayout(&USLayout);
116 
117  // 350ms keyboard poweron delay (look at NXP M68HC08 designer reference manual)
118  vTaskDelay(350 / portTICK_PERIOD_MS);
119 
120  // tries up to three times to reset keyboard
121  for (int i = 0; i < 3; ++i) {
122  m_keyboardAvailable = send_cmdReset();
123  if (m_keyboardAvailable)
124  break;
125  vTaskDelay(350 / portTICK_PERIOD_MS);
126  }
127  // give the time to the device to be fully initialized
128  vTaskDelay(200 / portTICK_PERIOD_MS);
129 
130  send_cmdSetScancodeSet(2);
131 
132  return m_keyboardAvailable;
133 }
134 
135 
137 {
138  if (m_SCodeToVKConverterTask) {
139  // virtual keys enabled, just 1 and 2 are allowed
140  if (value == 1 || value == 2) {
141  m_scancodeSet = value;
142  return true;
143  }
144  } else {
145  // no virtual keys enabled, just try to tell keyboard which set we need
146  if (send_cmdSetScancodeSet(value)) {
147  m_scancodeSet = value;
148  return true;
149  }
150  }
151  return false;
152 }
153 
154 
155 bool Keyboard::setLEDs(bool numLock, bool capsLock, bool scrollLock)
156 {
157  m_numLockLED = numLock;
158  m_capsLockLED = capsLock;
159  m_scrollLockLED = scrollLock;
160  return send_cmdLEDs(numLock, capsLock, scrollLock);
161 }
162 
163 
164 void Keyboard::getLEDs(bool * numLock, bool * capsLock, bool * scrollLock)
165 {
166  *numLock = m_numLockLED;
167  *capsLock = m_capsLockLED;
168  *scrollLock = m_scrollLockLED;
169 }
170 
171 
172 void Keyboard::updateLEDs()
173 {
174  send_cmdLEDs(m_NUMLOCK, m_CAPSLOCK, m_SCROLLLOCK);
175  m_numLockLED = m_NUMLOCK;
176  m_capsLockLED = m_CAPSLOCK;
177  m_scrollLockLED = m_SCROLLLOCK;
178 }
179 
180 
182 {
183  return dataAvailable();
184 }
185 
186 
187 int Keyboard::getNextScancode(int timeOutMS, bool requestResendOnTimeOut)
188 {
189  while (true) {
190  int r = getData(timeOutMS);
191  if (r == -1 && CLKTimeOutError()) {
192  // try to recover a stall sending a re-enable scanning command
193  send_cmdEnableScanning();
194  }
195  if (r == -1 && requestResendOnTimeOut) {
196  requestToResendLastByte();
197  continue;
198  }
199  return r;
200  }
201 }
202 
203 
205 {
206  m_layout = layout;
207 }
208 
209 
210 #if FABGLIB_HAS_VirtualKeyO_STRING
211 char const * Keyboard::virtualKeyToString(VirtualKey virtualKey)
212 {
213  char const * VKTOSTR[] = { "VK_NONE", "VK_SPACE", "VK_0", "VK_1", "VK_2", "VK_3", "VK_4", "VK_5", "VK_6", "VK_7", "VK_8", "VK_9", "VK_KP_0", "VK_KP_1", "VK_KP_2",
214  "VK_KP_3", "VK_KP_4", "VK_KP_5", "VK_KP_6", "VK_KP_7", "VK_KP_8", "VK_KP_9", "VK_a", "VK_b", "VK_c", "VK_d", "VK_e", "VK_f", "VK_g", "VK_h",
215  "VK_i", "VK_j", "VK_k", "VK_l", "VK_m", "VK_n", "VK_o", "VK_p", "VK_q", "VK_r", "VK_s", "VK_t", "VK_u", "VK_v", "VK_w", "VK_x", "VK_y", "VK_z",
216  "VK_A", "VK_B", "VK_C", "VK_D", "VK_E", "VK_F", "VK_G", "VK_H", "VK_I", "VK_J", "VK_K", "VK_L", "VK_M", "VK_N", "VK_O", "VK_P", "VK_Q", "VK_R",
217  "VK_S", "VK_T", "VK_U", "VK_V", "VK_W", "VK_X", "VK_Y", "VK_Z", "VK_GRAVEACCENT", "VK_ACUTEACCENT", "VK_QUOTE", "VK_QUOTEDBL", "VK_EQUALS", "VK_MINUS", "VK_KP_MINUS",
218  "VK_PLUS", "VK_KP_PLUS", "VK_KP_MULTIPLY", "VK_ASTERISK", "VK_BACKSLASH", "VK_KP_DIVIDE", "VK_SLASH", "VK_KP_PERIOD", "VK_PERIOD", "VK_COLON",
219  "VK_COMMA", "VK_SEMICOLON", "VK_AMPERSAND", "VK_VERTICALBAR", "VK_HASH", "VK_AT", "VK_CARET", "VK_DOLLAR", "VK_POUND", "VK_EURO", "VK_PERCENT",
220  "VK_EXCLAIM", "VK_QUESTION", "VK_LEFTBRACE", "VK_RIGHTBRACE", "VK_LEFTBRACKET", "VK_RIGHTBRACKET", "VK_LEFTPAREN", "VK_RIGHTPAREN", "VK_LESS",
221  "VK_GREATER", "VK_UNDERSCORE", "VK_DEGREE", "VK_SECTION", "VK_TILDE", "VK_NEGATION", "VK_LSHIFT", "VK_RSHIFT", "VK_LALT", "VK_RALT", "VK_LCTRL", "VK_RCTRL",
222  "VK_LGUI", "VK_RGUI", "VK_ESCAPE", "VK_PRINTSCREEN", "VK_SYSREQ", "VK_INSERT", "VK_KP_INSERT", "VK_DELETE", "VK_KP_DELETE", "VK_BACKSPACE", "VK_HOME", "VK_KP_HOME", "VK_END", "VK_KP_END", "VK_PAUSE", "VK_BREAK",
223  "VK_SCROLLLOCK", "VK_NUMLOCK", "VK_CAPSLOCK", "VK_TAB", "VK_RETURN", "VK_KP_ENTER", "VK_APPLICATION", "VK_PAGEUP", "VK_KP_PAGEUP", "VK_PAGEDOWN", "VK_KP_PAGEDOWN", "VK_UP", "VK_KP_UP",
224  "VK_DOWN", "VK_KP_DOWN", "VK_LEFT", "VK_KP_LEFT", "VK_RIGHT", "VK_KP_RIGHT", "VK_KP_CENTER", "VK_F1", "VK_F2", "VK_F3", "VK_F4", "VK_F5", "VK_F6", "VK_F7", "VK_F8", "VK_F9", "VK_F10", "VK_F11", "VK_F12",
225  "VK_GRAVE_a", "VK_GRAVE_e", "VK_ACUTE_e", "VK_GRAVE_i", "VK_GRAVE_o", "VK_GRAVE_u", "VK_CEDILLA_c", "VK_ESZETT", "VK_UMLAUT_u",
226  "VK_UMLAUT_o", "VK_UMLAUT_a", "VK_CEDILLA_C", "VK_TILDE_n", "VK_TILDE_N", "VK_UPPER_a", "VK_ACUTE_a", "VK_ACUTE_i", "VK_ACUTE_o", "VK_ACUTE_u", "VK_UMLAUT_i", "VK_EXCLAIM_INV", "VK_QUESTION_INV",
227  "VK_ACUTE_A","VK_ACUTE_E","VK_ACUTE_I","VK_ACUTE_O","VK_ACUTE_U", "VK_GRAVE_A","VK_GRAVE_E","VK_GRAVE_I","VK_GRAVE_O","VK_GRAVE_U", "VK_INTERPUNCT", "VK_DIAERESIS",
228  "VK_UMLAUT_e", "VK_UMLAUT_A", "VK_UMLAUT_E", "VK_UMLAUT_I", "VK_UMLAUT_O", "VK_UMLAUT_U", "VK_CARET_a", "VK_CARET_e", "VK_CARET_i", "VK_CARET_o", "VK_CARET_u", "VK_CARET_A", "VK_CARET_E",
229  "VK_CARET_I", "VK_CARET_O", "VK_CARET_U", "VK_ASCII",
230  };
231  return VKTOSTR[virtualKey];
232 }
233 #endif
234 
235 
236 // -1 = virtual key cannot be translated to ASCII
238 {
239  VirtualKeyItem item;
240  item.vk = virtualKey;
241  item.down = true;
242  item.CTRL = m_CTRL;
243  item.LALT = m_LALT;
244  item.RALT = m_RALT;
245  item.SHIFT = m_SHIFT;
246  item.GUI = m_GUI;
247  item.CAPSLOCK = m_CAPSLOCK;
248  item.NUMLOCK = m_NUMLOCK;
249  item.SCROLLLOCK = m_SCROLLLOCK;
250  return fabgl::virtualKeyToASCII(item, m_codepage);
251 }
252 
253 
254 VirtualKey Keyboard::scancodeToVK(uint8_t scancode, bool isExtended, KeyboardLayout const * layout)
255 {
256  VirtualKey vk = VK_NONE;
257 
258  if (layout == nullptr)
259  layout = m_layout;
260 
261  VirtualKeyDef const * def = isExtended ? layout->exScancodeToVK : layout->scancodeToVK;
262  for (; def->scancode; ++def)
263  if (def->scancode == scancode) {
264  vk = def->virtualKey;
265  break;
266  }
267 
268  if (vk == VK_NONE && layout->inherited)
269  vk = scancodeToVK(scancode, isExtended, layout->inherited);
270 
271  // manage keypad
272  // NUMLOCK ON, SHIFT OFF => generate VK_KP_number
273  // NUMLOCK ON, SHIFT ON => generate VK_KP_cursor_control (as when NUMLOCK is OFF)
274  // NUMLOCK OFF => generate VK_KP_cursor_control
275  if (m_NUMLOCK & !m_SHIFT) {
276  switch (vk) {
277  case VK_KP_DELETE:
278  vk = VK_KP_PERIOD;
279  break;
280  case VK_KP_INSERT:
281  vk = VK_KP_0;
282  break;
283  case VK_KP_END:
284  vk = VK_KP_1;
285  break;
286  case VK_KP_DOWN:
287  vk = VK_KP_2;
288  break;
289  case VK_KP_PAGEDOWN:
290  vk = VK_KP_3;
291  break;
292  case VK_KP_LEFT:
293  vk = VK_KP_4;
294  break;
295  case VK_KP_CENTER:
296  vk = VK_KP_5;
297  break;
298  case VK_KP_RIGHT:
299  vk = VK_KP_6;
300  break;
301  case VK_KP_HOME:
302  vk = VK_KP_7;
303  break;
304  case VK_KP_UP:
305  vk = VK_KP_8;
306  break;
307  case VK_KP_PAGEUP:
308  vk = VK_KP_9;
309  break;
310  default:
311  break;
312  }
313  }
314 
315  return vk;
316 }
317 
318 
319 VirtualKey Keyboard::manageCAPSLOCK(VirtualKey vk)
320 {
321  if (m_CAPSLOCK) {
322  // inverts letters case
323  if (vk >= VK_a && vk <= VK_z)
324  vk = (VirtualKey)(vk - VK_a + VK_A);
325  else if (vk >= VK_A && vk <= VK_Z)
326  vk = (VirtualKey)(vk - VK_A + VK_a);
327  }
328  return vk;
329 }
330 
331 
332 VirtualKey Keyboard::VKtoAlternateVK(VirtualKey in_vk, bool down, KeyboardLayout const * layout)
333 {
334  VirtualKey vk = VK_NONE;
335 
336  if (layout == nullptr)
337  layout = m_layout;
338 
339  // this avoids releasing a required key when SHIFT has been pressed after the key but before releasing
340  if (!down && isVKDown(in_vk))
341  vk = in_vk;
342 
343  if (vk == VK_NONE) {
344  // handle this case:
345  // - derived KEY up without any SHIFT (because released before the KEY, ie SHIFT+"1" => "!", but you release the SHIFT before "1")
346  // this avoid to maintain a KEY DOWN when you release the SHIFT key before the KEY ()
347  for (AltVirtualKeyDef const * def = layout->alternateVK; def->reqVirtualKey != VK_NONE; ++def) {
348  if (def->reqVirtualKey == in_vk && isVKDown(def->virtualKey)) {
349  vk = def->virtualKey;
350  break;
351  }
352  }
353  }
354 
355  if (vk == VK_NONE) {
356  // handle these cases:
357  // - KEY down with SHIFTs already down
358  // - KEY up with SHIFTs still down
359  for (AltVirtualKeyDef const * def = layout->alternateVK; def->reqVirtualKey != VK_NONE; ++def) {
360  if (def->reqVirtualKey == in_vk && def->ctrl == m_CTRL &&
361  def->lalt == m_LALT &&
362  def->ralt == m_RALT &&
363  def->shift == m_SHIFT) {
364  vk = def->virtualKey;
365  break;
366  }
367  }
368  }
369 
370  if (vk == VK_NONE && layout->inherited)
371  vk = VKtoAlternateVK(in_vk, down, layout->inherited);
372 
373  return vk == VK_NONE ? in_vk : vk;
374 }
375 
376 
377 bool Keyboard::blockingGetVirtualKey(VirtualKeyItem * item)
378 {
379  item->vk = VK_NONE;
380  item->down = true;
381  item->CTRL = m_CTRL;
382  item->LALT = m_LALT;
383  item->RALT = m_RALT;
384  item->SHIFT = m_SHIFT;
385  item->GUI = m_GUI;
386  item->CAPSLOCK = m_CAPSLOCK;
387  item->NUMLOCK = m_NUMLOCK;
388  item->SCROLLLOCK = m_SCROLLLOCK;
389 
390  uint8_t * scode = item->scancode;
391 
392  *scode = getNextScancode();
393  if (*scode == 0xE0) {
394  // two bytes scancode
395  *(++scode) = getNextScancode(100, true);
396  if (*scode == 0xF0) {
397  // two bytes scancode key up
398  *(++scode) = getNextScancode(100, true);
399  item->vk = scancodeToVK(*scode, true);
400  item->down = false;
401  } else {
402  // two bytes scancode key down
403  item->vk = scancodeToVK(*scode, true);
404  }
405  } else if (*scode == 0xE1) {
406  // special case: "PAUSE" : 0xE1, 0x14, 0x77, 0xE1, 0xF0, 0x14, 0xF0, 0x77
407  static const uint8_t PAUSECODES[] = {0x14, 0x77, 0xE1, 0xF0, 0x14, 0xF0, 0x77};
408  for (int i = 0; i < sizeof(PAUSECODES); ++i) {
409  *(++scode) = getNextScancode(100, true);
410  if (*scode != PAUSECODES[i])
411  break;
412  else if (i == sizeof(PAUSECODES) - 1)
413  item->vk = VK_PAUSE;
414  }
415  } else if (*scode == 0xF0) {
416  // one byte scancode, key up
417  *(++scode) = getNextScancode(100, true);
418  item->vk = scancodeToVK(*scode, false);
419  item->down = false;
420  } else {
421  // one byte scancode, key down
422  item->vk = scancodeToVK(*scode, false);
423  }
424 
425  if (item->vk != VK_NONE) {
426 
427  // manage CAPSLOCK
428  item->vk = manageCAPSLOCK(item->vk);
429 
430  // alternate VK (virtualkeys modified by shift, alt, ...)
431  item->vk = VKtoAlternateVK(item->vk, item->down);
432 
433  // update shift, alt, ctrl, capslock, numlock and scrollock states and LEDs
434  switch (item->vk) {
435  case VK_LCTRL:
436  case VK_RCTRL:
437  m_CTRL = item->down;
438  break;
439  case VK_LALT:
440  m_LALT = item->down;
441  break;
442  case VK_RALT:
443  m_RALT = item->down;
444  break;
445  case VK_LSHIFT:
446  case VK_RSHIFT:
447  m_SHIFT = item->down;
448  break;
449  case VK_LGUI:
450  case VK_RGUI:
451  m_GUI = item->down;
452  break;
453  case VK_CAPSLOCK:
454  if (!item->down) {
455  m_CAPSLOCK = !m_CAPSLOCK;
456  updateLEDs();
457  }
458  break;
459  case VK_NUMLOCK:
460  if (!item->down) {
461  m_NUMLOCK = !m_NUMLOCK;
462  updateLEDs();
463  }
464  break;
465  case VK_SCROLLLOCK:
466  if (!item->down) {
467  m_SCROLLLOCK = !m_SCROLLLOCK;
468  updateLEDs();
469  }
470  break;
471  default:
472  break;
473  }
474 
475  }
476 
477  // manage dead keys - Implemented by Carles Oriol (https://github.com/carlesoriol)
478  for (VirtualKey const * dk = m_layout->deadKeysVK; *dk != VK_NONE; ++dk) {
479  if (item->vk == *dk) {
480  m_lastDeadKey = item->vk;
481  item->vk = VK_NONE;
482  }
483  }
484  if (item->vk != m_lastDeadKey && item->vk != VK_NONE) {
485  for (DeadKeyVirtualKeyDef const * dk = m_layout->deadkeysToVK; dk->deadKey != VK_NONE; ++dk) {
486  if (item->vk == dk->reqVirtualKey && m_lastDeadKey == dk->deadKey) {
487  item->vk = dk->virtualKey;
488  break;
489  }
490  }
491  if (!item->down && (item->vk != m_lastDeadKey) && (item->vk != VK_RSHIFT) && (item->vk != VK_LSHIFT))
492  m_lastDeadKey = VK_NONE;
493  }
494 
495  // ending zero to item->scancode
496  if (scode < item->scancode + sizeof(VirtualKeyItem::scancode) - 1)
497  *(++scode) = 0;
498 
499  // fill ASCII field
500  int ascii = fabgl::virtualKeyToASCII(*item, m_codepage);
501  item->ASCII = ascii > -1 ? ascii : 0;
502 
503  return item->vk != VK_NONE;
504 }
505 
506 
507 void Keyboard::injectVirtualKey(VirtualKeyItem const & item, bool insert)
508 {
509  // update m_VKMap
510  if (item.down)
511  m_VKMap[(int)item.vk >> 3] |= 1 << ((int)item.vk & 7);
512  else
513  m_VKMap[(int)item.vk >> 3] &= ~(1 << ((int)item.vk & 7));
514 
515  // has VK queue? Insert VK into it.
516  if (m_virtualKeyQueue) {
517  auto ticksToWait = (m_uiApp ? 0 : portMAX_DELAY); // 0, and not portMAX_DELAY to avoid uiApp locks
518  if (insert)
519  xQueueSendToFront(m_virtualKeyQueue, &item, ticksToWait);
520  else
521  xQueueSendToBack(m_virtualKeyQueue, &item, ticksToWait);
522  }
523 }
524 
525 
526 void Keyboard::injectVirtualKey(VirtualKey virtualKey, bool keyDown, bool insert)
527 {
528  VirtualKeyItem item;
529  item.vk = virtualKey;
530  item.down = keyDown;
531  item.scancode[0] = 0; // this is a manual insert, not scancode associated
532  item.ASCII = virtualKeyToASCII(virtualKey);
533  item.CTRL = m_CTRL;
534  item.LALT = m_LALT;
535  item.RALT = m_RALT;
536  item.SHIFT = m_SHIFT;
537  item.GUI = m_GUI;
538  item.CAPSLOCK = m_CAPSLOCK;
539  item.NUMLOCK = m_NUMLOCK;
540  item.SCROLLLOCK = m_SCROLLLOCK;
541  injectVirtualKey(item, insert);
542 }
543 
544 
545 // inject a virtual key item into virtual key queue calling injectVirtualKey() and into m_uiApp
546 void Keyboard::postVirtualKeyItem(VirtualKeyItem const & item)
547 {
548  // add into m_virtualKeyQueue and update m_VKMap
549  injectVirtualKey(item, false);
550 
551  // need to send events to uiApp?
552  if (m_uiApp) {
553  uiEvent evt = uiEvent(nullptr, item.down ? UIEVT_KEYDOWN : UIEVT_KEYUP);
554  evt.params.key.VK = item.vk;
555  evt.params.key.ASCII = item.ASCII;
556  evt.params.key.LALT = item.LALT;
557  evt.params.key.RALT = item.RALT;
558  evt.params.key.CTRL = item.CTRL;
559  evt.params.key.SHIFT = item.SHIFT;
560  evt.params.key.GUI = item.GUI;
561  m_uiApp->postEvent(&evt);
562  }
563 }
564 
565 
566 // converts keypad virtual key to number (VK_KP_1 = 1, VK_KP_DOWN = 2, etc...)
567 // -1 = no convertible
568 int Keyboard::convKeypadVKToNum(VirtualKey vk)
569 {
570  switch (vk) {
571  case VK_KP_0:
572  case VK_KP_INSERT:
573  return 0;
574  case VK_KP_1:
575  case VK_KP_END:
576  return 1;
577  case VK_KP_2:
578  case VK_KP_DOWN:
579  return 2;
580  case VK_KP_3:
581  case VK_KP_PAGEDOWN:
582  return 3;
583  case VK_KP_4:
584  case VK_KP_LEFT:
585  return 4;
586  case VK_KP_5:
587  case VK_KP_CENTER:
588  return 5;
589  case VK_KP_6:
590  case VK_KP_RIGHT:
591  return 6;
592  case VK_KP_7:
593  case VK_KP_HOME:
594  return 7;
595  case VK_KP_8:
596  case VK_KP_UP:
597  return 8;
598  case VK_KP_9:
599  case VK_KP_PAGEUP:
600  return 9;
601  default:
602  return -1;
603  };
604 }
605 
606 
607 void Keyboard::SCodeToVKConverterTask(void * pvParameters)
608 {
609  Keyboard * keyboard = (Keyboard*) pvParameters;
610 
611  // manage ALT + Keypad num
612  uint8_t ALTNUMValue = 0; // current value (0 = no value, 0 is not allowed)
613 
614  while (true) {
615 
616  VirtualKeyItem item;
617 
618  if (keyboard->blockingGetVirtualKey(&item)) {
619 
620  // onVirtualKey may set item.vk = VK_NONE!
621  keyboard->onVirtualKey(&item.vk, item.down);
622 
623  if (item.vk != VK_NONE) {
624 
625  // manage left-ALT + NUM
626  if (!isALT(item.vk) && keyboard->m_LALT) {
627  // ALT was down, is this a keypad number?
628  int num = convKeypadVKToNum(item.vk);
629  if (num >= 0) {
630  // yes this is a keypad num, if down update ALTNUMValue
631  if (item.down)
632  ALTNUMValue = (ALTNUMValue * 10 + num) & 0xff;
633  } else {
634  // no, back to normal case
635  ALTNUMValue = 0;
636  keyboard->postVirtualKeyItem(item);
637  }
638  } else if (ALTNUMValue > 0 && isALT(item.vk) && !item.down) {
639  // ALT is up and ALTNUMValue contains a valid value, add it
640  keyboard->postVirtualKeyItem(item); // post ALT up
641  item.vk = VK_ASCII;
642  item.down = true;
643  item.scancode[0] = 0;
644  item.ASCII = ALTNUMValue;
645  keyboard->postVirtualKeyItem(item); // ascii key down
646  item.down = false;
647  keyboard->postVirtualKeyItem(item); // ascii key up
648  ALTNUMValue = 0;
649  } else {
650  // normal case
651  keyboard->postVirtualKeyItem(item);
652  }
653 
654  }
655 
656  }
657 
658  }
659 
660 }
661 
662 
664 {
665  bool r = m_VKMap[(int)virtualKey >> 3] & (1 << ((int)virtualKey & 7));
666 
667  // VK_PAUSE is never released (no scancode sent from keyboard on key up), so when queried it is like released
668  if (virtualKey == VK_PAUSE)
669  m_VKMap[(int)virtualKey >> 3] &= ~(1 << ((int)virtualKey & 7));
670 
671  return r;
672 }
673 
674 
675 bool Keyboard::getNextVirtualKey(VirtualKeyItem * item, int timeOutMS)
676 {
677  bool r = (m_SCodeToVKConverterTask && item && xQueueReceive(m_virtualKeyQueue, item, msToTicks(timeOutMS)) == pdTRUE);
678  if (r && m_scancodeSet == 1)
679  convertScancode2to1(item);
680  return r;
681 }
682 
683 
684 VirtualKey Keyboard::getNextVirtualKey(bool * keyDown, int timeOutMS)
685 {
686  VirtualKeyItem item;
687  if (getNextVirtualKey(&item, timeOutMS)) {
688  if (keyDown)
689  *keyDown = item.down;
690  return item.vk;
691  }
692  return VK_NONE;
693 }
694 
695 
697 {
698  return m_virtualKeyQueue ? uxQueueMessagesWaiting(m_virtualKeyQueue) : 0;
699 }
700 
701 
703 {
704  xQueueReset(m_virtualKeyQueue);
705 }
706 
707 
708 void Keyboard::convertScancode2to1(VirtualKeyItem * item)
709 {
710  uint8_t * rpos = item->scancode;
711  uint8_t * wpos = rpos;
712  uint8_t * epos = rpos + sizeof(VirtualKeyItem::scancode);
713  while (*rpos && rpos < epos) {
714  if (*rpos == 0xf0) {
715  ++rpos;
716  *wpos++ = 0x80 | convScancodeSet2To1(*rpos++);
717  } else
718  *wpos++ = convScancodeSet2To1(*rpos++);
719  }
720  if (wpos < epos)
721  *wpos = 0;
722 }
723 
724 
725 uint8_t Keyboard::convScancodeSet2To1(uint8_t code)
726 {
727  // 8042 scancodes set 2 to 1 translation table
728  static const uint8_t S2TOS1[256] = {
729  0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58, 0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59,
730  0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a, 0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b,
731  0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c, 0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d,
732  0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e, 0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f,
733  0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60, 0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61,
734  0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e, 0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76,
735  0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b, 0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f,
736  0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45, 0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54,
737  0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
738  0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
739  0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
740  0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
741  0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
742  0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
743  0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
744  0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
745  };
746  return S2TOS1[code];
747 }
748 
749 
750 } // end of namespace
#define FABGLIB_SCODETOVK_TASK_PRIORITY
Definition: fabglconf.h:98
void injectVirtualKey(VirtualKey virtualKey, bool keyDown, bool insert=false)
Adds or inserts a virtual key into the virtual keys queue.
Definition: keyboard.cpp:526
All in one structure to fully represent a keyboard layout.
Definition: kbdlayouts.h:82
bool lock(int timeOutMS)
Gets exclusive access to the device.
Definition: ps2device.cpp:91
VirtualKey deadKeysVK[8]
Definition: kbdlayouts.h:90
VirtualKey virtualKey
Definition: kbdlayouts.h:55
int virtualKeyToASCII(VirtualKey virtualKey)
Converts virtual key to ASCII.
Definition: keyboard.cpp:237
This file contains fabgl::Keyboard definition.
void emptyVirtualKeyQueue()
Empties the virtual keys queue.
Definition: keyboard.cpp:702
void getLEDs(bool *numLock, bool *capsLock, bool *scrollLock)
Gets keyboard LEDs status.
Definition: keyboard.cpp:164
bool isVKDown(VirtualKey virtualKey)
Gets the virtual keys status.
Definition: keyboard.cpp:663
A struct which contains a virtual key, key state and associated scan code.
Definition: fabutils.h:1284
#define FABGLIB_DEFAULT_SCODETOVK_TASK_STACK_SIZE
Definition: fabglconf.h:94
VirtualKey
Represents each possible real or derived (SHIFT + real) key.
Definition: fabutils.h:1016
bool setLEDs(bool numLock, bool capsLock, bool scrollLock)
Sets keyboard LEDs status.
Definition: keyboard.cpp:155
VirtualKeyDef exScancodeToVK[22]
Definition: kbdlayouts.h:87
void begin(gpio_num_t clkGPIO, gpio_num_t dataGPIO, bool generateVirtualKeys=true, bool createVKQueue=true)
Initializes Keyboard specifying CLOCK and DATA GPIOs.
Definition: keyboard.cpp:101
Definition: canvas.cpp:36
VirtualKey getNextVirtualKey(bool *keyDown=nullptr, int timeOutMS=-1)
Gets a virtual key from the queue.
Definition: keyboard.cpp:684
int virtualKeyAvailable()
Gets the number of virtual keys available in the queue.
Definition: keyboard.cpp:696
Associates scancode to virtualkey.
Definition: kbdlayouts.h:53
uint8_t scancode[8]
Definition: fabutils.h:1287
bool reset()
Sends a Reset command to the keyboard.
Definition: keyboard.cpp:110
int getNextScancode(int timeOutMS=-1, bool requestResendOnTimeOut=false)
Gets a scancode from the queue.
Definition: keyboard.cpp:187
DeadKeyVirtualKeyDef deadkeysToVK[60]
Definition: kbdlayouts.h:91
#define FABGLIB_KEYBOARD_VIRTUALKEY_QUEUE_SIZE
Definition: fabglconf.h:130
int scancodeAvailable()
Gets the number of scancodes available in the queue.
Definition: keyboard.cpp:181
static int scancodeToVirtualKeyTaskStackSize
Stack size of the task that converts scancodes to Virtual Keys Keyboard.
Definition: keyboard.h:386
bool postEvent(uiEvent const *event)
Places an event in the event queue and returns without waiting for the receiver to process the event...
Definition: fabui.cpp:536
bool setScancodeSet(int value)
Sets the scancode set.
Definition: keyboard.cpp:136
void setLayout(KeyboardLayout const *layout)
Sets keyboard layout.
Definition: keyboard.cpp:204
KeyboardLayout const * inherited
Definition: kbdlayouts.h:85
VirtualKeyDef scancodeToVK[86]
Definition: kbdlayouts.h:86
static void begin(gpio_num_t port0_clkGPIO, gpio_num_t port0_datGPIO, gpio_num_t port1_clkGPIO=GPIO_UNUSED, gpio_num_t port1_datGPIO=GPIO_UNUSED)
Initializes PS2 device controller.