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