blob: 008ae9cec6e298a77a04a489d50bbb9e9c1932dc [file] [log] [blame]
/*
* Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "keyboard_vkey.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
/* This requires ISO-C99 gcc standard. */
/* ref: http://www.unix-manuals.com/refs/misc/ascii-table.html */
keycode_info_t keycodes[VK_MAX_ENUM] = {
[0 ... (VK_MAX_ENUM - 1)] = { -1, -1, -1},
[VK_ESCAPE] = {VK_ESCAPE, VK_ESCAPE, -1},
[VK_OEM_3] = {'`', '~', -1},
[VK_1] = {'1', '!', -1},
[VK_2] = {'2', '@', -1},
[VK_3] = {'3', '#', -1},
[VK_4] = {'4', '$', -1},
[VK_5] = {'5', '%', -1},
[VK_6] = {'6', '^', -1},
[VK_7] = {'7', '&', -1},
[VK_8] = {'8', '*', -1},
[VK_9] = {'9', '(', -1},
[VK_0] = {'0', ')', -1},
[VK_OEM_MINUS] = {'-', '_', -1},
[VK_OEM_PLUS] = {'=', '+', -1},
[VK_BACK] = {VK_BACK, VK_BACK, -1},
[VK_TAB] = {VK_TAB, VK_TAB, -1},
[VK_Q] = {'q', 'Q', 0x11},
[VK_W] = {'w', 'W', 0x17},
[VK_E] = {'e', 'E', 0x5},
[VK_R] = {'r', 'R', 0x12},
[VK_T] = {'t', 'T', 0x14},
[VK_Y] = {'y', 'Y', 0x19},
[VK_U] = {'u', 'U', 0x15},
[VK_I] = {'i', 'I', 0x9},
[VK_O] = {'o', 'O', 0x0F},
[VK_P] = {'p', 'P', 0x10},
[VK_OEM_4] = {'[', '{', VK_ESCAPE},
[VK_OEM_6] = {']', '}', 0x29},
[VK_OEM_5] = {'\\', '|', 0x28},
[VK_A] = {'a', 'A', 0x1},
[VK_S] = {'s', 'S', 0x13},
[VK_D] = {'d', 'D', 0x4},
[VK_F] = {'f', 'F', 0x6},
[VK_G] = {'g', 'G', 0x7},
[VK_H] = {'h', 'H', 0x8},
[VK_J] = {'j', 'J', 0x0A},
[VK_K] = {'k', 'K', 0x0B},
[VK_L] = {'l', 'L', 0x0C},
[VK_OEM_1] = {';', ':', -1},
[VK_OEM_7] = {'\'', '"', -1},
[VK_RETURN] = {VK_RETURN, VK_RETURN, -1},
[VK_Z] = {'z', 'Z', 0x1A},
[VK_X] = {'x', 'X', 0x18},
[VK_C] = {'c', 'C', 0x3},
[VK_V] = {'v', 'V', 0x16},
[VK_B] = {'b', 'B', 0x2},
[VK_N] = {'n', 'N', 0x0E},
[VK_M] = {'m', 'M', 0x0D},
[VK_OEM_COMMA] = {',', ',', -1},
[VK_OEM_PERIOD] = {'.', '>', -1},
[VK_OEM_2] = {'/', '?', -1},
[VK_SPACE] = {VK_SPACE, VK_SPACE, -1},
[VK_DELETE] = {0x7F, 0x7F, -1},
[VK_DIVIDE] = {'/', '/', -1},
[VK_MULTIPLY] = {'*', '*', -1},
[VK_SUBTRACT] = {'-', '-', -1},
[VK_ADD] = {'+', '+', -1},
[VK_NUMPAD0] = {'0', '0', -1},
[VK_NUMPAD1] = {'1', '1', -1},
[VK_NUMPAD2] = {'2', '2', -1},
[VK_NUMPAD3] = {'3', '3', -1},
[VK_NUMPAD4] = {'4', '4', -1},
[VK_NUMPAD5] = {'5', '5', -1},
[VK_NUMPAD6] = {'6', '6', -1},
[VK_NUMPAD7] = {'7', '7', -1},
[VK_NUMPAD8] = {'8', '8', -1},
[VK_NUMPAD9] = {'9', '9', -1}
};
/* ref: http://techdocs.altium.com/display/FPGA/PS2+Keyboard+Scan+Codes
NOTE: This table does not contain the extended char codes, they are handled as special cases
by the keycode_ps2_to_vkey() helper function. This is done to reduce the memory footprint
of this lookup table from 65535 to 256 bytes.
*/
int16_t ps2_to_vkey[PS2_MAX_KEYCODES_BASIC] = {
[0 ... (PS2_MAX_KEYCODES_BASIC - 1)] = -1,
[PS2_KEY_ESC] = VK_ESCAPE,
[PS2_KEY_F1] = VK_F1,
[PS2_KEY_F2] = VK_F2,
[PS2_KEY_F3] = VK_F3,
[PS2_KEY_F4] = VK_F4,
[PS2_KEY_F5] = VK_F5,
[PS2_KEY_F6] = VK_F6,
[PS2_KEY_F7] = VK_F7,
[PS2_KEY_F8] = VK_F8,
[PS2_KEY_F9] = VK_F9,
[PS2_KEY_F10] = VK_F10,
[PS2_KEY_F11] = VK_F11,
[PS2_KEY_F12] = VK_F12,
[PS2_KEY_SCROLL_LOCK] = VK_SCROLL,
[PS2_KEY_TILDE] = VK_OEM_3,
[PS2_KEY_1] = VK_1,
[PS2_KEY_2] = VK_2,
[PS2_KEY_3] = VK_3,
[PS2_KEY_4] = VK_4,
[PS2_KEY_5] = VK_5,
[PS2_KEY_6] = VK_6,
[PS2_KEY_7] = VK_7,
[PS2_KEY_8] = VK_8,
[PS2_KEY_9] = VK_9,
[PS2_KEY_0] = VK_0,
[PS2_KEY_SUBTRACT] = VK_OEM_MINUS,
[PS2_KEY_EQUALS] = VK_OEM_PLUS,
[PS2_KEY_BACKSPACE] = VK_BACK,
[PS2_KEY_TAB] = VK_TAB,
[PS2_KEY_Q] = VK_Q,
[PS2_KEY_W] = VK_W,
[PS2_KEY_E] = VK_E,
[PS2_KEY_R] = VK_R,
[PS2_KEY_T] = VK_T,
[PS2_KEY_Y] = VK_Y,
[PS2_KEY_U] = VK_U,
[PS2_KEY_I] = VK_I,
[PS2_KEY_O] = VK_O,
[PS2_KEY_P] = VK_P,
[PS2_KEY_LBRACKET] = VK_OEM_4,
[PS2_KEY_RBRACKET] = VK_OEM_6,
[PS2_KEY_BACKSLASH] = VK_OEM_5,
[PS2_KEY_CAPS_LOCK] = VK_CAPITAL,
[PS2_KEY_A] = VK_A,
[PS2_KEY_S] = VK_S,
[PS2_KEY_D] = VK_D,
[PS2_KEY_F] = VK_F,
[PS2_KEY_G] = VK_G,
[PS2_KEY_H] = VK_H,
[PS2_KEY_J] = VK_J,
[PS2_KEY_K] = VK_K,
[PS2_KEY_L] = VK_L,
[PS2_KEY_COLON] = VK_OEM_1,
[PS2_KEY_TICK] = VK_OEM_7,
[PS2_KEY_ENTER] = VK_RETURN,
[PS2_KEY_SHIFT_LEFT] = VK_LSHIFT,
[PS2_KEY_Z] = VK_Z,
[PS2_KEY_X] = VK_X,
[PS2_KEY_C] = VK_C,
[PS2_KEY_V] = VK_V,
[PS2_KEY_B] = VK_B,
[PS2_KEY_N] = VK_N,
[PS2_KEY_M] = VK_M,
[PS2_KEY_COMMA] = VK_OEM_COMMA,
[PS2_KEY_DOT] = VK_OEM_PERIOD,
[PS2_KEY_SLASH] = VK_OEM_2,
[PS2_KEY_SHIFT_RIGHT] = VK_RSHIFT,
[PS2_KEY_CTRL_LEFT] = VK_LCONTROL,
[PS2_KEY_ALT_LEFT] = VK_MENU,
[PS2_KEY_SPACEBAR] = VK_SPACE,
[PS2_KEY_NUM_LOCK] = VK_NUMLOCK,
[PS2_KEY_NUM_MULTIPLY] = VK_MULTIPLY,
[PS2_KEY_NUM_MINUS] = VK_SUBTRACT,
[PS2_KEY_NUM_7] = VK_NUMPAD7,
[PS2_KEY_NUM_8] = VK_NUMPAD8,
[PS2_KEY_NUM_9] = VK_NUMPAD9,
[PS2_KEY_NUM_PLUS] = VK_ADD,
[PS2_KEY_NUM_4] = VK_NUMPAD4,
[PS2_KEY_NUM_5] = VK_NUMPAD5,
[PS2_KEY_NUM_6] = VK_NUMPAD6,
[PS2_KEY_NUM_1] = VK_NUMPAD1,
[PS2_KEY_NUM_2] = VK_NUMPAD2,
[PS2_KEY_NUM_3] = VK_NUMPAD3,
[PS2_KEY_NUM_0] = VK_NUMPAD0,
[PS2_KEY_NUM_DOT] = VK_DECIMAL
};
void
keycode_init(keycode_state_t *s,
void (*handle_keyevent_callback)(int16_t vkey, bool pressed, void *cookie),
void (*handle_chartyped_callback)(int c, void *cookie),
void (*handle_led_state_changed_callback)(void *cookie))
{
memset(s, 0, sizeof(keycode_state_t));
s->num_lock = true;
s->handle_keyevent_callback = handle_keyevent_callback;
s->handle_chartyped_callback = handle_chartyped_callback;
s->handle_led_state_changed_callback = handle_led_state_changed_callback;
}
#if KEYBOARD_KEY_DEBUG
const char*
keycode_vkey_desc(uint16_t vk)
{
switch (vk) {
case VK_LBUTTON:
return "VK_LBUTTON";
case VK_RBUTTON:
return "VK_RBUTTON";
case VK_CANCEL:
return "VK_CANCEL";
case VK_MBUTTON:
return "VK_MBUTTON";
case VK_XBUTTON1:
return "VK_XBUTTON1";
case VK_XBUTTON2:
return "VK_XBUTTON2";
case VK_BACK:
return "VK_BACK";
case VK_TAB:
return "VK_TAB";
case VK_CLEAR:
return "VK_CLEAR";
case VK_RETURN:
return "VK_RETURN";
case VK_CONTROL:
return "VK_CONTROL";
case VK_SHIFT:
return "VK_SHIFT";
case VK_MENU:
return "VK_MENU";
case VK_PAUSE:
return "VK_PAUSE";
case VK_CAPITAL:
return "VK_CAPITAL";
case VK_KANA:
return "VK_KANA";
case VK_JUNJA:
return "VK_JUNJA";
case VK_FINAL:
return "VK_FINAL";
case VK_HANJA:
return "VK_HANJA";
case VK_ESCAPE:
return "VK_ESCAPE";
case VK_NONCONVERT:
return "VK_NONCONVERT";
case VK_ACCEPT:
return "VK_ACCEPT";
case VK_MODECHANGE:
return "VK_MODECHANGE";
case VK_SPACE:
return "VK_SPACE";
case VK_PRIOR:
return "VK_PRIOR";
case VK_NEXT:
return "VK_NEXT";
case VK_END:
return "VK_END";
case VK_HOME:
return "VK_HOME";
case VK_LEFT:
return "VK_LEFT";
case VK_UP:
return "VK_UP";
case VK_RIGHT:
return "VK_RIGHT";
case VK_DOWN:
return "VK_DOWN";
case VK_SELECT:
return "VK_SELECT";
case VK_PRINT:
return "VK_PRINT";
case VK_EXECUTE:
return "VK_EXECUTE";
case VK_SNAPSHOT:
return "VK_SNAPSHOT";
case VK_INSERT:
return "VK_INSERT";
case VK_DELETE:
return "VK_DELETE";
case VK_HELP:
return "VK_HELP";
case VK_0:
return "VK_0";
case VK_1:
return "VK_1";
case VK_2:
return "VK_2";
case VK_3:
return "VK_3";
case VK_4:
return "VK_4";
case VK_5:
return "VK_5";
case VK_6:
return "VK_6";
case VK_7:
return "VK_7";
case VK_8:
return "VK_8";
case VK_9:
return "VK_9";
case VK_A:
return "VK_A";
case VK_B:
return "VK_B";
case VK_C:
return "VK_C";
case VK_D:
return "VK_D";
case VK_E:
return "VK_E";
case VK_F:
return "VK_F";
case VK_G:
return "VK_G";
case VK_H:
return "VK_H";
case VK_I:
return "VK_I";
case VK_J:
return "VK_J";
case VK_K:
return "VK_K";
case VK_L:
return "VK_L";
case VK_M:
return "VK_M";
case VK_N:
return "VK_N";
case VK_O:
return "VK_O";
case VK_P:
return "VK_P";
case VK_Q:
return "VK_Q";
case VK_R:
return "VK_R";
case VK_S:
return "VK_S";
case VK_T:
return "VK_T";
case VK_U:
return "VK_U";
case VK_V:
return "VK_V";
case VK_W:
return "VK_W";
case VK_X:
return "VK_X";
case VK_Y:
return "VK_Y";
case VK_Z:
return "VK_Z";
case VK_LWIN:
return "VK_LWIN";
case VK_RWIN:
return "VK_RWIN";
case VK_APPS:
return "VK_APPS";
case VK_SLEEP:
return "VK_SLEEP";
case VK_NUMPAD0:
return "VK_NUMPAD0";
case VK_NUMPAD1:
return "VK_NUMPAD1";
case VK_NUMPAD2:
return "VK_NUMPAD2";
case VK_NUMPAD3:
return "VK_NUMPAD3";
case VK_NUMPAD4:
return "VK_NUMPAD4";
case VK_NUMPAD5:
return "VK_NUMPAD5";
case VK_NUMPAD6:
return "VK_NUMPAD6";
case VK_NUMPAD7:
return "VK_NUMPAD7";
case VK_NUMPAD8:
return "VK_NUMPAD8";
case VK_NUMPAD9:
return "VK_NUMPAD9";
case VK_MULTIPLY:
return "VK_MULTIPLY";
case VK_ADD:
return "VK_ADD";
case VK_SEPARATOR:
return "VK_SEPARATOR";
case VK_SUBTRACT:
return "VK_SUBTRACT";
case VK_DECIMAL:
return "VK_DECIMAL";
case VK_DIVIDE:
return "VK_DIVIDE";
case VK_F1:
return "VK_F1";
case VK_F2:
return "VK_F2";
case VK_F3:
return "VK_F3";
case VK_F4:
return "VK_F4";
case VK_F5:
return "VK_F5";
case VK_F6:
return "VK_F6";
case VK_F7:
return "VK_F7";
case VK_F8:
return "VK_F8";
case VK_F9:
return "VK_F9";
case VK_F10:
return "VK_F10";
case VK_F11:
return "VK_F11";
case VK_F12:
return "VK_F12";
case VK_F13:
return "VK_F13";
case VK_F14:
return "VK_F14";
case VK_F15:
return "VK_F15";
case VK_F16:
return "VK_F16";
case VK_F17:
return "VK_F17";
case VK_F18:
return "VK_F18";
case VK_F19:
return "VK_F19";
case VK_F20:
return "VK_F20";
case VK_F21:
return "VK_F21";
case VK_F22:
return "VK_F22";
case VK_F23:
return "VK_F23";
case VK_F24:
return "VK_F24";
case VK_NUMLOCK:
return "VK_NUMLOCK";
case VK_SCROLL:
return "VK_SCROLL";
case VK_LSHIFT:
return "VK_LSHIFT";
case VK_RSHIFT:
return "VK_RSHIFT";
case VK_LCONTROL:
return "VK_LCONTROL";
case VK_RCONTROL:
return "VK_RCONTROL";
case VK_LMENU:
return "VK_LMENU";
case VK_RMENU:
return "VK_RMENU";
case VK_BROWSER_BACK:
return "VK_BROWSER_BACK";
case VK_BROWSER_FORWARD:
return "VK_BROWSER_FORWARD";
case VK_BROWSER_REFRESH:
return "VK_BROWSER_REFRESH";
case VK_BROWSER_STOP:
return "VK_BROWSER_STOP";
case VK_BROWSER_SEARCH:
return "VK_BROWSER_SEARCH";
case VK_BROWSER_FAVORITES:
return "VK_BROWSER_FAVORITES";
case VK_BROWSER_HOME:
return "VK_BROWSER_HOME";
case VK_VOLUME_MUTE:
return "VK_VOLUME_MUTE";
case VK_VOLUME_DOWN:
return "VK_VOLUME_DOWN";
case VK_VOLUME_UP:
return "VK_VOLUME_UP";
case VK_MEDIA_NEXT_TRACK:
return "VK_MEDIA_NEXT_TRACK";
case VK_MEDIA_PREV_TRACK:
return "VK_MEDIA_PREV_TRACK";
case VK_MEDIA_STOP:
return "VK_MEDIA_STOP";
case VK_MEDIA_PLAY_PAUSE:
return "VK_MEDIA_PLAY_PAUSE";
case VK_LAUNCH_MAIL:
return "VK_LAUNCH_MAIL";
case VK_LAUNCH_MEDIA_SELECT:
return "VK_LAUNCH_MEDIA_SELECT";
case VK_LAUNCH_APP1:
return "VK_LAUNCH_APP1";
case VK_LAUNCH_APP2:
return "VK_LAUNCH_APP2";
case VK_OEM_1:
return "VK_OEM_1";
case VK_OEM_PLUS:
return "VK_OEM_PLUS";
case VK_OEM_COMMA:
return "VK_OEM_COMMA";
case VK_OEM_MINUS:
return "VK_OEM_MINUS";
case VK_OEM_PERIOD:
return "VK_OEM_PERIOD";
case VK_OEM_2:
return "VK_OEM_2";
case VK_OEM_3:
return "VK_OEM_3";
case VK_OEM_4:
return "VK_OEM_4";
case VK_OEM_5:
return "VK_OEM_5";
case VK_OEM_6:
return "VK_OEM_6";
case VK_OEM_7:
return "VK_OEM_7";
case VK_OEM_8:
return "VK_OEM_8";
case VK_OEM_102:
return "VK_OEM_102";
case VK_PROCESSKEY:
return "VK_PROCESSKEY";
case VK_PACKET:
return "VK_PACKET";
case VK_ATTN:
return "VK_ATTN";
case VK_CRSEL:
return "VK_CRSEL";
case VK_EXSEL:
return "VK_EXSEL";
case VK_EREOF:
return "VK_EREOF";
case VK_PLAY:
return "VK_PLAY";
case VK_ZOOM:
return "VK_ZOOM";
case VK_NONAME:
return "VK_NONAME";
case VK_PA1:
return "VK_PA1";
case VK_OEM_CLEAR:
return "VK_OEM_CLEAR";
}
return "VK_UNKNOWN";
}
#endif /* KEYBOARD_KEY_DEBUG */
int16_t
keycode_info_char_modifier(keycode_info_t *info, bool ctrl, bool shift)
{
if (ctrl && !shift && info->ctrlchar != -1) {
return info->ctrlchar;
}
return shift ? info->uppercase : info->ch;
}
int16_t
keycode_info_char(keycode_state_t *s, keycode_info_t *info)
{
return keycode_info_char_modifier(info, s->keystate[VK_CONTROL],
s->keystate[VK_SHIFT] ^ s->caps_lock);
}
int16_t
keycode_ps2_to_vkey(int32_t ps2_keycode)
{
if (ps2_keycode >= 0 && ps2_keycode < PS2_MAX_KEYCODES_BASIC) {
return ps2_to_vkey[ps2_keycode];
}
/* Special case extended characters to avoid a large lookup table. */
switch (ps2_keycode) {
case PS2_KEY_PRTSCR:
return VK_SNAPSHOT;
case PS2_KEY_PAUSE:
return VK_PAUSE;
case PS2_KEY_WINDOWS_LEFT:
return VK_LEFT;
case PS2_KEY_ALT_RIGHT:
return VK_MENU;
case PS2_KEY_WINDOWS_RIGHT:
return VK_RIGHT;
case PS2_KEY_MENUS:
return VK_MENU;
case PS2_KEY_INSERT:
return VK_INSERT;
case PS2_KEY_HOME:
return VK_HOME;
case PS2_KEY_PAGE_UP:
return VK_PRIOR;
case PS2_KEY_DELETE:
return VK_DELETE;
case PS2_KEY_END:
return VK_END;
case PS2_KEY_PAGE_DOWN:
return VK_NEXT;
case PS2_KEY_UP_ARROW:
return VK_UP;
case PS2_KEY_LEFT_ARROW:
return VK_LEFT;
case PS2_KEY_DOWN_ARROW:
return VK_DOWN;
case PS2_KEY_RIGHT_ARROW:
return VK_RIGHT;
case PS2_KEY_NUM_DIVIDE:
return VK_DIVIDE;
case PS2_KEY_NUM_ENTER:
return VK_RETURN;
}
return -1;
}
static void
keycode_update_combined_vkeys(keycode_state_t *s)
{
s->keystate[VK_CONTROL] = s->keystate[VK_LCONTROL] || s->keystate[VK_RCONTROL];
s->keystate[VK_SHIFT] = s->keystate[VK_LSHIFT] || s->keystate[VK_RSHIFT];
}
keycode_info_t *
keycode_process_vkey_event(keycode_state_t *s, int32_t vkey, bool pressed, void* cookie)
{
assert(s);
if (vkey < 0) {
return NULL;
}
s->keystate[VK_PAUSE] = false;
s->keystate[vkey] = pressed;
keycode_update_combined_vkeys(s);
if (s->handle_keyevent_callback) {
s->handle_keyevent_callback(vkey, pressed, cookie);
}
if (pressed) {
if (vkey == VK_CAPITAL) {
s->caps_lock = !s->caps_lock;
s->led_state_changed = true;
}
if (vkey == VK_SCROLL) {
s->scroll_lock = !s->scroll_lock;
s->led_state_changed = true;
}
if (vkey == VK_NUMLOCK) {
s->num_lock = !s->num_lock;
s->led_state_changed = true;
}
if (s->led_state_changed && s->handle_led_state_changed_callback) {
s->handle_led_state_changed_callback(cookie);
}
int16_t typedc = keycode_info_char(s, &keycodes[vkey]);
if (typedc != -1 && s->handle_chartyped_callback) {
s->handle_chartyped_callback((int) typedc, cookie);
}
return &keycodes[vkey];
}
return NULL;
}
int16_t
keycode_process_vkey_event_to_char(keycode_state_t *s, int32_t vkey, bool pressed, void* cookie)
{
keycode_info_t *info = keycode_process_vkey_event(s, vkey, pressed, cookie);
return info ? keycode_info_char(s, info) : -1;
}
bool
keycode_get_async_vkey_state(keycode_state_t *s, int32_t vkey)
{
assert(vkey >= 0 && vkey < VK_MAX_ENUM);
return s->keystate[vkey];
}