Blame input.c

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
  
8864eb
  CLEARFROM(in, app);
452870
  in->app = app;
452870
  
843e7a
  LOGDBG("input: init: query keycode range");
8864eb
  XDisplayKeycodes(in->app->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
8864eb
  inputUpdateLayout(in, in->key0, in->key1);
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] = {};
8864eb
    XChangeKeyboardMapping(in->app->dpy, in->mapKey, 4, ks, 1);
843e7a
  }
452870
}
452870
452870
8864eb
void inputUpdateLayout(Input* in, int key0, int key1) {
8864eb
  if (key0 < in->key0) key0 = in->key0;
8864eb
  if (key1 > in->key1) key1 = in->key1;
8864eb
  if (key1 <= key0)
8864eb
    return;
8864eb
  if (in->mapKey && key0 == in->mapKey && key1 - key0 == 1)
8864eb
    return;
8864eb
    
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
  memset(in->masks, 0, sizeof(in->masks));
843e7a
    
843e7a
  LOGDBG("input: update layout: read modifiers");
8864eb
  XModifierKeymap *mods = XGetModifierMapping(in->app->dpy);
843e7a
  
843e7a
  LOGDBG("input: update layout: read keyboard mapping");
843e7a
  int mcnt = 0;
8864eb
  KeySym *ks = XGetKeyboardMapping(in->app->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]);
8864eb
  
8864eb
  inputUpdateModifiers(in);
843e7a
}
843e7a
843e7a
63daec
void inputUpdateModifiers(Input *in) {
63daec
  LOGDBG("input: update modifiers");
63daec
  XkbStateRec state = {};
8864eb
  XkbGetState(in->app->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 };
8864eb
  XChangeKeyboardMapping(in->app->dpy, in->mapKey, 4, ks, 1);
8864eb
  XSync(in->app->dpy, False);
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
  }
8864eb
  XTestFakeKeyEvent(in->app->dpy, keycode, press, CurrentTime);
8864eb
  XSync(in->app->dpy, False);
d3e9d7
  inputUpdateModifiers(in);
452870
}
452870