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-2020 Fabrizio Di Vittorio.
4  All rights reserved.
5 
6  This file is part of FabGL Library.
7 
8  FabGL is free software: you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation, either version 3 of the License, or
11  (at your option) any later version.
12 
13  FabGL is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with FabGL. If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 
23 
24 #include <string.h>
25 
26 #include "freertos/FreeRTOS.h"
27 #include "freertos/task.h"
28 #include "freertos/timers.h"
29 #include "freertos/queue.h"
30 
31 #include "keyboard.h"
32 
33 
34 
35 
36 namespace fabgl {
37 
38 
39 
40 /**************************************************************************************/
41 /* US LAYOUT */
42 /**************************************************************************************/
43 const KeyboardLayout USLayout {
44  // name
45  "US",
46 
47  // desc
48  "US English",
49 
50  // inherited layout
51  nullptr,
52 
53  // single byte scancodes
54  {
55  { 0x76, VK_ESCAPE },
56  { 0x05, VK_F1 },
57  { 0x06, VK_F2 },
58  { 0x04, VK_F3 },
59  { 0x0C, VK_F4 },
60  { 0x03, VK_F5 },
61  { 0x0B, VK_F6 },
62  { 0x83, VK_F7 },
63  { 0x0A, VK_F8 },
64  { 0x01, VK_F9 },
65  { 0x09, VK_F10 },
66  { 0x78, VK_F11 },
67  { 0x07, VK_F12 },
68  { 0x16, VK_1 },
69  { 0x1E, VK_2 },
70  { 0x26, VK_3 },
71  { 0x25, VK_4 },
72  { 0x2E, VK_5 },
73  { 0x36, VK_6 },
74  { 0x3D, VK_7 },
75  { 0x3E, VK_8 },
76  { 0x46, VK_9 },
77  { 0x45, VK_0 },
78  { 0x15, VK_q },
79  { 0x1D, VK_w },
80  { 0x24, VK_e },
81  { 0x2D, VK_r },
82  { 0x2C, VK_t },
83  { 0x35, VK_y },
84  { 0x3C, VK_u },
85  { 0x43, VK_i },
86  { 0x44, VK_o },
87  { 0x4D, VK_p },
88  { 0x1C, VK_a },
89  { 0x1b, VK_s },
90  { 0x23, VK_d },
91  { 0x2b, VK_f },
92  { 0x34, VK_g },
93  { 0x33, VK_h },
94  { 0x3B, VK_j },
95  { 0x42, VK_k },
96  { 0x4B, VK_l },
97  { 0x1A, VK_z },
98  { 0x22, VK_x },
99  { 0x21, VK_c },
100  { 0x2A, VK_v },
101  { 0x32, VK_b },
102  { 0x31, VK_n },
103  { 0x3A, VK_m },
104  { 0x0E, VK_GRAVEACCENT },
105  { 0x4E, VK_MINUS },
106  { 0x55, VK_EQUALS },
107  { 0x54, VK_LEFTBRACKET },
108  { 0x5B, VK_RIGHTBRACKET },
109  { 0x5D, VK_BACKSLASH },
110  { 0x4C, VK_SEMICOLON },
111  { 0x52, VK_QUOTE },
112  { 0x41, VK_COMMA },
113  { 0x49, VK_PERIOD },
114  { 0x4A, VK_SLASH },
115  { 0x70, VK_KP_INSERT },
116  { 0x69, VK_KP_END },
117  { 0x72, VK_KP_DOWN },
118  { 0x7A, VK_KP_PAGEDOWN },
119  { 0x6B, VK_KP_LEFT },
120  { 0x73, VK_KP_CENTER }, // "5" in the keypad
121  { 0x74, VK_KP_RIGHT },
122  { 0x6C, VK_KP_HOME },
123  { 0x75, VK_KP_UP },
124  { 0x7D, VK_KP_PAGEUP },
125  { 0x71, VK_KP_DELETE },
126  { 0x7C, VK_KP_MULTIPLY },
127  { 0x79, VK_KP_PLUS },
128  { 0x7B, VK_KP_MINUS },
129  { 0x66, VK_BACKSPACE },
130  { 0x0D, VK_TAB },
131  { 0x5A, VK_RETURN },
132  { 0x77, VK_NUMLOCK },
133  { 0x7E, VK_SCROLLLOCK },
134  { 0x58, VK_CAPSLOCK },
135  { 0x12, VK_LSHIFT },
136  { 0x59, VK_RSHIFT },
137  { 0x14, VK_LCTRL },
138  { 0x11, VK_LALT },
139  { 0x29, VK_SPACE },
140  { 0x84, VK_SYSREQ }, // ALT + PRINTSCREEN directly translated by the keyboard
141  },
142 
143  // extended scancodes (0xE0..)
144  {
145  { 0x14, VK_RCTRL },
146  { 0x11, VK_RALT },
147  { 0x1F, VK_LGUI },
148  { 0x27, VK_RGUI },
149  { 0x2F, VK_APPLICATION },
150  { 0x70, VK_INSERT },
151  { 0x71, VK_DELETE },
152  { 0x4A, VK_KP_DIVIDE },
153  { 0x5A, VK_KP_ENTER },
154  { 0x7D, VK_PAGEUP },
155  { 0x7A, VK_PAGEDOWN },
156  { 0x6C, VK_HOME },
157  { 0x69, VK_END },
158  { 0x75, VK_UP },
159  { 0x72, VK_DOWN },
160  { 0x6B, VK_LEFT },
161  { 0x74, VK_RIGHT },
162  // print screen is composed by "E0 12 E0 7C", here translated as separated VK_PRINTSCREEN1 and VK_PRINTSCREEN2
163  // VK_PRINTSCREEN2 is also generated by "CTRL or SHIFT" + "PRINTSCREEN". So pressing PRINTSCREEN both VK_PRINTSCREEN1 and VK_PRINTSCREEN2
164  // are generated, while pressing CTRL+PRINTSCREEN or SHIFT+PRINTSCREEN only VK_PRINTSCREEN2 is generated.
165  { 0x12, VK_PRINTSCREEN1 },
166  { 0x7C, VK_PRINTSCREEN2 },
167  // pressing CTRL + PAUSE will generate "E0 7E" instead of normal Pause sequence. Here is translated to VK_BREAK.
168  { 0x7E, VK_BREAK },
169  },
170 
171  // virtual keys generated by other virtual keys combinations
172  // in_key, { CTRL, ALT, SHIFT, CAPSLOCK, NUMLOCK }, out_key
173  {
174  { VK_1, { 0, 0, 1, 0, 0 }, VK_EXCLAIM }, // SHIFT "1" = "!"
175  { VK_2, { 0, 0, 1, 0, 0 }, VK_AT }, // SHIFT "2" = "@"
176  { VK_3, { 0, 0, 1, 0, 0 }, VK_HASH }, // SHIFT "3" = "#"
177  { VK_4, { 0, 0, 1, 0, 0 }, VK_DOLLAR }, // SHIFT "4" = "$"
178  { VK_5, { 0, 0, 1, 0, 0 }, VK_PERCENT }, // SHIFT "5" = "%"
179  { VK_6, { 0, 0, 1, 0, 0 }, VK_CARET }, // SHIFT "6" = "^"
180  { VK_7, { 0, 0, 1, 0, 0 }, VK_AMPERSAND }, // SHIFT "7" = "&"
181  { VK_8, { 0, 0, 1, 0, 0 }, VK_ASTERISK }, // SHIFT "8" = "*"
182  { VK_9, { 0, 0, 1, 0, 0 }, VK_LEFTPAREN }, // SHIFT "9" = "("
183  { VK_0, { 0, 0, 1, 0, 0 }, VK_RIGHTPAREN }, // SHIFT "0" = ")"
184 
185  { VK_GRAVEACCENT, { 0, 0, 1, 0, 0 }, VK_TILDE }, // SHIFT "`" = "~"
186  { VK_MINUS, { 0, 0, 1, 0, 0 }, VK_UNDERSCORE }, // SHIFT "-" = "_"
187  { VK_EQUALS, { 0, 0, 1, 0, 0 }, VK_PLUS }, // SHIFT "=" = "+"
188  { VK_LEFTBRACKET, { 0, 0, 1, 0, 0 }, VK_LEFTBRACE }, // SHIFT "[" = "{"
189  { VK_RIGHTBRACKET, { 0, 0, 1, 0, 0 }, VK_RIGHTBRACE }, // SHIFT "]" = "}"
190  { VK_BACKSLASH, { 0, 0, 1, 0, 0 }, VK_VERTICALBAR }, // SHIFT "\" = "|"
191  { VK_SEMICOLON, { 0, 0, 1, 0, 0 }, VK_COLON }, // SHIFT ";" = ":"
192  { VK_QUOTE, { 0, 0, 1, 0, 0 }, VK_QUOTEDBL }, // SHIFT "'" = """
193  { VK_COMMA, { 0, 0, 1, 0, 0 }, VK_LESS }, // SHIFT "," = "<"
194  { VK_PERIOD, { 0, 0, 1, 0, 0 }, VK_GREATER }, // SHIFT "." = ">"
195  { VK_SLASH, { 0, 0, 1, 0, 0 }, VK_QUESTION }, // SHIFT "/" = "?"
196 
197  // keypad with NUMLOCK enabled
198  { VK_KP_INSERT, { 0, 0, 0, 0, 1 }, VK_KP_0 }, // NUMLOCK + KP INS = "0"
199  { VK_KP_END, { 0, 0, 0, 0, 1 }, VK_KP_1 }, // NUMLOCK + KP END = "1"
200  { VK_KP_DOWN, { 0, 0, 0, 0, 1 }, VK_KP_2 }, // NUMLOCK + KP DOWN = "2"
201  { VK_KP_PAGEDOWN, { 0, 0, 0, 0, 1 }, VK_KP_3 }, // NUMLOCK + KP PAGEDOWN = "3"
202  { VK_KP_LEFT, { 0, 0, 0, 0, 1 }, VK_KP_4 }, // NUMLOCK + KP LEFT = "4"
203  { VK_KP_CENTER, { 0, 0, 0, 0, 1 }, VK_KP_5 }, // NUMLOCK + KP CENTER = "5"
204  { VK_KP_RIGHT, { 0, 0, 0, 0, 1 }, VK_KP_6 }, // NUMLOCK + KP RIGHT = "6"
205  { VK_KP_HOME, { 0, 0, 0, 0, 1 }, VK_KP_7 }, // NUMLOCK + KP HOME = "7"
206  { VK_KP_UP, { 0, 0, 0, 0, 1 }, VK_KP_8 }, // NUMLOCK + KP UP = "8"
207  { VK_KP_PAGEUP, { 0, 0, 0, 0, 1 }, VK_KP_9 }, // NUMLOCK + KP PAGEUP = "9"
208  { VK_KP_DELETE, { 0, 0, 0, 0, 1 }, VK_KP_PERIOD }, // NUMLOCK + KP DELETE = "."
209 
210  // SHIFT or CAPSLOCK "a".."z" = "A".."Z"
211  { VK_a, { 0, 0, 1, 1, 0 }, VK_A },
212  { VK_b, { 0, 0, 1, 1, 0 }, VK_B },
213  { VK_c, { 0, 0, 1, 1, 0 }, VK_C },
214  { VK_d, { 0, 0, 1, 1, 0 }, VK_D },
215  { VK_e, { 0, 0, 1, 1, 0 }, VK_E },
216  { VK_f, { 0, 0, 1, 1, 0 }, VK_F },
217  { VK_g, { 0, 0, 1, 1, 0 }, VK_G },
218  { VK_h, { 0, 0, 1, 1, 0 }, VK_H },
219  { VK_i, { 0, 0, 1, 1, 0 }, VK_I },
220  { VK_j, { 0, 0, 1, 1, 0 }, VK_J },
221  { VK_k, { 0, 0, 1, 1, 0 }, VK_K },
222  { VK_l, { 0, 0, 1, 1, 0 }, VK_L },
223  { VK_m, { 0, 0, 1, 1, 0 }, VK_M },
224  { VK_n, { 0, 0, 1, 1, 0 }, VK_N },
225  { VK_o, { 0, 0, 1, 1, 0 }, VK_O },
226  { VK_p, { 0, 0, 1, 1, 0 }, VK_P },
227  { VK_q, { 0, 0, 1, 1, 0 }, VK_Q },
228  { VK_r, { 0, 0, 1, 1, 0 }, VK_R },
229  { VK_s, { 0, 0, 1, 1, 0 }, VK_S },
230  { VK_t, { 0, 0, 1, 1, 0 }, VK_T },
231  { VK_u, { 0, 0, 1, 1, 0 }, VK_U },
232  { VK_v, { 0, 0, 1, 1, 0 }, VK_V },
233  { VK_w, { 0, 0, 1, 1, 0 }, VK_W },
234  { VK_x, { 0, 0, 1, 1, 0 }, VK_X },
235  { VK_y, { 0, 0, 1, 1, 0 }, VK_Y },
236  { VK_z, { 0, 0, 1, 1, 0 }, VK_Z },
237  },
238 
239  // deadkeys
240  {
241  },
242 
243  // deadkeys translation
244  {
245  },
246 };
247 
248 
249 /**************************************************************************************/
250 /* UK LAYOUT */
251 /**************************************************************************************/
252 const KeyboardLayout UKLayout {
253  // name
254  "UK",
255 
256  // desc
257  "UK British",
258 
259  // inherited layout
260  &USLayout,
261 
262  // single byte scancodes
263  {
264  { 0x5D, VK_HASH },
265  { 0x61, VK_BACKSLASH },
266  },
267 
268  // extended scancodes (0xE0..)
269  {
270  },
271 
272  // virtual keys generated by other virtual keys combinations
273  // in_key, { CTRL, ALT, SHIFT, CAPSLOCK, NUMLOCK }, out_key
274  {
275  { VK_GRAVEACCENT, { 0, 0, 1, 0, 0 }, VK_NEGATION }, // SHIFT "`" = "¬"
276  { VK_2, { 0, 0, 1, 0, 0 }, VK_QUOTEDBL }, // SHIFT "2" = """
277  { VK_3, { 0, 0, 1, 0, 0 }, VK_POUND }, // SHIFT "3" = "£"
278  { VK_QUOTE, { 0, 0, 1, 0, 0 }, VK_AT }, // SHIFT "'" = "@"
279  { VK_HASH, { 0, 0, 1, 0, 0 }, VK_TILDE }, // SHIFT "#" = "~"
280  { VK_BACKSLASH, { 0, 0, 1, 0, 0 }, VK_VERTICALBAR }, // SHIFT "\" = "|"
281  },
282 
283  // deadkeys
284  {
285  },
286 
287  // deadkeys translation
288  {
289  },
290 };
291 
292 
293 /**************************************************************************************/
294 /* GERMAN LAYOUT */
295 /**************************************************************************************/
296 const KeyboardLayout GermanLayout {
297  // name
298  "DE",
299 
300  // desc
301  "German",
302 
303  // inherited layout
304  &USLayout,
305 
306  // single byte scancodes
307  {
308  { 0x0E, VK_CARET },
309  { 0x4E, VK_ESZETT },
310  { 0x55, VK_ACUTEACCENT },
311  { 0x54, VK_UMLAUT_u },
312  { 0x5B, VK_PLUS },
313  { 0x4C, VK_UMLAUT_o },
314  { 0x52, VK_UMLAUT_a },
315  { 0x5D, VK_HASH },
316  { 0x61, VK_LESS },
317  { 0x4A, VK_MINUS },
318  { 0x35, VK_z },
319  { 0x1A, VK_y },
320  },
321 
322  // extended scancodes (0xE0..)
323  {
324  },
325 
326  // virtual keys generated by other virtual keys combinations
327  // in_key, { CTRL, ALT, SHIFT, CAPSLOCK, NUMLOCK }, out_key
328  {
329  { VK_CARET, { 0, 0, 1, 0, 0 }, VK_DEGREE }, // SHIFT "^" = "°"
330  { VK_2, { 0, 0, 1, 0, 0 }, VK_QUOTEDBL }, // SHIFT "2" = """
331  { VK_3, { 0, 0, 1, 0, 0 }, VK_SECTION }, // SHIFT "3" = "§"
332  { VK_6, { 0, 0, 1, 0, 0 }, VK_AMPERSAND }, // SHIFT "6" = "&"
333  { VK_7, { 0, 0, 1, 0, 0 }, VK_SLASH }, // SHIFT "7" = "/"
334  { VK_8, { 0, 0, 1, 0, 0 }, VK_LEFTPAREN }, // SHIFT "8" = "("
335  { VK_9, { 0, 0, 1, 0, 0 }, VK_RIGHTPAREN }, // SHIFT "9" = ")"
336  { VK_0, { 0, 0, 1, 0, 0 }, VK_EQUALS }, // SHIFT "0" = "="
337  { VK_ESZETT, { 0, 0, 1, 0, 0 }, VK_QUESTION }, // SHIFT "ß" = "?"
338  { VK_ACUTEACCENT, { 0, 0, 1, 0, 0 }, VK_GRAVEACCENT }, // SHIFT "´" = "`"
339  { VK_PLUS, { 0, 0, 1, 0, 0 }, VK_ASTERISK }, // SHIFT "+" = "*"
340  { VK_HASH, { 0, 0, 1, 0, 0 }, VK_QUOTE }, // SHIFT "#" = "'"
341  { VK_LESS, { 0, 0, 1, 0, 0 }, VK_GREATER }, // SHIFT "<" = ">"
342  { VK_COMMA, { 0, 0, 1, 0, 0 }, VK_SEMICOLON }, // SHIFT "," = ";"
343  { VK_PERIOD, { 0, 0, 1, 0, 0 }, VK_COLON }, // SHIFT "." = ":"
344  { VK_MINUS, { 0, 0, 1, 0, 0 }, VK_UNDERSCORE }, // SHIFT "-" = "_"
345  { VK_7, { 0, 1, 0, 0, 0 }, VK_LEFTBRACE }, // ALT "7" = "{"
346  { VK_8, { 0, 1, 0, 0, 0 }, VK_LEFTBRACKET }, // ALT "8" = "["
347  { VK_9, { 0, 1, 0, 0, 0 }, VK_RIGHTBRACKET }, // ALT "9" = "]"
348  { VK_0, { 0, 1, 0, 0, 0 }, VK_RIGHTBRACE }, // ALT "0" = "}"
349  { VK_ESZETT, { 0, 1, 0, 0, 0 }, VK_BACKSLASH }, // ALT "ß" = "\"
350  { VK_q, { 0, 1, 0, 0, 0 }, VK_AT }, // ALT "q" = "@"
351  { VK_e, { 0, 1, 0, 0, 0 }, VK_EURO }, // ALT "e" = "€"
352  { VK_PLUS, { 0, 1, 0, 0, 0 }, VK_TILDE }, // ALT "+" = "~"
353  { VK_LESS, { 0, 1, 0, 0, 0 }, VK_VERTICALBAR }, // ALT "<" = "|"
354  },
355 
356  // deadkeys
357  {
358  },
359 
360  // deadkeys translation
361  {
362  },
363 };
364 
365 
366 /**************************************************************************************/
367 /* ITALIAN LAYOUT */
368 /**************************************************************************************/
369 const KeyboardLayout ItalianLayout {
370  // name
371  "IT",
372 
373  // desc
374  "Italian",
375 
376  // inherited layout
377  &USLayout,
378 
379  // single byte scancodes
380  {
381  { 0x0E, VK_BACKSLASH },
382  { 0x4E, VK_QUOTE },
383  { 0x55, VK_GRAVE_i },
384  { 0x54, VK_GRAVE_e },
385  { 0x5B, VK_PLUS },
386  { 0x4C, VK_GRAVE_o },
387  { 0x52, VK_GRAVE_a },
388  { 0x5D, VK_GRAVE_u },
389  { 0x61, VK_LESS },
390  { 0x4A, VK_MINUS },
391  },
392 
393  // extended scancodes (0xE0..)
394  {
395  },
396 
397  // virtual keys generated by other virtual keys combinations
398  // in_key, { CTRL, ALT, SHIFT, CAPSLOCK, NUMLOCK }, out_key
399  {
400  { VK_BACKSLASH, { 0, 0, 1, 0, 0 }, VK_VERTICALBAR }, // SHIFT "\" = "|"
401  { VK_2, { 0, 0, 1, 0, 0 }, VK_QUOTEDBL }, // SHIFT "2" = """
402  { VK_3, { 0, 0, 1, 0, 0 }, VK_POUND }, // SHIFT "3" = "£"
403  { VK_6, { 0, 0, 1, 0, 0 }, VK_AMPERSAND }, // SHIFT "6" = "&"
404  { VK_7, { 0, 0, 1, 0, 0 }, VK_SLASH }, // SHIFT "7" = "/"
405  { VK_8, { 0, 0, 1, 0, 0 }, VK_LEFTPAREN }, // SHIFT "8" = "("
406  { VK_9, { 0, 0, 1, 0, 0 }, VK_RIGHTPAREN }, // SHIFT "9" = ")"
407  { VK_0, { 0, 0, 1, 0, 0 }, VK_EQUALS }, // SHIFT "0" = "="
408  { VK_QUOTE, { 0, 0, 1, 0, 0 }, VK_QUESTION }, // SHIFT "'" = "?"
409  { VK_GRAVE_i, { 0, 0, 1, 0, 0 }, VK_CARET }, // SHIFT "ì" = "^"
410  { VK_GRAVE_e, { 0, 0, 1, 0, 0 }, VK_ACUTE_e }, // SHIFT "è" = "é"
411  { VK_PLUS, { 0, 0, 1, 0, 0 }, VK_ASTERISK }, // SHIFT "+" = "*"
412  { VK_GRAVE_o, { 0, 0, 1, 0, 0 }, VK_CEDILLA_c }, // SHIFT "ò" = "ç"
413  { VK_GRAVE_a, { 0, 0, 1, 0, 0 }, VK_DEGREE }, // SHIFT "à" = "°"
414  { VK_GRAVE_u, { 0, 0, 1, 0, 0 }, VK_SECTION }, // SHIFT "ù" = "§"
415  { VK_LESS, { 0, 0, 1, 0, 0 }, VK_GREATER }, // SHIFT "<" = ">"
416  { VK_COMMA, { 0, 0, 1, 0, 0 }, VK_SEMICOLON }, // SHIFT "," = ";"
417  { VK_PERIOD, { 0, 0, 1, 0, 0 }, VK_COLON }, // SHIFT "." = ":"
418  { VK_MINUS, { 0, 0, 1, 0, 0 }, VK_UNDERSCORE }, // SHIFT "-" = "_"
419  { VK_BACKSLASH, { 0, 1, 0, 0, 0 }, VK_GRAVEACCENT }, // ALT "\" = "`"
420  { VK_5, { 0, 1, 0, 0, 0 }, VK_TILDE }, // ALT "5" = "~"
421  { VK_e, { 0, 1, 0, 0, 0 }, VK_EURO }, // ALT "e" = "€"
422  { VK_GRAVE_e, { 0, 1, 0, 0, 0 }, VK_LEFTBRACKET }, // ALT "è" = "["
423  { VK_PLUS, { 0, 1, 0, 0, 0 }, VK_RIGHTBRACKET }, // ALT "+" = "]"
424  { VK_GRAVE_o, { 0, 1, 0, 0, 0 }, VK_AT }, // ALT "ò" = "@"
425  { VK_GRAVE_a, { 0, 1, 0, 0, 0 }, VK_HASH }, // ALT "à" = "#"
426  { VK_GRAVE_e, { 0, 1, 1, 0, 0 }, VK_LEFTBRACE }, // SHIFT ALT "è" = "{"
427  { VK_PLUS, { 0, 1, 1, 0, 0 }, VK_RIGHTBRACE }, // SHIFT ALT "+" = "}"
428  },
429 
430  // deadkeys
431  {
432  },
433 
434  // deadkeys translation
435  {
436  },
437 };
438 
439 
440 /**************************************************************************************/
441 /* SPANISH LAYOUT */
442 /* */
443 /* Implemented by Carles Oriol (https://github.com/carlesoriol) */
444 /**************************************************************************************/
445 const KeyboardLayout SpanishLayout {
446  // name
447  "ES",
448 
449  // desc
450  "Spanish",
451 
452  // inherited layout
453  &USLayout, // To avoid conflict key combinations
454 
455  // single byte scancodes
456  {
457  { 0x0E, VK_DEGREE },
458  { 0x4E, VK_QUOTE },
459  { 0x55, VK_EXCLAIM_INV }, // inverted exclaim
460  { 0x5D, VK_CEDILLA_c },
461 
462  { 0x54, VK_GRAVEACCENT },
463  { 0x5B, VK_PLUS },
464 
465  { 0x4C, VK_TILDE_n }, // spanish ñ
466  { 0x52, VK_ACUTEACCENT },
467 
468  { 0x61, VK_LESS },
469  { 0x4A, VK_MINUS }
470 
471 
472  },
473 
474  // extended scancodes (0xE0..)
475  {
476  },
477 
478 
479  // virtual keys generated by other virtual keys combinations
480  // in_key, { CTRL, ALT, SHIFT, CAPSLOCK, NUMLOCK }, out_key
481  {
482 
483  { VK_DEGREE, { 0, 1, 0, 0, 0 }, VK_BACKSLASH }, // ALT "º" = "\"
484  { VK_DEGREE, { 0, 0, 1, 0, 0 }, VK_UPPER_a }, // SHIFT "º" = "ª"
485 
486  { VK_1, { 0, 1, 0, 0, 0 }, VK_VERTICALBAR }, // ALT "1" = "|"
487  { VK_1, { 0, 0, 1, 0, 0 }, VK_EXCLAIM }, // SHIFT "1" = "!"
488 
489  { VK_2, { 0, 0, 1, 0, 0 }, VK_QUOTEDBL }, // SHIFT "2" = """
490  { VK_2, { 0, 1, 0, 0, 0 }, VK_AT }, // ALT "2" = "@"
491 
492  { VK_3, { 0, 0, 1, 0, 0 }, VK_INTERPUNCT }, // SHIFT "3" = "·"
493  { VK_3, { 0, 1, 0, 0, 0 }, VK_HASH }, // SHIFT "3" = "#"
494 
495  { VK_4, { 0, 1, 0, 0, 0 }, VK_TILDE }, // SHIFT "4" = "~"
496 
497  { VK_6, { 0, 0, 1, 0, 0 }, VK_AMPERSAND }, // SHIFT "6" = "&"
498  { VK_6, { 0, 1, 0, 0, 0 }, VK_NEGATION }, // ALT "6" = "¬"
499 
500  { VK_7, { 0, 0, 1, 0, 0 }, VK_SLASH }, // SHIFT "7" = "/"
501  { VK_8, { 0, 0, 1, 0, 0 }, VK_LEFTPAREN }, // SHIFT "8" = "("
502  { VK_9, { 0, 0, 1, 0, 0 }, VK_RIGHTPAREN }, // SHIFT "9" = ")"
503  { VK_0, { 0, 0, 1, 0, 0 }, VK_EQUALS }, // SHIFT "0" = "="
504 
505  { VK_QUOTE, { 0, 0, 1, 0, 0 }, VK_QUESTION }, // SHIFT "'" = "?"
506  { VK_EXCLAIM_INV, { 0, 0, 1, 0, 0 }, VK_QUESTION_INV }, // SHIFT "¡" = "¿"
507 
508  { VK_GRAVEACCENT, { 0, 0, 1, 0, 0 }, VK_CARET }, // SHIFT "`" = "^"
509  { VK_GRAVEACCENT, { 0, 1, 0, 0, 0 }, VK_LEFTBRACKET }, // SHIFT "`" = "["
510 
511  { VK_PLUS, { 0, 0, 1, 0, 0 }, VK_ASTERISK }, // SHIFT "'" = "*"
512  { VK_PLUS, { 0, 1, 0, 0, 0 }, VK_RIGHTBRACKET }, // SHIFT "'" = "]"
513 
514  { VK_CEDILLA_c, { 0, 0, 1, 0, 0 }, VK_CEDILLA_C }, // SHIFT "ç" = "Ç" // should be upper case
515  { VK_CEDILLA_c, { 0, 1, 0, 0, 0 }, VK_RIGHTBRACE }, // ALT "ç" = "}" // should be upper case
516 
517  { VK_ACUTEACCENT, { 0, 0, 1, 0, 0 }, VK_DIAERESIS }, // ALT "'" = "¨" // should be diaeresis for vocals
518  { VK_ACUTEACCENT, { 0, 1, 0, 0, 0 }, VK_LEFTBRACE }, // ALT "'" = "{" //
519 
520  { VK_LESS, { 0, 0, 1, 0, 0 }, VK_GREATER }, // SHIFT "<" = ">"
521  { VK_LESS, { 0, 1, 0, 0, 0 }, VK_VERTICALBAR }, // ALT "<" = "|"
522 
523  { VK_COMMA, { 0, 0, 1, 0, 0 }, VK_SEMICOLON }, // SHIFT "," = ";"
524  { VK_PERIOD, { 0, 0, 1, 0, 0 }, VK_COLON }, // SHIFT "." = ":"
525 
526  { VK_MINUS, { 0, 0, 1, 0, 0 }, VK_UNDERSCORE }, // SHIFT "-" = "_"
527 
528  { VK_TILDE_n, { 0, 0, 1, 0, 0 }, VK_TILDE_N }, // SHIFT "ñ" = "Ñ"
529  },
530 
531  // deadkeys
532  {
535  VK_DIAERESIS,
536  VK_CARET,
537  },
538 
539  // deadkeys translation
540  {
544  { VK_CARET, VK_SPACE, VK_CARET },
545 
551 
557 
563 
569 
570  { VK_CARET, VK_a, VK_CARET_a },
571  { VK_CARET, VK_e, VK_CARET_e },
572  { VK_CARET, VK_i, VK_CARET_i },
573  { VK_CARET, VK_o, VK_CARET_o },
574  { VK_CARET, VK_u, VK_CARET_u },
575 
576  { VK_CARET, VK_A, VK_CARET_A },
577  { VK_CARET, VK_E, VK_CARET_E },
578  { VK_CARET, VK_I, VK_CARET_I },
579  { VK_CARET, VK_O, VK_CARET_O },
580  { VK_CARET, VK_U, VK_CARET_U },
581 
587 
593  },
594 };
595 
596 
597 
598 
599 
600 int Keyboard::scancodeToVirtualKeyTaskStackSize = FABGLIB_DEFAULT_SCODETOVK_TASK_STACK_SIZE;
601 
602 
603 
604 Keyboard::Keyboard()
605  : m_keyboardAvailable(false),
606  m_lastDeadKey(VK_NONE)
607 {
608 }
609 
610 
611 void Keyboard::begin(bool generateVirtualKeys, bool createVKQueue, int PS2Port)
612 {
613  PS2Device::begin(PS2Port);
614 
615  m_CTRL = false;
616  m_ALT = false;
617  m_SHIFT = false;
618  m_CAPSLOCK = false;
619  m_NUMLOCK = false;
620  m_SCROLLLOCK = false;
621 
622  m_numLockLED = false;
623  m_capsLockLED = false;
624  m_scrollLockLED = false;
625 
626  m_SCodeToVKConverterTask = nullptr;
627  m_virtualKeyQueue = nullptr;
628 
629  m_uiApp = nullptr;
630 
631  reset();
632 
633  if (generateVirtualKeys || createVKQueue) {
634  if (createVKQueue)
635  m_virtualKeyQueue = xQueueCreate(FABGLIB_KEYBOARD_VIRTUALKEY_QUEUE_SIZE, sizeof(uint16_t));
636  xTaskCreate(&SCodeToVKConverterTask, "", Keyboard::scancodeToVirtualKeyTaskStackSize, this, FABGLIB_SCODETOVK_TASK_PRIORITY, &m_SCodeToVKConverterTask);
637  }
638 }
639 
640 
641 void Keyboard::begin(gpio_num_t clkGPIO, gpio_num_t dataGPIO, bool generateVirtualKeys, bool createVKQueue)
642 {
644  PS2->begin(clkGPIO, dataGPIO);
645  PS2->setKeyboard(this);
646  begin(generateVirtualKeys, createVKQueue, 0);
647 }
648 
649 
650 // reset keyboard, set scancode 2 and US layout
652 {
653  memset(m_VKMap, 0, sizeof(m_VKMap));
654 
655  // sets default layout
656  setLayout(&USLayout);
657 
658  // tries up to three times to reset keyboard
659  for (int i = 0; i < 3; ++i) {
660  m_keyboardAvailable = send_cmdReset() && send_cmdSetScancodeSet(2);
661  if (m_keyboardAvailable)
662  break;
663  vTaskDelay(500 / portTICK_PERIOD_MS);
664  }
665 
666  return m_keyboardAvailable;
667 }
668 
669 
670 void Keyboard::getLEDs(bool * numLock, bool * capsLock, bool * scrollLock)
671 {
672  *numLock = m_numLockLED;
673  *capsLock = m_capsLockLED;
674  *scrollLock = m_scrollLockLED;
675 }
676 
677 
679 {
680  return dataAvailable();
681 }
682 
683 
684 int Keyboard::getNextScancode(int timeOutMS, bool requestResendOnTimeOut)
685 {
686  while (true) {
687  int r = getData(timeOutMS);
688  if (r == -1 && requestResendOnTimeOut) {
689  requestToResendLastByte();
690  continue;
691  }
692  return r;
693  }
694 }
695 
696 
697 void Keyboard::updateLEDs()
698 {
699  send_cmdLEDs(m_NUMLOCK, m_CAPSLOCK, m_SCROLLLOCK);
700  m_numLockLED = m_NUMLOCK;
701  m_capsLockLED = m_CAPSLOCK;
702  m_scrollLockLED = m_SCROLLLOCK;
703 }
704 
705 
707 {
708  m_layout = layout;
709 }
710 
711 
712 #if FABGLIB_HAS_VirtualKeyO_STRING
713 char const * Keyboard::virtualKeyToString(VirtualKey virtualKey)
714 {
715  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",
716  "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",
717  "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",
718  "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",
719  "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",
720  "VK_PLUS", "VK_KP_PLUS", "VK_KP_MULTIPLY", "VK_ASTERISK", "VK_BACKSLASH", "VK_KP_DIVIDE", "VK_SLASH", "VK_KP_PERIOD", "VK_PERIOD", "VK_COLON",
721  "VK_COMMA", "VK_SEMICOLON", "VK_AMPERSAND", "VK_VERTICALBAR", "VK_HASH", "VK_AT", "VK_CARET", "VK_DOLLAR", "VK_POUND", "VK_EURO", "VK_PERCENT",
722  "VK_EXCLAIM", "VK_QUESTION", "VK_LEFTBRACE", "VK_RIGHTBRACE", "VK_LEFTBRACKET", "VK_RIGHTBRACKET", "VK_LEFTPAREN", "VK_RIGHTPAREN", "VK_LESS",
723  "VK_GREATER", "VK_UNDERSCORE", "VK_DEGREE", "VK_SECTION", "VK_TILDE", "VK_NEGATION", "VK_LSHIFT", "VK_RSHIFT", "VK_LALT", "VK_RALT", "VK_LCTRL", "VK_RCTRL",
724  "VK_LGUI", "VK_RGUI", "VK_ESCAPE", "VK_PRINTSCREEN1", "VK_PRINTSCREEN2", "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",
725  "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",
726  "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",
727  "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",
728  "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",
729  "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",
730  "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","VK_CARET_I","VK_CARET_O","VK_CARET_U",
731  };
732  return VKTOSTR[virtualKey];
733 }
734 #endif
735 
736 
737 // -1 = virtual key cannot be translated to ASCII
739 {
740  switch (virtualKey) {
741  case VK_SPACE:
742  return m_CTRL ? ASCII_NUL : ASCII_SPC; // CTRL + SPACE = NUL, otherwise 0x20
743 
744  case VK_0 ... VK_9:
745  return virtualKey - VK_0 + '0';
746 
747  case VK_KP_0 ... VK_KP_9:
748  return virtualKey - VK_KP_0 + '0';
749 
750  case VK_a ... VK_z:
751  return virtualKey - VK_a + (m_CTRL ? ASCII_SOH : 'a'); // CTRL + letter = SOH (a) ...SUB (z), otherwise the lower letter
752 
753  case VK_A ... VK_Z:
754  return virtualKey - VK_A + (m_CTRL ? ASCII_SOH : 'A'); // CTRL + letter = SOH (A) ...SUB (Z), otherwise the lower letter
755 
756  case VK_GRAVE_a:
757  return 0xE0; // à
758 
759  case VK_GRAVE_e:
760  return 0xE8; // è
761 
762  case VK_ACUTE_e:
763  return 0xE9; // é
764 
765  case VK_GRAVE_i:
766  return 0xEC; // ì
767 
768  case VK_GRAVE_o:
769  return 0xF2; // ò
770 
771  case VK_GRAVE_u:
772  return 0xF9; // ù
773 
774  case VK_CEDILLA_c:
775  return 0x87; // ç
776 
777  case VK_ESZETT:
778  return 0xDF; // ß
779 
780  case VK_UMLAUT_u:
781  return 0xFC; // ü
782 
783  case VK_UMLAUT_o:
784  return 0xF6; // ö
785 
786  case VK_UMLAUT_a:
787  return 0xE4; // ä
788 
789  case VK_GRAVEACCENT:
790  return 0x60; // "`"
791 
792  case VK_ACUTEACCENT:
793  return '\''; // 0xB4
794 
795  case VK_QUOTE:
796  return '\''; // "'"
797 
798  case VK_QUOTEDBL:
799  return '"';
800 
801  case VK_EQUALS:
802  return '=';
803 
804  case VK_MINUS:
805  case VK_KP_MINUS:
806  return '-';
807 
808  case VK_PLUS:
809  case VK_KP_PLUS:
810  return '+';
811 
812  case VK_KP_MULTIPLY:
813  case VK_ASTERISK:
814  return '*';
815 
816  case VK_BACKSLASH:
817  return m_CTRL ? ASCII_FS : '\\'; // CTRL \ = FS, otherwise '\'
818 
819  case VK_KP_DIVIDE:
820  case VK_SLASH:
821  return '/';
822 
823  case VK_KP_PERIOD:
824  case VK_PERIOD:
825  return '.';
826 
827  case VK_COLON:
828  return ':';
829 
830  case VK_COMMA:
831  return ',';
832 
833  case VK_SEMICOLON:
834  return ';';
835 
836  case VK_AMPERSAND:
837  return '&';
838 
839  case VK_VERTICALBAR:
840  return '|';
841 
842  case VK_HASH:
843  return '#';
844 
845  case VK_AT:
846  return '@';
847 
848  case VK_CARET:
849  return '^';
850 
851  case VK_DOLLAR:
852  return '$';
853 
854  case VK_POUND:
855  return 0xA3; // '£'
856 
857  case VK_EURO:
858  return 0xEE; // '€' - non 8 bit ascii
859 
860  case VK_PERCENT:
861  return '%';
862 
863  case VK_EXCLAIM:
864  return '!';
865 
866  case VK_QUESTION:
867  return m_CTRL ? ASCII_US : '?'; // CTRL ? = US, otherwise '?'
868 
869  case VK_LEFTBRACE:
870  return '{';
871 
872  case VK_RIGHTBRACE:
873  return '}';
874 
875  case VK_LEFTBRACKET:
876  return m_CTRL ? ASCII_ESC : '['; // CTRL [ = ESC, otherwise '['
877 
878  case VK_RIGHTBRACKET:
879  return m_CTRL ? ASCII_GS : ']'; // CTRL ] = GS, otherwise ']'
880 
881  case VK_LEFTPAREN:
882  return '(';
883 
884  case VK_RIGHTPAREN:
885  return ')';
886 
887  case VK_LESS:
888  return '<';
889 
890  case VK_GREATER:
891  return '>';
892 
893  case VK_UNDERSCORE:
894  return '_';
895 
896  case VK_DEGREE:
897  return 0xf8; // in ISO/IEC 8859 it should 0xB0, but in code page 437 it is 0xF8
898 
899  case VK_SECTION:
900  return 0xA7; // "§"
901 
902  case VK_TILDE:
903  return m_CTRL ? ASCII_RS : '~'; // CTRL ~ = RS, otherwise "~"
904 
905  case VK_NEGATION:
906  return 0xAA; // "¬"
907 
908  case VK_BACKSPACE:
909  return ASCII_BS;
910 
911  case VK_DELETE:
912  case VK_KP_DELETE:
913  return ASCII_DEL;
914 
915  case VK_RETURN:
916  case VK_KP_ENTER:
917  return ASCII_CR;
918 
919  case VK_TAB:
920  return ASCII_HT;
921 
922  case VK_ESCAPE:
923  return ASCII_ESC;
924 
925  case VK_SCROLLLOCK:
926  return m_SCROLLLOCK ? ASCII_XOFF : ASCII_XON;
927 
928  // spanish and catalan symbols
929 
930  case VK_CEDILLA_C:
931  return 0x80; // 'Ç'
932 
933  case VK_TILDE_n:
934  return 0xA4; // 'ñ'
935 
936  case VK_TILDE_N:
937  return 0xA5; // 'Ñ'
938 
939  case VK_UPPER_a:
940  return 0xA6; // 'ª'
941 
942  case VK_ACUTE_a:
943  return 0xA0; // 'á'
944 
945  case VK_ACUTE_o:
946  return 0xA2; // 'ó'
947 
948  case VK_ACUTE_u:
949  return 0xA3; // 'ú'
950 
951  case VK_UMLAUT_i:
952  return 0x8B; // 'ï'
953 
954  case VK_EXCLAIM_INV:
955  return 0xAD; // '¡'
956 
957  case VK_QUESTION_INV:
958  return 0xA8; // '¿'
959 
960  case VK_ACUTE_A:
961  return 0xB5; // 'Á'
962 
963  case VK_ACUTE_E:
964  return 0x90; // 'É'
965 
966  case VK_ACUTE_I:
967  return 0xD6; // 'Í'
968 
969  case VK_ACUTE_O:
970  return 0xE0; // 'Ó'
971 
972  case VK_ACUTE_U:
973  return 0xE9; // 'Ú'
974 
975  case VK_GRAVE_A:
976  return 0xB7; // 'À'
977 
978  case VK_GRAVE_E:
979  return 0xD4; // 'È'
980 
981  case VK_GRAVE_I:
982  return 0xDE; // 'Ì'
983 
984  case VK_GRAVE_O:
985  return 0xE3; // 'Ò'
986 
987  case VK_GRAVE_U:
988  return 0xEB; // 'Ù'
989 
990  case VK_INTERPUNCT:
991  return 0xFA; // '·'
992 
993  case VK_DIAERESIS: // '¨'
994  return '"';
995 
996  case VK_UMLAUT_e: // 'ë'
997  return 0xEB;
998 
999  case VK_UMLAUT_A: // 'Ä'
1000  return 0XC4;
1001 
1002  case VK_UMLAUT_E: // 'Ë'
1003  return 0XCB;
1004 
1005  case VK_UMLAUT_I: // 'Ï'
1006  return 0XCF;
1007 
1008  case VK_UMLAUT_O: // 'Ö'
1009  return 0XD6;
1010 
1011  case VK_UMLAUT_U: // 'Ü'
1012  return 0XDC;
1013 
1014  case VK_CARET_a: // 'â'
1015  return 0XE2;
1016 
1017  case VK_CARET_e: // 'ê'
1018  return 0XEA;
1019 
1020  case VK_CARET_i: // 'î'
1021  return 0XEE;
1022 
1023  case VK_CARET_o: // 'ô'
1024  return 0xF6;
1025 
1026  case VK_CARET_u: // 'û'
1027  return 0xFC;
1028 
1029  case VK_CARET_A: // 'Â'
1030  return 0XC2;
1031 
1032  case VK_CARET_E: // 'Ê'
1033  return 0XCA;
1034 
1035  case VK_CARET_I: // 'Î'
1036  return 0XCE;
1037 
1038  case VK_CARET_O: // 'Ô'
1039  return 0XD4;
1040 
1041  case VK_CARET_U: // 'Û'
1042  return 0XDB;
1043 
1044  default:
1045  return -1;
1046  }
1047 }
1048 
1049 
1050 VirtualKey Keyboard::scancodeToVK(uint8_t scancode, bool isExtended, KeyboardLayout const * layout)
1051 {
1052  VirtualKey vk = VK_NONE;
1053 
1054  if (layout == nullptr)
1055  layout = m_layout;
1056 
1057  VirtualKeyDef const * def = isExtended ? layout->exScancodeToVK : layout->scancodeToVK;
1058  for (; def->scancode; ++def)
1059  if (def->scancode == scancode) {
1060  vk = def->virtualKey;
1061  break;
1062  }
1063 
1064  if (vk == VK_NONE && layout->inherited)
1065  vk = scancodeToVK(scancode, isExtended, layout->inherited);
1066 
1067  return vk;
1068 }
1069 
1070 
1071 VirtualKey Keyboard::VKtoAlternateVK(VirtualKey in_vk, KeyboardLayout const * layout)
1072 {
1073  VirtualKey vk = VK_NONE;
1074 
1075  if (layout == nullptr)
1076  layout = m_layout;
1077 
1078  for (AltVirtualKeyDef const * def = layout->alternateVK; def->reqVirtualKey != VK_NONE; ++def) {
1079  if (def->reqVirtualKey == in_vk && def->ctrl == m_CTRL &&
1080  def->alt == m_ALT &&
1081  (def->shift == m_SHIFT || (def->capslock && def->capslock == m_CAPSLOCK)) &&
1082  def->numlock == m_NUMLOCK) {
1083  vk = def->virtualKey;
1084  break;
1085  }
1086  }
1087 
1088  if (vk == VK_NONE && layout->inherited)
1089  vk = VKtoAlternateVK(in_vk, layout->inherited);
1090 
1091  return vk == VK_NONE ? in_vk : vk;
1092 }
1093 
1094 
1095 VirtualKey Keyboard::blockingGetVirtualKey(bool * keyDown)
1096 {
1097  VirtualKey vk = VK_NONE;
1098  bool kdown = true;
1099  do {
1100  int scode = getNextScancode();
1101  if (scode == 0xE0) {
1102  // two bytes scancode
1103  scode = getNextScancode(100, true);
1104  if (scode == 0xF0) {
1105  // two bytes scancode key up
1106  scode = getNextScancode(100, true);
1107  vk = scancodeToVK(scode, true);
1108  kdown = false;
1109  } else {
1110  // two bytes scancode key down
1111  vk = scancodeToVK(scode, true);
1112  }
1113  } else if (scode == 0xE1) {
1114  // special case: "PAUSE" : 0xE1, 0x14, 0x77, 0xE1, 0xF0, 0x14, 0xF0, 0x77
1115  const uint8_t PAUSECODES[] = {0x14, 0x77, 0xE1, 0xF0, 0x14, 0xF0, 0x77};
1116  int i;
1117  for (i = 0; i < 7; ++i) {
1118  scode = getNextScancode(100, true);
1119  if (scode != PAUSECODES[i])
1120  break;
1121  }
1122  if (i == 7)
1123  vk = VK_PAUSE;
1124  } else if (scode == 0xF0) {
1125  // key up
1126  scode = getNextScancode(100, true);
1127  vk = scancodeToVK(scode, false);
1128  kdown = false;
1129  } else {
1130  // one byte scancode key down
1131  vk = scancodeToVK(scode, false);
1132  }
1133  } while (false);
1134 
1135  if (vk != VK_NONE) {
1136 
1137  // alternate VK (virtualkeys modified by shift, alt, ...)
1138  vk = VKtoAlternateVK(vk);
1139 
1140  // update shift, alt, ctrl, capslock, numlock and scrollock states and LEDs
1141  switch (vk) {
1142  case VK_LCTRL:
1143  case VK_RCTRL:
1144  m_CTRL = kdown;
1145  break;
1146  case VK_LALT:
1147  case VK_RALT:
1148  m_ALT = kdown;
1149  break;
1150  case VK_LSHIFT:
1151  case VK_RSHIFT:
1152  m_SHIFT = kdown;
1153  break;
1154  case VK_CAPSLOCK:
1155  if (!kdown) {
1156  m_CAPSLOCK = !m_CAPSLOCK;
1157  updateLEDs();
1158  }
1159  break;
1160  case VK_NUMLOCK:
1161  if (!kdown) {
1162  m_NUMLOCK = !m_NUMLOCK;
1163  updateLEDs();
1164  }
1165  break;
1166  case VK_SCROLLLOCK:
1167  if (!kdown) {
1168  m_SCROLLLOCK = !m_SCROLLLOCK;
1169  updateLEDs();
1170  }
1171  break;
1172  default:
1173  break;
1174  }
1175 
1176  }
1177 
1178  if (keyDown)
1179  *keyDown = kdown;
1180 
1181  // manage dead keys - Implemented by Carles Oriol (https://github.com/carlesoriol)
1182  for (VirtualKey const * dk = m_layout->deadKeysVK; *dk != VK_NONE; ++dk) {
1183  if (vk == *dk) {
1184  m_lastDeadKey = vk;
1185  vk = VK_NONE;
1186  }
1187  }
1188  if (vk != m_lastDeadKey && vk != VK_NONE) {
1189  for (DeadKeyVirtualKeyDef const * dk = m_layout->deadkeysToVK; dk->deadKey != VK_NONE; ++dk) {
1190  if (vk == dk->reqVirtualKey && m_lastDeadKey == dk->deadKey) {
1191  vk = dk->virtualKey;
1192  break;
1193  }
1194  }
1195  if (!kdown && (vk != m_lastDeadKey) && (vk != VK_RSHIFT) && (vk != VK_LSHIFT))
1196  m_lastDeadKey = VK_NONE;
1197  }
1198 
1199  return vk;
1200 }
1201 
1202 
1203 void Keyboard::injectVirtualKey(VirtualKey virtualKey, bool keyDown, bool insert)
1204 {
1205  // update m_VKMap
1206  if (keyDown)
1207  m_VKMap[(int)virtualKey >> 3] |= 1 << ((int)virtualKey & 7);
1208  else
1209  m_VKMap[(int)virtualKey >> 3] &= ~(1 << ((int)virtualKey & 7));
1210 
1211  // has VK queue? Insert VK into it.
1212  if (m_virtualKeyQueue) {
1213  uint16_t code = (uint16_t)virtualKey | (keyDown ? 0x8000 : 0);
1214  auto ticksToWait = (m_uiApp ? 0 : portMAX_DELAY); // 0, and not portMAX_DELAY to avoid uiApp locks
1215  if (insert)
1216  xQueueSendToFront(m_virtualKeyQueue, &code, ticksToWait);
1217  else
1218  xQueueSendToBack(m_virtualKeyQueue, &code, ticksToWait);
1219  }
1220 }
1221 
1222 
1223 void Keyboard::SCodeToVKConverterTask(void * pvParameters)
1224 {
1225  Keyboard * keyboard = (Keyboard*) pvParameters;
1226  while (true) {
1227  bool keyDown;
1228  VirtualKey vk = keyboard->blockingGetVirtualKey(&keyDown);
1229 
1230  keyboard->onVirtualKey(&vk, keyDown);
1231 
1232  if (vk != VK_NONE) {
1233 
1234  // add into m_virtualKeyQueue and update m_VKMap
1235  keyboard->injectVirtualKey(vk, keyDown);
1236 
1237  // need to send events to uiApp?
1238  if (keyboard->m_uiApp) {
1239  uiEvent evt = uiEvent(nullptr, keyDown ? UIEVT_KEYDOWN : UIEVT_KEYUP);
1240  evt.params.key.VK = vk;
1241  evt.params.key.LALT = keyboard->isVKDown(VK_LALT);
1242  evt.params.key.RALT = keyboard->isVKDown(VK_RALT);
1243  evt.params.key.CTRL = keyboard->isVKDown(VK_LCTRL) || keyboard->isVKDown(VK_RCTRL);
1244  evt.params.key.SHIFT = keyboard->isVKDown(VK_LSHIFT) || keyboard->isVKDown(VK_RSHIFT);
1245  evt.params.key.GUI = keyboard->isVKDown(VK_LGUI) || keyboard->isVKDown(VK_RGUI);
1246  keyboard->m_uiApp->postEvent(&evt);
1247  }
1248 
1249  }
1250  }
1251 }
1252 
1253 
1255 {
1256  if (value)
1257  vTaskSuspend(m_SCodeToVKConverterTask);
1258  else
1259  vTaskResume(m_SCodeToVKConverterTask);
1260 }
1261 
1262 
1264 {
1265  bool r = m_VKMap[(int)virtualKey >> 3] & (1 << ((int)virtualKey & 7));
1266 
1267  // VK_PAUSE is never released (no scancode sent from keyboard on key up), so when queried it is like released
1268  if (virtualKey == VK_PAUSE)
1269  m_VKMap[(int)virtualKey >> 3] &= ~(1 << ((int)virtualKey & 7));
1270 
1271  return r;
1272 }
1273 
1274 
1275 VirtualKey Keyboard::getNextVirtualKey(bool * keyDown, int timeOutMS)
1276 {
1277  VirtualKey vk = VK_NONE;
1278  if (m_SCodeToVKConverterTask) {
1279  uint16_t code;
1280  if (xQueueReceive(m_virtualKeyQueue, &code, msToTicks(timeOutMS)) == pdTRUE) {
1281  vk = (VirtualKey) (code & 0x7FFF);
1282  if (keyDown)
1283  *keyDown = code & 0x8000;
1284  }
1285  }
1286  return vk;
1287 }
1288 
1289 
1291 {
1292  return m_virtualKeyQueue ? uxQueueMessagesWaiting(m_virtualKeyQueue) : 0;
1293 }
1294 
1295 
1297 {
1298  xQueueReset(m_virtualKeyQueue);
1299 }
1300 
1301 
1302 
1303 
1304 } // end of namespace
void injectVirtualKey(VirtualKey virtualKey, bool keyDown, bool insert=false)
Adds or inserts a virtual key into the virtual keys queue.
Definition: keyboard.cpp:1203
All in one structure to fully represent a keyboard layout.
Definition: keyboard.h:79
DeadKeyVirtualKeyDef deadkeysToVK[64]
Definition: keyboard.h:88
VirtualKey deadKeysVK[8]
Definition: keyboard.h:87
VirtualKeyDef exScancodeToVK[32]
Definition: keyboard.h:84
VirtualKey virtualKey
Definition: keyboard.h:51
int virtualKeyToASCII(VirtualKey virtualKey)
Converts virtual key to ASCII.
Definition: keyboard.cpp:738
This file contains fabgl::Keyboard definition.
void emptyVirtualKeyQueue()
Empties the virtual keys queue.
Definition: keyboard.cpp:1296
Delegate< VirtualKey *, bool > onVirtualKey
Delegate called whenever a new virtual key is decoded from scancodes.
Definition: keyboard.h:413
void getLEDs(bool *numLock, bool *capsLock, bool *scrollLock)
Gets keyboard LEDs status.
Definition: keyboard.cpp:670
bool isVKDown(VirtualKey virtualKey)
Gets the virtual keys status.
Definition: keyboard.cpp:1263
The PS2 Keyboard controller class.
Definition: keyboard.h:166
VirtualKeyDef scancodeToVK[92]
Definition: keyboard.h:83
VirtualKey
Represents each possible real or derived (SHIFT + real) key.
Definition: fabutils.h:951
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:641
static PS2Controller * instance()
Returns the singleton instance of PS2Controller class.
Definition: canvas.cpp:31
VirtualKey getNextVirtualKey(bool *keyDown=nullptr, int timeOutMS=-1)
Gets a virtual key from the queue.
Definition: keyboard.cpp:1275
int virtualKeyAvailable()
Gets the number of virtual keys available in the queue.
Definition: keyboard.cpp:1290
Associates scancode to virtualkey.
Definition: keyboard.h:49
bool reset()
Sends a Reset command to the keyboard.
Definition: keyboard.cpp:651
int getNextScancode(int timeOutMS=-1, bool requestResendOnTimeOut=false)
Gets a scancode from the queue.
Definition: keyboard.cpp:684
int scancodeAvailable()
Gets the number of scancodes available in the queue.
Definition: keyboard.cpp:678
static int scancodeToVirtualKeyTaskStackSize
Stack size of the task that converts scancodes to Virtual Keys Keyboard.
Definition: keyboard.h:426
The PS2 device controller class.
Definition: ps2controller.h:74
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:519
void setLayout(KeyboardLayout const *layout)
Sets keyboard layout.
Definition: keyboard.cpp:706
KeyboardLayout const * inherited
Definition: keyboard.h:82
void suspendVirtualKeyGeneration(bool value)
Suspends or resume the virtual key generation task.
Definition: keyboard.cpp:1254
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.