Recovery support touch


代碼是基於android4.1的。

1recovery輸入事件及處理分析

1.1時序圖


1.2代碼分析

1.2.1 輸入事件初始化

Recovery的入口是recovery.cpp中的main函數,當然會根據參數的不同,進入recovery的模式也就不一樣,這裏我們就不一一介紹了,我們這裏主要看圖形界面模式,即有個人機交互的見面,用戶可以通過按鍵選擇不同的執行操作。

根據上面的時序圖中,我們可以看到,在main函數中,需要做一些界面顯示、輸入事件的初始化工作。而在這裏,我們就主要先關注輸入事件的初始化工作,即在main函數中調用了ui.cppInit()方法,下面看看其代碼:

void RecoveryUI::Init(){

   ev_init(input_callback, NULL); //輸入事件初始化,並註冊回調函數

   pthread_create(&input_t, NULL, input_thread, NULL);//創建新的線程讀取輸入事件

}

在該方法中,主要完成了兩個動作,第一就是初始化話輸入事件,註冊了回調函數,當有輸入事件的時候回調,第二就是創建了一個新的線程,用於讀取輸入事件的數據。

我們先看看events.c中的ev_init()方法,輸入事件初始化,代碼如下:

int ev_init(ev_callbackinput_cb, void *data)

{   ......

   dir =opendir("/dev/input");//打開文件

   if(dir != 0) {

       while((de = readdir(dir))) {

           unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];

           if(strncmp(de->d_name,"event",5))continue;

           fd = openat(dirfd(dir),de->d_name, O_RDONLY); //打開設備節點

           if(fd < 0) continue;

           

           if (ioctl(fd, EVIOCGBIT(0,sizeof(ev_bits)), ev_bits) < 0){//獲取節點特性

               close(fd);

               continue;

           }

           //判斷是否是按鍵設備

           if (!test_bit(EV_KEY, ev_bits)&& !test_bit(EV_REL,ev_bits) ) {

               close(fd);

               continue;

           }

           //保存設備的信息

           ev_fds[ev_count].fd = fd;

           ev_fds[ev_count].events = POLLIN;

           ev_fdinfo[ev_count].cb = input_cb;

           ev_fdinfo[ev_count].data = data;

           ev_count++;

           ev_dev_count++;

           if(ev_dev_count == MAX_DEVICES) break;

       }

   }

   return 0;

}

#define test_bit(bit, array) \

   ((array)[(bit)/BITS_PER_LONG] & (1<< ((bit) %BITS_PER_LONG)))

代碼看起來還是非常簡單的,輸入設備的設備節點都在/dev/input/目錄下,所以需要掃面下面所有的設備節點。

調用openat()打開設備節點,這是linux的系統調用,這裏就不說了。

調用ioctl,並用宏EVIOCGBIT產生參數,獲取一個設備的特性,這裏特性會說明該設備是按鍵設備還是觸摸設備等,並將特性存在中數組ev_bits中。

定義了test_bit的宏,用於判斷ev_bits中的特性是否是我們想要的,在linux input系統中,EV_KEY指的是按鍵設備,EV_REL值相對座標,如光標移動。看到這裏了,如果我們想要接受觸摸消息,那麼我將在這裏添加EV_ABS的支持。後續會說明添加具體方法。

當打開的設備是我們想要監聽的設備的時候,我們將設備節點的文件描述符等信息添加到ev_fds結構體數組中,還有將註冊的回調函數input_cb添加到結構體數據ev_fdinfo中。

到此就完成了輸入事件的初始化,接下來就看在創建的新線程中讀取輸入事件。

1.2.2 創建線程讀取輸入事件

在前面的RecoveryUI::Init()方法中,我們看到了這麼一句:

 pthread_create(&input_t, NULL,input_thread, NULL); //創建新的線程讀取輸入事件

調用了pthread_create()方法創建新的線程,該方法的原型如下:

int pthread_create(pthread_t*restricttidp,const pthread_attr_t *restrict_attr,void**start_rtn)(void*),void *restrict arg);

