2

I use an external keyboard with my laptop and a script to change the keyboard layout depending on whether the keyboard is plugged in. This works fine, but I find it shouldn't be necessary to run the script manually: it should run by itself whenever the external keyboard is plugged in or out. I've tried to set this up with a udev rule, but for some reason it doesn't work. My udev rule is:

KERNEL=="3-1.1", SUBSYSTEM=="usb", RUN+="/root/bin/udev.sh"

I have confirmed that this script runs once whenever I plug the keyboard in or out. The contents of the script are:

#!/usr/bin/bash
su - toothrot -c "/usr/local/bin/loadkb.sh" &

And the contents of /usr/local/bin/loadkb.sh are:

#!/bin/bash

export DISPLAY=:0 
export XAUTHORITY=/home/toothrot/.Xauthority
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus

killall xcape

if lsusb | grep HHKB
then {
    setxkbmap us,gr \
        -variant carpalx-plus-intl,daedalus \
        -option grp:win_space_toggle \
        -option lv3:rwin_switch \
        -option altwin:meta_alt \
        -option shift:breaks_caps \
        -option lv5:lsgt_switch_lock
    xmodmap ~/.xmodmap/hh 
    xcape -e 'Shift_L=F13;Shift_R=F14;Super_L=Prior;Hyper_L=Prior'
}
else {
    setxkbmap us,gr \
        -variant carpalx-plus-intl,daedalus \
        -option grp:win_space_toggle \
        -option lv3:ralt_switch \
        -option altwin:meta_alt \
        -option shift:breaks_caps -option lv5:lsgt_switch_lock
    xmodmap ~/.xmodmap/apple
    xcape -e 'Control_L=Escape;Shift_L=F14;Shift_R=F13;Alt_L=Escape;Alt_R=Escape'
}
fi

I have confirmed that running /root/bin/udev.sh as root works for my toothrot X session. Only when run by a udev rule, the script doesn't work. What am I missing?

Or is there a way to achieve the result I want without using a udev rule, maybe from within X?

5
  • As an experiment, you might place echo HHKB >/tmp/kbdlog at the top of the then block, and echo other >/tmp/kbdlog at the top of the else, to ensure the correct path is taken – Fox Mar 28 '17 at 17:37
  • @Fox, I've confirmed that this writes the expected content to /tmp/kbdlog. – Toothrot Mar 28 '17 at 20:56
  • Could you send the stdout and stderr of each command to a similar log? That is, command >/tmp/kbdlog 2>&1 for each command? See if any errors pop up there? – Fox Mar 28 '17 at 21:00
  • 1
    Udev-rules are run from the kernel and apparently execute in some kind of restricted environment. There are multiple questions on this site where doing something fancy or X related inside an udev-rule didn't work. I don't know how to solve this, if it's solvable at all. However, if the differences are simple enough, alternatively you can try to change the scancode to keycode mapping on the input-layer with the hwdb integrated into udev. – dirkt Mar 29 '17 at 11:10
  • askubuntu.com/questions/337411/… may help: someone has managed to do something similar. – Gilles 'SO- stop being evil' Jun 7 '19 at 20:57
0

Inspired by this blog article I got xmodmap to work with the following rule in /etc/udev/rules.d/70-usb-keyboards.rules:

ACTION=="add", ATTRS{idVendor}=="0000", ATTRS{idProduct}=="0000", RUN+="/bin/sh -c 'DISPLAY=:0.0 XAUTHORITY=/home/toothrot/.Xauthority sh -c \"setxkbmap us,gr;xmodmap /home/toothrot/.xmodmap/hh\" &'"

Replace vendor and product id with the values of your device from lsusb and adjust the script to your liking, then don’t forget to reload the udev rules with:

udevadm control --reload

I’d always leave the detection to udev, so this is the rule for when the keyboard is added. To act on removal add another rule line with:

ACTION=="remove"

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.