TwinView & Wacom

This page describes how to setup a Wacom Intuos3 6×8 tablet for use with a dual screen setup (TwinView) in Ubuntu 10.04 LTS. I have a Dell Latitude E6410 as screen 0 (1440×900) and a Samsung SyncMaster 2243BW as screen 1 (1680×1050). This combination results in a TwinView screen of 3120×1050. The script below is run once after plugging in the tablet.

#!/bin/bash

# Setup Wacom tablet to screen 1 of TwinView setup

# Get all values with: xsetwacom -s get "Wacom Intuos3 6x8" all

# Screen 0: 1440x900
# Screen 1: 1680x1050
# TwinView desktop: 3120x1050

# xsetwacom --get "Wacom Intuos3 6x8" TopX	0
# xsetwacom --get "Wacom Intuos3 6x8" BottomX	40640

# Twinview Desktop X value	WX = 3120
# Monitor X value		SX = 1680
# Tablet X value		TX = 40640

# Values to use Screen 1:
# 	TopX = TX - ( TX * WX / SX / 2 ) = 2903
#	(ref: http://art.ubuntuforums.org/showthread.php?t=1461978)

# Setup stylus
xsetwacom --set "Wacom Intuos3 6x8" TwinView horizontal
xsetwacom --set "Wacom Intuos3 6x8" Screen_No 1
xsetwacom --set "Wacom Intuos3 6x8" TopX 2903

# Setup eraser
xsetwacom --set "Wacom Intuos3 6x8 eraser" TwinView horizontal
xsetwacom --set "Wacom Intuos3 6x8 eraser" Screen_No 1
xsetwacom --set "Wacom Intuos3 6x8 eraser" TopX 2903

# Setup strips on pad (DOES NOT WORK)
# xsetwacom --set "Wacom Intuos3 6x8 pad" StripRUp "key pgup"
# xsetwacom --set "Wacom Intuos3 6x8 pad" StripRDn "key pgdn"
# xsetwacom --set "Wacom Intuos3 6x8 pad" StripLUp "key +"
# xsetwacom --set "Wacom Intuos3 6x8 pad" StripLDn "key -"

# Setup keys on pad
# ---------	 ---------
# |   | 1 | 	 | 5 |   |
# | 3 |---|	 |---| 7 |
# |   | 2 |	 | 6 |   |
# |-------|	 |-------|
# |   4   | 	 |   8   |
# ---------	 ---------
xsetwacom --set "Wacom Intuos3 6x8 pad" Button1 "key ctrl z"	# Undo last step
xsetwacom --set "Wacom Intuos3 6x8 pad" Button2 "key ctrl y"	# Redo last step
xsetwacom --set "Wacom Intuos3 6x8 pad" Button3 "key shift e"	# Eraser fuction for stylus
xsetwacom --set "Wacom Intuos3 6x8 pad" Button4 "key r"		# Rectangle tool for stylus
xsetwacom --set "Wacom Intuos3 6x8 pad" Button5 "key n"		# Pencil tool for stylus
xsetwacom --set "Wacom Intuos3 6x8 pad" Button6 "key p"		# Paintbrush tool for stylus
xsetwacom --set "Wacom Intuos3 6x8 pad" Button7 "key k"		# Ink tool for stylus
xsetwacom --set "Wacom Intuos3 6x8 pad" Button8 "key a"		# Airbrush tool for stylus

The tablet is activated in Gimp 2.6.8 by changing the mode of the stylus, eraser, cursor and pad to Screen. This option is set by following Edit > Preferences > Input Devices > Configure Extended Input Devices.

Outdated information:
The section below describes the configuration of the dual screen setup with Wacom tablet on a Dell XPS M1330 laptop with Ubuntu 9.04. The secondary LCD (screen 1) is a Samsung SyncMaster 2243BW, which is used with a Wacom Intuos3 tablet. The version of the restricted graphics driver is Nvidia version 180. The code below is the contents of the /etc/X11/xorg.conf file:

Section "Monitor"
	Identifier     "Monitor0"
	VendorName     "Dell"
	ModelName      "LPL"
	HorizSync       30.0 - 75.0
	VertRefresh     60.0
	Option         "DPMS"
EndSection

Section "Monitor"
	Identifier     "Monitor1"
	VendorName     "Samsung"
	ModelName      "SyncMaster"
	HorizSync       30.0 - 81.0
	VertRefresh     56.0 - 75.0
EndSection

Section "Screen"
	Identifier     "Screen0"
	Device         "Device0"
	Monitor        "Monitor0"
	Option         "TwinView" "0"
	Option         "metamodes" "DFP: 1280x800 +0+0"
	DefaultDepth	24
	SubSection "Display"
		Depth       24
	EndSubSection
EndSection

Section "Screen"
	Identifier     "Screen1"
	Device         "Device1"
	Monitor        "Monitor1"
	DefaultDepth    24
	Option         "TwinView" "0"
	Option         "TwinViewXineramaInfoOrder" "CRT-0"
	Option         "metamodes" "CRT: 1680x1050 +0+0"
	SubSection "Display"
		Depth       24
	EndSubSection