若成功則返回0,否則返回出錯編號  

返回成功時,由tidp指向的內存單元被設置爲新創建線程的線程IDattr參數用於制定各種不同的線程屬性。新創建的線程從start_rtn函數的地址開始運行,該函數只有一個萬能指針參數arg,如果需要向start_rtn函數傳遞的參數不止一個,那麼需要把這些參數放到一個結構中,然後把這個結構的地址作爲arg的參數傳入。

linux下用C開發多線程程序,Linux系統下的多線程遵循POSIX線程接口,稱爲pthread。  由restrict修飾的指針是最初唯一對指針所指向的對象進行存取的方法,僅當第二個指針基於第一個時,才能對對象進行存取。對對象的存取都限定於基於由restrict修飾的指針表達式中。restrict修飾的指針主要用於函數形參,或指向由malloc()分配的內存空間。restrict數據類型不改變程序的語義。編譯器能通過作出restrict修飾的指針是存取對象的唯一方法的假設,更好地優化某些類型的例程。下面看四個參數:

第一個參數爲指向線程標識符指針。  

第二個參數用來設置線程屬性。  

第三個參數是線程運行函數的起始地址。  

第四個參數是運行函數的參數。  

另外,在編譯時注意加上-lpthread參數,以調用鏈接庫。因爲pthread並非Linux系統的默認庫。

在我們這裏用到的代碼中,第一個參數的定義爲:pthread_t input_t;爲指向線程標識符指針。接下來我們主要看新線程的入口函數input_thread()方法,代碼如下:

void*RecoveryUI::input_thread(void *cookie)

{

    for(;;) {

       if(!ev_wait(-1)) //查看是否有輸入事件

           ev_dispatch();//有輸入事件,那麼將派發輸入事件

   }

   return NULL;

}

在一個循環裏面不斷的查詢是否有輸入事件,那麼,我們看看events.c中的ev_wait()方法,代碼如下:

int ev_wait(int timeout)

{

    intr;

    r =poll(ev_fds, ev_count,timeout);

    if(r <= 0)

       return -1;

   return 0;

}

還記得在輸入事件初始化的時候,將按鍵等我們想監聽的設備點信息添加到了ev_fds結構體中,在這裏我們將用到了。

系統調用poll()方法,如果ev_fds包含的設備節點中有消息,那麼返回的值r將大於0,所以ev_wait()方法的返回值爲0。再回頭看看input_thread()方法,當ev_wait()返回值等於零的時候,將調用ev_dispatch()派發輸入消息。那麼我們看看events.c中的該方法代碼:

void ev_dispatch(void)

{

   unsigned n;

    intret;

    for(n = 0; n < ev_count; n++) {

       ev_callback cb = ev_fdinfo[n].cb;

       if (cb && (ev_fds[n].revents& ev_fds[n].events))

           cb(ev_fds[n].fd, ev_fds[n].revents,ev_fdinfo[n].data);//回調

   }

}

找到ev_fdinfo結構體數組中之前註冊的回調函數,實際是調用了ui.cpp中的RecoveryUI::input_callback()方法,下面看看其代碼:

intRecoveryUI::input_callback(int fd, short revents, void*data)

{

   struct input_event ev;

    intret;

   ret = ev_get_input(fd, revents,&ev);//讀取輸入消息

    if(ret)

       return -1;

    if(ev.type == EV_SYN) {

       return 0;

} else if (ev.type == EV_REL) {

 ......

   }

    if(ev.type == EV_KEY&& ev.code <=KEY_MAX)//按鍵消息

       self->process_key(ev.code,ev.value);//處理按鍵消息

 

   return 0;

}

在之前的調用用,只是通知有輸入消息,那麼在回調函數中,我們就需要去讀取輸入消息了,定義了結構體input_event的變量ev,調用了events.cev_get_input()方法,看下其代碼:

int ev_get_input(int fd,short revents, struct input_event *ev)

{

    intr;

    if(revents & POLLIN) {

       r = read(fd, ev,sizeof(*ev));//讀取input消息,數據存在ev變量中

       if (r == sizeof(*ev))

           return 0;

   }

   return -1;

}

 

