July 23, 2010

Debian in more depth: adding touch support

There are two options for touchscreen support in X-Windows:

xserver-xorg-input-evtouch - Touchscreen-Driver for X.Org/XFree86 server
xserver-xorg-input-tslib - tslib touchscreen driver for X.Org/XFree86 server

The first uses the input event layer of Linux to read directly from touchscreen devices. The second uses a library named tslib to perform filtering and translation of raw coordinates from a touchscreen device in order to generate X-Windows events.

In this post, I’m going to walk through the details of tslib, since I haven’t yet had any success in getting evtouch calibration to work.

Before we start, we’ll need to add another package to our installation. The libts-bin package contains the touchscreen calibration and test utilities for tslib.

debian:~# apt-get install libts-bin

We’ll also need to configure tslib through the use of /etc/ts.conf . The following is a pretty typical setup that tells tslib to read from the input event layer, perform a little filtering, and translate the results into screen coordinates.

# Uncomment if you wish to use the linux input layer event interface
module_raw input

# Uncomment if you're using a Sharp Zaurus SL-5500/SL-5000d
# module_raw collie

# Uncomment if you're using a Sharp Zaurus SL-C700/C750/C760/C860
# module_raw corgi

# Uncomment if you're using a device with a UCB1200/1300/1400 TS interface
# module_raw ucb1x00

# Uncomment if you're using an HP iPaq h3600 or similar
# module_raw h3600

# Uncomment if you're using a Hitachi Webpad
# module_raw mk712

# Uncomment if you're using an IBM Arctic II
# module_raw arctic2

module pthres pmin=1
module variance delta=30
module dejitter delta=100
module linear

Once this file is created, we can use the ts_calibrate and ts_test utilities to calibrate and test the touchscreen. Note that you’ll need to set the TSLIB_TSDEVICE environment variable to tell tslib what input device is the touchscreen. It’s typically /dev/input/event0 because the touchscreen driver is loaded early in platform initialization.

ts_calibrate will prompt you to touch each of five points. This calibration routine helps it to determine how it should scale and translate raw input readings to produce screen locations.

debian:~# export TSLIB_TSDEVICE=/dev/input/event0
debian:~# ts_calibrate

ts_calibrate screenshot

After touching these points, you can see the set of coefficients it produced in /etc/pointercal.

debian:~# cat /etc/pointercal
243 54107 -2786888 32553 -1 -1549556 6553636debian:~#

You can also test the touchscreen with the ts_test utility. It will allow you to either drag a cross-hair icon around on the screen or draw lines between the points it reads:

debian:~# ts_test

ts_test screenshot

The next step is to configure X-Windows to use the touchscreen driver. We do this by adding an InputDevice section to /etc/X11/xorg.conf:

Section "InputDevice"
	Identifier "tslib"
	Driver "tslib"
	Option "ScreenNumber"	"0"
	Option "Width"		"800"
	Option "Height"		"480"
	Option "Rotate"		"NONE"
	Option "TslibDevice"	"/dev/input/event3"
	Option "CorePointer"
EndSection

Once you’ve done this, you can startx and things will almost work. You should see the pointer move as you touch various points on the screen and move in the right directions, but the scaling won’t work properly.

Many thanks to Ricardo Soza for figuring this one out. It’s a known bug with a known fix. Here’s the workaround:

debian:~# apt-get build-dep xserver-xorg-input-tslib && apt-get source xserver-xorg-input-tslib
... update xf86-input-tslib_0.0.4/src/tslib with the patch below
debian:~# cd xf86-input-tslib_0.0.4
debian:~/xf86-input-tslib-0.0.4# debian/rules binary

I got the patch by screen-scraping the bug report:

Index: src/tslib.c
===================================================================
--- a/src/tslib.c	(revision 28)
+++ b/src/tslib.c	(working copy)
@@ -184,9 +184,11 @@
 	InputInfoPtr pInfo;
 	unsigned char map[MAXBUTTONS + 1];
 	int i;
+	struct ts_priv *priv;

 	ErrorF("%sn", __FUNCTION__);
 	pInfo = device->public.devicePrivate;
+	priv = pInfo->private;

 	switch (what) {
 	case DEVICE_INIT:
@@ -222,15 +224,17 @@
 			return !Success;
 		}

-		InitValuatorAxisStruct(device, 0, 0, /* min val */ 1023,	/* max val */
-					       1024,	/* resolution */
-					       0,	/* min_res */
-					       1024);	/* max_res */
+		InitValuatorAxisStruct(device, 0, 0,    	/* min val */
+					       priv->width - 1,	/* max val */
+					       priv->width,	/* resolution */
+					       0,		/* min_res */
+					       priv->width);	/* max_res */

-		InitValuatorAxisStruct(device, 1, 0, /* min val */ 1023,	/* max val */
-					       1024,	/* resolution */
-					       0,	/* min_res */
-					       1024);	/* max_res */
+		InitValuatorAxisStruct(device, 1, 0,    	/* min val */
+					       priv->height - 1,/* max val */
+					       priv->height,	/* resolution */
+					       0,		/* min_res */
+					       priv->height);	/* max_res */

Once done, you can restart the X-server and move the mouse pointer around with the touchscreen!