|
|
452870 |
|
|
|
452870 |
#include "app.h"
|
|
|
452870 |
|
|
|
843e7a |
#include <unistd.h></unistd.h>
|
|
|
843e7a |
|
|
|
452870 |
#include <x11 xutil.h=""></x11>
|
|
|
843e7a |
#include <x11 xkblib.h=""></x11>
|
|
|
452870 |
#include <x11 extensions="" xtest.h=""></x11>
|
|
|
452870 |
|
|
|
452870 |
|
|
|
63daec |
|
|
|
63daec |
// prepare and shoose keys by algorithm from here:
|
|
|
63daec |
// https://www.x.org/releases/current/doc/libX11/libX11/libX11.html#Manipulating_the_Keyboard_Encoding
|
|
|
63daec |
|
|
|
63daec |
void inputPrepareKeysyms(unsigned int *ks0, unsigned int *ks1, int *isLetter, int *isKeypad) {
|
|
|
63daec |
if (!*ks0) *ks0 = *ks1;
|
|
|
63daec |
|
|
|
63daec |
*isLetter = 0;
|
|
|
63daec |
if (*ks0) {
|
|
|
63daec |
KeySym kl = 0, ku = 0;
|
|
|
63daec |
XConvertCase(*ks0, &kl, &ku);
|
|
|
061fcf |
if (*ks0 == kl && ku && kl != ku) {
|
|
|
63daec |
if (!*ks1) *ks1 = ku;
|
|
|
63daec |
*isLetter = 1;
|
|
|
63daec |
}
|
|
|
63daec |
}
|
|
|
63daec |
|
|
|
63daec |
if (!*ks1) *ks1 = *ks0;
|
|
|
d942fc |
*isKeypad = (0xFF80 <= *ks1 && *ks1 <= 0xFFBD) || (0x11000000 <= *ks1 && *ks1 <= 0x1100FFFF);
|
|
|
63daec |
}
|
|
|
63daec |
|
|
|
63daec |
|
|
|
63daec |
int inputChooseKeysym(unsigned int modifiers, unsigned int ks0, unsigned int ks1, int isLetter, int isKeypad) {
|
|
|
63daec |
if (!ks1) return 0;
|
|
|
63daec |
if (!ks0) return 1;
|
|
|
63daec |
|
|
|
63daec |
// normalize bits to zero or one only
|
|
|
63daec |
isLetter = !!isLetter;
|
|
|
63daec |
isKeypad = !!isKeypad;
|
|
|
63daec |
int shift = !!(modifiers & IM_SHIFT_BIT);
|
|
|
63daec |
int capsLock = !!(modifiers & IM_CAPSLOCK_BIT);
|
|
|
63daec |
int shiftLock = !!(modifiers & IM_SHIFTLOCK_BIT);
|
|
|
63daec |
int numLock = !!(modifiers & IM_NUMLOCK_BIT);
|
|
|
63daec |
|
|
|
63daec |
if (capsLock) // capsLock priority
|
|
|
63daec |
shiftLock = 0;
|
|
|
569832 |
if (isKeypad)
|
|
|
569832 |
return numLock;
|
|
|
63daec |
if (!shift && !capsLock && !shiftLock)
|
|
|
63daec |
return 0;
|
|
|
63daec |
if (!shift && capsLock)
|
|
|
63daec |
return !!isLetter;
|
|
|
63daec |
if (shift && capsLock)
|
|
|
63daec |
return !isLetter;
|
|
|
63daec |
return shift || shiftLock;
|
|
|
63daec |
}
|
|
|
63daec |
|
|
|
63daec |
|
|
|
63daec |
|
|
|
63daec |
|
|
|
63daec |
|
|
|
452870 |
int inputInit(Input *in, App *app) {
|
|
|
452870 |
LOGDBG("input: init");
|
|
|
452870 |
|
|
|
452870 |
in->app = app;
|
|
|
843e7a |
in->key0 = in->key1 = in->mapKey = 0;
|
|
|
843e7a |
in->mapDown = 0;
|
|
|
843e7a |
in->updated = 0;
|
|
|
63daec |
in->modifiers = 0;
|
|
|
843e7a |
memset(in->keys, 0, sizeof(in->keys));
|
|
|
843e7a |
memset(in->masks, 0, sizeof(in->masks));
|
|
|
452870 |
|
|
|
452870 |
LOGDBG("input: init: connect to X11");
|
|
|
452870 |
in->dpy = XOpenDisplay(NULL);
|
|
|
452870 |
if (!in->dpy)
|
|
|
452870 |
return LOGERR("input: init: cannot connect to X11");
|
|
|
452870 |
|
|
|
843e7a |
LOGDBG("input: init: query keycode range");
|
|
|
843e7a |
XDisplayKeycodes(in->dpy, &in->key0, &in->key1);
|
|
|
843e7a |
++in->key1;
|
|
|
843e7a |
if (in->key0 < 0) in->key0 = 0;
|
|
|
843e7a |
if (in->key1 > IN_MAXKEYS) in->key1 = IN_MAXKEYS;
|
|
|
843e7a |
LOGDBG("input: init: keycode range [%d, %d)", in->key0, in->key1);
|
|
|
452870 |
|
|
|
843e7a |
inputUpdateLayout(in);
|
|
|
63daec |
inputUpdateModifiers(in);
|
|
|
452870 |
|
|
|
452870 |
return 1;
|
|
|
452870 |
}
|
|
|
452870 |
|
|
|
452870 |
|
|
|
452870 |
void inputDeinit(Input *in) {
|
|
|
452870 |
LOGDBG("input: deinit");
|
|
|
843e7a |
if (in->mapKey) {
|
|
|
843e7a |
LOGDBG("input: deinit: unmap temporary key %d", in->mapKey);
|
|
|
843e7a |
if (in->mapDown)
|
|
|
843e7a |
inputEvent(in, in->mapKey, 0);
|
|
|
843e7a |
KeySym ks[4] = {};
|
|
|
843e7a |
XChangeKeyboardMapping(in->dpy, in->mapKey, 4, ks, 1);
|
|
|
843e7a |
}
|
|
|
452870 |
XCloseDisplay(in->dpy);
|
|
|
452870 |
}
|
|
|
452870 |
|
|
|
452870 |
|
|
|
843e7a |
void inputUpdateLayout(Input *in) {
|
|
|
843e7a |
LOGDBG("input: update layout");
|
|
|
452870 |
|
|
|
843e7a |
static const KeySym imKeys[IM_COUNT] = {
|
|
|
843e7a |
XK_Shift_L,
|
|
|
843e7a |
XK_Caps_Lock,
|
|
|
843e7a |
XK_Shift_Lock,
|
|
|
843e7a |
XK_Num_Lock,
|
|
|
569832 |
XK_Mode_switch,
|
|
|
569832 |
XK_Scroll_Lock };
|
|
|
452870 |
|
|
|
843e7a |
in->updated = 0;
|
|
|
843e7a |
memset(in->masks, 0, sizeof(in->masks));
|
|
|
843e7a |
|
|
|
843e7a |
LOGDBG("input: update layout: read modifiers");
|
|
|
843e7a |
XModifierKeymap *mods = XGetModifierMapping(in->dpy);
|
|
|
843e7a |
|
|
|
843e7a |
LOGDBG("input: update layout: read keyboard mapping");
|
|
|
843e7a |
int mcnt = 0;
|
|
|
843e7a |
KeySym *ks = XGetKeyboardMapping(in->dpy, in->key0, in->key1 - in->key0, &mcnt);
|
|
|
843e7a |
for(int i = in->key0; i < in->key1; ++i, ks += mcnt) {
|
|
|
843e7a |
// search key for temporary maps
|
|
|
843e7a |
if (!in->mapKey) {
|
|
|
843e7a |
int found = 1;
|
|
|
843e7a |
for(int i = 0; i < mcnt; ++i)
|
|
|
843e7a |
if (ks[i]) { found = 0; break; }
|
|
|
843e7a |
if (found) {
|
|
|
843e7a |
in->mapKey = i;
|
|
|
843e7a |
LOGDBG("input: update layout: choose keycode for temporary map: %d", in->mapKey);
|
|
|
843e7a |
}
|
|
|
452870 |
}
|
|
|
452870 |
|
|
|
843e7a |
//if (i == 50 || i == 62 || i == 66) {
|
|
|
843e7a |
// printf("keysyms for key #%d (%d):\n", i, mcnt);
|
|
|
843e7a |
// for(int i = 0; i < mcnt; ++i) {
|
|
|
843e7a |
// printf(" %lu\n", ks[i]);
|
|
|
843e7a |
// }
|
|
|
843e7a |
// printf("\n");
|
|
|
843e7a |
//}
|
|
|
843e7a |
|
|
|
843e7a |
// update state masks
|
|
|
843e7a |
for(int imask = 0; imask < IM_COUNT; ++imask)
|
|
|
843e7a |
for(int iks = 0; iks < mcnt; ++iks)
|
|
|
843e7a |
if (ks[iks] == imKeys[imask]) {
|
|
|
843e7a |
LOGDBG("input: update layout: keycode for mask %d is %d", imask, i);
|
|
|
843e7a |
for(int imod = 0; imod < XkbNumModifiers; ++imod)
|
|
|
843e7a |
for(int ik = 0; ik < mods->max_keypermod; ++ik)
|
|
|
843e7a |
if (mods->modifiermap[imod*mods->max_keypermod + ik] == i)
|
|
|
843e7a |
in->masks[imask] |= 1 << imod;
|
|
|
843e7a |
}
|
|
|
843e7a |
|
|
|
843e7a |
// get base keys
|
|
|
843e7a |
unsigned int k[] = {
|
|
|
843e7a |
mcnt > 0 ? ks[0] : 0,
|
|
|
843e7a |
mcnt > 1 ? ks[1] : 0,
|
|
|
843e7a |
mcnt > 2 ? ks[2] : 0,
|
|
|
843e7a |
mcnt > 3 ? ks[3] : 0 };
|
|
|
843e7a |
if (!k[2] && !k[3])
|
|
|
843e7a |
k[2] = k[0], k[3] = k[1];
|
|
|
452870 |
|
|
|
843e7a |
int isLetter[2] = {};
|
|
|
63daec |
int isKeypad[2] = {};
|
|
|
63daec |
inputPrepareKeysyms(&k[0], &k[1], &isLetter[0], &isKeypad[0]);
|
|
|
63daec |
|
|
|
63daec |
// fill key modes
|
|
|
63daec |
for(unsigned int modifiers = 0; modifiers < (1 << IM_COUNT); ++modifiers) {
|
|
|
63daec |
int g = !!(modifiers & IM_GROUP_BIT);
|
|
|
63daec |
int ki = inputChooseKeysym(modifiers, k[g*2], k[g*2 + 1], isLetter[g], isKeypad[g]);
|
|
|
63daec |
in->keys[i][modifiers] = k[g*2 + ki];
|
|
|
843e7a |
}
|
|
|
452870 |
}
|
|
|
452870 |
|
|
|
63daec |
// Shift modifier index must be 0
|
|
|
63daec |
in->masks[IM_SHIFT] = 1 << 0;
|
|
|
63daec |
// when caps and shift locks in the same modifier - this modifier is CapsLock
|
|
|
63daec |
in->masks[IM_SHIFTLOCK] &= ~in->masks[IM_CAPSLOCK];
|
|
|
63daec |
// Lock modifier index must be 1
|
|
|
63daec |
in->masks[IM_CAPSLOCK] &= 1 << 1;
|
|
|
63daec |
in->masks[IM_SHIFTLOCK] &= 1 << 1;
|
|
|
63daec |
|
|
|
843e7a |
XFreeModifiermap(mods);
|
|
|
843e7a |
|
|
|
843e7a |
for(int i = 0; i < IM_COUNT; ++i)
|
|
|
843e7a |
LOGDBG("input: update layout: masks[%d] = %08u", i, in->masks[i]);
|
|
|
843e7a |
}
|
|
|
843e7a |
|
|
|
843e7a |
|
|
|
63daec |
void inputUpdateModifiers(Input *in) {
|
|
|
63daec |
LOGDBG("input: update modifiers");
|
|
|
63daec |
XkbStateRec state = {};
|
|
|
63daec |
XkbGetState(in->dpy, XkbUseCoreKbd, &state);
|
|
|
63daec |
in->modifiers = 0;
|
|
|
63daec |
for(int i = 0; i < IM_COUNT; ++i)
|
|
|
63daec |
if (in->masks[i] & state.mods)
|
|
|
63daec |
in->modifiers |= (1 << i);
|
|
|
63daec |
LOGDBG("input: update modifiers: current state %08x, chosen mod %02x", state.mods, in->modifiers);
|
|
|
63daec |
}
|
|
|
63daec |
|
|
|
63daec |
|
|
|
843e7a |
int inputKeycode(Input *in, unsigned int keySym) {
|
|
|
843e7a |
LOGDBG("input: keycode: keySym=%u", keySym);
|
|
|
843e7a |
|
|
|
843e7a |
if (!keySym)
|
|
|
843e7a |
return 0;
|
|
|
63daec |
|
|
|
843e7a |
for(int i = in->key0; i < in->key1; ++i) {
|
|
|
63daec |
if (in->keys[i][in->modifiers] == keySym) {
|
|
|
843e7a |
LOGDBG("input: keycode: found keycode %d", i);
|
|
|
843e7a |
return i;
|
|
|
843e7a |
}
|
|
|
843e7a |
}
|
|
|
843e7a |
|
|
|
843e7a |
if (!in->mapKey) {
|
|
|
843e7a |
LOGWRN("input: keycode: no keycode mapped to keySym[%u], and no free keycodes for temporary mapping", keySym);
|
|
|
843e7a |
return 0;
|
|
|
843e7a |
}
|
|
|
843e7a |
|
|
|
843e7a |
LOGDBG("input: keycode: temporary remap keySym[%u] to %d", keySym, in->mapKey);
|
|
|
843e7a |
|
|
|
843e7a |
if (in->mapDown) {
|
|
|
843e7a |
LOGWRN("input: event: temporary remap: mapped keys collision, release previously mapped key");
|
|
|
843e7a |
inputEvent(in, in->mapKey, 0);
|
|
|
843e7a |
}
|
|
|
843e7a |
|
|
|
843e7a |
for(int i = 0; i < IM_COUNT; ++i)
|
|
|
843e7a |
in->keys[in->mapKey][i] = keySym;
|
|
|
843e7a |
KeySym ks[4] = { keySym, keySym, keySym, keySym };
|
|
|
843e7a |
XChangeKeyboardMapping(in->dpy, in->mapKey, 4, ks, 1);
|
|
|
843e7a |
XSync(in->dpy, False);
|
|
|
843e7a |
in->updated = 1;
|
|
|
843e7a |
|
|
|
843e7a |
return in->mapKey;
|
|
|
843e7a |
}
|
|
|
843e7a |
|
|
|
843e7a |
|
|
|
843e7a |
void inputEvent(Input *in, int keycode, int press) {
|
|
|
843e7a |
LOGDBG("input: event: keycode=%d press=%d", keycode, press);
|
|
|
843e7a |
press = !!press;
|
|
|
843e7a |
if (keycode < in->key0 || keycode >= in->key1) {
|
|
|
843e7a |
LOGERR("input: event: bad keycode=%d", keycode);
|
|
|
843e7a |
return;
|
|
|
843e7a |
}
|
|
|
452870 |
XTestFakeKeyEvent(in->dpy, keycode, press, CurrentTime);
|
|
|
452870 |
XSync(in->dpy, False);
|
|
|
d3e9d7 |
inputUpdateModifiers(in);
|
|
|
452870 |
}
|
|
|
452870 |
|