ThinkPad X220 Laptop Keys

Documentation Distribution License

Copyright (C) 2017, 2021 Bob Proulx bob@proulx.com

This document is licensed under a Creative Commons Attribution-NoDerivatives 4.0 International License .

Making the ThinkPad X220 Laptop Keys Work

I recently installed Debian Jessie 8 on a Lenovo ThinkPad X220 laptop. Most things worked without any special configuration. However the audio controls and the screen monitor brightness controls did not. Researching the differences between the X220 and previous models such as the T60p showed that previously these involved changes to the nvram but now does not. Previously the tpb package would handle the laptop keys. Unfortunately this did not handle the X220 keys.

Using xev showed that all of the keys produced keycodes. These were the ones that had no function for me.

keycode 122 XF86AudioLowerVolume
keycode 123 XF86AudioRaiseVolume
keycode 198 XF86AudioMicMute
keycode 156 XF86Launch1
keycode 198 XF86AudioMicMute
keycode 232 XF86MonBrightnessDown
keycode 233 XF86MonBrightnessUp

Using a full Desktop Environment such as Xfce handled the volume up and down keys through X events but did not handle the monitor brightness keys nor the others. Most importantly this only worked in an X Desktop Environment and not an X window manager session such as using fvwm or i3 window managers. I am mostly using i3, a tiling window manager, these days and I wanted these keys to work there and everywhere. Which means not using X key events but using the ACPI key events.

Installing Software

The acpi_listen program can be used to dump the ACPI events. This was useful to determine what ACPI events were triggered by those keys.

acpi_listen

Additionally the acpi-support package was not installed by default. Plus alsa-utils is used to implement the functions.

apt-get install acpi-support alsa-utils

Research and Development

I determined the following ACPI events were being created but not handled.

event=video/brightnessdown BRTDN 00000087 00000000
event=video/brightnessup BRTUP 00000086 00000000
event=button/f20 F20 00000080 00000000
event=button/mute MUTE 00000080 00000000
event=button/volumedown VOLDN 00000080 00000000
event=button/volumeup VOLUP 00000080 00000000

Local Configuration Addition

I created the following files to handle those events.

/etc/acpi/events/local-thinkpad-micmute
/etc/acpi/events/local-thinkpad-brightnessup
/etc/acpi/events/local-thinkpad-volup
/etc/acpi/events/local-thinkpad-brightnessdown
/etc/acpi/events/local-thinkpad-voldown
/etc/acpi/events/local-thinkpad-mute
/etc/acpi/local-thinkpad-brightness.sh

I have bundled all of these files into one local-acpi-x220-keys.tar.gz file for easy download. However I will also list out each file individually for easy review and individual manual install. These next all go into the /etc/acpi/events directory.

File /etc/acpi/events/local-thinkpad-brightnessdown:

event=video/brightnessdown BRTDN 00000087 00000000
action=/etc/acpi/local-thinkpad-brightness.sh down

File /etc/acpi/events/local-thinkpad-brightnessup:

event=video/brightnessup BRTUP 00000086 00000000
action=/etc/acpi/local-thinkpad-brightness.sh up

File /etc/acpi/events/local-thinkpad-micmute:

event=button/f20 F20 00000080 00000000
action=amixer -q sset -- Capture toggle

File /etc/acpi/events/local-thinkpad-mute:

event=button/mute MUTE 00000080 00000000
action=amixer -q sset -- Master toggle

File /etc/acpi/events/local-thinkpad-voldown:

event=button/volumedown VOLDN 00000080 00000000
action=amixer -q sset -- Master 5%%- unmute

File /etc/acpi/events/local-thinkpad-volup:

event=button/volumeup VOLUP 00000080 00000000
action=amixer -q sset -- Master 5%%+ unmute

The functionality needed for the brightness up and down is a little too much for a one-liner in the event file command line definition itself. I created that as a helper script in the /etc/acpi directory just like the other helper utilities there.

This script is ThinkPad X220 specific. At least it is ThinkPad specific. It makes use of the /sys/class/backlight/intel_backlight interface which won't generically exist. Therefore it has a check for that interface and simply exits without doing anything if it does not exist.

File /etc/acpi/local-thinkpad-brightness.sh (in the /etc/acpi directory):

#!/bin/sh

# Copyright 2017, 2021 Bob Proulx <bob@proulx.com>
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved.  This file is offered as-is,
# without any warranty.

test -e /sys/class/backlight/intel_backlight/brightness || exit 0

max_brightness=$(cat /sys/class/backlight/intel_backlight/max_brightness)
brightness=$(cat /sys/class/backlight/intel_backlight/brightness)
increment=$((max_brightness / 20))  # a 5% change
case $1 in
  up)
    new=$(($brightness + $increment))
    if [ $new -ge $max_brightness ]; then
      new=$max_brightness
    fi
    ;;
  down)
    new=$(($brightness - $increment))
    if [ $new -le 150 ]; then
      new=150  # prevent a 0 completely dark display to avoid confusion
    fi
    ;;
esac

echo $new > /sys/class/backlight/intel_backlight/brightness

exit 0

Restart the acpi daemon

service acpid restart

Working Laptop Keys

At this point the laptop keys were all completely functional. I could make them work in my fvwm or i3 window managers, in the Xfce Desktop Environment, or on the Linux text console.

The Catch

Of course there is one problem. Because the audio mute creates an X key event which is also handled by the Xfce Desktop Environment, when using the Xfce Desktop Environment, it means that it gets handled twice in that environment. Once by the above ACPI handler and once by the Xfce X event handler. Each toggles the mute. Which means that in Xfce this key which otherwise works there no longer works there due to being toggled twice. The same thing happens for volume up and volume down too but doubling those simply causes more volume or less volume and does not present itself as a problem. However since I needed this because I was not using Xfce but using the i3 window manager, this isn't actually a problem for me. It is only a problem if I flip back to Xfce. In which case I should remove the audio button control configurations and just leave the monitor brightness control configurations.

In order to fix the above in Xfce I wish to find a way to disable Xfce's handling of those X events. Perhaps by preventing those X events from being sent. I think maybe those are being sent by the use of acpi_fakekey in acpi-support. I need to investigate.

On Screen Display

To have an OSD (on-screen-display) feedback such as that which is provided by the tpb package there is the xosd-bin package providing the osd_cat program. That can be connected to xbindkeys to present an OSD when these actions are triggered. This works very well to provide feedback when changing things like screen brightness.

Configure the xbindkeys program using this ~/.xbindkeysrc file.

"osd-display-handler"
    XF86MonBrightnessUp

"osd-display-handler"
    XF86MonBrightnessDown

And the $HOME/bin/osd-display-handler script is:

#!/bin/sh

# Copyright 2017, 2021 Bob Proulx <bob@proulx.com>
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved.  This file is offered as-is,
# without any warranty.

if [ ! -e /sys/class/backlight/intel_backlight/brightness ]; then
  echo "Error: missing /sys/class/backlight/intel_backlight/brightness" 1>&2
  exit 0
fi

max=$(cat /sys/class/backlight/intel_backlight/max_brightness)
bright=$(cat /sys/class/backlight/intel_backlight/brightness)

percent=$(awk -v m=$max -v b=$bright 'BEGIN{printf("%.0f\n",100*b/m);}')

pkill osd_cat

osd_cat -p bottom -i 150 -c blue -d 2 -T "Backlight" -b percentage -P $percent &

exit 0