EndSection

Section "Module"
	Load           "dbe"
	Load           "extmod"
	Load           "type1"
	Load           "freetype"
	Load           "glx"
EndSection

Section "ServerLayout"
	Identifier     "Default Layout"
	Screen      0  "Screen0" 0 0
	Screen      1  "Screen1" RightOf "Screen0"
EndSection

Section "Device"
	Identifier     "Device0"
	VendorName     "NVIDIA Corporation"
	BoardName      "GeForce 8400M GS"
	BusID          "PCI:1:0:0"
	Screen          0
	Driver	"nvidia"
	Option	"NoLogo"	"True"
EndSection

Section "Device"
	Identifier     "Device1"
	VendorName     "NVIDIA Corporation"
	BoardName      "GeForce 8400M GS"
	BusID          "PCI:1:0:0"
	Screen          1
	Driver	"nvidia"
	Option	"NoLogo"	"True"
EndSection

Section "ServerFlags"
	Option         "Xinerama" "1"
EndSection

After installing the latest Wacom drivers, the GTK+ package needs to be patched and compiled from source to correct the cursor offset in Gimp and Inkscape:

$sudo apt-get build-dep libgtk2.0-0
$sudo apt-get source libgtk2.0-0

Special thanks to some guy named “gator_ml yahoo de” for the three years old patch to gdk/x11/gdkinput-x11.c, which still needs to be applied to the current version of GTK+ (!?!):

--- gtk+-2.8.18/gdk/x11/gdkinput-x11.c.org	2006-03-04 06:25:45.000000000 +0100
+++ gtk+-2.8.18/gdk/x11/gdkinput-x11.c	2006-05-29 16:38:24.000000000 +0200
@@ -455,11 +455,19 @@

   if (gdkdev->info.mode == GDK_MODE_SCREEN)
     {
-      x_scale = gdk_screen_get_width (gdk_drawable_get_screen (input_window->window)) / device_width;
-      y_scale = gdk_screen_get_height (gdk_drawable_get_screen (input_window->window)) / device_height;
-
-      x_offset = - input_window->root_x;
-      y_offset = - input_window->root_y;
+      int core_pointer_x, core_pointer_y, cur_monitor;
+      GdkRectangle mon_geometry;
+      GdkScreen *cur_screen;
+      gdk_display_get_pointer(gdk_drawable_get_display(input_window->window),
+			      &cur_screen,&core_pointer_x,&core_pointer_y,0);
+      cur_monitor= gdk_screen_get_monitor_at_point(cur_screen,
+						   core_pointer_x,
+						   core_pointer_y);
+      gdk_screen_get_monitor_geometry(cur_screen,cur_monitor,&mon_geometry);
+      x_scale = mon_geometry.width / device_width;
+      y_scale = mon_geometry.height / device_height;
+      x_offset = mon_geometry.x - input_window->root_x;
+      y_offset = mon_geometry.y - input_window->root_y;
     }
   else				/* GDK_MODE_WINDOW */
     {

Continue to compile, make and install the patched GTK+ version:

./configure --prefix=/usr --with-xinput=yes
$sudo make
$sudo make install

Compile the following code (gcc -O2 monitor_wacom.c -o .monitor_wacom) to create a daemon, which restores settings on a restart. Thanks to the guys on the Ubuntu forum:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <assert.h>

void log_msg(const char *msg) {

	FILE *log_file = fopen(".monitor_wacom.log", "a");

	if (log_file) {
		time_t time_g;
		time(&time_g);
		char date_str[100];
		strcpy(date_str, asctime(localtime(&time_g)));
		date_str[strlen(date_str) - 1] = '\0'; /* get rid of the \n */
		fprintf(log_file, "%s : %s\n", date_str, msg);
	}

	fclose(log_file);
}

void run_script(const char *filename) { /* only run xsetwacom lines */
	FILE *script_file = fopen(filename, "r");

	if (!script_file) {
		log_msg("Failed to open script (double check your entry in Startup Applications). Terminating.");
		exit(1);
	}

	char buf[1024];
	char l_buf[1024];
	int count = 0;
	while ((fgets(buf, 1023, script_file))) {
		if (strstr(buf, "xsetwacom")) {
			int r = system(buf);
			if (r != 0) {
				sprintf(buf, "xsetwacom returned error on line - %s", buf);
				log_msg(l_buf);
			}
			++count;
		}
	}

	sprintf(l_buf, "Script ran. %d matching lines executed.", count);

	log_msg(l_buf);
}

int main(int argc, char **argv) {

	char script_name[1024];

	if (argc != 2) {
		log_msg("Script not specified, using .xinitrc");
		strcpy(script_name, ".xinitrc");
	} else {
		strcpy(script_name, argv[1]);
	}

	run_script(script_name);

	FILE* in = popen("dbus-monitor --session type='signal',interface='org.gnome.ScreenSaver',member='ActiveChanged'", "r");

	if (!in) {
		log_msg("Cannot spawn dbus-monitor process. Terminating.");
		return 1;
	}

	while (1) {
		char buf[1024];
		assert(fgets(buf, 1023, in));

		if (buf[0] == 's' /* early exit optimization */ && strstr(buf, "ActiveChanged") != NULL) {
			assert(fgets(buf, 1023, in));
			if (strstr(buf, "false") != NULL) {
				/* resumed */
				log_msg("Exit from screensaver detected. Running script.");
				run_script(script_name);
			} else {
				/* put to sleep */
			}
		}
	}
}

Add the daemon as a startup application by adding an entry to System > Preferences > Startup Applications, with the command “/home/yourusername/.monitor_wacom /home/yourusername/.xinitrc”.

The next step is to create a file in /etc/init.d for translation of the HAL device names to the names used by the Wacom drivers:

$ cat /etc/init.d/wacom
# Find any Wacom devices
for udi in `hal-find-by-property --key input.x11_driver --string wacom`
do
type=`hal-get-property --udi $udi --key input.x11_options.Type`
# Rewrite the names that the Xserver will use
hal-set-property --udi $udi --key info.product --string $type
case $type in
stylus|eraser)
;;
esac
done

This script can be added to the start-up modes by creating symlinks in /etc/rc{2-5}.d as S27Wacom:

$sudo update-rc.d wacom-names start 27 2 3 4 5 .

There is no need to add a custom HAL fdi file to /etc/hal/fdi/policy. The default HAL fdi is modified to add the eraser, cursor and pad devices:

$ cat /usr/share/hal/fdi/policy/20thirdparty/10-wacom.fdi
<?xml version="1.0" encoding="UTF-8"?> <!-- -*- SGML -*- -->
<deviceinfo version="0.2">
  <device>
    <match key="info.category" contains="input">
      <match key="info.product" contains="Wacom">
	<merge key="input.x11_driver" type="string">wacom</merge>
	<merge key="input.x11_options.Type" type="string">stylus</merge>
        <append key="info.callouts.add" type="strlist">hal-setup-wacom</append>
        <append key="wacom.types" type="strlist">eraser</append>
        <append key="wacom.types" type="strlist">cursor</append>
        <append key="wacom.types" type="strlist">pad</append>
      </match>
    </match>
    <match key="info.capabilities" contains="serial">
      <match key="@info.parent:pnp.id" contains_outof="WACf001;WACf002;WACf003;WACf004;
WACf005;WACf006;WACf007;WACf008;WACf009;WACf00a;WACf00b;WACf00c;FUJ02e5">
        <append key="info.capabilities" type="strlist">input</append>
        <merge key="input.x11_driver" type="string">wacom</merge>
        <merge key="input.x11_options.Type" type="string">stylus</merge>
        <merge key="input.x11_options.ForceDevice" type="string">ISDV4</merge>
        <merge key="input.device" type="copy_property">serial.device</merge>
        <append key="info.callouts.add" type="strlist">hal-setup-wacom</append>
        <append key="wacom.types" type="strlist">eraser</append>
        <match key="@info.parent:pnp.id" contains_outof="WACf008;WACf009">
          <!-- Serial tablets with touch capabilities -->
          <append key="wacom.types" type="strlist">touch</append>
        </match>
      </match>
    </match>
  </device>
  <!-- Match the Wacom Bluetooth A5 pen tablet -->
  <device>
    <match key="info.capabilities" contains="input.mouse">
      <match key="info.product" contains="WACOM">
        <match key="info.product" contains="Tablet">
          <merge key="input.x11_driver" type="string">wacom</merge>
          <merge key="input.x11_options.Type" type="string">stylus</merge>
          <append key="info.callouts.add" type="strlist">hal-setup-wacom</append>
          <append key="wacom.types" type="strlist">eraser</append>
          <append key="wacom.types" type="strlist">cursor</append>
        </match>
      </match>
    </match>
  </device>
</deviceinfo>

The following commands should give the 4 devices of the Wacom tablet. The xidump command will give more Xinput devices than shown below:

$ xsetwacom list
stylus           stylus
pad              pad
cursor           cursor
eraser           eraser
$ xidump -l
stylus                         extension
pad                            extension
cursor                         extension
eraser                         extension

The tablet is activated in Gimp 2.6 by changing the mode of the stylus, eraser, cursor and pad to Screen. This option is set by following Edit > Preferences > Input Devices > Configure Extended Input Devices.

The buttons on the pad can be programmed using a script file:

$ cat scripts/wacom
#!/bin/bash

# Set scrolling on right strip
xsetwacom set pad StripRUp "key core pgup"
xsetwacom set pad StripRDn "key core pgdn"

# Set zoom on left strip
xsetwacom set pad StripLUp "key core -"
xsetwacom set pad StripLDn "key core +"

# Button to Ctrl Z (undo)
xsetwacom set pad Button3 "core key ctrl z"

# Button FN2 to Ctrl Y (redo)
xsetwacom set pad Button4 "core key ctrl y"

# Button for pencil
xsetwacom set pad Button5 "core key n"

# Button for brush
xsetwacom set pad Button6 "core key p"

# Button for ink
xsetwacom set pad Button7 "core key k"

# Button for eraser
xsetwacom set pad Button8 "core key shift e"