手機設備上常用觸摸屏進行用戶操作,非常方便快捷,而且正好有個項目上用到這個設備,所以就花時間研究了一下。好了,還是老規則:大體了解概念先,細節線索找代碼: 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;pointIndex<imtEvent->pointnums;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<finger;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);