使用SystemTap接受来自键盘、鼠标的输入

Linux内核先使用input_event接受外部的输入,再传送到适当的子系统(subsystem)。这个事件包括键盘或鼠标的事件,因此通过检测input_event函数,就可以获取用户的键盘输入或鼠标输入。


void input_event(struct input_dev*dev,

unsigned int type, unsigned int code, int value)


如上所示,input_event函数可以接受对于设备的type、code、value输入。type表示设备种类,例如,1为键盘,2为鼠标(或相对坐标定点设备),3为触摸屏(或绝对坐标定点设备)。需要注意的是,便携式电脑的触摸板中1、2、3都存在。

另外,在鼠标的事件中按钮(button)操作的type也是1。这些组合的详细情况请参考Linux内核的头文件include/linux/input.h。

键盘输入

value为0表示键盘的键未按下,value非0则表示键按下。code中将出现按下的键的代码,因此可以通过这个组合来判断键盘的输入。当code小于256时,就表示键盘的事件。通过使用下列脚本,就可以确认键盘的输入代码。


probe kernel.function("input_event"){

if($type!=1||$code>=256)

next;

printf("Keyboard:%d%s\n",$code,$value?"Down":"Up");

}


表8-15为笔者的环境中经常使用的键的代码对应表。

使用SystemTap接受来自键盘、鼠标的输入 - 图1

那么使用键盘在界面上移动@的kbd_cursor.stp就如下所示。


global x=20,y=20

probe kernel.function("input_event"){

if($type!=1||$code>=256||$value!=1)

next

if($code==103)

y—

else if($code==108)

y++

else if($code==105)

x—

else if($code==106)

x++

}

probe timer.ms(100){

if(x<=0)

x=1

else if(x>=41)

x=40

if(y<=0)

y=1

else if(y>=41)

y=40

ansi_clear_screen()

ansi_cursor_move(x, y)

print("@")

}


鼠标输入

鼠标输入比键盘输入复杂。这是因为定点设备有小红帽(trackpoint)和触摸板、触摸屏等多种形式。本节将介绍一般鼠标(相对移动距离设备)、在笔者的环境下测试的触摸板(绝对坐标设备)。对于这些定点设备,都可以使用下列方法来确认设备的输入。


stap-e'probe kernel.function("input_event"){println($$parms)}'


让$$parms作为检测函数的参数,由此来获得系统的信息,并作为字符串传递给用户。执行这个脚本并操作鼠标等,就可以看出传送了什么样的事件。

一般的鼠标通过下列组合传送相对移动距离(见表8-16)。

使用SystemTap接受来自键盘、鼠标的输入 - 图2

在笔者的环境下,触摸板(Synaptics)是返回绝对坐标的设备。可以得知从这个设备发送的有下表所示的信号(见表8-17)。

使用SystemTap接受来自键盘、鼠标的输入 - 图3

但是,是否已按下按钮的信息容易造成连击(连续进入信号),因此处理时需要注意。另外,每个设备的位置信息的偏移量都是不同的,因此处理时也需要注意。

如何让绝对坐标和相对坐标进行相同的处理呢?最基本的方法是将所有的输入统一为绝对坐标或相对坐标进行处理。下面是如前面一样通过鼠标移动“@”的脚本。


global mpos_x=0,mpos_y=0

global x=20,y=20

Mouse Button hooking

probe kernel.function("input_event"){

rx=0;ry=0

if($type==2){#Relative position

if($code==0)

rx=$value

else if($code==1)

ry=$value

}else if($type==1&&$code==0x145&&$value==0){

Take a finger—Reset position

mpos_x=0;mpos_y=0

}else if($type==3){#Absolute-check slide

if($code==0){

if(mpos_x!=0)

rx=$value-mpos_x

mpos_x=$value;

}else if($code==1){

if(mpos_y!=0)

ry=$value-mpos_y

mpos_y=$value;

}

}

if(rx==0&&ry==0)

next

x+=rx/10;y+=ry/10

}

probe timer.ms(100){

if(x<=0)

x=1

else if(x>=41)

x=40

if(y<=0)

y=1

else if(y>=41)

y=40

ansi_clear_screen()

ansi_cursor_move(x, y/2)

print("@")

}


可以看出,timer部分的处理程序与键盘的情况相同,而input_event的处理程序则出现了很大的不同。在这个示例中,为了与通过绝对坐标返回的触摸板相对应,将触摸板返回的坐标改为与上一次的坐标(mpos_x、mpos_y)的相对距离。另外,使用松开手指的事件来重置上次的坐标。从触摸板的使用方法来思考就可以理解,用触摸板来移动光标需要用手指反复滑动很多次。一次滑动操作结束,下一次滑动操作开始时,又会从几乎与上次一致的位置开始滑动。从上次松开手指的位置来看,这时的位置就相当于回到了滑动前的初始位置,这样就很不方便。