input事件的获取

来源:互联网 发布:怎样给淘宝店铺起名字 编辑:程序博客网 时间:2024/06/02 02:13

转自http://blog.csdn.net/coldsnow33/article/details/16890975 

loop线程已经运行起来了,如果不出意外,它是不会终止的;不妨以此为起点,再开始一段新的旅程,我要去探索input事件的获取。

一 EventHub构造函数


EventHub是所有输入事件的中央处理站,凡是与输入事件有关的事它都管。上帝创造万事万物都是有原因的,看看构造它是出于什么目的。

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. EventHub::EventHub(void) :  
  2.         mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1),  
  3.         mOpeningDevices(0), mClosingDevices(0),  
  4.         mNeedToSendFinishedDeviceScan(false),  
  5.         mNeedToReopenDevices(false), mNeedToScanDevices(true),  
  6.         mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {  
  7.     acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);  
  8.   
  9.     mEpollFd = epoll_create(EPOLL_SIZE_HINT);  
  10.     LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);  
  11.   
  12.     mINotifyFd = inotify_init();  
  13.     int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);  
  14.     LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s.  errno=%d",  
  15.             DEVICE_PATH, errno);  
  16.   
  17.     struct epoll_event eventItem;  
  18.     memset(&eventItem, 0, sizeof(eventItem));  
  19.     eventItem.events = EPOLLIN;  
  20.     eventItem.data.u32 = EPOLL_ID_INOTIFY;  
  21.     result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);  
  22.     LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);  
  23.   
  24.     int wakeFds[2];  
  25.     result = pipe(wakeFds);  
  26.     LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);  
  27.   
  28.     mWakeReadPipeFd = wakeFds[0];  
  29.     mWakeWritePipeFd = wakeFds[1];  
  30.   
  31.     result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);  
  32.     LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",  
  33.             errno);  
  34.   
  35.     result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);  
  36.     LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",  
  37.             errno);  
  38.   
  39.     eventItem.data.u32 = EPOLL_ID_WAKE;  
  40.     result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);  
  41.     LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",  
  42.             errno);  
  43. }  

前面一堆类似mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD)初始化成员变量的就比较简单了,向下看需要补充点知识,epoll机制和inotify机制。
1 epoll_create()生成一个epoll专用的描述符mEpollFd。
mINotifyFd = inotify_init();
2 添加一个epoll事件,监测mINotifyFd文件描述符可读,eventItem.events = EPOLLIN表示可读。
eventItem.data.u32 = EPOLL_ID_INOTIFY;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
3 创建一个inotify实例,返回一个文件描述符。
mINotifyFd = inotify_init();
4 添加一个watch,监测DEVICE_PATH的创建和删除。
static const char *DEVICE_PATH = "/dev/input";
IN_DELETE | IN_CREATE表示添加和删除。
int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
至此,要知道的是有个inotify的watch一直监视着"/dev/input"的创建和删除;有个epoll可以查询,要使用epoll_wait查询imINotifyFd的变化是否可读)。
5 貌似出现了一个系统调用,它是pipe(),于是我们得到了唤醒读pipe和唤醒写pipe,如果向mWakeWritePipeFd写,那么mWakeReadPipeFd就会有变化。
mWakeReadPipeFd = wakeFds[0];
  mWakeWritePipeFd = wakeFds[1];
6 用fcntl()将读写pipe都设置为非阻塞方式,避免读空pipe、写满pipe时的阻塞。
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
7 又添加了一个epoll事件,这次是为了查询读pipe可读。
eventItem.data.u32 = EPOLL_ID_WAKE;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
8 下面的用于epoll通知,与device无关,后面会用到。
eventItem.data.u32 = EPOLL_ID_INOTIFY;
eventItem.data.u32 = EPOLL_ID_WAKE;

二 EventHub::getEvents


说好InputReader::loopOnce是起点的,该回来集合了。

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. class InputReader : public InputReaderInterface {  
  2. ......  
  3.   static const int EVENT_BUFFER_SIZE = 256;  
  4.   RawEvent mEventBuffer[EVENT_BUFFER_SIZE];  
  5. }  
  6. void InputReader::loopOnce() {  
  7. ......  
  8.   size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);  
  9. ......  
  10. }  

