User Tools

Site Tools


Sidebar

No ai soldati italiani all'estero

Indice

Eventi

Energia

Rigacci.Org usa energia elettrica da fonti rinnovabili, grazie al gruppo di acquisto Merci Dolci.

Merci Dolci - Energia Rinnovabile

Software libero!

Petizione contro i brevetti software

Faunalia: Soluzioni GIS professionali

Debian

www.gnu.org www.kernel.org

doc:appunti:linux:sa:remap_keyboard_keys

Remap keyboard keys in GNU/Linux

How to swap primary and secondary functions of the Fn key.

I have a Teclast F6 notebook where the function keys (F1, F2, … F12) are mapped on the keyboard as secondary: you have to press them together with the Fn key to get the function key. The primary function of the keys are the multimedia actions, like MUTE, VOLUMEDOWN, VOLUMEUP, PREVIOUSSONG, NEXTSONG, etc.

Function keys require Fn combination

I searched a recipe to swap the first function of the key with the secondary one. The recipe here explained works almost at 100%, both into the textual console and into the X.org graphical environment. Unfortunately I was unable to swap the F1/DISPLAYTOGGLE, F6/BRIGHTNESSDOWN and F7/BRIGHTNESSUP keys.

Pressing the BRIGHTNESSDOWN and BRIGHTNESSUP keys generates ACPI events: this bypass the kernel keboard subsystem. The events are processed before they arrive to the input subsystem, so you can't see them unsing tools like xev or evtest. You can view ACPI events by running acpi_listen (from the acpid Debian package, the acpid service must be started):

acpi_listen 
video/brightnessdown BRTDN 00000087 00000000 K
video/brightnessup BRTUP 00000086 00000000 K

It seems that the LCD (DISPLAYTOGGLE) multimedia function is intercepted by the hardware and it is not handled as an input event nor as an ACPI event by the operating system.

Inspect the events generated by the keyboard

Use the lsinput command line tool (from the input-utils Debian package) to discover the input number associated to the keyboard. In my case it is input #0:

lsinput
/dev/input/event0
   bustype : BUS_I8042
   vendor  : 0x1
   product : 0x1
   version : 43841
   name    : "AT Translated Set 2 keyboard"
   phys    : "isa0060/serio0/input0"
   bits ev : (null) (null) (null) (null) (null)
...

Use the evtest command line tool (from the omonymous Debian package) to inspect the input events generated by you keyboard. In my case the keyboard is associated to input device #0, the evtest program is run by root into a text console or into a terminal:

evtest 
No device specified, trying to scan all of /dev/input/event*
Available devices:
/dev/input/event0:      AT Translated Set 2 keyboard
...
Select the device event number [0-17]: 0

Pressing and releasing the VOLUMEUP key (this is the F4 key without the Fn modifier keys), generates the following events:

Event: time 1620744541.871139, -------------- SYN_REPORT ------------
Event: time 1620744550.791890, type 4 (EV_MSC), code 4 (MSC_SCAN), value b0
Event: time 1620744550.791890, type 1 (EV_KEY), code 115 (KEY_VOLUMEUP), value 1
Event: time 1620744550.791890, -------------- SYN_REPORT ------------
Event: time 1620744550.892937, type 4 (EV_MSC), code 4 (MSC_SCAN), value b0
Event: time 1620744550.892937, type 1 (EV_KEY), code 115 (KEY_VOLUMEUP), value 0

Pressing and releasing the Fn+F4 key generates the following events:

Event: time 1620744539.251257, -------------- SYN_REPORT ------------
Event: time 1620744541.769000, type 4 (EV_MSC), code 4 (MSC_SCAN), value 3e
Event: time 1620744541.769000, type 1 (EV_KEY), code 62 (KEY_F4), value 1
Event: time 1620744541.769000, -------------- SYN_REPORT ------------
Event: time 1620744541.871139, type 4 (EV_MSC), code 4 (MSC_SCAN), value 3e
Event: time 1620744541.871139, type 1 (EV_KEY), code 62 (KEY_F4), value 0

You have to take note of the MSC_SCAN values (0xb0 and 0x3e respectively) and the EV_KEY labels (KEY_VOLUMEUP and KEY_F4 respectively).

The scancodes shown by the evtest program should be the sames shown by the showkey command line tool (from the kbd Debian package), with the difference that showkey operates at a lower level FIXME:

showkey --scancode

Customize events using udev

You have to create a file, e.g. /etc/udev/hwdb.d/90-custom-keyboard.hwdb with the following:

evdev:atkbd:dmi:*
 KEYBOARD_KEY_b0=f4
 KEYBOARD_KEY_3e=volumeup

As you can see, the scan code and the event code are associated in swapped order. The event code must be written in lowercase and without the KEY_ prefix.

The first line of the file is a device selector, it must match your keyboard. The evdev:atkbd:dmi:* string will eventually match any keyboard attached to the host. If you want to be more specific (e.g. to match only the keyboard of a notebook, but not an external one attached via USB), you can use the evemu-describe tool from the Debian package evemu-tools and look for a more specific descriptor; e.g. to match the keyboard of a Teclast F6 notebook I used the following selector:

evdev:atkbd:dmi:bvnTECLAST:bvr*:bd*:svnTECLAST:pnF6:pvr*

Every line following the device selector, must be indented by one space and cannot contain comments.

You can get the label of event codes (to be placed at the right of the equal sign) by reading the EV_KEY part printed by evtest or by inspecting the file /usr/include/linux/input-event-codes.h. You must remove the KEY_ prefix and convert the label to lowercase.

To update the hardware database and to trigger a kernel device coldplug event:

