Fixed bug 2096 - Mapping from scancode to keycode doesn't work for remapped modifier keys
authorSam Lantinga <slouken@libsdl.org>
Thu, 28 May 2015 12:48:20 -0700
changeset 96789e8323b058d6
parent 9677 8201e3e3d026
child 9679 7fc4a8be47a8
Fixed bug 2096 - Mapping from scancode to keycode doesn't work for remapped modifier keys

Jacob Lee

If a user has a non-standard keyboard mapping -- say, their caps lock key has been mapped to Ctrl -- then SDL_GetModState() is no longer accurate: it only considers the unmapped keys. This is a regression from SDL 1.2.

I think there are two parts to this bug: first, GetModState should use keycodes, rather than scancodes, which is easy enough.

Unfortunately, on my system, SDL considers Caps Lock, even when mapped as Control, to be both SDL_SCANCODE_CAPSLOCK and SDLK_CAPSLOCK. The output from checkkeys for it is:

INFO: Key pressed : scancode 57 = CapsLock, keycode 0x40000039 = CapsLock modifiers: CAPS

Whereas the output for xev is:

KeyPress event, serial 41, synthetic NO, window 0x4a00001,
root 0x9a, subw 0x0, time 40218333, (144,177), root:(1458,222),
state 0x10, keycode 66 (keysym 0xffe3, Control_L), same_screen YES,
XKeysymToKeycode returns keycode: 37
XLookupString gives 0 bytes:
XmbLookupString gives 0 bytes:
XFilterEvent returns: False

I think the problem is that X11_UpdateKeymap in SDL_x11keyboard.c only builds a mapping for keycodes associated with a Unicode character (anything where X11_KeyCodeToUcs returns a value). In the case of caps lock, SDL scancode 57 becomes x11 keycode 66, which becomes x11 keysym 65507(Control_L), which does not have a unicode value.