EventHub::getEvents()要做的事情太多了,一点一点分析吧。

1 RawEvent

从EventHub中取出的原始事件。

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. struct RawEvent {  
  2.     nsecs_t when; //时间  
  3.     int32_t deviceId; //device ID,如果是内嵌键盘mBuiltInKeyboardId为0  
  4.     int32_t type; //device操作,添加,移除或者事件类型  
  5.     int32_t code; //事件编码  
  6.     int32_t value; //值  
  7. };  

2 input_event

这是kernel里完全对应的一个事件结构

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. struct input_event {  
  2.  struct timeval time;  
  3.  __u16 type;  
  4.  __u16 code;  
  5.  __s32 value;  
  6. };  

3 mNeedToReopenDevices是说需要重复打开,构造EventHub的时候,它肯定是false的;还不知道什么时候需要这个东东,先放一放。
4 mClosingDevices是说有device added/removed了,初始化的时候它是0,又飘过。
5 mNeedToScanDevices是说需要扫描设备,它是true不能再飘了。

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. void EventHub::scanDevicesLocked() {  
  2.     status_t res = scanDirLocked(DEVICE_PATH);  
  3.     if(res < 0) {  
  4.         ALOGE("scan dir failed for %s\n", DEVICE_PATH);  
  5.     }  
  6.     if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {  
  7.         createVirtualKeyboardLocked();  
  8.     }  
  9. }  
