Android 触摸屏Event上报操作

手机设备上常用触摸屏进行用户操作,非常方便快捷,而且正好有个项目上用到这个设备,所以就花时间研究了一下。好了,还是老规则:大体了解概念先,细节线索找代码:

InputReader.cpp 中有针对单点触摸SingleTouchInputMapper及多点触摸MultiTouchInputMapper的处理代码,这两个类都继承自TouchInputMapper,由syncTouch处理最终的触摸屏动作的发送。

1、首先了解一下触摸屏中的几个参数概念:

“接触”一词用来描述一个物体直接碰到另一个物体的表面。

单点触摸的参数解析:

ABS_X,ABS_Y分别对应触摸屏的x,y坐标

ABS_PRESSURE是压力值,一般触摸屏也只是分是否有按下去,按下去的话值会大于多少,没有按的话值小于多少

ABS_TOOL_WIDTH 触摸工具的宽度

多点触摸的参数解析:

ABS_MT_POSITION_X 接触面的形心的X坐标值 

ABS_MT_POSITION_Y 接触面的形心的Y坐标值 

 ABS_MT_TOUCH_MAJOR和ABS_MT_WIDTH_MAJOR 分别被用来提供手指的大小和触摸面积大小 

TOUCH 和 WIDTH参数给出了个,想想如果一个手指按在玻璃上,透过玻璃你将看到两个区域,一个是手指与玻璃接触的区域,用 ABS_MT_TOUCH_MAJOR描述,一个是手指本身大小的区域,ABS_MT_WIDTH_MAJOR描述, 手指与玻璃接触的面积要小于手指本身的大小,通过这两个参数,可以换算出手指的压力。也可通过 ABS_MT_PRESSURE参数直接提供手指的压力。

除了 MAJOR这个参数,还可以提供一个 MINOR参数,手指可以被认为是一个椭圆,MAJOR和 MINOR可以认为是这个椭圆的长轴和短轴,椭圆的中心可以被 ORIENTATION这个参数描述。

ABS_MT_PRESSURE

接触工具对接触面的压力大小,可以用来代替上面的四个参数。

ABS_MT_ORIENTATION

描述随圆的转动趋势,这是一个抽相值,O值表示接触面在平行与触摸屏的Y轴,向左是负值,向右是正值,如果完全平行于X轴,则上向返回最大值。如果接触面是圆形,则可以忽略这个参数。如果内核不能获得这个参数有有效值,但可以区分接触面的长短轴,这个功能还是可以被部份支持,在一些设备中, ABS_MT_ORIENTATION 的值只能是 0和1。

ABS_MT_TOOL_TYPE描述接触工具类型(手指,触控笔等 ),很多内核驱动无法区分此参数如手指及笔,如果是这样,该参数可以不用,协议目前支持MT_TOOL_FINGER和MT_TOOL_PEN两种类型。
ABS_MT_BLOB_ID形状集ID,集合几个点以描述一个形状,很多驱动没有形状属性,此参数可以不用。

ABS_MT_TRACKING_ID描述了从接触开始到释放的整个过程的集合,如果设备不支持,此参数可是不用。

计算方法:

一些设备将触摸面作为一个矩形上报,可以通过下面这些公式来计算出协议中所需要的信息。

ABS_MT_TOUCH_MAJOR := max(X, Y)

ABS_MT_TOUCH_MINOR := min(X, Y)

ABS_MT_ORIENTATION  := bool(X > Y)

ABS_MT_ORIENTATION的取值范围为0至1,用来标识矩形接触面偏向X轴或Y轴的程度。

触摸轨迹

仅有少数设备可以明触的标识真实的 trackingID,多数情况下 trackingID只能来标识一次触摸动作的过程。

手势

多点触摸指定的应用是创建手势动作, TOUCH和 WIDTH参数经常用来区别手指的压力和手指间的距离,另外 MINOR类的参数可以用来区别设备的接触面的大小(点接触还是面接触),ORIENTATION可以产生旋转事件。

以上的含义从http://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt 翻译而来,可以自行下载

2、代码入手

第一部分:直接写/dev/input/eventX接点实现

单点触摸事件发送序列:

EV_KEY (BTN_TOUCH) 发送是否在按下或弹出(0或者1)

ABS_X 

ABS_Y

ABS_PRESSURE

ABS_TOOL_WIDTH

代码演示:

event.type  = EV_KEY;

event.code  = BTN_TOUCH;

mDown = (istEvent->pointers[0].abs_pressure > 0)?1:0 ;

Event.value  = mDown;

write(fd,&event,sizeof(event)) ;

/* 触摸屏键按下坐标定位  */

event.type  = EV_ABS;

    event.code  = ABS_X;

    event.value = istEvent->pointers[0].abs_x;

    write(fd,&event,sizeof(event)) ;

event.type  = EV_ABS;

    event.code  = ABS_Y;

    event.value = istEvent->pointers[0].abs_y;

    write(fd,&event,sizeof(event)) ;

    /* 触摸屏接触面的压力大小 */

event.type  = EV_ABS;

    event.code  = ABS_PRESSURE;

    event.value = istEvent->pointers[0].abs_pressure;

    write(fd,&event,sizeof(event)) ;

event.type   = EV_ABS;

event.code   = ABS_TOOL_WIDTH;

event.value  = istEvent->pointers[0].abs_touch_major;

/* 结束完整帧数据,发送同步信号 */

    event.type  = EV_SYN; 

    event.code  = SYN_REPORT;

    event.value = 0;

    write(fd, &event, sizeof(event));

    

    Ok,非常简单,对比inputreader.cpp中的代码,注意process代码:

void SingleTouchInputMapper::process(const RawEvent* rawEvent) {

    switch (rawEvent->type) {

    case EV_KEY:  //一定要注意发送这个event,否则触摸事件不会发送出去,直接丢弃

        switch (rawEvent->scanCode) {

        case BTN_TOUCH:

            mAccumulator.fields |= Accumulator::FIELD_BTN_TOUCH;

            mAccumulator.btnTouch = rawEvent->value != 0;

            // Don't sync immediately.  Wait until the next SYN_REPORT since we might

            // not have received valid position information yet.  This logic assumes that

            // BTN_TOUCH is always followed by SYN_REPORT as part of a complete 

            // packet.

            break;

        }

        break;

...

}

void SingleTouchInputMapper::sync(nsecs_t when) {

...

    if (mDown) {  // 这就是上面为何要发送的原因

        mCurrentTouch.pointerCount = 1;

        mCurrentTouch.pointers[0].id = 0;

        mCurrentTouch.pointers[0].x = mX;

        mCurrentTouch.pointers[0].y = mY;

        mCurrentTouch.pointers[0].pressure = mPressure;

        mCurrentTouch.pointers[0].touchMajor = 0;

        mCurrentTouch.pointers[0].touchMinor = 0;

        mCurrentTouch.pointers[0].toolMajor = mToolWidth;

        mCurrentTouch.pointers[0].toolMinor = mToolWidth;

        mCurrentTouch.pointers[0].orientation = 0;

        mCurrentTouch.idToIndex[0] = 0;

        mCurrentTouch.idBits.markBit(0);

}

...

}

多点触摸事件发送序列:

ABS_MT_TOUCH_MAJOR

ABS_MT_PRESSURE

ABS_MT_POSITION_X

ABS_MT_POSITION_Y

SYN_MT_REPORT      //上报第一个点

ABS_MT_TOUCH_MAJOR

ABS_MT_PRESSURE

ABS_MT_POSITION_X

ABS_MT_POSITION_Y

SYN_MT_REPORT      //上报第二个点

... //以上顺序组织多点touch event即可

SYN_REPORT         //最后发送的完整包动作

代码演示:

for(pointIndex=0;pointIndexpointnums;pointIndex++)

{

/* 触摸屏键按下坐标定位  */

event.type  = EV_ABS;

event.code  = ABS_MT_POSITION_X;

event.value = imtEvent->pointers[pointIndex].abs_x;

write(fd,&event,sizeof(event)) ;

event.type  = EV_ABS;

event.code  = ABS_MT_POSITION_Y;

event.value = imtEvent->pointers[pointIndex].abs_y;

write(fd,&event,sizeof(event)) ;

    /* 触摸屏接触面的压力大小 */

event.type  = EV_ABS;

    event.code  = ABS_MT_PRESSURE;

    event.value = imtEvent->pointers[pointIndex].abs_pressure;

    write(fd,&event,sizeof(event)) ;

/* 触摸屏接触面积大小 */

event.type   = EV_ABS;

event.code   = ABS_MT_TOUCH_MAJOR;

event.value  = imtEvent->pointers[pointIndex].abs_touch_major;

event.type  = EV_SYN;

event.code  = SYN_MT_REPORT;

event.value = 0;

write(fd,&event,sizeof(event)) ;

}

/* 结束完整帧数据,发送同步信号 */

event.type  = EV_SYN;

event.code  = SYN_REPORT;

event.value = 0;

write(fd,&event,sizeof(event)) ;

Ok,只要深入研究下inputreader.cpp的代码即可轻松解决这些问题,更扩展的功能扩大及缩小,以及旋转的功能都可以搞定。

对于多点触摸的功能,首先在linux内核的input输入模型需要支持,这个在linux/input.h可以见到,多点触摸功能依赖于以下几个主要的软件位:

#define ABS_MT_TOUCH_MAJOR 0x30/* Major axis of touching ellipse */ 

#define ABS_MT_TOUCH_MINOR 0x31/* Minor axis (omit if circular) */ 

#define ABS_MT_WIDTH_MAJOR 0x32/* Major axis of approaching ellipse */ 

#define ABS_MT_WIDTH_MINOR 0x33/* Minor axis (omit if circular) */ 

#define ABS_MT_ORIENTATION 0x34/* Ellipse orientation */ 

#define ABS_MT_POSITION_X 0x35/* Center X ellipse position */ 

#define ABS_MT_POSITION_Y 0x36/* Center Y ellipse position */ 

#define ABS_MT_TOOL_TYPE 0x37/* Type of touching device */ 

#define ABS_MT_BLOB_ID 0x38/* Group a set of packets as a blob */

第二部分:利用linux input输入模型发送

主要函数介绍:

static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)

{

input_event(dev, EV_ABS, code, value);

}

static inline void input_mt_sync(struct input_dev *dev)

{

input_event(dev, EV_SYN, SYN_MT_REPORT, 0);

}

static inline void input_sync(struct input_dev *dev)

{

input_event(dev, EV_SYN, SYN_REPORT, 0);

}

代码示例:

设定初始参数(支持单点触摸及多点触摸):

    set_bit(EV_SYN, ts->input_dev->evbit);

    set_bit(EV_KEY, ts->input_dev->evbit);

    set_bit(EV_ABS, ts->input_dev->evbit);

    set_bit(BTN_TOUCH,ts->input_dev->keybit);

    

    max_x = 1280;

    max_y = 720;

    

    input_set_abs_params(ts->input_dev, ABS_X, 0, max_x, 0, 0);

    input_set_abs_params(ts->input_dev, ABS_Y, 0, max_y, 0, 0);

    input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 255, 0, 0);

    input_set_abs_params(ts->input_dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);

    input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, max_x, 0, 0);

    input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, max_y, 0, 0);

    input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);

    input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 15, 0, 0);

    input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); 

    input_register_device(ts->input_dev);    

按下时:

for(i=0;i){

    input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 1);

    input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 100);

    input_report_abs(ts->input_dev, ABS_MT_POSITION_X, ts->x[i]);

    input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, ts->y[i]);

    input_mt_sync(ts->input_dev);

    ts->upsend=0;

}

input_sync(ts->input_dev);

弹起时:

    input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);

    input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0);

    input_mt_sync(ts->input_dev);

    input_sync(ts->input_dev);

 

转载于:https://www.cnblogs.com/MMLoveMeMM/articles/4031698.html

你可能感兴趣的:(Android 触摸屏Event上报操作)