在該方法中,非常簡單,系統調用read()方法去讀fd指定的設備節點的數據,並保存在ev變量中。

我們回到input_callback()方法中看,調用ev_get_input()後,讀取到的input數據存儲在ev結構體中,我們看看其結構體的定義:

struct input_event{

      struct timeval time;//時間

      __u16 type;  //事件的類型,可以確定出是按鍵消息還是觸摸消息,或者其他.

      __u16 code;  //數據的類型,假如是觸摸消息,那麼code可以確定消息是x座標還y座標或者其他

      __s32 value; //數據的值

};

input_callback()方法中,我們看到了調用ui.cppRecoveryUI::process_key()方法處理按鍵消息。

1.2.3 處理按鍵消息

在上一小節中,講到了在ui.cppRecoveryUI::process_key()方法中處理按鍵消息,下面我們看看其代碼:

voidRecoveryUI::process_key(int key_code, int updown) {

    boolregister_key = false;

    if(updown) {

} else {

     register_key =true;//表示是按鍵的up消息

   }

    if(register_key) {

       switch (CheckKey(key_code)) {

         case RecoveryUI::IGNORE:

           break;

         case RecoveryUI::TOGGLE:

           ShowText(!IsTextVisible());

           break;

 

         case RecoveryUI::REBOOT:

           android_reboot(ANDROID_RB_RESTART, 0, 0);

           break;

         case RecoveryUI::ENQUEUE:

           pthread_mutex_lock(&key_queue_mutex);//互斥鎖

           const int queue_max = sizeof(key_queue) /sizeof(key_queue[0]);

           if (key_queue_len < queue_max) {

               key_queue[key_queue_len++] = key_code; //將按鍵號記錄下來

               pthread_cond_signal(&key_queue_cond);//喚醒阻塞線程開始讀數據

           }

           pthread_mutex_unlock(&key_queue_mutex);

           break;

       }

   }

}

在代碼中,我們可以看到,這裏指處理按鍵的up消息,所以我們只需要關注switch語句中的RecoveryUI::ENQUEUE:處理。

其實傳進來的參數 key_code就是按鍵號,在這裏的處理,就是把它存入key_queue[]數組中。但是我們知道,process_key()方法是在新開的線程中被調用的,當然還需要將消息傳送到主線程中去,所以,這裏就需要用到了互斥鎖和線程阻塞和喚醒。

在主線程初始化的時候,就初始化了互斥鎖和線程阻塞喚醒的條件變量,代碼如下:

 pthread_mutex_tkey_queue_mutex;

 pthread_cond_t key_queue_cond;

 pthread_mutex_init(&key_queue_mutex,NULL);//初始化互斥鎖

 pthread_cond_init(&key_queue_cond,NULL);//初始化條件變量

我對互斥鎖的理解是被鎖的代碼保證當前只有一個調用着,這樣可以完成代碼的同步操作。

我們在設計的時候,當沒有輸入消息的時候,主線程會進入阻塞狀態,當有輸入消息的時候,將會喚醒主線程。

所以,在上面代碼中,將對數組key_queue[]的賦值放到互斥鎖裏面,然後調用pthread_cond_signal(&key_queue_cond)喚醒主線程開始讀取按鍵消息。

1.2.4 主線程讀取輸入消息

    在這節介紹之前,先來學習兩個知識點吧,互斥鎖線程阻塞喚醒

    爲了跟上一節的輸入消息處理銜接上,我們這裏先看ui.ccpRecoveryUI::WaitKey()方法,因爲該方法直接讀取到了key_queue[]數組中的數據,其代碼如下:

intRecoveryUI::WaitKey()

