15

I've recently bought a Unicomp keyboard that comes with swapped right-alt and Windows keys. The keyboard identifies like this on lsusb:

Bus 003 Device 002: ID 17f6:0822 Unicomp, Inc 

Is there a way to have the kernel (i.e. not xmodmap-based) swap the right-alt and windows keys so every application sees them in the swapped places even if they get raw keyboard input (swapping stuff with xmodmap won't do that)? Is there a way to have that only for this one keyboard?

  • I suspect you could do this with udev by matching the serial number of the keyboard and then calling a script which does the remapping. You'd probably need a similar script to be called on detach for the device to put it back. – jam May 30 '14 at 11:51
  • 1
    @jam Would this remap all attached keyboards? I can't imagine that Linux is so inflexible that it can only manage one mapping table for all attached (USB) keyboards. – FUZxxl May 30 '14 at 11:53
  • @jam Additionally, you would really help me if you could describe how to do the actual swapping. I did not manage to find anything useful on this, only xmodmap stuff (which I don't want to use). – FUZxxl May 30 '14 at 11:55
  • If you don't want to use xmodmap, what you're asking seems to be too specific for my knowledge to help you sorry. The method I proposed would use xmodmap to swap the keycodes for those keys for all devices, for the duration your specific keyboard is attached, and then put it back. Are you expecting to be using multiple keyboards simultaneously? – jam Jun 2 '14 at 7:31
  • @jam If I used Xmodmap, X programs would still see the wrong keycodes as the X server also sends untranslated key codes to the client. This matters for instance for video games. There is supposed to be a solution in the kernel that doesn't make my life more complicated with applications that read out scan codes. – FUZxxl Jun 2 '14 at 7:42
26

Yes, it's possible using XKB. Unlike xmodmap, XKB can remap your keys for individual devices.

Note: Make sure you have xkbcomp > 1.2.0

First list your devices with:

xinput list

You'll get something like this:

⎡ Virtual core pointer                      id=2    [master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer                id=4    [slave  pointer  (2)]
⎜   ↳ Wacom Bamboo Pen Pen stylus               id=11   [slave  pointer  (2)]
⎜   ↳ Wacom Bamboo Pen Finger touch             id=12   [slave  pointer  (2)]
⎜   ↳ Logitech USB-PS/2 Optical Mouse           id=13   [slave  pointer  (2)]
⎜   ↳ Wacom Bamboo Pen Pen eraser               id=14   [slave  pointer  (2)]
⎜   ↳ Wacom Bamboo Pen Finger pad               id=15   [slave  pointer  (2)]
⎜   ↳ GASIA USB KB V11                          id=17   [slave  pointer  (2)]
⎣ Virtual core keyboard                     id=3    [master keyboard (2)]
    ↳ Virtual core XTEST keyboard               id=5    [slave  keyboard (3)]
    ↳ Power Button                              id=6    [slave  keyboard (3)]
    ↳ Power Button                              id=7    [slave  keyboard (3)]
    ↳ G19 Gaming Keyboard                       id=8    [slave  keyboard (3)]
    ↳ G19 Gaming Keyboard                       id=9    [slave  keyboard (3)]
    ↳ Logitech G19 Gaming Keyboard              id=10   [slave  keyboard (3)]
    ↳ GASIA USB KB V11                          id=16   [slave  keyboard (3)]

Identify the string of your device and edit the following shell script, changing the sed line with one that fits your device's name. Then change the keys you need remapped.

Example: Load xev and press a key you want to remap. Suppose you find out it's keycode 84. Lookup 84 in https://gist.github.com/zoqaeski/3880640. The key name there is <KP5>. Then lookup the key you want it replaced by (in the same link, farther below) and copy what's inside the brackets. Repeat the process for all the keys you want.

remote_id=$(
    xinput list |
    sed -n 's/.*GASIA.*id=\([0-9]*\).*keyboard.*/\1/p'
)
[ "$remote_id" ] || exit

# remap the following keys, only for my custom vintage atari joystick connected
# through an old USB keyboard:
#
# keypad 5 -> keypad 6
# . -> keypad 2
# [ -> keypad 8
# left shift -> left control

mkdir -p /tmp/xkb/symbols
cat >/tmp/xkb/symbols/custom <<\EOF
xkb_symbols "remote" {
    key <KP5>  { [ KP_Right, KP_6, U2192, U21D2 ]       };
    key <I129> { [ KP_Down, KP_2, U2193, U21D3 ]       };
    key <AD12> { [ KP_Up, KP_8, U2191, U21D1 ]  };
    key <LFSH> { [ Control_L ]        };
};
EOF

setxkbmap -device $remote_id -print | sed 's/\(xkb_symbols.*\)"/\1+custom(remote)"/' | xkbcomp -I/tmp/xkb -i $remote_id -synch - $DISPLAY 2>/dev/null

Then source it (you can add it to your .xinitrc). All done! Now pressing the keys should generate the desired output, only for the device you specified.

Edit: Recently, I've noticed that, for some reason, the new configuration isn't applied immediately. You must first press a key on your other keyboard, then test the configured keys on your modified keyboard. I don't know why this happens, maybe some sort of cache.

  • Let me try this on monday when I get back to the computer where I use this keyboard. – FUZxxl Jan 25 '15 at 1:42
  • I just tried that out and it didn't have any effect at all. Can I send you the files I created so you can have a look at them? – FUZxxl Jun 12 '15 at 10:54
  • 1
    a) sed -n 's/.*G19 Gaming Keyboard.*id=\([0-9]*\).*keyboard.*/\1/p' c) Yes, you should definitely test it first replacing $remote_id with the id number. Note there are two references to $remote_id there, did you change both? – Watcom Jun 20 '16 at 2:29
  • 1
    Oh, and when testing the hard-coded $remote_id, make sure you comment-out the line [ "$remote_id" ] || exit if you haven't already otherwise it will just bail out. – Watcom Jun 20 '16 at 2:37
  • 1
    @stats-hb $9 will not work, you meant 9? – Watcom Aug 16 '16 at 22:15
1

For anyone else who's coming here off Google and wants an answer more in line with what the questioner was originally hoping for, I am aware of two ways to remap events at the evdev level so that the change applies to all applications:

  1. udev provides an API for modifying the hardware database entries which control the mappings between scancodes and keycodes. This ArchiWiki page, which contains instructions, explicitly says that it'll work for both X11 and console input.

    The gist is that you create a custom entry in /etc/udev/hwdb.d/ which consists of a device match pattern and some scancode-to-keycode remapping definitions, then run systemd-hwdb update to rebuild the database and udevadm trigger apply it without a reboot.

  2. Given that Wayland doesn't use X11's keyboard subsystem and major Wayland compositors like GNOME Shell and Weston don't implement UIs to configure the relevant aspects of libinput, someone wrote a daemon named evdevremapkeys which resolves the problem similarly to the G15Daemon userspace driver for Logitech G15 gaming keyboards.

    (It swallows events it intends to remap, so nothing else listening on the device can see them, then emits the corrected events via the uinput API for creating kernel-level input devices from userspace.)

0

For those who didn't succeed with @Watcom option, just put your new mapping file, smth like:

xkb_symbols "remote" {
    key <KP5>  { [ KP_Right, KP_6, U2192, U21D2 ]       };
    key <I129> { [ KP_Down, KP_2, U2193, U21D3 ]       };
    key <AD12> { [ KP_Up, KP_8, U2191, U21D1 ]  };
    key <LFSH> { [ Control_L ]        };
};

to /usr/share/X11/xkb/symbols/ as maybe root (ubuntu, may differ for your distrib), call the file 'custom'. Ask for your current layout string with setxkbmap -device <device id> -print | grep xkb_symbols and add +custom to it. Set new layout with remapped keys with and modified layout string:

setxkbmap -device <device id> -layout "us+ru:2+us:3+inet(evdev)+capslock(grouplock)+custom"

Effect is not permanent and unfortunately resets when other keyboard is connected, didn't figure how to fix it yet. You can add the command above to your .bashrc though, so keys are swapped on restart if necessary.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.