37

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?

6
  • 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, 2014 at 11:51
  • 2
    @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, 2014 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, 2014 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, 2014 at 7:31
  • 1
    @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, 2014 at 7:42

3 Answers 3

37

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
# This is a name for the file, it could be anything you
# want. For us, we'll name it "custom". This is important
# later.
#
# The KP_* come from /usr/include/X11/keysymdef.h
# Also note the name, "remote" is there in the stanza
# definition.
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

# (1) We list our current definition
# (2) Modify it to have a keyboard mapping using the name
#     we used above, in this case it's the "remote" definition
#     described in the file named "custom" which we specify in
#     this world as "custom(remote)".
# (3) Now we take that as input back into our definition of the
#     keyboard. This includes the file we just made, read in last,
#     so as to override any prior definitions.  Importantly we 
#     need to include the directory of the place we placed the file
#     to be considered when reading things in.
#
# Also notice that we aren't including exactly the 
# directory we specified above. In this case, it will be looking
# for a directory structure similar to /usr/share/X11/xkb
# 
# What we provided was a "symbols" file. That's why above we put
# the file into a "symbols" directory, which is not being included
# below.
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.

19
  • Let me try this on monday when I get back to the computer where I use this keyboard.
    – FUZxxl
    Jan 25, 2015 at 1:42
  • 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, 2016 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, 2016 at 2:37
  • 1
    @stats-hb $9 will not work, you meant 9?
    – Watcom
    Aug 16, 2016 at 22:15
  • 1
    this works for me lampjs.wordpress.com/2015/06/26/…
    – usil
    Jun 19, 2017 at 18:56
20

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.)

3
  • I'm coming here off DuckDuckGo, but thanks for the answer anyway (:
    – sm4rk0
    Oct 9, 2019 at 14:00
  • 3
    This answer is much better than the "chosen one". No custom dirty/hacky scripts. It led me directly to arch linux's wiki. Following the instructions, I created a hwdb file with a clean list of remappings. Worked like a charm on two different machines. Thanks!!
    – vvaltchev
    Nov 5, 2020 at 23:33
  • Thanks for this comment. Here I explain step-by-step how to replace the super and alt key on an apple keyboard, using your method.
    – nammerkage
    Feb 3, 2022 at 8:33
1

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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .