级别: 初级 Nathan Harrington, 程序员, IBM
2008 年 7 月 24 日 通过分析 Synaptics TouchPad 的 synclient 程序输出来启用 Linux® 应用程序的点击(swipe)和按压(pinch)手势。
多触点界面为在应用程序中集成新的交互模式提供了大量优点。Mac OS X 和 Microsoft® Windows® 中较新的硬件和驱动程序支持除指向和单击以外的更多的手势,从而可以实现更高效的应用程序导航。本文提供了一些工具和代码,可以在支持 Linux 的旧式硬件上添加某些新手势支持。本文中提供的 Perl 代码是在 synclient 程序输出的基础上构建的,它将允许您把特定应用程序函数赋给 “三指击键(Three-Finger Swipe)” 以及打开按压(Open-Pinch)和关闭按压(Close-Pinch)手势。
要求
硬件
本文中提供的代码专门针对仅配备了 Synaptics TouchPad 的计算机,并且在 IBM® ThinkPad T30 中开发。在从 Acer Aspire 到 Toshiba Tecra 的许多膝上型计算机中,您都可以找到 Synaptics 触摸板。要获得 Synaptics TouchPad 软件项目硬件兼容性列表以查看您的硬件是否在列表中,请参阅 参考资料 小节。
软件
您需要具有提供 evdev 支持的最新 Linux 内核。幸运的是,大多数最新发行版都内置了这项功能。许多发行版还附带了 Synaptics 包,该包包含用于监视 TouchPad 事件的 synclient。例如,Fedora Core 还附带了合适的 X Window System 配置,用户稍微修改后即可启用 TouchPad。其他发行版(例如 Ubuntu V7.10)可能需要更多配置,然后才能正常运行 Synaptics 包 — 用 sudo apt-get install tpconfig 命令安装。有关在 Linux 中使用 Synaptics TouchPad 实现基本功能的更多信息,请参阅 参考资料。
您还需要有 CPAN 的 Time::HiRes 模块才能提供处理 TouchPad 事件的亚秒级时间控制。此外,您还需要 X11::GuiTest 模块才能把 synthetic X Window 事件发送给应用程序。要获得这些工具,请参阅 参考资料。
确保基本功能
如果用 TouchPad 启用了鼠标控制,请检查是否有足够的多触点检测以提供手势支持。运行 synclient -m 100 命令并尝试在 TouchPad 中进行不同的触摸操作。您应当会看到类似下面的输出:
清单 1. 示例 synclient -m 100 输出
time x y z f w l r u d m multi gl gm gr gdx gdy
13.872 5680 4409 0 0 0 0 0 0 0 0 00000000 0 0 0 0 0
14.891 1072 3945 28 1 4 0 0 0 0 0 00000000 0 0 0 0 0
14.994 3529 2667 104 2 5 0 0 0 0 0 00000000 0 0 0 0 0
15.605 3669 3667 0 0 0 0 0 0 0 0 00000000 0 0 0 0 0
16.625 2628 2841 255 3 5 0 0 0 0 0 00000000 0 0 0 0 0
17.951 3117 2843 255 3 5 0 0 0 0 0 00000000 0 0 0 0 0
18.053 2902 3142 3 1 15 0 0 0 0 0 00000000 0 0 0 0 0
18.155 2430 3062 0 0 0 0 0 0 0 0 00000000 0 0 0 0 0
|
尝试单指、双指和三指触摸以确保事件会被正确检测到。确保 TouchPad 可以检测到三指,因为要添加的第一个手势是 “三指击键”。在使用不同的距离按两根手指时,请注意 TouchPad 如何获得零触点以及 X 和 Y 坐标读数。下面的处理脚本将使用其中的一些特性来帮助检测打开按压和关闭按压。按 Ctrl+c 组合键退出 synclient 程序。
一般的程序方法
使用 syclient 输出监视 TouchPad 状态是一种把更多界面选项添加到 Linux 应用程序的简单且有效的方法。下面介绍的 gestureListener.pl 程序将打开从 synclient 程序读取信息的管道,并且处理 TouchPad 事件以检测手势。这些特定手势与发送给 X Window System 中处于当前焦点的应用程序的键盘命令绑定在一起。
点击手势
要检测的三指击键是一种相对简单的手势,因为它只需要用三根手指在 TouchPad 上向左或向右移动。清单 2 显示了开始处理手势检测的 synclient 输出所需的 gestureListener.pl 程序的开始部分。
清单 2. gestureListener.pl 程序的开始部分
#!/usr/bin/perl -w
# gestureListener.pl listens for pinch and swipe events
use strict;
use Time::HiRes();
use X11::GUITest qw( :ALL );
my @xHist = (); # x coordinate history
my @yHist = (); # y coordinate history
my @xHistThree = (); # x coordinate history (three fingers)
my $lastTime = 0; # time monitor for TouchPad event reset
my $eventTime = 0; # ensure enough time has passed between events
my $eventString = "default"; # the event to execute
my $centerTouchPad = 3000;
my $openSt = 1000; # start of open pinch
my $openEn = 500; # end of open pinch
my $closeSt = 1000; # start of close pinch
my $closeEn = 1000; # end of close pinch
my $synCmd = qq{synclient TouchpadOff=1 -m 10};
my $currWind = GetInputFocus();
die "couldn't get input window" unless $currWind;
open(INFILE," $synCmd |") or die "can't read from synclient";
|
注意,centerTouchPad 变量和其他参数可能需要根据具体的 Synaptics 硬件或驱动程序级别进行自定义。synclient 命令的 TouchPadOff=1 选项将关闭常规的 TouchPad 事件。红色的 “鼠标杆”(位于 ThinkPad 和其他膝上型计算机中)仍然可用,并且支持 PS2 和 USB 鼠标。不一定需要关闭 TouchPad,但是它将减少从点击和按压中识别与非手势相关的鼠标事件。
调用 GetInputFocus 将为持有焦点的窗口查找当前窗口标识符。这将允许 SendKeys 命令(稍后使用)把 synthetic X Window 事件发送给当前持有焦点的窗口。清单 3 将启动主程序循环并读取 synclient 输出。
清单 3. 主逻辑循环开始
while( my $line = <INFILE>)
{
chomp($line);
my( $time, $x, $y, $z, $f ) = split " ", $line;
next if( $time =~ /time/ ); #ignore header lines
if( $time - $lastTime > 1 )
{
@xHist = ();
@yHist = ();
@xHistThree = ();
}#if time reset
$lastTime = $time;
|
每次超时都重设事件检测历史记录数组,这对于消除 TouchPad 之间的联系至关重要。清单 4 显示了主程序循环中的三指检测的开始部分。
清单 4. 三指处理
# three finger swipe detection
if( $f eq "3" )
{
@xHist = ();
@yHist = ();
push @xHistThree, $x;
if( @xHistThree > 10 )
{
my @srt = sort @xHistThree;
my @revSrt = reverse sort @xHistThree;
if( "@srt" eq "@xHistThree" )
{
# alt + right arrow - forward
$eventString = "'%({RIG})";
}elsif( "@revSrt" eq "@xHistThree" )
{
# alt + left arrow - back
$eventString = "'%({LEF})";
}#if forward or backward
@xHistThree= ();
}#if more than 10 data points in 3 finger array
|
在收集了 10 个点的三指数据后,系统将处理 X 坐标以创建升序排序和降序排序。如果升序排序匹配 X 坐标的当前值,则设置右击(right-swipe)条件。反过来,如果匹配降序排序,则设置左击(left-swipe)条件。eventString 变量将保存该条件,并按如下所示执行:
清单 5. 主逻辑 — 事件执行
}else
{
# reset all data points, yes you can have 0 fingers at x,y
@xHist = ();
@yHist = ();
@xHistThree = ();
}# if not one or two or three fingers
# only process one event per time window
if( $eventString ne "default" )
{
if( abs(time - $eventTime) > 1 )
{
$eventTime = time;
SendKeys( "$eventString");
}#if enough time has passed
$eventString = "default";
}#if non default event
}#synclient line in
close(INFILE);
|
此时,如果未检测到三根手指,则点击或按压的每个数据结构都将被重设。如果设置了一个事件并且当前时间距离最后一个事件的执行时间足够远,则将执行一个新事件。SendKeys 子程序将把相应的事件(Alt + 左箭头键或右箭头键)发送给当前持有焦点的应用程序。如演示视频所示(请参阅 参考资料),这些三指手势用于在浏览历史记录时向前和向后移动。
按压手势
按压手势的检测复杂得多,尤其是使用较旧的硬件开发本文中的代码。使用诸如 kst 之类的工具实时监视打开和关闭手势有助于从 TouchPad 数据中提取相关特征。在第 65 行(else 上方)中插入清单 6 中的代码以开始 “关闭按压检测” 部分。
清单 6. 关闭按压检测
}elsif( $f eq "2" || $f eq "1" )
{
# accept 1 or 2 finger entries as part of pinch section
@xHistThree = ();
push @xHist, $x;
push @yHist, $y;
if( @xHist > 50 )
{
if( (getStrAvg(\@xHist) > $closeSt && getStrAvg(\@yHist) > $closeSt) &&
(getEndAvg(\@yHist) < $closeEn && getEndAvg(\@yHist) < $closeEn) )
{
# wide to narrow detected, now search for enough 'wiggle'
my $tenX = 0;
my $tenY = 0;
for my $each( @xHist[40..49] ){ $tenX += $each }
for my $each( @yHist[40..49] ){ $tenY += $each }
$tenX = $tenX / 10;
$tenY = $tenY / 10;
my $diffX = 0;
my $diffY = 0;
for my $each( @xHist[40..49] ){ $diffX += abs( $each - $tenX ) }
for my $each( @yHist[40..49] ){ $diffY += abs( $each - $tenY ) }
# ctrl - decrease font size
if( ($diffX+$diffY) > 80 ){ $eventString = "^({-})" }
@xHist = ();
@yHist = ();
@xHistThree = ();
}#if x and y in range
}#if enough data for 50 close pinch detection
|
按压检测步骤也可以接受单指或双指操作,从而增强整体跟踪。通过接受单点触摸或双点触摸作为按压动作的一部分,可以让您更轻松地测定触摸和释放操作的时间偏移以及在按压动作过程中把一根手指移开 TouchPad 的问题。
在收集的数据点超过 50 个以后,将计算过去的 50 个数据点的平均开始位置和结束位置。第三条 if 语句将执行四次单独检查,以确保开始点和结束点位于关闭按压检测的正确部分中。具体指开始点的 X 和 Y 平均值必须大于关闭按压的开始点。也就是说,手指必须位于触摸板的角落位置。反过来,结束点必须位于关闭按压的结束点以内。getStrAvg 和 getEndAvg 将创建三个开始点和结束点的平均值,这些点是更可靠的数据检查点。
真正的多触点功能将允许读取每个手指位置的 X 和 Y 坐标。可用的 Synaptics 硬件没有这项功能,但是在将双指触点 “平均” 为 synclient 程序的一个输出值时提供了一致行为。监视 synclient 程序的输出,当两根手指接触触摸板的西南角和东北角时,值迅速地从角落转移到 TouchPad 的中央。如果手指继续保持在角落位置,则 synclient 输出将显示 X 和 Y 坐标一直停在中心附近。随着手指移向中央,这个自动计算的平均值将显示微小的波动。上面的代码部分将检测数据的波动以表示关闭按压,与手指一直按在角落位置获得的 synthetic 平均数据点相反。
X 和 Y 坐标历史的十个痕迹(trailing)值一起取平均值。然后将逐个计算每个痕迹值与这 10 个痕迹值的平均值之间的差额。如果整体差额足够大(在本例中是指大于 80),则已经检测到足够的波动并且将在 eventString 中设置关闭按压条件。
在第 103 行(关闭按压第 50 点的 if 关闭括号之下)中添加清单 7 中所示的代码,进行打开按压检测。
清单 7. 打开按压检测
#open pinch requires substantially fewer data points
if( @xHist > 10 )
{
if( (getStrAvg(\@xHist) < $openSt && getStrAvg(\@yHist) < $openSt) &&
(getEndAvg(\@yHist) > $openEn && getEndAvg(\@yHist) > $openEn) )
{
# ctrl + increase font size
$eventString = "^({+})";
@xHist = ();
@yHist = ();
@xHistThree = ();
}#if absx and absy
}#if enough data
|
要可靠地检测打开按压动作,除了需要充分多的数据点以外,波动检测步骤不是必需的。通过使用诸如 kst 之类的程序实时监视,synclient 输出将显示为打开按压手势收集的范围更广的数据点。自动计算两个手指位置的平均值不会在打开按压手势过程中对 synclient X 和 Y 坐标产生很大影响。因此,要准确地检测手势,不再需要识别波动。清单 8 显示了计算平均值的 getStrAvg 和 getEndAvg 子例程:
清单 8. 两个计算平均值的子例程
sub getStrAvg
{
my $arrRef = $_[0];
my $val = (@$arrRef[0] + @$arrRef[1] + @$arrRef[2]) / 3;
$val = abs( $val - $centerTouchPad );
return($val);
}#getStrAvg
sub getEndAvg
{
my $arrRef = $_[0];
my $val = (@$arrRef[@$arrRef-3] + @$arrRef[@$arrRef-2] +
@$arrRef[@$arrRef-1]) / 3;
$val = abs( $val - $centerTouchPad );
return($val);
}#getEndAvg
|
把清单 8 中所示的代码放到第 144 行(文件末尾)以完成 gestureListener.pl 程序。getStrAvg 和 getEndAvg 分别负责返回前三个数组元素的平均值和后三个数组元素的平均值。
用法
要激活程序,只需运行 perl gestureListener.pl 命令。把 Web 浏览窗口拖入焦点,并尝试通过左击和右击触摸板在浏览历史中移动。注意,由于触摸板尺寸较小以及硬件功能的内在限制,因此可能需要进行一些练习才能可靠地触发手势事件。要获得在作者的 ThinkPad 上完成的手部动作的示例,请查看演示视频(参阅 参考资料)。如果需要重新启用 TouchPad “鼠标” 功能,请在退出 gestureListener.pl 程序后运行 synclient TouchPadOff=0。
结束语
手势与事件之间的联系
请记住,SendKeys 命令将把相应的命令(Alt+Left、Ctrl+- 等)发送到当前持有焦点的应用程序中,而不管这将触发什么功能。要考虑的相对简单的更改是根据窗口名称指定不同的击键或鼠标事件,然后发送给应用程序。
更多示例和修改
本文展示了分析和处理 synclient 输出并创建 synthetic X 事件,结合这些操作可以向 Synaptics TouchPad 添加功能,并且是惟一的方法。考虑添加其他手势识别,例如通过从 synclient 输出中进一步提取特征来识别按压旋转动作。此外,修改 Synaptics 驱动程序源代码以支持内核级别的其他功能,并重写应用程序以更好地利用这些新的输入方法。
下载 | 描述 | 名字 | 大小 | 下载方法 |
|---|
| 样例代码 | os-touchpad.gestureListener_0.1.zip | 2KB | HTTP |
|---|
参考资料 学习
获得产品和技术
- 使用 IBM 试用软件 改进您的下一个开发项目,这些软件可以通过下载或从 DVD 中获得。
- 下载 IBM 产品评估版,并开始使用 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。
讨论
关于作者  | 
|  | Nathan Harrington 是 IBM 的一名程序员,目前从事 Linux 和资源定位技术方面的工作。 |
对本文的评价
|