8

In Ubuntu 18.04 I use the following autostart script:

[Desktop Entry] Type=Application Exec=/home/user/.xinitrc Version=1.0 X-GNOME-Autostart-enabled=true Name=xmodmap Comment=xmodmap script 

which just xmodmap /path/.Xmodmap &.

When the system boots up, it works. When the system awakes from sleep, the remapping no longer works. How can I fix this?

EDIT: (reply to a comment) This also does not fix the issue:

$ cat /etc/systemd/system/xmodmapbindings.service [Unit] Description=xmodmap bindings Before=sleep.target StopWhenUnneeded=yes [Service] Type=oneshot RemainAfterExit=yes ExecStop=-/home/norake/.xinitrc [Install] WantedBy=sleep.target $ cat ~/.xinitrc #!/bin/bash if [ "$USER" != norake ]; then su norake -c 'sleep 5; /usr/bin/xmodmap /home/norake/.Xmodmap' & # without su, without sleep, without fork (&): doesn't work either else (sleep 5; /usr/bin/xmodmap /home/norake/.Xmodmap) & fi 

sleep 30 doesn't work either. Of course the script run manually works.

10
  • see https://askubuntu.com/questions/92218/how-to-execute-a-command-after-resume-from-suspend/92235#92235 Commented Mar 9, 2019 at 18:27
  • I mean, two other answers, not the one for Ubuntu14 Commented Mar 9, 2019 at 20:14
  • @Andra ok, new edit :) If useful: systemd[1]: Started xmodmap bindings. systemd[1]: xmodmapbindings.service: Unit not needed anymore. Stopping. systemd[1]: Stopping xmodmap bindings... systemd[1]: Stopped xmodmap bindings. Commented Mar 9, 2019 at 20:40
  • Let me add one more comment, DISPLAY=:0 does not help. Commented Nov 24, 2020 at 17:04
  • I have found the solution: DISPLAY=:0; export DISPLAY Commented Nov 25, 2020 at 11:55

5 Answers 5

4
+25

Your modified settings are also "lost" when you unplug and replug the keyboard, and this is what is happening here: the suspend generates hotplug events as the keyboard is disabled when entering suspend and reenabled when exiting.

From the point of view of the X server, the keyboard connected after returning from suspend is a new keyboard, so it is brought up with the standard bindings, the same way a second keyboard would.