{

   pthread_mutex_lock(&key_queue_mutex);//互斥鎖

    do{

       struct timeval now;

       struct timespec timeout;

       gettimeofday(&now, NULL);

       timeout.tv_sec = now.tv_sec;

       timeout.tv_nsec = now.tv_usec * 1000;

       timeout.tv_sec +=UI_WAIT_KEY_TIMEOUT_SEC;

       int rc = 0;

       while (key_queue_len == 0 && rc !=ETIMEDOUT) {

           rc =pthread_cond_timedwait(&key_queue_cond,&key_queue_mutex,

                                       &timeout);//主線程阻塞了

       }

    }while (usb_connected() &&key_queue_len == 0);

 

    intkey = -1;

    if(key_queue_len > 0) {

       key =key_queue[0];//獲取數組裏面第一個值

       memcpy(&key_queue[0],&key_queue[1], sizeof(int) *--key_queue_len);//數據移動

   }

   pthread_mutex_unlock(&key_queue_mutex);

   return key;

}

這裏的整個函數代碼都放在互斥鎖裏面,是在主線程中被調用的。我們知道當沒有輸入消息的時候,key_queue_len=0,所以將在中pthread_cond_timedwait()阻塞。對於這個阻塞,解除阻塞有兩種方法,第一種當然是在有輸入事件的時候,調用pthread_cond_signal(...)解除阻塞,第二種的timeout時間到了,會解除阻塞喚醒主線程。

所以,當有輸入事件的時候,主線程被喚醒,然後將key_queue[]數組中的第一個元素賦值給key變量,然後返回。這裏還有一個對key_queue[]的操作,當取出數組第一個元素的時候,將後面的元素通過指針的方式,移動到前面一位。

好了,這時候應該知道了主線程如何獲取到案件消息了。下面我們將從recovery.cppmain函數開始,講解主線程得得到按鍵消息是怎麼處理的。在main方法中,進入人機交互界面調用的方法是prompt_and_wait(),其代碼如下:

static void

prompt_and_wait(Device*device) {

   const char* const* headers =prepend_title(device->GetMenuHeaders());

    for(;;) {

       finish_recovery(NULL);

       //等待用戶線程菜單

       int chosen_item =get_menu_selection(headers,device->GetMenuItems(), 0, 0, device);

       chosen_item =device->InvokeMenuItem(chosen_item);

       int status;

       int wipe_cache;

       switch (chosen_item) {

           case Device::REBOOT://重啓

               return;

           case Device::WIPE_DATA://格式化data分區

               ......

               break;

           case Device::WIPE_CACHE://格式化cache分區

               ......

               break;

           case Device::APPLY_EXT://SD卡中選擇升級包升級

               .......

               break;

           case Device::APPLY_CACHE://cache分區中選擇升級包升級

               ......

               break;

 

           case Device::APPLY_ADB_SIDELOAD://通過adb進行升級

               ......

               break;

       }

   }

}

在該方法中,有一個for的無線循環,在該循環中,調用了get_menu_selection()方法,這個方法是核心,它會經過一步步調用,最終調用到RecoveryUI::WaitKey()方法獲取到按鍵事件,然後做出相應的處理,並返回體現用戶選擇的chosen_item變量,再經過InvokeMenuItem()處理,得到用戶真正的操作,下面的switch方法中,就是對用戶選擇做出相應的動作。

那麼,在這裏,我們就主要關注get_menu_selection()方法,裏面有我們想要的東西,其代碼如下:

static int

get_menu_selection(constchar* const * headers, const char* const * items,

                  int menu_only, int initial_selection, Device* device) {

   ui->StartMenu(headers,items, initial_selection);//顯示在屏幕上的菜單

    intselected = initial_selection;

    intchosen_item = -1;

   while (chosen_item < 0) {

       int key =ui->WaitKey();//看到了這個苦等的方法,獲取按鍵號

       int visible = ui->IsTextVisible();

       int action = device->HandleMenuKey(key,visible);//處理按鍵消息

       if (action < 0) {

           switch (action) {

               case Device::kHighlightUp:;//光標上移

                   --selected;

                   selected = ui->SelectMenu(selected)

                   break;

               case Device::kHighlightDown://光標下移

                   ++selected;

                   selected = ui->SelectMenu(selected);

                   break;

               case Device::kInvokeItem://選擇了當前的菜單

                                  chosen_item = selected;

                   break;

               case Device::kNoAction:

                   break;

           }

       } else if (!menu_only) {

           chosen_item = action;

       }

   }

   ui->EndMenu();

   return chosen_item;

}

