2011/09/04

Custom gesture support on Ubuntu

恰巧在IBM developer看到了一篇Ubuntu上gesture的教學,但因為那篇是perl及年久失修(?),所以我稍微在Ubuntu 11.04及用python重寫了這個應用。

這篇文章將會給介紹Ubuntu上怎麼來寫筆電的touchpad gesture應用,主要用python、synclient和xdotool。synclient是用來抓touchpad被按下的座標值及指數。xdotool則用來送出相對應的鍵盤行為。

synclient

首先,在Ubuntu 11.04上,若要啟用synclient必需先修改xorg.conf,加入Option "SHMConfig" "on",如下

//file: /usr/share/X11/xorg.conf.d/50-synaptics.conf
Section "InputClass"
        Identifier "touchpad catchall"
        Driver "synaptics"
        MatchIsTouchpad "on"
        MatchDevicePath "/dev/input/event*"
        Option "SHMConfig" "on"
EndSection

接著可在terminal上使用synclient,來看一下他的輸出:

doro@doro-UL80Jt ~/src/touchpad $ synclient -m 10
    time     x    y   z f  w  l r u d m     multi  gl gm gr gdx gdy
   0.000   418  413   0 0  0  0 0 0 0 0  00000000
   0.617   363  359  31 1  0  0 0 0 0 0  00000000
   0.627   362  356  31 1  0  0 0 0 0 0  00000000
   0.637   363  352  31 1  0  0 0 0 0 0  00000000
   0.657   364  349  31 1  0  0 0 0 0 0  00000000
   0.677   368  347  31 1  0  0 0 0 0 0  00000000
   0.688   371  344  31 1  0  0 0 0 0 0  00000000
   0.708   373  340  31 1  0  0 0 0 0 0  00000000
   0.728   375  336  31 1  0  0 0 0 0 0  00000000
   0.738   376  333  31 1  0  0 0 0 0 0  00000000
   0.849   376  333   0 0  0  0 0 0 0 0  00000000
   1.688   232  672  31 2  0  0 0 0 0 0  00000000
   1.718   274  679  31 3  0  0 0 0 0 0  00000000
   1.799   274  679   0 0  0  0 0 0 0 0  00000000

這個指令會輸出目前touchpad被按下的點(x,y)以及f欄位標示出指數。因此我們便可利用這3個值來判斷手勢。最後再利用xdotool來執行我們要做的行為。

Example

底下這個例子,將會實作
  • 若3指按下時,送出super+w,進入expo mode 
  • 若3指按下後移動上/下/左/右超過100個單位,送出ctrl+alt+Up/Down/Left/Right,來做work space的切換 

#!/usr/bin/python
# -*- coding: utf-8 -*-

import logging
logging.basicConfig(filename='/tmp/multitouch.log',
        level=logging.DEBUG, 
        format='[%(asctime)s][%(name)s][%(levelname)s] %(message)s')
logger = logging.getLogger('multitouch')

import subprocess
import re

if __name__ == "__main__":
    cmd = 'synclient -m 10'

    p = subprocess.Popen(cmd, stdout = subprocess.PIPE, 
            stderr = subprocess.STDOUT, shell = True)
    skip = False
    try:
        while True:
            line = p.stdout.readline()
            if not line:
                break
            try:
                tokens = [x for x in re.split('([^0-9\.])+', line.strip()) if x.strip()]
                x, y, fingers = int(tokens[1]), int(tokens[2]), int(tokens[4])
                logger.debug('Result: ' + str(tokens))
                if fingers == 3:
                    if skip:
                        continue
                    skip = True
                    start_x, start_y = x, y
                else:
                    if skip:
                        diff_x, diff_y = (x - start_x), (y - start_y)
                        if diff_x > 100:
                            logger.info('send...diff_x > 100')
                            subprocess.Popen("xdotool key ctrl+alt+Right", shell=True)
                        elif diff_x < -100:
                            logger.info('send...diff_x < -100')
                            subprocess.Popen("xdotool key ctrl+alt+Left", shell=True)
                        elif diff_y > 100:
                            logger.info('send...diff_y > 100')
                            subprocess.Popen("xdotool key ctrl+alt+Down", shell=True)
                        elif diff_y < -100:
                            logger.info('send...diff_y < -100')
                            subprocess.Popen("xdotool key ctrl+alt+Up", shell=True)
                        else:
                            logger.info('send...super+w')
                            subprocess.Popen("xdotool key super+w", shell=True)
                    skip = False
            except (IndexError, ValueError):
                pass
    except KeyboardInterrupt:
        pass 
(keyboard is better, but just for fun :D)