linux keycode physical/language switch, numpad tweaks

This commit is contained in:
Alex Baines 2020-05-09 21:13:20 +01:00
parent 4ae8883d4c
commit 8b8fd3314d
1 changed files with 220 additions and 70 deletions

View File

@ -164,6 +164,7 @@ struct Linux_Vars {
Linux_Input_Chunk input; Linux_Input_Chunk input;
int xkb_event; int xkb_event;
int xkb_group; // active keyboard layout (0-3) int xkb_group; // active keyboard layout (0-3)
KeyCode prev_filtered_key;
Key_Mode key_mode; Key_Mode key_mode;
@ -1035,66 +1036,16 @@ linux_x11_init(int argc, char** argv, Plat_Settings* settings) {
} }
} }
global Key_Code keycode_lookup_table[255]; global Key_Code keycode_lookup_table_physical[255];
global Key_Code keycode_lookup_table_language[255];
struct SymCode {
KeySym sym;
Key_Code code;
};
internal void internal void
linux_keycode_init(Display* dpy){ linux_keycode_init_common(Display* dpy, Key_Code* keycode_lookup_table, SymCode* sym_table, SymCode* p, size_t sym_table_size){
block_zero_array(keycode_lookup_table);
// Find these keys by physical position, and map to QWERTY KeyCodes
#define K(k) glue(KeyCode_, k)
static const u8 positional_keys[] = {
K(1), K(2), K(3), K(4), K(5), K(6), K(7), K(8), K(9), K(0), K(Minus), K(Equal),
K(Q), K(W), K(E), K(R), K(T), K(Y), K(U), K(I), K(O), K(P), K(LeftBracket), K(RightBracket),
K(A), K(S), K(D), K(F), K(G), K(H), K(J), K(K), K(L), K(Semicolon), K(Quote), /*uk hash*/0,
K(Z), K(X), K(C), K(V), K(B), K(N), K(M), K(Comma), K(Period), K(ForwardSlash), 0, 0
};
#undef K
// XKB gives the alphanumeric keys names like AE01 -> E is the row (from B-E), 01 is the column (01-12).
// to get key names in .ps file: setxkbmap -print | xkbcomp - - | xkbprint -label name - out.ps
static const int ncols = 12;
static const int nrows = 4;
for(int i = XkbMinLegalKeyCode; i <= XkbMaxLegalKeyCode; ++i) {
const char* name = linuxvars.xkb->names->keys[i].name;
// alphanumeric keys
if(name[0] == 'A' && name[1] >= 'B' && name[1] <= 'E') {
int row = (nrows - 1) - (name[1] - 'B');
int col = (name[2] - '0') * 10 + (name[3] - '0') - 1;
if(row >= 0 && row < nrows && col >= 0 && col < ncols) {
keycode_lookup_table[i] = positional_keys[row * ncols + col];
}
}
// numpad
else if(name[0] == 'K' && name[1] == 'P' && name[2] >= '0' && name[2] <= '9' && !name[3]) {
keycode_lookup_table[i] = KeyCode_NumPad0 + name[2] - '0';
}
// a few special cases:
else if(memcmp(name, "TLDE", XkbKeyNameLength) == 0) {
keycode_lookup_table[i] = KeyCode_Tick;
} else if(memcmp(name, "BKSL", XkbKeyNameLength) == 0) {
keycode_lookup_table[i] = KeyCode_BackwardSlash;
} else if(memcmp(name, "LSGT", XkbKeyNameLength) == 0) {
// UK extra key between left shift and Z
// it prints \ and | with shift. KeyCode_Backslash will be where UK # is.
keycode_lookup_table[i] = KeyCode_Ex0;
}
}
// Find the rest by their key label
struct SymCode { KeySym sym; Key_Code code; };
SymCode sym_table[108];
SymCode* p = sym_table;
*p++ = { XK_space, KeyCode_Space }; *p++ = { XK_space, KeyCode_Space };
*p++ = { XK_Tab, KeyCode_Tab }; *p++ = { XK_Tab, KeyCode_Tab };
@ -1142,7 +1093,7 @@ linux_keycode_init(Display* dpy){
*p++ = { XK_KP_Enter, KeyCode_Return }; // NumPadEnter? *p++ = { XK_KP_Enter, KeyCode_Return }; // NumPadEnter?
const int table_size = p - sym_table; const int table_size = p - sym_table;
Assert(table_size < ArrayCount(sym_table)); Assert(table_size < sym_table_size);
Key_Code next_extra = KeyCode_Ex1; Key_Code next_extra = KeyCode_Ex1;
const Key_Code max_extra = KeyCode_Ex29; const Key_Code max_extra = KeyCode_Ex29;
@ -1159,6 +1110,26 @@ linux_keycode_init(Display* dpy){
for(j = 0; j < table_size; ++j) { for(j = 0; j < table_size; ++j) {
if(sym_table[j].sym == sym) { if(sym_table[j].sym == sym) {
keycode_lookup_table[i] = sym_table[j].code; keycode_lookup_table[i] = sym_table[j].code;
//printf("lookup %s = %d\n", key_code_name[sym_table[j].code], i);
break;
}
}
if(j != table_size){
continue;
}
// nothing found - try with shift held (needed for e.g. belgian numbers to bind).
KeySym shift_sym = NoSymbol;
if(!XkbTranslateKeyCode(linuxvars.xkb, i, XkbBuildCoreState(ShiftMask, linuxvars.xkb_group), NULL, &shift_sym)) {
continue;
}
for(j = 0; j < table_size; ++j) {
if(sym_table[j].sym == shift_sym) {
keycode_lookup_table[i] = sym_table[j].code;
//printf("lookup %s = %d\n", key_code_name[sym_table[j].code], i);
break; break;
} }
} }
@ -1168,6 +1139,109 @@ linux_keycode_init(Display* dpy){
keycode_lookup_table[i] = next_extra++; keycode_lookup_table[i] = next_extra++;
} }
} }
}
internal void
linux_keycode_init_language(Display* dpy, Key_Code* keycode_lookup_table){
SymCode sym_table[300];
SymCode* p = sym_table;
for(unsigned int i = 0; i <= 26; ++i) {
*p++ = { XK_a + i, KeyCode_A + i};
}
for(unsigned int i = 0; i <= 26; ++i) {
*p++ = { XK_A + i, KeyCode_A + i};
}
for(unsigned int i = 0; i <= 9; ++i) {
*p++ = { XK_0 + i, KeyCode_0 + i};
}
*p++ = { XK_grave, KeyCode_Tick };
*p++ = { XK_minus, KeyCode_Minus };
*p++ = { XK_equal, KeyCode_Equal };
*p++ = { XK_bracketleft, KeyCode_LeftBracket };
*p++ = { XK_bracketright, KeyCode_RightBracket };
*p++ = { XK_semicolon, KeyCode_Semicolon };
*p++ = { XK_apostrophe, KeyCode_Quote };
*p++ = { XK_comma, KeyCode_Comma };
*p++ = { XK_period, KeyCode_Period };
*p++ = { XK_slash, KeyCode_ForwardSlash };
*p++ = { XK_backslash, KeyCode_BackwardSlash };
linux_keycode_init_common(dpy, keycode_lookup_table, sym_table, p, ArrayCount(sym_table));
}
internal void
linux_keycode_init_physical(Display* dpy, Key_Code* keycode_lookup_table){
// Find common keys by their key label
SymCode sym_table[100];
linux_keycode_init_common(dpy, keycode_lookup_table, sym_table, sym_table, ArrayCount(sym_table));
// Find these keys by physical position, and map to QWERTY KeyCodes
#define K(k) glue(KeyCode_, k)
static const u8 positional_keys[] = {
K(1), K(2), K(3), K(4), K(5), K(6), K(7), K(8), K(9), K(0), K(Minus), K(Equal),
K(Q), K(W), K(E), K(R), K(T), K(Y), K(U), K(I), K(O), K(P), K(LeftBracket), K(RightBracket),
K(A), K(S), K(D), K(F), K(G), K(H), K(J), K(K), K(L), K(Semicolon), K(Quote), /*uk hash*/0,
K(Z), K(X), K(C), K(V), K(B), K(N), K(M), K(Comma), K(Period), K(ForwardSlash), 0, 0
};
#undef K
// XKB gives the alphanumeric keys names like AE01 -> E is the row (from B-E), 01 is the column (01-12).
// to get key names in .ps file: setxkbmap -print | xkbcomp - - | xkbprint -label name - out.ps
static const int ncols = 12;
static const int nrows = 4;
for(int i = XkbMinLegalKeyCode; i <= XkbMaxLegalKeyCode; ++i) {
const char* name = linuxvars.xkb->names->keys[i].name;
// alphanumeric keys
if(name[0] == 'A' && name[1] >= 'B' && name[1] <= 'E') {
int row = (nrows - 1) - (name[1] - 'B');
int col = (name[2] - '0') * 10 + (name[3] - '0') - 1;
if(row >= 0 && row < nrows && col >= 0 && col < ncols) {
keycode_lookup_table[i] = positional_keys[row * ncols + col];
}
}
// numpad
else if(name[0] == 'K' && name[1] == 'P' && name[2] >= '0' && name[2] <= '9' && !name[3]) {
// don't overwrite - for e.g. laptops with numpad keys embedded in the normal ones, toggling with numlock
if(keycode_lookup_table[i] == 0) {
keycode_lookup_table[i] = KeyCode_NumPad0 + name[2] - '0';
}
}
// a few special cases:
else if(memcmp(name, "TLDE", XkbKeyNameLength) == 0) {
keycode_lookup_table[i] = KeyCode_Tick;
} else if(memcmp(name, "BKSL", XkbKeyNameLength) == 0) {
keycode_lookup_table[i] = KeyCode_BackwardSlash;
} else if(memcmp(name, "LSGT", XkbKeyNameLength) == 0) {
// UK extra key between left shift and Z
// it prints \ and | with shift. KeyCode_Backslash will be where UK # is.
keycode_lookup_table[i] = KeyCode_Ex0;
}
}
}
internal void
linux_keycode_init(Display* dpy){
block_zero_array(keycode_lookup_table_physical);
block_zero_array(keycode_lookup_table_language);
linux_keycode_init_physical(dpy, keycode_lookup_table_physical);
linux_keycode_init_language(dpy, keycode_lookup_table_language);
} }
internal void internal void
@ -1341,6 +1415,33 @@ linux_filter_text(Arena* arena, u8* buf, int len) {
return SCu8(result, outp - result); return SCu8(result, outp - result);
} }
internal KeyCode
linux_numlock_convert(KeyCode in){
static const KeyCode lookup[] = {
KeyCode_Insert,
KeyCode_End,
KeyCode_Down,
KeyCode_PageDown,
KeyCode_Left,
0,
KeyCode_Right,
KeyCode_Home,
KeyCode_Up,
KeyCode_PageUp,
0, 0, 0,
KeyCode_Delete,
};
if(in >= KeyCode_NumPad0 && in <= KeyCode_NumPadDot) {
KeyCode ret = lookup[in - KeyCode_NumPad0];
if(ret != 0) {
return ret;
}
}
return in;
}
internal void internal void
linux_handle_x11_events() { linux_handle_x11_events() {
static XEvent prev_event = {}; static XEvent prev_event = {};
@ -1350,12 +1451,22 @@ linux_handle_x11_events() {
XEvent event; XEvent event;
XNextEvent(linuxvars.dpy, &event); XNextEvent(linuxvars.dpy, &event);
b32 filtered = false;
if (XFilterEvent(&event, None) == True){ if (XFilterEvent(&event, None) == True){
filtered = true;
if(event.type != KeyPress && event.type != KeyRelease) {
continue; continue;
} }
}
u64 event_id = (u64)event.xkey.serial << 32 | event.xkey.time;
switch(event.type) { switch(event.type) {
case KeyPress: { case KeyPress: {
// DEBUG
linuxvars.key_mode = KeyMode_Physical;
should_step = true; should_step = true;
Input_Modifier_Set_Fixed* mods = &linuxvars.input.pers.modifiers; Input_Modifier_Set_Fixed* mods = &linuxvars.input.pers.modifiers;
@ -1383,12 +1494,41 @@ linux_handle_x11_events() {
add_modifier(mods, KeyCode_Shift); add_modifier(mods, KeyCode_Shift);
} }
Key_Code key = keycode_lookup_table[(u8)event.xkey.keycode]; Key_Code key;
//printf("key %d = %s\n", event.xkey.keycode, key_code_name[key]); if(linuxvars.key_mode == KeyMode_Physical) {
key = keycode_lookup_table_physical[(u8)event.xkey.keycode];
} else {
key = keycode_lookup_table_language[(u8)event.xkey.keycode];
}
if(!(state & Mod2Mask)) {
key = linux_numlock_convert(key);
}
//printf("key %d = %s (f:%d)\n", event.xkey.keycode, key_code_name[key], filtered);
b32 is_dead = false;
if (keysym >= XK_dead_grave && keysym <= XK_dead_greek && len == 0) {
is_dead = true;
printf(" *** Set Dead Key flag here!\n");
}
if(!is_dead && filtered) {
linuxvars.prev_filtered_key = key;
break;
}
// send a keycode for the key after the dead key
if(!key && linuxvars.prev_filtered_key) {
key = linuxvars.prev_filtered_key;
linuxvars.prev_filtered_key = 0;
}
Input_Event* key_event = NULL; Input_Event* key_event = NULL;
if(key) { if(key) {
add_modifier(mods, key); add_modifier(mods, key);
// printf(" push key %d\n", key);
key_event = push_input_event(&linuxvars.frame_arena, &linuxvars.input.trans.event_list); key_event = push_input_event(&linuxvars.frame_arena, &linuxvars.input.trans.event_list);
key_event->kind = InputEventKind_KeyStroke; key_event->kind = InputEventKind_KeyStroke;
key_event->key.code = key; key_event->key.code = key;
@ -1399,6 +1539,7 @@ linux_handle_x11_events() {
if(status == XLookupChars || status == XLookupBoth) { if(status == XLookupChars || status == XLookupBoth) {
String_Const_u8 str = linux_filter_text(&linuxvars.frame_arena, buf, len); String_Const_u8 str = linux_filter_text(&linuxvars.frame_arena, buf, len);
if(str.size) { if(str.size) {
// printf(" push txt %d\n", key);
text_event = push_input_event(&linuxvars.frame_arena, &linuxvars.input.trans.event_list); text_event = push_input_event(&linuxvars.frame_arena, &linuxvars.input.trans.event_list);
text_event->kind = InputEventKind_TextInsert; text_event->kind = InputEventKind_TextInsert;
text_event->text.string = str; text_event->text.string = str;
@ -1408,7 +1549,6 @@ linux_handle_x11_events() {
if(key_event && text_event) { if(key_event && text_event) {
key_event->key.first_dependent_text = text_event; key_event->key.first_dependent_text = text_event;
} }
} break; } break;
case KeyRelease: { case KeyRelease: {
@ -1422,7 +1562,17 @@ linux_handle_x11_events() {
set_modifier(mods, KeyCode_CapsLock, state & LockMask); set_modifier(mods, KeyCode_CapsLock, state & LockMask);
set_modifier(mods, KeyCode_Alt, state & Mod1Mask); set_modifier(mods, KeyCode_Alt, state & Mod1Mask);
Key_Code key = keycode_lookup_table[(u8)event.xkey.keycode]; Key_Code key;
if(linuxvars.key_mode == KeyMode_Physical) {
key = keycode_lookup_table_physical[(u8)event.xkey.keycode];
} else {
key = keycode_lookup_table_language[(u8)event.xkey.keycode];
}
// num lock off -> convert KP keys to Insert, Home, End etc.
if(!(state & Mod2Mask)) {
key = linux_numlock_convert(key);
}
Input_Event* key_event = NULL; Input_Event* key_event = NULL;
if(key) { if(key) {