這裏首先需要調用 ui->StartMenu(),在屏幕上顯示菜單對話框,接着在一個while()循環中等待用戶按鍵消息。這裏我們看到了調用ui->WaitKey()方法,其實就是我們前面介紹的,讀取按鍵消息,返回的是按鍵號,然後根據按鍵號,調用HandleMenuKey()方法處理,得到用戶的意圖,有上下移動光標,選擇當前菜單進入。

好了,有了上面的介紹,那麼現在我們開始加入觸摸的支持了。

2 添加touch支持

有了上面的介紹,加入觸摸的支持應該就不是什麼難事了的。說起來應該有三個步驟:

第一,應該添加觸摸消息的輸入。

第二,處理觸摸消息,將觸摸消息處理成上、下移動、點擊三種事件。

第三,添加主線程讀取觸摸消息及處理。

當然,還必須保證在recovery模式中,觸摸驅動的加載。按照上面定的三個步驟,我們一步步來實現。

2.1添加觸摸消息的輸入

    這個非常簡單,只需要在events.c中的ev_init()方法中添加EV_ABS的支持即可,代碼如下:

int ev_init(ev_callbackinput_cb, void *data)

{

      ......

           

           if(!test_bit(EV_KEY, ev_bits) &&!test_bit(EV_REL, ev_bits)&&!test_bit(EV_ABS,ev_bits)) {

               close(fd);

               continue;

           }

         ......

}

添加了這麼一句,就可以將觸摸事件的設備節點添加到了ev_fds[]結構體的數組中了。

2.2 處理觸摸消息

在處理觸摸消息之前,我給大家一個打印信息,是對一次觸摸消息的打印:

printf,type=3,code=57,value=0    //觸摸屏幕的手指id號,0表示第一個手指

printf,type=3,code=48,value=200  //表示觸摸的工具的接觸面爲200

printf,type=3,code=53,value=549  //x座標

printf,type=3,code=54,value=596  //y座標

printf,type=3,code=50,value=1    //可以不關注

 

printf,type=0,code=2,value=0     //上報了一次觸摸消息完畢

printf,type=0,code=0,value=0

 

printf,type=3,code=57,value=0

printf,type=3,code=48,value=200

printf,type=3,code=53,value=549

printf,type=3,code=54,value=596

printf,type=3,code=50,value=1

 

printf,type=0,code=2,value=0

printf,type=0,code=0,value=0

 

printf,type=3,code=48,value=0     //手指離開了觸摸面

printf,type=0,code=0,value=0

我觸摸了屏幕立即抽開,就有了上面的打印。在上面我們可以看到,一條打印代表一條輸入消息,需要5條消息才能描述一個觸摸面,上面描述了兩個觸摸面,兩次的座標是一樣的。input_event,type=3,說明是觸摸消息。下面看看input_event.code代表的意思:

#define ABS_MT_POSITION_X  0x35 //表示x座標

#define ABS_MT_POSITION_Y 0x36   //表示y座標

#define ABS_MT_TOUCH_MAJOR  0x30 //接觸面的長軸。

#define ABS_MT_WIDTH_MAJOR  0x32 //接觸工具的長軸

#define ABS_MT_TRACKING_ID  0x39  //表示當前多觸摸手指分配的ID

好了,有了上面的一點點介紹,就可以對其處理了。

需要在回到函數中添加處理觸摸事件的方法,代碼如下:

intRecoveryUI::input_callback(int fd, short revents, void*data)

{

   struct input_event ev;

    intret;

    ret= ev_get_input(fd, revents, &ev);

   ......

      if(self->touch_handle_input(ev))//處理觸摸消息

             return 0;

   ......

    if(ev.type == EV_KEY && ev.code<= KEY_MAX)

       self->process_key(ev.code, ev.value);

 

   return 0;

}

在介紹touch_handle_input()方法方法之前,先看看我定義的幾個變量及方法,在ui.h中添加如下:

