天天看點

Linux/Android多點觸摸協定【轉】

關于Linux多點觸摸協定大家可以參考kernel中的文檔:​​https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt​​,而這裡根據實際的例子來了解多點觸摸協定。

多點觸摸協定有兩種,A協定和B協定。

首先來看A協定,協定上說了報點格式是這樣的,以兩點為例:

如果第一個觸點離開(擡起),這裡的意思是說還有一個觸點,需要繼續上報這個觸點。

如果兩個觸點都離開了,那麼隻需要報告一個同步事件就可以了。

而代碼示例如下:

[cpp] ​​view plain​​ ​​copy​​

for (i = 0; i < count; i++) {  

        input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x);  

        input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].x);  

        input_mt_sync(input_dev);  

}  

if (!count)  

input_sync(input_dev);  

其中count值表示觸點個數,如果是2個,那麼這裡值就為2,如果所有觸點都離開了,那麼count值就為0。

上面可以說是最簡單,也是最基本的A協定報點了。除了報點以外,我們也來關注一下input device注冊時需要setting的一些東西。

input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, MAX_X, 0, 0);  

input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, MAX_Y, 0, 0);  

__set_bit(EV_SYN, input_dev->evbit);  

__set_bit(EV_ABS, input_dev->evbit);  

__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);  

可能你會看到有的代碼會多下面這兩句:

__set_bit(ABS_MT_POSITION_X, input_dev->absbit);  

其實這兩句(包括上面的__set_bit(EV_ABS, input_dev->evbit);)是可有可無的,因為在input_set_abs_params函數中會做相應的設定。

而這句__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);也是必須要有的,否則在Android中會出現一個白色小圓環,感覺像是缺少idc檔案一樣。最後通過getevent -p指令看一下觸摸屏的setting。

對于B協定就稍微顯得有點複雜。B協定需要硬體支援,和A協定主要差別在哪裡呢?B協定可以使用一個ID來辨別觸點,可以減少上報到使用者空間的資料量,這個ID(ABS_MT_TRACKING_ID)可以有硬體提供或者從原始資料計算而得。那>麼下面我們就來看B協定怎麼上報資料的。

如果觸點45隻是在x方向做了移動,那麼應該怎麼報告這個事件呢?

可以看到減少了很多資料的上報,這就是同A協定最大的差別。

這裡為什麼沒有發送ABS_MT_SLOT 0事件呢,因為之前slot已經被置成了0,再次發送ABS_MT_SLOT 0是會被忽略掉的。

如果第二個觸點被擡起,發送下面的事件序列。

其他event

ABS_MT_POSITION_X和ABS_MT_POSITION_Y是多點觸摸協定的最小事件集,是最基本的事件,也是必須的事件。除此之外呢,還包括下面的一些時間集(需要設定支援):

ABS_MT_TOUCH_MAJOR

ABS_MT_TOUCH_MINOR

ABS_MT_TOUCH*用來表示接觸點區域大小(即手指與玻璃接觸區域大小),通常接觸區域是一個橢圓形狀,那麼MAJOR就表示橢圓的長軸,而MINOR就表示橢圓的短軸。如果接觸區域是圓形的話,那麼可以忽略MINOR,而MAJOR就表示圓形的直徑大小。

ABS_MT_WIDTH_MAJOR

ABS_MT_WIDTH_MINOR

上面的TOUCH表示接觸區域的大小,而WIDTH則表示為接觸工具的大小(例如手指,觸控筆等)。

ABS_MT_PRESSURE

而PRESSURE表示壓力值,這個壓力值可以通過上面的4個參數計算而得,例如:ABS_MT_TOUCH_MAJOR/ABS_MT_WIDTH_MAJOR,可以看到接觸面積越大,壓力值也就越大。當然這個壓力值也可以直接由裝置提供。

ABS_MT_DISTANCE

觸點與接觸面的距離,0表示觸點在接觸面的表面(已經實實在在的接觸到了),而正數表示在接觸面的上方。

ABS_MT_ORIENTATION

觸點的方向。

ABS_MT_TOOL_X

ABS_MT_TOOL_Y

ABS_MT_TOOL_TYPE

關于上報虛拟按鍵值

通常觸摸屏下方都有3個虛拟按鍵,而這3個按鍵同其它實體按鍵(例如:power按鍵、音量按鍵)又有所不同,它是觸摸屏提供的一組虛拟按鍵,我們通過觸摸屏會得到這一組按鍵的坐标值,可以通過這個坐标值上報相應的按鍵值來實

現按鍵功能,那麼怎麼來上報這個按鍵值呢。首先需要對input裝置做一些setting:

__set_bit(KEY_MENU, input_dev->keybit);  

__set_bit(KEY_HOMEPAGE, input_dev->keybit);  

__set_bit(KEY_BACK, input_dev->keybit);  

__set_bit(EV_KEY, input_dev->evbit);  

OK,這些鍵值呢在kernel中是定義在uapi/linux/input.h中的,而通常我們的driver隻需要包含linux/input.h就可以了,這個檔案中include了的uapi/linux/input.h。

好的,再來看怎麼上報鍵值。

按鍵按下:

input_report_key(input_dev, key_value, 1);  

按鍵擡起:

input_report_key(input_dev, key_value, 0);  

如果是按鍵一直被按下,重複上報按鍵被按下那部分就可以了。

有的地方可能會看到直接使用input_event函數,例如:

input_event(input_dev, EV_KEY, key_value, 1);  

大家也可以去看看input_report_key函數,它其實是對input_event函數做了封裝,不管是input_report_abs也好,還是input_sync,最終都是調用的input_event函數,是以真正上報event的函數其實是input_event函數。

最後一點在setting時除了__set_bit之外,可能還會看到另外一個函數input_set_capability,這個函數實作在drivers/input/input.c中,而它最終還是調用了__set_bit函數,是以最後效果都是一樣的。

繼續閱讀