Using hdaps for screen rotation
In the middle of fighting to get the X61's screen rotate button to talk to xrandr, I realized that the laptop already has an accelerometer in it and given that, the screen rotate button was a crappy UI anyway.
So, here's a way to make the laptop automatically rotate the screen, based on the accelerometer, when in tablet mode. It was written on a system running Ubuntu 8.10, but any system with ACPI and HDAPS configured should work.
First, we need two files to tell acpid to stick a flag in the filesystem when switching between tablet and laptop modes.
- /etc/acpi/lenovo-rotate
event=ibm/hotkey HKEY 00000080 00005009 action=/usr/bin/touch /var/lib/acpi-support/tablet
- /etc/acpi/lenovo-rotate-back
event=ibm/hotkey HKEY 00000080 0000500a action=/bin/rm -f /var/lib/acpi-support/tablet
Second, we need something to rotate the screen when the HDAPS device passes a certain movement threshold.
- autorotate.py
#!/usr/bin/env python import glob import select import struct import sys import os from fcntl import ioctl JS_EVENT_FMT = "IhBB" JS_EVENT_SIZE = struct.calcsize(JS_EVENT_FMT) # Event types -- JS_EVENT_BUTTON = 0x01 # button pressed/released JS_EVENT_AXIS = 0x02 # joystick moved JS_EVENT_INIT = 0x80 # "virtual" event flag # ioctl magic codes for device info -- JSIOCGAXES = 0x80016a11 # axis count JSIOCGBUTTONS = 0x80016a12 # button count JSIOCGNAME = 0x81006a13 # device name # ~0.56 is straight vertical TILT_THRESHOLD = 0.38 # Names of xinput devices to rotate with the screen TOUCH_DEVICES = "stylus eraser pad touch".split() def find_hdaps(): for filename in glob.glob("/dev/input/js*") + glob.glob("/dev/js*"): try: dev = open(filename, "rb") except (IOError, OSError): continue else: axes = struct.unpack( "b", ioctl(dev, JSIOCGAXES, " "))[0] buttons = struct.unpack( "b", ioctl(dev, JSIOCGBUTTONS, " "))[0] name = struct.unpack( "1024s", ioctl(dev, JSIOCGNAME, " " * 1024))[0] name = name.rstrip().strip("\x00") if name == "hdaps": return dev else: dev.close() def set_state(state): os.system("xrandr -o %s" % state) pen_rot = ["normal", "right", "left", "inverted"].index(state) for dev in TOUCH_DEVICES: os.system("xsetwacom set %s rotate %d" % (dev, pen_rot)) def main(argv): state = "normal" dev = find_hdaps() if not dev: raise SystemExit("Unable to find HDAPS joystick device.") while select.select([dev], [], []): # Unpack the event and send it! evt = dev.read(JS_EVENT_SIZE) time, value, etype, number = struct.unpack(JS_EVENT_FMT, evt) if etype & JS_EVENT_AXIS: value = value / 32768.0 if number == 0 and value > TILT_THRESHOLD: new_state = "normal" elif number == 0 and value < -TILT_THRESHOLD: new_state = "inverted" elif number == 1 and value < -TILT_THRESHOLD: new_state = "left" elif number == 1 and value > TILT_THRESHOLD: new_state = "right" else: new_state = state if state != new_state: if (new_state != "normal" and not os.path.exists("/var/lib/acpi-support/tablet")): new_state = "normal" if new_state != state: state = new_state set_state(state) if __name__ == "__main__": main(sys.argv[1:])
Then, /etc/init.d/acpid reload
, and run autorotate.py
. Switch your laptop to tablet mode, and spin it around. The screen should rotate appropriately. If you find it rotates too easily or not easily enough, tweak the TILT_THRESHOLD variable. If you named your XInput devices for the touchscreen differently, change the TOUCH_DEVICES string.