structTouchEvent{

         int x;

               int y;

      }mTouchEvent[5],lastEvent,firstEvent;

      int touch_id,move_pile;

定義了結構體TouchEvent用來存儲觸摸事件的xy軸座標,因爲觸摸屏支持5個手指觸摸,所以mTouchEvent[5]數組分別存儲5個手指的座標,lastEvent表示最新的一個觸摸座標,firstEvent表示觸摸按下的第一個座標。

我自己定義了touch_handle_input()方法,處理觸摸消息,其定義在ui.cpp中,代碼如下:

intRecoveryUI::touch_handle_input(input_event ev){

      if(ev.type==EV_ABS){

             int touch_code = 0;

       switch(ev.code){

          case ABS_MT_TRACKING_ID:

                  touch_id = ev.value;

                   break;

                caseABS_MT_TOUCH_MAJOR:

                   if(ev.value==0){//所有手指離開觸摸面

                        if((firstEvent.y==lastEvent.y)){

                             int*sreenPara=self->GetScreenPara();//獲取當前屏幕顯示數據

                                  int select =mTouchEvent[0].y/sreenPara[2]-sreenPara[0];

                                  if(select>=0&&select<sreenPara[1]){

                                         menu_select = select;//記錄點擊的菜單列編號

                                         touch_code =Device::kInvokeItem;//點擊動作

                                  }

                           }

                           for(int i=0;i<5;i++){

                                  mTouchEvent[i].x = 0;

                                  mTouchEvent[i].y = 0;

                           }

                           lastEvent.x=lastEvent.y=0;

                           firstEvent.x=firstEvent.y=0;

                           

                      }

                   break;

                caseABS_MT_WIDTH_MAJOR:

                  break;

               caseABS_MT_POSITION_X: //記錄x座標

                   lastEvent.x =ev.value;

                      if(mTouchEvent[touch_id].x == 0){

                         mTouchEvent[touch_id].x = ev.value;

                         }

                     if(firstEvent.x==0&&touch_id==0){

                firstEvent.x = ev.value;

                      }

 

                  break;

               caseABS_MT_POSITION_Y: //記錄y座標

             if((ev.value-lastEvent.y)*move_pile<0){

                   move_pile = 0;

                                  key_queue_len = 0;

                      }else if(lastEvent.y!=0){

                         move_pile += ev.value-lastEvent.y;

                      }

                   lastEvent.y =ev.value;

             if(firstEvent.y==0&&touch_id==0){

                firstEvent.y = ev.value;

                      }

                   if(mTouchEvent[touch_id].y ==0){

                         mTouchEvent[touch_id].y = ev.value;

                         }elseif((ev.value-mTouchEvent[touch_id].y)>20){

                             touch_code =Device::kHighlightDown;//向下移動

                              mTouchEvent[touch_id].y =ev.value;

                              mTouchEvent[touch_id].x =lastEvent.x;

                         }elseif((ev.value-mTouchEvent[touch_id].y)<-20){

                             touch_code =Device::kHighlightUp;//向上移動

                              mTouchEvent[touch_id].y =ev.value;

                              mTouchEvent[touch_id].x =lastEvent.x;

                         }

                      break;

                default :

                           break;

             }

             pthread_mutex_lock(&key_queue_mutex);

             const int queue_max = sizeof(key_queue) /sizeof(key_queue[0]);

       if (key_queue_len <queue_max&&touch_code!=0){

           key_queue[key_queue_len++] =touch_code;//往數組中添加數據

                    pthread_cond_signal(&key_queue_cond); //喚醒主線程豬肚數據    

       }

             pthread_mutex_unlock(&key_queue_mutex);

      return1;   

      }else if(ev.type == EV_SYN){

        touch_id = -1;

        return 0;

      }

      return 0;

}

其實代碼量也沒多少的。觸摸消息是一條條傳過來的,而且需要5條消息才能描述一個觸摸面,所以需要定義一些變量來存儲消息。