[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. status_t EventHub::scanDirLocked(const char *dirname)  
  2. {  
  3.     char devname[PATH_MAX];  
  4.     char *filename;  
  5.     DIR *dir;  
  6.     struct dirent *de;  
  7.     dir = opendir(dirname); //打开目录"/dev/input"  
  8.     if(dir == NULL)  
  9.         return -1;  
  10.     strcpy(devname, dirname); //devname = "/dev/input"  
  11.     filename = devname + strlen(devname);//filename就是devname上的一个游标,此时游到了strlen(devname)处  
  12.     *filename++ = '/';//devname = "/dev/input/",filename又游了一格  
  13.     while((de = readdir(dir))) {//返回目录中下一个文件的文件名,文件名以在文件系统中的排序返回。  
  14.   
  15.         if(de->d_name[0] == '.' &&//一个点表示当前目录  
  16.            (de->d_name[1] == '\0' ||//两个点表示上一级目录  
  17.             (de->d_name[1] == '.' && de->d_name[2] == '\0')))//这些都不是想要的  
  18.             continue;  
  19.         strcpy(filename, de->d_name);//假设找到为event0,则devname = "/dev/input/event0"  
  20.         openDeviceLocked(devname);  
  21. /*openDeviceLocked创建device,并初始化device->configuration(IDC配置文件),device->KeyMap->keyLayoutMap(*kl按键布局文件)、device->KeyMap->keyCharacterMap(按键字符映射文件)。还初始化了device->classes输入设备类别,比如device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT。创建device->id和device的映射。 
  22. */  
  23.     }//通过while循环创建/dev/input目录文件对应的所以device  
  24.     closedir(dir);  
  25.     return 0;  
  26. }  

status_t EventHub::openDeviceLocked(const char *devicePath)
scanDevicesLocked()中如果没有找到device->id为VIRTUAL_KEYBOARD_ID(-1)的device,则创建一个device为-1的虚拟键盘device。之所以能这样找就是因为openDeviceLocked()创建了device->id和device的映射。
扫描完device要设置标志mNeedToSendFinishedDeviceScan = true。
6 while (mOpeningDevices != NULL)一直到open的device处理完为止,event就是InputReader的mEventBuffer[EVENT_BUFFER_SIZE],capacity是EVENT_BUFFER_SIZE(256),也就是说目前支持同时处理256个device。这些device都是需要add的。
7 FINISHED_DEVICE_SCAN是个什么事件?这是event最后一次一定会发送的事件,会上报所有添加/删除设备事件中最后一次扫描到的事件。
8 mPendingEventIndex和mPendingEventCount构造的时候是0,所以第一次for循环不会进来,所以mPendingINotify为false,所以deviceChanged也为false,而event != buffer,这个for就退出来了。返回到loopOnce(),进入处理流程。

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. if (count) {  
  2.      processEventsLocked(mEventBuffer, count);  
  3.  }  

InputReader::processEventsLocked()中根据rawEvent->type进行事件处理。到下一次进入getEvents()时,event != buffer就不会成立了,就可以epoll_wait()来查询前面设置的几个事件是否发生,有几个?一个是mINotifyFd,一个是mWakeReadPipeFd,一个是我们open的input device。
9 到下下一次进入getEvents()时,mPendingEventIndex < mPendingEventCount就满足了,接着分类处理epoll_event。
(1) eventItem.data.u32 == EPOLL_ID_INOTIFY,mPendingINotify = true。在后面,
readNotify()将会改变deives列表,所以必须在处理了所有event之后执行,确保关闭device之前,我们读完了所以剩余事件。可见,这个notify机制是监测是否有device移除的。
InputReader::processEventsLocked()中根据rawEvent->type进行事件处理。到下一次进入getEvents()时,event != buffer就不会成立了,就可以epoll_wait()来查询前面设置的几个事件是否发生,有几个?一个是mINotifyFd,一个是mWakeReadPipeFd,一个是我们open的input device。
9 到下下一次进入getEvents()时,mPendingEventIndex < mPendingEventCount就满足了,接着分类处理epoll_event。
(1) eventItem.data.u32 == EPOLL_ID_INOTIFY,mPendingINotify = true。在后面,
readNotify()将会改变deives列表,所以必须在处理了所有event之后执行,确保关闭device之前,我们读完了所以剩余事件。可见,这个notify机制是监测是否有device移除的。

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {  
  2.     mPendingINotify = false;  
  3.     readNotifyLocked();  
  4.     deviceChanged = true;  
  5. }  
[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. status_t EventHub::readNotifyLocked() {  
  2.     int res;  
  3.     char devname[PATH_MAX];  
  4.     char *filename;  
  5.     char event_buf[512];  
  6.     int event_size;  
  7.     int event_pos = 0;  
  8.     struct inotify_event *event;  
  9.   
  10.     ALOGV("EventHub::readNotify nfd: %d\n", mINotifyFd);  
  11.     res = read(mINotifyFd, event_buf, sizeof(event_buf));//读取notify的事件,就是dev/input有没有增加或者删除  
  12.     if(res < (int)sizeof(*event)) {//没读到事件  
  13.         if(errno == EINTR)  
  14.             return 0;  
  15.         ALOGW("could not get event, %s\n", strerror(errno));  
  16.         return -1;  
  17.     }  
  18.     //printf("got %d bytes of event information\n", res);  
  19.   
  20.     strcpy(devname, DEVICE_PATH);  
  21.     filename = devname + strlen(devname);  
  22.     *filename++ = '/';//dev/input/  
  23.   
  24.     while(res >= (int)sizeof(*event)) {//读到了事件  
  25.         event = (struct inotify_event *)(event_buf + event_pos);  
  26.         //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");  
  27.         if(event->len) {  
  28.             strcpy(filename, event->name);  
  29.             if(event->mask & IN_CREATE) {//如果事件掩码是创建新文件  
  30.                 openDeviceLocked(devname);//这个函数专门一篇文章说了一下  
  31.             } else {  
  32.                 ALOGI("Removing device '%s' due to inotify event\n", devname);  
  33.                 closeDeviceByPathLocked(devname);  
  34. /* 
  35. mOpeningDevices标记的刚刚open的第一个device,当所有RawEvent的DEVICE_ADDED事件都处理完后,mOpeningDevices为NULL。所以close的时候,先看一下通过mOpeningDevices能不能找到要close的device,如果能,分情况: 
  36. 要删除的device是mOpeningDevices链中的一个,那么找到要删除的前一个pred,pred->next = device->next;然后delete device。 
  37. 要删除的device是mOpeningDevices,那就没有前一个了mOpeningDevices = device->next;然后delete device。 
  38. 如果不能,现在就不能删除了,万一还有事件没有处理完,它的client还在呢,得通知它。现在只做标记: 
  39.         device->next = mClosingDevices; 
  40.         mClosingDevices = device; 
  41. 显然在下一次getEvents()中会处理。 
  42. */  
  43.             }  
  44.         }  
  45.         event_size = sizeof(*event) + event->len;  
  46.         res -= event_size;  
  47.         event_pos += event_size;  
  48.     }  
  49.     return 0;  
  50. }  

(2) eventItem.data.u32 == EPOLL_ID_WAKE,awoken = true。还要读mWakeReadPipeFd,一直读到没有东西可读为止。为什么能读到,说明有写mWakeWritePipeFd阿。

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. void EventHub::wake() {  
  2.     ALOGV("wake() called");  
  3.   
  4.     ssize_t nWrite;  
  5.     do {  
  6.         nWrite = write(mWakeWritePipeFd, "W", 1);  
  7.     } while (nWrite == -1 && errno == EINTR);  
  8.   
  9.     if (nWrite != 1 && errno != EAGAIN) {  
  10.         ALOGW("Could not write wake signal, errno=%d", errno);  
  11.     }  
  12. }  

那什么时候需要wake()呢?比如requestRefreshConfiguration,需要重新load配置文件的时候,我们就不能继续处理epoll_wait()查询到的事件了,要break出for循环,更新了配置文件后再来处理epoll_wait()查询到的事件。
(3) 最后就到了input event了。eventItem.data.u32就是device->id,如果存在,就能找到对应的device。如果device不存在了,执行close动作。读错了给出警告。再继续就是正确的动作了。
一个input事件确实产生的时候,与内核进入evdev所有事件的简单时间戳相比,有些input外设可能有更好的时间概念。这是Android定制的input协议扩展,主要用于基于device drivers的虚拟input设备。iev.type == EV_MSC表示事件类型是重写时间戳。iev.code == MSC_ANDROID_TIME_SEC是秒,iev.code == MSC_ANDROID_TIME_USEC是微妙。接下来重要的是copy事件。

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. #else  
  2.                         event->when = now;  
  3. #endif  
  4.                         event->deviceId = deviceId;  
  5.                         event->type = iev.type;  
  6.                         event->code = iev.code;  
  7.                         event->value = iev.value;  

每copy一个事件event += 1;事件buffer加1,capacity -= 1;buffer长度减一。capacity == 0表示buffer已经满了,只能下一次循环再把事件读到buffer里了,先break出处理epoll事件的while,去loopOnce()里处理下满的buffer;别忘了事件指针mPendingEventIndex -= 1,不然下次不读了。
eventItem.events & EPOLLHUP表示有事件删除,需要close input设备。
上述(1)、(4)和mPendingINotify && mPendingEventIndex >= mPendingEventCount满足时涉及到/dev/input device增加和减少的都会设置deviceChanged = true.
10 如果deviceChanged = true立即处理device的add和remove,用了个continue,返回到for的开始了,有需要close的设备就执行while (mClosingDevices),增加一个DEVICE_REMOVED事件;需要add的设备,readNotifyLocked()时,mOpeningDevices就不为NULL了,再添加DEVICE_ADDED,最后还要添加FINISHED_DEVICE_SCAN。这里就很疑问,如果deviceChanged = true和buffer满了,同时出现就有问题,要立即处理deviceChanged,event会溢出;仔细看,同时出现的情况是不存在的。
11 至此,还有一个mNeedToReopenDevices的标志没有说,什么时候用到这个标志?例如刷新了config文件,refreshConfigurationLocked()->(mEventHub->requestReopenDevices())->mNeedToReopenDevices = true。

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. // Reopen input devices if needed.  
  2. if (mNeedToReopenDevices) {  
  3.     mNeedToReopenDevices = false;  
  4.   
  5.     ALOGI("Reopening all input devices due to a configuration change.");  
  6.   
  7.     closeAllDevicesLocked();  
  8.     mNeedToScanDevices = true;  
  9.     break// return to the caller before we actually rescan  
  10. }  

这很简单了,先关闭所有device,设置重新扫描标志,break出while,就进入loopOnce()处理了;再回来的时候就重新扫描了。
写完这些input事件就获取到了,会保存在RawEvent mEventBuffer[EVENT_BUFFER_SIZE]中。


0 0
原创粉丝点击