天天看点

06.输入系统:第10课第23节_输入系统_多点触摸驱动程序_Reader线程

在前面的小节中,我们以按键为例,深入的讲解了android的输入系统,该小节讲Reader线程对触摸屏的相关处理。下面是两个reader线程的流程图,分别针对按键与触摸屏:

针对按键:

06.输入系统:第10课第23节_输入系统_多点触摸驱动程序_Reader线程

针对触摸屏:

06.输入系统:第10课第23节_输入系统_多点触摸驱动程序_Reader线程

框架分析

如果有兴趣,大家可以详细的分析一下源代码,该处只做简单的介绍:从前面的学习中,我们知道Reader线程是一个大的循环,这个大循环中不停的从输入设备中读取数据,这个工作由evenHub.cpp完成,我们打开inputReader.cpp,找到LooOnce函数,

void InputReader::loopOnce() {
	/*获取数据*/
	mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE)
           

他是如何获取数据的呢?在前面的小节我们分析过,会使用epoll_wait与inotify监测/dev/input目录,主要分为分为两大类:

1.有输入设备接入或者拔出(增加或者减少节点):
	a.增加设备节点,会构造一个newDevice的类,用来表示这个设备,该类包含呢open,ioctol(如		读取kl,idc,kcm配置文件)等。
2.节点有数据能够被读取(有设备上报输入事件):
           

在这里通过mEventHub->getEvents获取的都是事件,也就是说他还没有处理,如当有设备接入时,就能获得这个事件,但是获得之后需要怎么处理呢?可能处理方式如下:

1.添加设备,用于epoll_wait监测:调用addDeviceLocked构造InputDevice对象,查看inputReader.cpp源代码:

void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
	inputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
		InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),controllerNumber, identifier, classes);
		// Keyboard-like devices.
	   	if (keyboardSource != 0) {
	        device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
	    }
		 // Touchscreens and touchpad devices.
   		if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
        	device->addMapper(new MultiTouchInputMapper(device));
           

从上面我们可以看到,首先device = new InputDevice,创建一个设备如果是按键,调用的是:

如果是触摸屏调用的是:

分别添加一个按键设备,触摸屏设备。这里的KeyboardInputMapper与 MultiTouchInputMapper至关重要,他们将是处理数据的核心。

2.移除设备,从epoll_wait中移除,不再对其进行监测。

3.当epoll_wait监测到有数据之后,即获取数据

然后做简单的处理,那么他是怎么处理数据的呢?回到InputReader::loopOnce方法:

void InputReader::loopOnce() {
	/*获取事件*/
	mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
	/*处理事件*/
	processEventsLocked(mEventBuffer, count);
		processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
			device->process(rawEvents, count);
           

该处的device就是前面构造的device = new InputDevice,之前提到过,其内包含了open,ioctlr等。那么device->process(rawEvents, count);是如何处理的呢?:

device->process(rawEvents, count);
	mapper->process(rawEvent);
           

这里的maper就是前面的:

device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
device->addMapper(new MultiTouchInputMapper(device));
           

所以说KeyboardInputMapper,MultiTouchInputMapper是与硬件通信的核心。下面我们把重点放在触摸屏的mapper->process(rawEvent)上面,来进行进一步分析。

触摸屏相关

我们在inputReader.cpp中可以找到MultiTouchInputMapper::MultiTouchInputMapper方法。在分析源码之前,我们想象一下Reader线程会做什么事情:

大家都知道触摸屏和我们的显示屏是两个设备,不一定相同,那么他肯定有一个转化过程

1.触摸屏的坐标不等于LCD的坐标,如触摸屏的分辨率高于显示屏的分辨率,我们需要按照一定比例进行转换。

2.对于触摸屏,在浏览图片的时候,我们是可以使用两个手指对其进行缩放的,这些就是对手势的识别,这是APP的工作,输入系统只是上报那些点。

根据分析,MultiTouchInputMapper要做的事情为,1.记录原始数据。2.处理(转换坐标)3.发送给Dispatcher线程。

下面我们分析源代码,假设我们的手指同时画了两条线,在我们的触摸屏gslx680.c文件中,可以找到函数report_data中的以下内容:

input_report_abs(ts->input, ABS_MT_PRESSURE, id);
		input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, 1);
		input_report_abs(ts->input, ABS_MT_POSITION_X, x);
		input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
		input_mt_sync(ts->input);
           

这是上报一个点,report_data函数被循环调用,上报多个点,最后调用input_sync(ts->input)同步所有点,即代表此时刻上报完成,下一个时刻也是同样原理。

我们找到:

/*该函数会被循环执行*/
void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
	TouchInputMapper::process(rawEvent);//该函数对于我们的触摸屏机会没有作用
		/*下面三个函数,对于我们的触摸屏没有任何作用*/
	    mCursorButtonAccumulator.process(rawEvent);/*处理鼠标按键*/
	    mCursorScrollAccumulator.process(rawEvent);/*处理鼠标滚轮*/
	    mTouchButtonAccumulator.process(rawEvent);/*处理触摸屏按键*/
	    /*遇到SYN_REPORT,进行同步,代表同一时刻已经上报完成*/
		if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
        	sync(rawEvent->when);
        		/*这是一个虚函数,其实现为void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {*/
        		syncTouch(when, next);
        			int32_t trackingId = inSlot->getTrackingId();//获得一个ID
        			mPointerIdBits = newPointerIdBits;//如果没有ID则分配一个
        			
	mMultiTouchMotionAccumulator.process(rawEvent);
		} else {
			Slot* slot = &mSlots[mCurrentSlot];
	        switch (rawEvent->code) {
	        case ABS_MT_POSITION_X:
	            slot->mInUse = true;
	             slot->mAbsMTPositionX = rawEvent->value;
	             break;
	         .......
	         ........
	        case ABS_MT_WIDTH_MINOR:
	            slot->mInUse = true;
	            slot->mAbsMTWidthMinor = rawEvent->value;
	            slot->mHaveAbsMTWidthMinor = true;
	            break;
	        case ABS_MT_ORIENTATION:
	            slot->mInUse = true;
	            slot->mAbsMTOrientation = rawEvent->value;
	            break;
	         }
	 	} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
 		mCurrentSlot += 1; 	
 	/*对数据进行处理,坐标转换等等*/
 	processRawTouches(false /*timeout*/);
 		cookAndDispatch(mCurrentRawState.when);
	 		cookPointerData();//把触摸屏坐标转化为lcd坐标
	 		/*分发给dispatch线程*/
	 		dispatchTouches(when, policyFlags);
	 		
		    /* Copy current touch to last touch in preparation for the next cycle:把当前的值记录为上一次的值。
		    其目的是为了,如果驱动没有上报id,只能根据这个数据才能给新的触电分配id*/
		    mLastRawState.copyFrom(mCurrentRawState);
		    mLastCookedState.copyFrom(mCurrentCookedState);
 	
           

这就是一个总的分析流程。

继续阅读