對於滑動消息,我是根據一次滑動的距離爲20個座標點的時候,認爲是一次up、或down動作,而根據第一次觸摸的座標和離開的時候的座標相等的時候,認爲is一次點擊動作。

我們看下手指離開觸摸屏的處理,當手指離開的時候,如果firstEvent.y==lastEvent.y,那麼然爲是一個點擊的動作,則需要作出響應。GetScreenPara()方法是在screen_ui.cpp中定義的,代碼如下:

int*ScreenRecoveryUI::GetScreenPara()

{

  int*ScreenPara = new int[3];

 ScreenPara[0] = menu_top;  //菜單的頭信息的列數

 ScreenPara[1] = menu_items; //用戶可以選擇的菜單列數

 ScreenPara[2] = CHAR_HEIGHT; //每列菜單佔的高度

  returnScreenPara;

}

其實這個方法就是獲取了當前顯示菜單的佈局數據,通過這三個數據,我就可以計算出每列菜單所在的座標範圍,舉個列子,用戶可以選擇的第2個菜單的座標範圍應該是從

(menu_top+1)*CHAR_HEIGHT(menu_top+2)*CHAR_HEIGHT之間。有了這些數據,我就可以根據點擊的y座標,計算出用戶點擊的是哪一列菜單了。

好了,觸摸處理的三個動作,我存儲在一個變量touch_code中,觸摸消息與按鍵消息公用一個數據數組key_queue[],所以爲了避免與按鍵消息的衝突,我將觸摸消息處理後定義了負數,存在touch_code變量中。描述三個動作的三個變量,是在Device中原來已經定義好了的:

Device::kInvokeItem = -4 //選中

Device::kHighlightDown = -3//向下移動

Device::kHighlightUp = -2//向上移動

   與按鍵消息類似,將消息存在key_queue[]數組中,主線程還是需要通過WaitKey()獲取。下面看主線程的處理。

2.3 主線程處理觸摸消息

    主線程觸摸消息的處理,在get_menu_selection()方法中,該後的代碼如下:

static int

get_menu_selection(constchar* const * headers, const char* const * items,

                  int menu_only, int initial_selection, Device* device) {

   ui->StartMenu(headers, items,initial_selection);

    intselected = initial_selection;

    intchosen_item = -1;

   while (chosen_item < 0) {

       int key =ui->WaitKey();//讀取輸入消息

       int visible = ui->IsTextVisible();

       int action ;

       if (key == -1) {   //ui_wait_key() timed out

           if (ui->WasTextEverVisible()) {

               continue;

           } else {

               LOGI("timed out waiting for key input; rebooting.\n");

               ui->EndMenu();

               return 0; // XXX fixme

           }

       }elseif(key<=Device::kHighlightUp&&key>=Device::kInvokeItem){//觸摸消息

           action = key;

       }else {

           action =device->HandleMenuKey(key,visible);//按鍵消息

       } 

       if (action < 0) {

           switch (action) {

               case Device::kHighlightUp:

                   --selected;

                   selected= ui->SelectMenu(selected);

                   break;

               case Device::kHighlightDown:

                   ++selected;

                   selected = ui->SelectMenu(selected);

                   break;

               case Device::kInvokeItem:

                                  if(ui->menu_select!=-1){//觸摸消息

                                         chosen_item = ui->menu_select;

                                         ui->menu_select = -1;

                                         }else{

                                         chosen_item = selected;

                                         }

                   break;

               case Device::kNoAction:

                   break;

           }

       } else if (!menu_only) {

           chosen_item = action;

       }

   }

   ui->EndMenu();

   return chosen_item;

}

   ui->WaitKey()讀取的輸入消息存在key變量中,key=-1,說明是主線程阻塞超時,不需要任何操作。當key是爲-2-3-4的任意一個值時,說明是觸摸事件。那麼就可以做處理了。

 

 

參考:

使用EVIOCGBIT ioctl可以獲取設備的能力和特性:

http://hi.baidu.com/fountainblog/item/6b30290781a06a13cd34ea31

互斥鎖:http://baike.baidu.com/view/4518300.htm

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章