That is a known shortcoming of the USB keyboard drivers, and would be difficult to fix in the kernel (because USB is re-enumerated on resume, so we'd need a method of keeping the identity of a device even if it is assigned a new number), and difficult to fix in the X server (because it would need to keep a history of devices).

Likely the best thing you could do is run the command as part of your session startup and as part of hotplug processing for the keyboard, but I can't think of a fully clean solution here.

3
  • Disconnecting and reconnecting any input device causes the reset, which is something I hate too. It seems the real solution here is to modify keyboard layout. Commented Nov 25, 2020 at 11:54
  • Could you provide some information about how to add a script to hotplug processing? Commented Dec 2, 2020 at 4:02
  • I think the actual problem is that "reconnecting the keyboard" happens at kernel level (say "root"), and it's hard to trigger a user-level action (e.g. via udev) from that: Imagine multiple users each have a different setting in their home directory: Which user to pick? Still, you could try a system-wide setting. Maybe see superuser.com/q/1576107 Commented Nov 5, 2024 at 21:48
3

Remember to replace $USER with your actual username:

  1. Generate your .Xmodmap file with the mapping, in my case it is:

    keycode 96 = XF86AudioLowerVolume keycode 95 = XF86AudioMute keycode 76 = XF86AudioNext keycode 75 = XF86AudioPlay keycode 74 = XF86AudioPrev 

    and place it under /home/$USER/.Xmodmap path

  2. Create a startup application preference (go to Startup Application Preferences from super-button search menu) and add the following:

    Name: xmodmap Command: /bin/bash -c "sleep 7 && xmodmap ~/.Xmodmap" Comment: 

    and Save it. And close the Startup Application Preferences

  3. Now go to /usr/lib/systemd/system-sleep folder, like this:

    cd /usr/lib/systemd/system-sleep 
  4. Create a file named xkeyboard.sh and make it executable. You can do it like this (sudo rights are required):

    sudo touch xkeyboard.sh && sudo chmod +x xkeyboard.sh 
  5. Now get your XAUTHORITY variable value. You can do it like this:

    echo $XAUTHORITY 

    In my case it returned me the following:

    /run/user/1000/gdm/Xauthority 

    Also get your DISPLAY variable value.

    echo $DISPLAY 

    Often :0 but sometimes :1.0 or others. Requires the colon (:).

    Write these variables down somewhere.

  6. Open your newly created file for editing, you can do it like this:

    sudo nano xkeyboard.sh 
  7. Paste this code. Replace $USER with your username; $XAUTH with the value of XAUTHORITY you wrote down before; and :0 with the value of DISPLAY you wrote down before.

    #!/bin/bash echo "Running xkeyboard.sh with argument: $1" >> /tmp/xkeyboard.log if [ "${1}" == "pre" ]; then echo "pre" >> /tmp/xkeyboard.log # Nothing to do for pre-sleep elif [ "${1}" == "post" ]; then echo "post" >> /tmp/xkeyboard.log export DISPLAY=:0 # Set the DISPLAY variable export XAUTHORITY=$XAUTH /bin/bash -c "sleep 7 && xmodmap home/$USER/.Xmodmap" >> /tmp/xkeyboard.log 2>&1 fi 

    In this code I've added up logging to /tmp/xkeyboard.log file, so if keyboard mapping doesn't work, you can go there and checkout what's the matter.

1

This is my low-tech hacky solution to a special case of this problem:

My keyboard has a Print key, where the Menu key should be, so my xmodmap remaps just that one key.

I set up a keybinding (in the gnome shell in my case) for the Print key to run

/bin/bash -c "/usr/bin/xmodmap $HOME/.Xmodmap" 

So after suspend, the first time I press the Print key, it will trigger xmodmap and from then on it works as a menu key.

(After that, my keyboard no longer has a Print key.)

This approach should work whenever:

  1. You don't mind an initial "blind" key press.
  2. You have at least one button you know you will press before or as soon as the remapping becomes relevant.
  3. The initial key symbol of this button is no longer present on the new mapping.

Update: I have since improved my solution with xdotool to also trigger the context menu itself on the initial rebind step:

/bin/bash -c "xdotool keyup Print key --clearmodifiers Menu && /usr/bin/xmodmap $HOME/.Xmodmap" 
3
  • I like this solution. I use numpad-zero as control and created a custom shortcut in Ubuntu with this key and the command sh -c 'xmodmap ~/.Xmodmap && notify-send Xmodmap "Xmodmap is active"'. First time I click numpad-zero it enables Xmodmap and the second time it works as control. One issue is that having directly xmodmap ~/.Xmodmap as the command wasn't working for me because it always enabled Xmodmap instead of switching to ctrl on 2nd click, I had to wrap it with the sh -c ''. Commented Dec 4, 2023 at 15:19
  • Thanks for reminding me of this post, so I could add a revision that may interest you as well: It even triggers the desired key together with the rebind on the first key press. (I remember vaguely that the rebind still behaved better in some situations, so I did not go with xdotool only.) Is sh -c any different from the /bin/bash -c I am using? I just tested it again with xev and see the remap the first time and then the regular Menu key. Commented Dec 4, 2023 at 17:05
  • sh is POSIX compliant and a subset of bash. In some systems it appears sh would point to bash although nowadays, at least in Ubuntu, sh is pointing to dash instead. $ file -h /bin/sh returns /bin/sh: symbolic link to dash in my case. The reason I used sh -c is that there is no guarantee that the path \bin\bash -c exists although in practice it usually does. (Related: stackoverflow.com/q/5725296/3715151) Commented Dec 5, 2023 at 8:44
0

Here is a working solution, got help from various sources but mostly written by me. caps is the xmodmap script (CapsLock => F13).

#!/bin/bash USERN=cemkalyoncu SCRIPT=/home/cemkalyoncu/Installed/caps case $1 in post) DISPLAY=:0 export DISPLAY su $USERN -c "$SCRIPT" #screen #su $USERN -c "sleep 30; kwin --replace &" & esac 

I have also left the kwin --replace as kwin sometimes causes glitches and it requires reset, but it is not a deamon and should be detached using screen. It is only if you are using KDE. If not installed, install screen. Exporting display here is the key to get it working. Running with your user is also necessary. Even when you are the one resuming, it is still not your account. Sleep is not necessary for xmodmap. But kwin is automatically reset by the system few seconds after resume, but sometimes fail; therefore the delay is necessary.

0

If you feel a bit icky about forcing $DISPLAY etc. from the outside, a tiny hack is:

  1. Create a small script running permanently (e.g. xmodmapper.sh):

    #!/bin/sh while true; do echo "reloading xmodmap" sleep 2 xmodmap ~/.Xmodmap kill -STOP $$ done 
  2. Run it as part of your X session, e.g. add ~/xmodmapper.sh & to your ~/.xinitrc

  3. Add a rule to some udev rule file (e.g. /etc/udev/rules.d/80-keyboard.rule):

    SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="1b1c/1b2d/329", RUN+="/usr/bin/killall -CONT xmodmapper.sh" 

    and replace ENV{PRODUCT} with your USB device - mine is based on this syslog line:

    New USB device found, idVendor=1b1c, idProduct=1b2d, bcdDevice= 3.29 

This feels a bit more robust to me than the other approaches, though YMMV. (Especially since I actually have other udev rules bound to the keyboard already anyway.)

P.S. The sleep is important, unfortunately there's a race condition otherwise when the Xmodmap is called before Xorg had time to register the keyboard once it's connected. Solving it properly e.g. using xev is left as an exercise for the reader.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.