systemd-hwdb update
udevadm trigger --verbose /dev/input/event0

You can also check that your changes were effective using udevadm:

udevadm info /dev/input/event0
...
P: /devices/platform/i8042/serio0/input/input0/event0
...
E: KEYBOARD_KEY_3e=volumeup
E: KEYBOARD_KEY_b0=f4
...

WARNING: If you remove some key binding from the configuration file, triggering the coldplug event is not sufficient to remove the keybinding from the running kernel; you have to reboot.

Configuration example for the Teclast F6 notebook

The following is the file /etc/udev/hwdb.d/90-custom-keyboard.hwdb which I use on my Teclast F6 notebook:

# /etc/udev/hwdb.d/90-custom-keyboard.hwdb
#
# Keyboard remapping for the Teclast F6 notebook.
# 2021-05-13 Niccolo Rigacci <niccolo@rigacci.org>
#
# The following udev hwdb configuration swaps the Fn behaviour
# on keys F2, F3, F4, F8, F9, F10, F11 and F12.
# It also binds the Fn+ESC to Ctrl+LeftWinLogo+Esc, which can
# be used as keyboard shortcut into XFCE or other desktop
# environments to execute a script and toggle the touchpad.
#
# Function keys F1, F6 and F7 cannot be remapped using
# udev Hardware Database (as far as I know).
#
# To make this file effective execute:
#   systemd-hwdb update
#   udevadm trigger --verbose /dev/input/event0
#
# To view current binding:
#   udevadm info /dev/input/event0
#
# * Use evemu-describe to view the keyboard DMI selector.
# * Use evtest to view hex codes of the KEYBOARD_KEY_*
#   (look at the MSC_SCAN value).
# * Use evtest or grep /usr/include/linux/input-event-codes.h
#   to view the EV_KEY labels (remove the 'KEY_' prefix and
#   convert to lowercase).
#
# The Fn+ESC key produces three keys:
#   KEYBOARD_KEY_1d => code 29  KEY_LEFTCTRL
#   KEYBOARD_KEY_db => code 125 KEY_LEFTMETA (LeftLogo)
#   KEYBOARD_KEY_76 => code 85  KEY_ZENKAKUHANKAKU
#
# Function keys F1, F6 and F7 do not generate events
# (verified with evtest and showkey), so they cannot
# be remapped.
#
# The following EV_KEY do not work:
#   KEY_DISPLAYTOGGLE
#   KEY_BRIGHTNESSDOWN
#   KEY_BRIGHTNESSUP
#
evdev:atkbd:dmi:bvnTECLAST:bvr*:bd*:svnTECLAST:pnF6:pvr*
 KEYBOARD_KEY_a0=f2
 KEYBOARD_KEY_ae=f3
 KEYBOARD_KEY_b0=f4
 KEYBOARD_KEY_90=f8
 KEYBOARD_KEY_99=f9
 KEYBOARD_KEY_c5=f10
 KEYBOARD_KEY_d2=f11
 KEYBOARD_KEY_b7=f12
 KEYBOARD_KEY_3c=mute
 KEYBOARD_KEY_3d=volumedown
 KEYBOARD_KEY_3e=volumeup
 KEYBOARD_KEY_42=previoussong
 KEYBOARD_KEY_43=nextsong
 KEYBOARD_KEY_44=pause
 KEYBOARD_KEY_57=insert
 KEYBOARD_KEY_58=sysrq
 KEYBOARD_KEY_76=esc

If you are interested, look at the page Disable notebook touchpad in GNU/Linx to know how to enable the Fn+ESC touchpad toggle key.

Scancodes and keycodes

Using showkey --scancodes you can see that some keyboard keys produce scancodes that the kernel does not associate to any keycode (action). You can use setkeycodes to make such an association.

xmodmap

The xmodmap command is used to modify keymaps in X; this method does not work into the textual console.

FIXME The current keymap table (see the -pke option below) shows several keysym names for each keycode, e.g. the F5 key has 15 keysyms (are they are associated with different modifiers? Which?):

keycode  71 = F5 F5 F5 F5 F5 F5 XF86Switch_VT_5 F5 F5 XF86Switch_VT_5 F5 F5 F5 F5 XF86Switch_VT_5

A keymap associates keycodes to keysyms. Each keycode can be associated to several keysyms: they are used upon the modifier key that is pressed in conjunction with this key. The modifiers are (FIXME four modifiers, but the table has more!):

  • No modfier
  • Shift - This is the shift modfier: keys Shift_L keycode 50 (0x32) or Shift_R keycode 62 (0x3e).
  • Mode_switch - This is the mod1 modifier: Alt_L keycode 64 (0x40).
  • Shift+Mode_switch

To view the modifier map use xmodmap -pm (each modifier can be activated by up to 4 different keys).

WARNING: xmodmap handles keycodes, which are not the scancodes nor the keycodes shown by showkey.

To view all the key bindings, e.g. the keycodes and the associated keysyms, execute:

xmodmap -pke

You can see keysym for regualr keys (e.g. F1) and for special multimedia keys (e.g. XF86AudioLowerVolume):

Example: map F5 to VOLUMEDOWN:

xmodmap -e "keycode 71 = XF86AudioLowerVolume"

These keysyms do not produce the expected action; i.e. they don't do anything, despite I associate them to a key and depite that evtest and xevdo report the expected action:

  • XF86TouchpadToggle, XF86TouchpadOn, XF86TouchpadOff
  • XF86KbdBrightnessDown, XF86MonBrightnessUp

Web References

doc/appunti/linux/sa/remap_keyboard_keys.txt · Last modified: 2021/07/05 18:55 by niccolo