To fix this, I suspect that SDL needs a mapping of the rest of the x11 keysyms to their corresponding SDL key codes.
src/events/SDL_keyboard.c
src/video/x11/SDL_x11keyboard.c
     1.1 --- a/src/events/SDL_keyboard.c	Thu May 28 15:36:27 2015 -0400
     1.2 +++ b/src/events/SDL_keyboard.c	Thu May 28 12:48:20 2015 -0700
     1.3 @@ -662,6 +662,8 @@
     1.4  {
     1.5      SDL_Keyboard *keyboard = &SDL_keyboard;
     1.6      int posted;
     1.7 +    SDL_Keymod modifier;
     1.8 +    SDL_Keycode keycode;
     1.9      Uint16 modstate;
    1.10      Uint32 type;
    1.11      Uint8 repeat;
    1.12 @@ -673,82 +675,6 @@
    1.13      printf("The '%s' key has been %s\n", SDL_GetScancodeName(scancode),
    1.14             state == SDL_PRESSED ? "pressed" : "released");
    1.15  #endif
    1.16 -    if (state == SDL_PRESSED) {
    1.17 -        modstate = keyboard->modstate;
    1.18 -        switch (scancode) {
    1.19 -        case SDL_SCANCODE_NUMLOCKCLEAR:
    1.20 -            keyboard->modstate ^= KMOD_NUM;
    1.21 -            break;
    1.22 -        case SDL_SCANCODE_CAPSLOCK:
    1.23 -            keyboard->modstate ^= KMOD_CAPS;
    1.24 -            break;
    1.25 -        case SDL_SCANCODE_LCTRL:
    1.26 -            keyboard->modstate |= KMOD_LCTRL;
    1.27 -            break;
    1.28 -        case SDL_SCANCODE_RCTRL:
    1.29 -            keyboard->modstate |= KMOD_RCTRL;
    1.30 -            break;
    1.31 -        case SDL_SCANCODE_LSHIFT:
    1.32 -            keyboard->modstate |= KMOD_LSHIFT;
    1.33 -            break;
    1.34 -        case SDL_SCANCODE_RSHIFT:
    1.35 -            keyboard->modstate |= KMOD_RSHIFT;
    1.36 -            break;
    1.37 -        case SDL_SCANCODE_LALT:
    1.38 -            keyboard->modstate |= KMOD_LALT;
    1.39 -            break;
    1.40 -        case SDL_SCANCODE_RALT:
    1.41 -            keyboard->modstate |= KMOD_RALT;
    1.42 -            break;
    1.43 -        case SDL_SCANCODE_LGUI:
    1.44 -            keyboard->modstate |= KMOD_LGUI;
    1.45 -            break;
    1.46 -        case SDL_SCANCODE_RGUI:
    1.47 -            keyboard->modstate |= KMOD_RGUI;
    1.48 -            break;
    1.49 -        case SDL_SCANCODE_MODE:
    1.50 -            keyboard->modstate |= KMOD_MODE;
    1.51 -            break;
    1.52 -        default:
    1.53 -            break;
    1.54 -        }
    1.55 -    } else {
    1.56 -        switch (scancode) {
    1.57 -        case SDL_SCANCODE_NUMLOCKCLEAR:
    1.58 -        case SDL_SCANCODE_CAPSLOCK:
    1.59 -            break;
    1.60 -        case SDL_SCANCODE_LCTRL:
    1.61 -            keyboard->modstate &= ~KMOD_LCTRL;
    1.62 -            break;
    1.63 -        case SDL_SCANCODE_RCTRL:
    1.64 -            keyboard->modstate &= ~KMOD_RCTRL;
    1.65 -            break;
    1.66 -        case SDL_SCANCODE_LSHIFT:
    1.67 -            keyboard->modstate &= ~KMOD_LSHIFT;
    1.68 -            break;
    1.69 -        case SDL_SCANCODE_RSHIFT:
    1.70 -            keyboard->modstate &= ~KMOD_RSHIFT;
    1.71 -            break;
    1.72 -        case SDL_SCANCODE_LALT:
    1.73 -            keyboard->modstate &= ~KMOD_LALT;
    1.74 -            break;
    1.75 -        case SDL_SCANCODE_RALT:
    1.76 -            keyboard->modstate &= ~KMOD_RALT;
    1.77 -            break;
    1.78 -        case SDL_SCANCODE_LGUI:
    1.79 -            keyboard->modstate &= ~KMOD_LGUI;
    1.80 -            break;
    1.81 -        case SDL_SCANCODE_RGUI:
    1.82 -            keyboard->modstate &= ~KMOD_RGUI;
    1.83 -            break;
    1.84 -        case SDL_SCANCODE_MODE:
    1.85 -            keyboard->modstate &= ~KMOD_MODE;
    1.86 -            break;
    1.87 -        default:
    1.88 -            break;
    1.89 -        }
    1.90 -        modstate = keyboard->modstate;
    1.91 -    }
    1.92  
    1.93      /* Figure out what type of event this is */
    1.94      switch (state) {
    1.95 @@ -775,6 +701,59 @@
    1.96      /* Update internal keyboard state */
    1.97      keyboard->keystate[scancode] = state;
    1.98  
    1.99 +    keycode = keyboard->keymap[scancode];
   1.100 +
   1.101 +    /* Update modifiers state if applicable */
   1.102 +    switch (keycode) {
   1.103 +    case SDLK_LCTRL:
   1.104 +        modifier = KMOD_LCTRL;
   1.105 +        break;
   1.106 +    case SDLK_RCTRL:
   1.107 +        modifier = KMOD_RCTRL;
   1.108 +        break;
   1.109 +    case SDLK_LSHIFT:
   1.110 +        modifier = KMOD_LSHIFT;
   1.111 +        break;
   1.112 +    case SDLK_RSHIFT:
   1.113 +        modifier = KMOD_RSHIFT;
   1.114 +        break;
   1.115 +    case SDLK_LALT:
   1.116 +        modifier = KMOD_LALT;
   1.117 +        break;
   1.118 +    case SDLK_RALT:
   1.119 +        modifier = KMOD_RALT;
   1.120 +        break;
   1.121 +    case SDLK_LGUI:
   1.122 +        modifier = KMOD_LGUI;
   1.123 +        break;
   1.124 +    case SDLK_RGUI:
   1.125 +        modifier = KMOD_RGUI;
   1.126 +        break;
   1.127 +    case SDLK_MODE:
   1.128 +        modifier = KMOD_MODE;
   1.129 +        break;
   1.130 +    default:
   1.131 +        modifier = KMOD_NONE;
   1.132 +        break;
   1.133 +    }
   1.134 +    if (SDL_KEYDOWN == type) {
   1.135 +        modstate = keyboard->modstate;
   1.136 +        switch (keycode) {
   1.137 +        case SDLK_NUMLOCKCLEAR:
   1.138 +            keyboard->modstate ^= KMOD_NUM;
   1.139 +            break;
   1.140 +        case SDLK_CAPSLOCK:
   1.141 +            keyboard->modstate ^= KMOD_CAPS;
   1.142 +            break;
   1.143 +        default:
   1.144 +            keyboard->modstate |= modifier;
   1.145 +            break;
   1.146 +        }
   1.147 +    } else {
   1.148 +        keyboard->modstate &= ~modifier;
   1.149 +        modstate = keyboard->modstate;
   1.150 +    }
   1.151 +
   1.152      /* Post the event, if desired */
   1.153      posted = 0;
   1.154      if (SDL_GetEventState(type) == SDL_ENABLE) {
   1.155 @@ -783,7 +762,7 @@
   1.156          event.key.state = state;
   1.157          event.key.repeat = repeat;
   1.158          event.key.keysym.scancode = scancode;
   1.159 -        event.key.keysym.sym = keyboard->keymap[scancode];
   1.160 +        event.key.keysym.sym = keycode;
   1.161          event.key.keysym.mod = modstate;
   1.162          event.key.windowID = keyboard->focus ? keyboard->focus->id : 0;
   1.163          posted = (SDL_PushEvent(&event) > 0);
     2.1 --- a/src/video/x11/SDL_x11keyboard.c	Thu May 28 15:36:27 2015 -0400
     2.2 +++ b/src/video/x11/SDL_x11keyboard.c	Thu May 28 12:48:20 2015 -0700
     2.3 @@ -252,8 +252,7 @@
     2.4  #endif
     2.5          SDL_memcpy(&data->key_layout[min_keycode], scancode_set[best_index].table,
     2.6                     sizeof(SDL_Scancode) * scancode_set[best_index].table_size);
     2.7 -    }
     2.8 -    else {
     2.9 +    } else {
    2.10          SDL_Keycode keymap[SDL_NUM_SCANCODES];
    2.11  
    2.12          printf
    2.13 @@ -316,6 +315,8 @@
    2.14          key = X11_KeyCodeToUcs4(data->display, (KeyCode)i);
    2.15          if (key) {
    2.16              keymap[scancode] = key;
    2.17 +        } else {
    2.18 +            keymap[scancode] = SDL_SCANCODE_TO_KEYCODE(X11_KeyCodeToSDLScancode(data->display, (KeyCode)i));
    2.19          }
    2.20      }
    2.21      SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES);