Tuesday, June 23, 2009

Ubuntu Touchpad Tweaks (or, making it more like the Mac)

I'm a Mac user for the most part, but Macs all come with cameras (yeah, I know you can have an Apple Certified Technician disable the internal camera, but it's a hassle I'm not going to go through), so I have a ThinkPad SL500 and a Dell Mini 9, both of which are cameraless, that I can tote with me to secured locations. (My cellphone is a BlackBerry 8830 for the same reason.) So I have Ubuntu Linux (9.04 Desktop on the ThinkPad, 9.10 Alpha 2 UNR on the Mini) to carry around with me. With OpenOffice it's all I really need on the go.

One thing the Mac makes ridiculously easy and intuitive (I suspect they eat their own dogfood in Cupertino, just a hunch) is touchpad operation. For instance, it will ignore taps made while typing, and having the touchpad disabled when an external mouse is hooked up is a simple checkbox in the mouse control panel. Easy.

To do the same thing under Ubuntu is possible, but, erm, no so easy. It was actually kind of hair pulling for me. But I eventually got it, with some help from a fellow Ubuntu user (he's an expert, I'm a well situated amateur). Here are the steps I took, in hopes these help others...


  1. Setup a udev rule.
    This runs whenever I connect, or disconnect, a mouse (it doesn't catch when the system is started up with a mouse already attached; I'll work on that eventually). Here are the contents of /etc/udev/rules.d/89-mouse-touchpad.rules (use the terminal to run the command 'sudo gedit <filename^gt;' to edit system files):
    ACTION=="add", SUBSYSTEM=="input", ID_CLASS="mouse", RUN+="/usr/local/bin/touchpad"
    ACTION=="remove", SUBSYSTEM=="input", ID_CLASS="mouse", RUN+="/usr/local/bin/touchpad"



  2. Determine what driver the touchpad is using
    $ /usr/bin/hal-find-by-capability --capability input.touchpad
    /org/freedesktop/Hal/devices/platform_i8042_i8042_AUX3_port_logicaldev_input

    $ /usr/bin/hal-get-property --udi \
    /org/freedesktop/Hal/devices/platform_i8042_i8042_AUX3_port_logicaldev_input \
    --key input.x11_driver
    synaptics


  3. Enable SHMConfig for the touchpad via HAL
    by creating the file /etc/hal/fdi/policy/shmconfig.fdi (Note that this is considered a potential security issue.):
    <?xml version="1.0" encoding="UTF-8"?>
    <deviceinfo version="0.2">
    <device>
    <match key="input.x11_driver" string="synaptics">
    <merge key="input.x11_options.SHMConfig" type="string">True</merge>
    </match>
    </device>
    </deviceinfo>


  4. Configure syndaemon
    syndaemon is used to deactivate the touchpad while typing; it's part of the package xserver-xorg-input-synaptics and almost certainly installed by default. Set it up to run at system boot by adding the line /usr/bin/syndaemon -i 2.5 -d before the exit 0 line in /etc/rc.local The command man syndaemon will reveal other options that may be useful (e.g., not disabling the touchpad when modifier keys are pressed, or disabling taps but not movement).


  5. Install the /usr/local/bin/touchpad script:
    #!/usr/bin/perl
    use warnings;
    use strict;

    # Interact with environment; set DISPLAY;
    # determine what ACTION script invoked to handle:
    $ENV{'DISPLAY'} = ':0.0';
    my $action = $ENV{'ACTION'} || "unknown";
    my $devicemodel = $ENV{'ID_MODEL'} || "[ID_MODEL unset]";
    my $logstring = $0 . " called for ACTION '"
    . $action . "' of device " . $devicemodel;
    `/usr/bin/logger '$logstring'`;

    # Open the X server to accept controls from xinput
    # (potential security risk...):
    my $xhost_result = `/usr/bin/xhost + 2>&1`;
    `/usr/bin/logger 'xhost run with result: $xhost_result'`;

    # Find the Unique Device Identifier (UDI) for every touchpad on
    # the system (there's probably only just the one, but...) and
    # cycle through them:
    my @udis = `/usr/bin/hal-find-by-capability --capability input.touchpad`;
    foreach my $udi (@udis) {
    chomp $udi;

    # Find the info.product value(s) for this touchpad's UDI and
    # cycle through it (them?):
    my $cmd = "/usr/bin/hal-get-property --udi $udi --key info.product";
    my @results = `$cmd`;
    foreach my $product (@results) {
    chomp $product;
    my $xinput = "/usr/bin/xinput set-int-prop \""
    . $product . "\" \"Device Enabled\" 8 ";
    my $message;
    if($action eq 'add') {
    $message = "Disabling $udi";
    $xinput .= "0";
    } else {
    $message = "Enabling $udi";
    $xinput .= "1";
    }
    `/usr/bin/logger $message`;

    # Actually disable / enable the touchpad by toggling
    # the Device Enabled property (1 or 0) via xinput:
    my $cmdoutput = `$xinput 2>&1`;
    `/usr/bin/logger '$xinput result: $cmdoutput'`;
    my $lclcmd = "/usr/bin/xinput list-props \""
    . $product
    . "\" | /bin/grep 'Device Enabled' "
    . "| /usr/bin/tr -d '\011'";
    $cmdoutput = `$lclcmd 2>&1`;
    `/usr/bin/logger 'Current state of $product is: $cmdoutput'`;
    }
    }
    (I may have gone a little overboard on the logging, and there may be a more secure way to do this than by using xhost. Caveat lector.)


  6. Run xhost on login
    Until this step is taken, the system is in a bit of a catch-22; xhost (run from the touchpad script) can't connect to the X server to tell it to allow xinput to connect to the X server, because xhost hasn't been run to allow xhost to connect... So, on a 9.04 Desktop box, click through to: System -> Preferences -> Startup Applications and Add the following entry:
    Name: Xhost
    Command: /usr/bin/xhost +
    Comment: Enable mouse defeat of touchpad



And we're done. Reboot (necessary for the hal configuration to take effect; /etc/init.d/hal restart doesn't seem to do it) and enjoy!

No comments:

Post a Comment