I frequently work on pairing stations where there are multiple keyboards installed. I can use setxkbmap with -device <ID> to set the layout for a specific keyboard (using an ID from xinput), but often it’s not obvious which keyboard I’m at. It would be better to avoid the back-and-forth of trying both keyboards, so I’d like to write a quick tool to get this information for setxkbmap. I’d expect a typical use case like the following:
$ setxkbmap -device "$(get-keyboard-id)" -layout gb Press Enter to detect keyboard ID
Which interface provides this information on Linux? Ideally it should work without X, but that’s not a requirement (there doesn’t seem to be many tools which support this without X).
Findings so far:
- Linux must know which keyboard I’m typing on to support different layouts for multiple keyboards simultaneously.
xinput→ list.c →list_xi2→XIQueryDeviceprovides device IDs usable bysetxkbmap.showkeyandxevdon’t print keyboard IDs.xinput list-props $IDshows where keyboard events are sent. However, using code from another answer it seems this device doesn’t print anything to identify the keyboard.-
One almost possible solution is to run
xinput --test <ID> &for each keyboard ID and see which one returns something first. The problem with that is figuring out which “keyboards” are actually keyboards:$ xinput | grep keyboard ⎣ Virtual core keyboard id=3 [master keyboard (2)] ↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)] ↳ Power Button id=6 [slave keyboard (3)] ↳ Video Bus id=7 [slave keyboard (3)] ↳ Power Button id=8 [slave keyboard (3)] ↳ Sleep Button id=9 [slave keyboard (3)] ↳ WebCam SC-13HDL10931N id=10 [slave keyboard (3)] ↳ AT Translated Set 2 keyboard id=11 [slave keyboard (3)]
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
Disable device
Here’s one idea towards identifying which keyboard is which. You can use the command xinput to enable and disable devices.
Example
$ xinput list
⎡ Virtual core pointer id=2 [master pointer (3)]
⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)]
⎜ ↳ SynPS/2 Synaptics TouchPad id=12 [slave pointer (2)]
⎜ ↳ TPPS/2 IBM TrackPoint id=13 [slave pointer (2)]
⎜ ↳ Logitech USB Receiver id=9 [slave pointer (2)]
⎜ ↳ Logitech USB Receiver id=10 [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)]
↳ Video Bus id=7 [slave keyboard (3)]
↳ Sleep Button id=8 [slave keyboard (3)]
↳ AT Translated Set 2 keyboard id=11 [slave keyboard (3)]
↳ ThinkPad Extra Buttons id=14 [slave keyboard (3)]
The above output shows the various devices that I have on my Thinkpad laptop. I only have 1 keyboard attached, this one:
↳ AT Translated Set 2 keyboard id=11 [slave keyboard (3)]
Now take a look at the properties available through this device:
$ xinput list-props "AT Translated Set 2 keyboard"
Device 'AT Translated Set 2 keyboard':
Device Enabled (124): 1
Coordinate Transformation Matrix (126): 1.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.
From the above you can see that it’s enabled, so let’s disable it:
$ xinput set-prop "AT Translated Set 2 keyboard" "Device Enabled" 0
To enable it:
$ xinput set-prop "AT Translated Set 2 keyboard" "Device Enabled" 1
The idea?
You could enable disable one of the keyboards using this command to determine which one you’re on.
References
Method 2
More digging revealed another solution using plain Bash and a normal user account. Script:
#!/usr/bin/env bash
set -o errexit -o nounset -o noclobber -o pipefail
# Remove leftover files and processes on exit
trap 'rm --recursive -- "$dir"; kill -- -$$' EXIT
dir="$(mktemp --directory)"
cd "$dir"
# Log key presses to file
xinput --list --id-only | while read id
do
# Only check devices linked to an event source
if xinput --list-props "$id" | grep --quiet --extended-regexp '^s+Device Node.*/dev/input/event'
then
xinput test "$id" > "$id" &
fi
done
# Check for key presses
while sleep 0.1
do
for file in *
do
if [[ -s "$file" ]]
then
echo "$file"
exit
fi
done
done
Method 3
The question sounds a bit contradictory since you’re citing X tools but ask for a solution that “ideally should work without X”.
About your 4th finding:
xinput will give you the correspondence
$ xinput list-props 11
Device 'AT Translated Set 2 keyboard':
Device Enabled (145): 1
Coordinate Transformation Matrix (147): 1.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 1.000000
Device Product ID (266): 1, 1
Device Node (267): "/dev/input/event0"
at least with the following version
$ xinput --version xinput version 1.6.1 XI version on server: 2.3
First step: detecting the keyboard event device in C
—
#include <stdio.h>
//#include <unistd.h>
#include <fcntl.h>
#include <linux/input.h>
// typical use : sudo ./a.out /dev/input/event*
int main (int argc, char *argv[])
{
struct input_event ev[64];
int fd[argc],rd,idev,value, size = sizeof (struct input_event);
char name[256] = "Unknown";
if(argc==1) return -1;
int ndev=1;
while(ndev<argc && (fd[ndev] = open (argv[ndev], O_RDONLY|O_NONBLOCK)) != -1){
ndev++;
}
fprintf (stderr,"Found %i devices.n", ndev);
if(ndev==1) return -1;
while (1){
for(idev=1; idev<argc; idev++){
if( (rd=read (fd[idev], ev, size * 64)) >= size){
value = ev[0].value;
if (value != ' ' && ev[1].value == 1 && ev[1].type == 1){
ioctl (fd[idev], EVIOCGNAME (sizeof (name)), name);
printf ("%sn", name);
return idev;
}
}
}
// sleep(1);
}
return -1;
}
Many thanks to this page. I’ve stripped most safety checks from the code I borrowed there, for clarity, in real code you probably want them.
Note that key presses are echoed, so you may indeed want to kindly ask the user to hit a modifier key (Shift, Control…) rather than any key.
Second step: use xinput to get the X ID from the device name
Compile the above C source and use this way:
xinput list --id-only "keyboard:$(sudo ./a.out /dev/input/event*)"
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0