Can I launch macros and shortcuts from a second keyboard on Linux?

While my other answer will probably work on most Linuxes, even if they're many years old, SystemD and udev actually makes things easier:

  • use lsusb to find the vendor and product code of your additional keyboard. (In my case, it's Vendor 145F, Product 0177. Make sure to have the letters in uppercase.)

  • create a file /etc/udev/hwdb.d/90-extra-keyboard.hwdb, with contents similar to this:

evdev:input:b0003v145Fp0177*
 KEYBOARD_KEY_7005b=stopcd

The first line identifies the device: the four letters after the v is the vendor code, after the p, it's the product code, from the previous step. Every further line maps a scancode to a symbolic name. To get the scancode, run evtest:

Event: time 1553711252.888538, -------------- SYN_REPORT ------------
Event: time 1553711257.656558, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70059
Event: time 1553711257.656558, type 1 (EV_KEY), code 79 (KEY_KP1), value 1

To find out what to use for the symbolic name, look at the list of #define KEY_… lines in /usr/include/linux/input-event-codes.h:

#define KEY_PLAYPAUSE           164
#define KEY_PREVIOUSSONG        165
#define KEY_STOPCD              166
#define KEY_RECORD              167
  • re-build and load internal databases by running systemd-hwdb update; udevadm trigger

  • verify the new settings work by running evtest again, or by assigning shortcuts in your settings.

When trying this out in applications, just remember that if your desktop environment already uses that shortcut, the application won't even see the keypress.


In principle, yes. When I last did this in 2012, the way to go was to try and tell Linux that the second keyboard is very weird and really means XF86VolumeUp when it sends the keypress for Num 1, or whatever. Fortunately, there's quite a few keycodes that don't exist on standard keyboards that you can use in this way.

Let's run through this for a single key:

  • use xev | grep -i key to find the keycodes of the keys on your extra keyboard. I'll just do keypap 9, as an example, with keycode 81. (NB: my extra keypad has a key labeled "00" but that sends "0" twice, I have no idea how to handle that.)
      KeyPress event, serial 38, synthetic NO, window 0x4400001,
        state 0x10, keycode 81 (keysym 0xffb9, KP_9), same_screen YES,
      KeyRelease event, serial 39, synthetic NO, window 0x4400001,
        state 0x10, keycode 81 (keysym 0xffb9, KP_9), same_screen YES,
        request MappingKeyboard, first_keycode 8, count 248
  • check setxkbmap -query for the rules of your (main) keyboard. We'll try to turn your extra keyboard into a dozen or so media keys on that layout. For me, it's
rules:      evdev
model:      pc105
layout:     de
variant:    nodeadkeys
  • find enough entries in /usr/share/X11/xkb/symbols/inet. The right-hand side is what the application will see, possibly with shift key modifiers etc., the left-hand side are scancodes. I'll pick this one, because my keyboard doesn't have a "calculator" key:
key <I148>   {      [ XF86Calculator        ]       };
  • create a file in /usr/share/X11/xkb/keycodes/ that maps scancodes to keycodes (from the first step). Yes, this seems backwards. My file is named trustkeypad and looks like this:
default xkb_keycodes "trustkeypad" {
    minimum= 8;
    maximum= 255;

    // keypad 9
    <I148> = 81;
};
  • compile this file with xkbcomp -xkb trustkeypad

  • try it out with setxkbmap -v -v -device 17 -keycodes (filename). (The 17 is the id from your xinput.) The result for me looks like this:

Warning! Multiple definitions of keycodes
         Using command line, ignoring rules file
Applied rules from evdev:
rules:      evdev
model:      pc105
layout:     de
variant:    nodeadkeys
Trying to build keymap using the following components:
keycodes:   trustkeypad
types:      complete
compat:     complete
symbols:    pc+de(nodeadkeys)+inet(evdev)
geometry:   pc(pc105)

and that looks good, in particular, the keycodes line. If you run xev now, you should see the new symbols being generated. Alas, I get only a half-victory right now:

KeyPress event, serial 39, synthetic NO, window 0x4600001,
    root 0x168, subw 0x0, time 24043275, (1666,897), root:(1670,950),
    state 0x10, keycode 81 (keysym 0x1008ff1d, XF86Calculator), same_screen YES,
    XLookupString gives 0 bytes: 
    XmbLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyRelease event, serial 39, synthetic NO, window 0x4600001,
    root 0x168, subw 0x0, time 24043403, (1666,897), root:(1670,950),
    state 0x10, keycode 81 (keysym 0x1008ff1d, XF86Calculator), same_screen YES,
    XLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyPress event, serial 39, synthetic NO, window 0x4600001,
    root 0x168, subw 0x0, time 24043403, (1666,897), root:(1670,950),
    state 0x10, keycode 77 (keysym 0x0, NoSymbol), same_screen YES,
    XLookupString gives 0 bytes: 
    XmbLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyRelease event, serial 39, synthetic NO, window 0x4600001,
    root 0x168, subw 0x0, time 24043411, (1666,897), root:(1670,950),
    state 0x10, keycode 77 (keysym 0x0, NoSymbol), same_screen YES,
    XLookupString gives 0 bytes: 
    XFilterEvent returns: False

but after I crashed X a couple of times by accident (do not think that setting the minimum value in the keycodes file higher will help), that went away. Maybe a simple reboot can help at this point. After you're successful, you should only get something like

KeyPress event, serial 38, synthetic NO, window 0x3e00001,
    root 0x168, subw 0x0, time 26443862, (930,972), root:(934,1025),
    state 0x10, keycode 81 (keysym 0x1008ff1d, XF86Calculator), same_screen YES,
    XLookupString gives 0 bytes: 
    XmbLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyRelease event, serial 39, synthetic NO, window 0x3e00001,
    root 0x168, subw 0x0, time 26444022, (930,972), root:(934,1025),
    state 0x10, keycode 81 (keysym 0x1008ff1d, XF86Calculator), same_screen YES,
    XLookupString gives 0 bytes: 
    XFilterEvent returns: False

and you can verify in your user settings that you can now assign a shortcut to this key, and it will say it's assigned to the Calculator key.

Still missing here: figuring out how to make this the standard setting, also across reboots. This will probably involve looking at udev, and https://help.ubuntu.com/community/Custom%20keyboard%20layout%20definitions might be useful, even though it has an "outdated" tag dating from 2014.