Android input系统原理二
1.inputmanager启动源码分析
在SystemServer.java中构造了 inputmanagerservice的对象,在其构造函数中,最重要的是这个nativeInit函数。 下面是核心代码
inputManager = new InputManagerService(context);public InputManagerService(Context context) {
.........mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
.........LocalServices.addService(InputManagerInternal.class, new LocalService());
}
nativeinit函数
对应的jni函数文件名:com_android_server_input_InputManagerService.cpp
nativeInit 构造了一个NativeInputManager,核心代码中构造了一个EventHub和一个InputManager,并且将EventHub传递给了InputManager。那么InputManager就持有了EventHub类。后续可以看到这个EventHub是读取驱动节点数据的核心类。有用到epoll来监听节点
EventHub::EventHub(void) :mEpollFd = epoll_create(EPOLL_SIZE_HINT);}
在InputManager的构造函数中可以看到接收了InputReaderPolicyInterface 和 InputDispatcherPolicyInterface 虚基类的两个子类对象,因为外部传入的两个this,说明NativeInputManager是继承了这两个虚基类的,这里就不放代码了。
NativeInputManager::NativeInputManager(jobject contextObj,jobject serviceObj, const sp<Looper>& looper) :mLooper(looper), mInteractive(true) {
...........sp<EventHub> eventHub = new EventHub();mInputManager = new InputManager(eventHub, this, this);
}InputManager::InputManager(const sp<EventHubInterface>& eventHub,const sp<InputReaderPolicyInterface>& readerPolicy,const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {mDispatcher = new InputDispatcher(dispatcherPolicy);mReader = new InputReader(eventHub, readerPolicy, mDispatcher);initialize();
}
然后去创建了继承了线程的对象
void InputManager::initialize() {mReaderThread = new InputReaderThread(mReader);mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
线程的启动是在nativeStart,最后走到InputManager.cpp中的start函数启动线程
status_t InputManager::start() {status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);if (result) {ALOGE("Could not start InputDispatcher thread due to error %d.", result);return result;}result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);if (result) {ALOGE("Could not start InputReader thread due to error %d.", result);mDispatcherThread->requestExit();return result;}return OK;
}
2.inputReader源码
启动后调用到InputReader.cpp的loopOnce,
void InputReader::loopOnce() {
.........size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
.........
}在InputReader.h中可以看到mEventBuffer的声明和定义,相当于给getEvents传递了一个长度为256 类型为RawEvent的数组
// The event queue.
static const int EVENT_BUFFER_SIZE = 256;
RawEvent mEventBuffer[EVENT_BUFFER_SIZE];在EventHub.h可以看到RawEvent的声明,结构体中的这些变量和getevent命令看到的数据非常类似。
/** A raw event as retrieved from the EventHub.*/
struct RawEvent {nsecs_t when;int32_t deviceId;int32_t type;int32_t code;int32_t value;
};
在getEvents 函数中,代码量过大,因此贴核心代码
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {struct input_event readBuffer[bufferSize];//创建了input_event 的数组,结构体中的数据和RawEvent实际上大差不差RawEvent* event = buffer;//event 指向数组头size_t capacity = bufferSize;bool awoken = false;for (;;) {if (mNeedToScanDevices) {//默认第一次进来需要扫描设备mNeedToScanDevices = false;scanDevicesLocked();mNeedToSendFinishedDeviceScan = true;}
.........
//看后面代码继续分析
}
scanDevicesLocked函数的详细解释
static const char *DEVICE_PATH = "/dev/input";void EventHub::scanDevicesLocked() {status_t res = scanDirLocked(DEVICE_PATH);if(res < 0) {ALOGE("scan dir failed for %s\n", DEVICE_PATH);}if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {createVirtualKeyboardLocked();}
}//打开文件夹/dev/input,读取文件夹下面所有节点,然后在分别打开对应的文件openDeviceLocked
//有了这些信息之后,可以通过这些信息去创建Device对象, 对对应设备加载配置文件或者配置属性
status_t EventHub::scanDirLocked(const char *dirname)
{char devname[PATH_MAX];char *filename;DIR *dir;struct dirent *de;dir = opendir(dirname);if(dir == NULL)return -1;strcpy(devname, dirname);filename = devname + strlen(devname);*filename++ = '/';while((de = readdir(dir))) {if(de->d_name[0] == '.' &&(de->d_name[1] == '\0' ||(de->d_name[1] == '.' && de->d_name[2] == '\0')))continue;strcpy(filename, de->d_name);openDeviceLocked(devname);}closedir(dir);return 0;
}//这个函数很长,核心就是通过ioctl获取一些device 的信息,
status_t EventHub::openDeviceLocked(const char *devicePath) {// Get device driver version.int driverVersion;if(ioctl(fd, EVIOCGVERSION, &driverVersion)) {ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));close(fd);return -1;}// Get device identifier.struct input_id inputId;if(ioctl(fd, EVIOCGID, &inputId)) {ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));close(fd);return -1;}// Allocate device. (The device object takes ownership of the fd at this point.)int32_t deviceId = mNextDeviceId++;Device* device = new Device(fd, deviceId, String8(devicePath), identifier);// Load the configuration file for the device.loadConfigurationLocked(device);//加载配置文件// See if this is a touch pad.//对触摸屏配置属性 判断是不是多指触摸// Is this a new modern multi-touch driver?if (test_bit(ABS_MT_POSITION_X, device->absBitmask)&& test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {// Some joysticks such as the PS3 controller report axes that conflict// with the ABS_MT range. Try to confirm that the device really is// a touch screen.if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;}// Is this an old style single-touch driver?} else if (test_bit(BTN_TOUCH, device->keyBitmask)&& test_bit(ABS_X, device->absBitmask)&& test_bit(ABS_Y, device->absBitmask)) {device->classes |= INPUT_DEVICE_CLASS_TOUCH;// Is this a BT stylus?} else if ((test_bit(ABS_PRESSURE, device->absBitmask) ||test_bit(BTN_TOUCH, device->keyBitmask))&& !test_bit(ABS_X, device->absBitmask)&& !test_bit(ABS_Y, device->absBitmask)) {device->classes |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS;// Keyboard will try to claim some of the buttons but we really want to reserve those so we// can fuse it with the touch screen data, so just take them back. Note this means an// external stylus cannot also be a keyboard device.device->classes &= ~INPUT_DEVICE_CLASS_KEYBOARD;}// Load the key map.//加载按键的配置文件// We need to do this for joysticks too because the key layout may specify axes.status_t keyMapStatus = NAME_NOT_FOUND;if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) {// Load the keymap for the device.keyMapStatus = loadKeyMapLocked(device);}if (registerDeviceForEpollLocked(device) != OK) {delete device;return -1;}configureFd(device);addDeviceLocked(device);添加device 将所有扫描到的device通过头插的方式用链表链接起来}注册监听fd
status_t EventHub::registerDeviceForEpollLocked(Device* device) {struct epoll_event eventItem;memset(&eventItem, 0, sizeof(eventItem));eventItem.events = EPOLLIN;if (mUsingEpollWakeup) {eventItem.events |= EPOLLWAKEUP;}eventItem.data.u32 = device->id;if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, device->fd, &eventItem)) {ALOGE("Could not add device fd to epoll instance. errno=%d", errno);return -errno;}return OK;
}//这一段检查输入设备是否为键盘(INPUT_DEVICE_CLASS_KEYBOARD),然后通过 ioctl 调用 EVIOCSREP //来禁用内核的键盘按键重复功能,因为用户态代码会自己处理按键重复逻辑void EventHub::configureFd(Device* device) {// Set fd parameters with ioctl, such as key repeat, suspend block, and clock typeif (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {// Disable kernel key repeat since we handle it ourselvesunsigned int repeatRate[] = {0, 0};if (ioctl(device->fd, EVIOCSREP, repeatRate)) {ALOGW("Unable to disable kernel key repeat for %s: %s",device->path.string(), strerror(errno));}}String8 wakeMechanism("EPOLLWAKEUP");
//这里是为设备配置挂起阻止机制。当 mUsingEpollWakeup 未被启用时,它会尝试通过 ioctl 调用 //EVIOCSSUSPENDBLOCK 来阻止设备进入挂起状态。这个机制用于保证当设备处于挂起状态时,仍然能够唤醒//系统处理输入事件if (!mUsingEpollWakeup) {
#ifndef EVIOCSSUSPENDBLOCK// uapi headers don't include EVIOCSSUSPENDBLOCK, and future kernels// will use an epoll flag instead, so as long as we want to support// this feature, we need to be prepared to define the ioctl ourselves.
#define EVIOCSSUSPENDBLOCK _IOW('E', 0x91, int)
#endifif (ioctl(device->fd, EVIOCSSUSPENDBLOCK, 1)) {wakeMechanism = "<none>";} else {wakeMechanism = "EVIOCSSUSPENDBLOCK";}}
//设置输入设备使用单调时钟(CLOCK_MONOTONIC)来记录事件的时间戳。单调时钟保证时间总是向前移动,//避免系统时钟调整(如夏令时变化)对输入事件的时间戳产生影响// Tell the kernel that we want to use the monotonic clock for reporting timestamps// associated with input events. This is important because the input system// uses the timestamps extensively and assumes they were recorded using the monotonic// clock.int clockId = CLOCK_MONOTONIC;bool usingClockIoctl = !ioctl(device->fd, EVIOCSCLOCKID, &clockId);ALOGI("wakeMechanism=%s, usingClockIoctl=%s", wakeMechanism.string(),toString(usingClockIoctl));
}void EventHub::addDeviceLocked(Device* device) {mDevices.add(device->id, device);device->next = mOpeningDevices;mOpeningDevices = device;
}
scanDevicesLocked 总结就是扫描监听文件,通过ioctl去驱动中获取不同设备的信息,不同输入设陪配置流程不同,加载配置文件,创建Device,最后将Device存起来。
//这段代码就是将刚才形成链表的device分别遍历将id取出来,赋值给event存起来。
while (mOpeningDevices != NULL) {Device* device = mOpeningDevices;ALOGV("Reporting device opened: id=%d, name=%s\n",device->id, device->path.string());mOpeningDevices = device->next;event->when = now;event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;event->type = DEVICE_ADDED;event += 1;mNeedToSendFinishedDeviceScan = true;if (--capacity == 0) {break;}
}
//这里为true,如果有5个device的话,这里在添加一个,那么就总共有6个了。
if (mNeedToSendFinishedDeviceScan) {mNeedToSendFinishedDeviceScan = false;event->when = now;event->type = FINISHED_DEVICE_SCAN;event += 1;if (--capacity == 0) {break;}
}
接下来就会执行这里,因为第一次扫描设备,其它代码都不会执行
// Return now if we have collected any events or if we were explicitly awoken.
if (event != buffer || awoken) {break;
}//break之后,返回扫描到的设备数
// All done, return the number of events we read.
return event - buffer;
在InputReader.cpp中,在getEvents函数之后会返回设备数,如果扫描到了5个设备,那么这里会返回6,具体可以看上面getEvents的函数源码解析
void InputReader::loopOnce() {int32_t oldGeneration;int32_t timeoutMillis;bool inputDevicesChanged = false;
..........size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);{ // acquire lockAutoMutex _l(mLock);mReaderIsAliveCondition.broadcast();if (count) {processEventsLocked(mEventBuffer, count);}if (mNextTimeout != LLONG_MAX) {nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);if (now >= mNextTimeout) {
#if DEBUG_RAW_EVENTSALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
#endifmNextTimeout = LLONG_MAX;timeoutExpiredLocked(now);}}if (oldGeneration != mGeneration) {inputDevicesChanged = true;getInputDevicesLocked(inputDevices);}} // release lock// Send out a message that the describes the changed input devices.if (inputDevicesChanged) {mPolicy->notifyInputDevicesChanged(inputDevices);}// Flush queued events out to the listener.// This must happen outside of the lock because the listener could potentially call// back into the InputReader's methods, such as getScanCodeState, or become blocked// on another thread similarly waiting to acquire the InputReader lock thereby// resulting in a deadlock. This situation is actually quite plausible because the// listener is actually the input dispatcher, which calls into the window manager,// which occasionally calls into the input reader.mQueuedListener->flush();
}
就下来会进入到processEventsLocked对device进行处理,很明显,根据device type,这里将会进入addDeviceLocked
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {for (const RawEvent* rawEvent = rawEvents; count;) {int32_t type = rawEvent->type;size_t batchSize = 1;if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {int32_t deviceId = rawEvent->deviceId;while (batchSize < count) {if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT|| rawEvent[batchSize].deviceId != deviceId) {break;}batchSize += 1;}
#if DEBUG_RAW_EVENTSALOGD("BatchSize: %zu Count: %zu", batchSize, count);
#endifprocessEventsForDeviceLocked(deviceId, rawEvent, batchSize);} else {switch (rawEvent->type) {case EventHubInterface::DEVICE_ADDED:addDeviceLocked(rawEvent->when, rawEvent->deviceId);break;case EventHubInterface::DEVICE_REMOVED:removeDeviceLocked(rawEvent->when, rawEvent->deviceId);break;case EventHubInterface::FINISHED_DEVICE_SCAN:handleConfigurationChangedLocked(rawEvent->when);break;default:ALOG_ASSERT(false); // can't happenbreak;}}count -= batchSize;rawEvent += batchSize;}
}void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
//这里验证deviceId是否在mDevices中存在,如果存在,就不需要再添加一遍了ssize_t deviceIndex = mDevices.indexOfKey(deviceId);if (deviceIndex >= 0) {ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);return;}//根据deviceId去获取device的相关信息InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);uint32_t classes = mEventHub->getDeviceClasses(deviceId);int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);//创建InputDeviceInputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);device->configure(when, &mConfig, 0);device->reset(when);if (device->isIgnored()) {ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId,identifier.name.string());} else {ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId,identifier.name.string(), device->getSources());}//将InputDevice放入到mDevices存起来mDevices.add(deviceId, device);bumpGenerationLocked();if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {notifyExternalStylusPresenceChanged();}
}//这里是给每个InputDevice都添加了一个Mapper,由Mapper将原始数据转换为真正的触摸数据
InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,const InputDeviceIdentifier& identifier, uint32_t classes) {InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),controllerNumber, identifier, classes);// External devices.if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {device->setExternal(true);}// Devices with mics.if (classes & INPUT_DEVICE_CLASS_MIC) {device->setMic(true);}// Switch-like devices.if (classes & INPUT_DEVICE_CLASS_SWITCH) {device->addMapper(new SwitchInputMapper(device));}// Scroll wheel-like devices.if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {device->addMapper(new RotaryEncoderInputMapper(device));}// Vibrator-like devices.if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {device->addMapper(new VibratorInputMapper(device));}// Keyboard-like devices.uint32_t keyboardSource = 0;int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {keyboardSource |= AINPUT_SOURCE_KEYBOARD;}if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;}if (classes & INPUT_DEVICE_CLASS_DPAD) {keyboardSource |= AINPUT_SOURCE_DPAD;}if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {keyboardSource |= AINPUT_SOURCE_GAMEPAD;}if (keyboardSource != 0) {device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));}// Cursor-like devices.if (classes & INPUT_DEVICE_CLASS_CURSOR) {device->addMapper(new CursorInputMapper(device));}// Touchscreens and touchpad devices.if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {device->addMapper(new MultiTouchInputMapper(device));} else if (classes & INPUT_DEVICE_CLASS_TOUCH) {device->addMapper(new SingleTouchInputMapper(device));}// Joystick-like devices.if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {device->addMapper(new JoystickInputMapper(device));}// External stylus-like devices.if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {device->addMapper(new ExternalStylusInputMapper(device));}return device;
}
在processEventsLocked中,会循环处理每个device。
然后循环第二次进入getevent,前面的流程都不会执行,在这里会等待事件的发生,如果有事件发生,那么mPendingEventItems中将会存储eventItem
mPendingEventIndex = 0;release_wake_lock(WAKE_LOCK_ID);int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
如果有事件发生,那么mPendingEventIndex将会为0,mPendingEventCount至少也是1.就会进入这个while循环中。
while (mPendingEventIndex < mPendingEventCount) {const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];//判断是否有新的节点添加或删除if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {if (eventItem.events & EPOLLIN) {mPendingINotify = true;} else {ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);}continue;}........//获取对应device,通过device来读取event,最多读取256个inputeventssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);if (deviceIndex < 0) {ALOGW("Received unexpected epoll event 0x%08x for unknown device id %d.",eventItem.events, eventItem.data.u32);continue;}Device* device = mDevices.valueAt(deviceIndex);if (eventItem.events & EPOLLIN) {int32_t readSize = read(device->fd, readBuffer,sizeof(struct input_event) * capacity);if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
..........} else if (readSize < 0) {
..........} else if ((readSize % sizeof(struct input_event)) != 0) {
..........} else {int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;//计算读取到了多少个input_eventsize_t count = size_t(readSize) / sizeof(struct input_event);for (size_t i = 0; i < count; i++) {struct input_event& iev = readBuffer[i];ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d",device->path.string(),(int) iev.time.tv_sec, (int) iev.time.tv_usec,iev.type, iev.code, iev.value);// Some input devices may have a better concept of the time// when an input event was actually generated than the kernel// which simply timestamps all events on entry to evdev.// This is a custom Android extension of the input protocol// mainly intended for use with uinput based device drivers.if (iev.type == EV_MSC) {if (iev.code == MSC_ANDROID_TIME_SEC) {device->timestampOverrideSec = iev.value;continue;} else if (iev.code == MSC_ANDROID_TIME_USEC) {device->timestampOverrideUsec = iev.value;continue;}}if (device->timestampOverrideSec || device->timestampOverrideUsec) {iev.time.tv_sec = device->timestampOverrideSec;iev.time.tv_usec = device->timestampOverrideUsec;if (iev.type == EV_SYN && iev.code == SYN_REPORT) {device->timestampOverrideSec = 0;device->timestampOverrideUsec = 0;}ALOGV("applied override time %d.%06d",int(iev.time.tv_sec), int(iev.time.tv_usec));}// Use the time specified in the event instead of the current time// so that downstream code can get more accurate estimates of// event dispatch latency from the time the event is enqueued onto// the evdev client buffer.//// The event's timestamp fortuitously uses the same monotonic clock// time base as the rest of Android. The kernel event device driver// (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().// The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere// calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a// system call that also queries ktime_get_ts().event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL+ nsecs_t(iev.time.tv_usec) * 1000LL;ALOGV("event time %" PRId64 ", now %" PRId64, event->when, now);// Bug 7291243: Add a guard in case the kernel generates timestamps// that appear to be far into the future because they were generated// using the wrong clock source.//// This can happen because when the input device is initially opened// it has a default clock source of CLOCK_REALTIME. Any input events// enqueued right after the device is opened will have timestamps// generated using CLOCK_REALTIME. We later set the clock source// to CLOCK_MONOTONIC but it is already too late.//// Invalid input event timestamps can result in ANRs, crashes and// and other issues that are hard to track down. We must not let them// propagate through the system.//// Log a warning so that we notice the problem and recover gracefully.if (event->when >= now + 10 * 1000000000LL) {// Double-check. Time may have moved on.nsecs_t time = systemTime(SYSTEM_TIME_MONOTONIC);if (event->when > time) {ALOGW("An input event from %s has a timestamp that appears to ""have been generated using the wrong clock source ""(expected CLOCK_MONOTONIC): ""event time %" PRId64 ", current time %" PRId64", call time %" PRId64 ". ""Using current time instead.",device->path.string(), event->when, time, now);event->when = time;} else {ALOGV("Event time is ok but failed the fast path and required ""an extra call to systemTime: ""event time %" PRId64 ", current time %" PRId64", call time %" PRId64 ".",event->when, time, now);}}//在将inputevent转化为RawEventevent->deviceId = deviceId;event->type = iev.type;event->code = iev.code;event->value = iev.value;event += 1;capacity -= 1;}if (capacity == 0) {// The result buffer is full. Reset the pending event index// so we will try to read the device again on the next iteration.mPendingEventIndex -= 1;break;}}}到这里又因为event != buffer退出循环了,后续去处理RawEvent
// Return now if we have collected any events or if we were explicitly awoken.
if (event != buffer || awoken) {break;
}
调用processEventsLocked对数据进行处理,这里本质上是找出同一个设备产生的一些了device进行处理
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {for (const RawEvent* rawEvent = rawEvents; count;) {int32_t type = rawEvent->type;size_t batchSize = 1;//使用这个逻辑对具体时间进行处理//1.如果某个设备产生了10个数据,预期是把这10个数据同时处理掉,但是如果这10个数据之间//有其它设备产生的数据,那么就会退出下面这个while循环。先处理同一个设备产生的数据。if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {int32_t deviceId = rawEvent->deviceId;while (batchSize < count) {if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT|| rawEvent[batchSize].deviceId != deviceId) {break;}batchSize += 1;}
#if DEBUG_RAW_EVENTSALOGD("BatchSize: %zu Count: %zu", batchSize, count);
#endif//在这里处理由同一个device产生的一连串的数据processEventsForDeviceLocked(deviceId, rawEvent, batchSize);} else {
....
....}count -= batchSize;rawEvent += batchSize;}
}
processEventsForDeviceLocked中找出id对应的InputDevice调用process进行处理
void InputReader::processEventsForDeviceLocked(int32_t deviceId,const RawEvent* rawEvents, size_t count) {ssize_t deviceIndex = mDevices.indexOfKey(deviceId);if (deviceIndex < 0) {ALOGW("Discarding event for unknown deviceId %d.", deviceId);return;}InputDevice* device = mDevices.valueAt(deviceIndex);if (device->isIgnored()) {//ALOGD("Discarding event for ignored deviceId %d.", deviceId);return;}device->process(rawEvents, count);
}
在InputDevice::process函数中,默认mDropUntilNextSync为false,所以调用mapper->process进行处理
void InputDevice::process(const RawEvent* rawEvents, size_t count) {// Process all of the events in order for each mapper.// We cannot simply ask each mapper to process them in bulk because mappers may// have side-effects that must be interleaved. For example, joystick movement events and// gamepad button presses are handled by different mappers but they should be dispatched// in the order received.size_t numMappers = mMappers.size();for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
#if DEBUG_RAW_EVENTSALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,rawEvent->when);
#endifif (mDropUntilNextSync) {if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {mDropUntilNextSync = false;
#if DEBUG_RAW_EVENTSALOGD("Recovered from input event buffer overrun.");
#endif} else {
#if DEBUG_RAW_EVENTSALOGD("Dropped input event while waiting for next input sync.");
#endif}} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {//是一种异常情况,不具体分析ALOGI("Detected input event buffer overrun for device %s.", getName().string());mDropUntilNextSync = true;reset(rawEvent->when);} else {for (size_t i = 0; i < numMappers; i++) {InputMapper* mapper = mMappers[i];mapper->process(rawEvent);}}--count;}
}
如果设备是触摸屏,那么会调用MultiTouchInputMapper的process进行处理,对照数据进行分析
void MultiTouchInputMapper::process(const RawEvent* rawEvent) {TouchInputMapper::process(rawEvent);mMultiTouchMotionAccumulator.process(rawEvent);
}void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {if (rawEvent->type == EV_ABS) {//最开始数据是ABS类型bool newSlot = false;//是不是用的slot协议,一般都是这种协议if (mUsingSlotsProtocol) {//code最开始是ABS_MT_SLOTif (rawEvent->code == ABS_MT_SLOT) {//将触点id保存mCurrentSlot = rawEvent->value;//表示有新的slotnewSlot = true;}} else if (mCurrentSlot < 0) {mCurrentSlot = 0;}//mCurrentSlot 到这里不会小于0,触点大于等于16时将会报错不支持。 第一次slot判断到此结束if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
#if DEBUG_POINTERSif (newSlot) {ALOGW("MultiTouch device emitted invalid slot index %d but it ""should be between 0 and %zd; ignoring this slot.",mCurrentSlot, mSlotCount - 1);}
#endif} else {//当mCurrentSlot为0时Slot* slot = &mSlots[mCurrentSlot];switch (rawEvent->code) {case ABS_MT_POSITION_X:slot->mInUse = true;slot->mAbsMTPositionX = rawEvent->value;break;case ABS_MT_POSITION_Y:slot->mInUse = true;slot->mAbsMTPositionY = rawEvent->value;break;case ABS_MT_TOUCH_MAJOR:slot->mInUse = true;slot->mAbsMTTouchMajor = rawEvent->value;break;case ABS_MT_TOUCH_MINOR:slot->mInUse = true;slot->mAbsMTTouchMinor = rawEvent->value;slot->mHaveAbsMTTouchMinor = true;break;case ABS_MT_WIDTH_MAJOR:slot->mInUse = true;slot->mAbsMTWidthMajor = 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;//abs 数据第二次处理,value不为0,表示触点没松开,触点松开时value为1case ABS_MT_TRACKING_ID:if (mUsingSlotsProtocol && rawEvent->value < 0) {// The slot is no longer in use but it retains its previous contents,// which may be reused for subsequent touches.slot->mInUse = false;} else {slot->mInUse = true;slot->mAbsMTTrackingId = rawEvent->value;}break;case ABS_MT_PRESSURE:slot->mInUse = true;slot->mAbsMTPressure = rawEvent->value;break;case ABS_MT_DISTANCE:slot->mInUse = true;slot->mAbsMTDistance = rawEvent->value;break;case ABS_MT_TOOL_TYPE:slot->mInUse = true;slot->mAbsMTToolType = rawEvent->value;slot->mHaveAbsMTToolType = true;break;}}} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {// MultiTouch Sync: The driver has returned all data for *one* of the pointers.mCurrentSlot += 1;} else if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) {mDeviceTimestamp = rawEvent->value;}
}void TouchInputMapper::process(const RawEvent* rawEvent) {mCursorButtonAccumulator.process(rawEvent);mCursorScrollAccumulator.process(rawEvent);mTouchButtonAccumulator.process(rawEvent);//同步尾if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {sync(rawEvent->when);}
}void TouchInputMapper::sync(nsecs_t when) {const RawState* last = mRawStatesPending.isEmpty() ?&mCurrentRawState : &mRawStatesPending.top();// Push a new state.mRawStatesPending.push();RawState* next = &mRawStatesPending.editTop();next->clear();next->when = when;// Sync button state.next->buttonState = mTouchButtonAccumulator.getButtonState()| mCursorButtonAccumulator.getButtonState();// Sync scrollnext->rawVScroll = mCursorScrollAccumulator.getRelativeVWheel();next->rawHScroll = mCursorScrollAccumulator.getRelativeHWheel();mCursorScrollAccumulator.finishSync();// Sync touchsyncTouch(when, next);// Assign pointer ids.if (!mHavePointerIds) {assignPointerIds(last, next);}#if DEBUG_RAW_EVENTSALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, ""hovering ids 0x%08x -> 0x%08x",last->rawPointerData.pointerCount,next->rawPointerData.pointerCount,last->rawPointerData.touchingIdBits.value,next->rawPointerData.touchingIdBits.value,last->rawPointerData.hoveringIdBits.value,next->rawPointerData.hoveringIdBits.value);
#endifprocessRawTouches(false /*timeout*/);
}void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {//获取slotcount 值为16size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();size_t outCount = 0;BitSet32 newPointerIdBits;mHavePointerIds = true;//这里遍历每个slot 判断触点是不是还在,如果触点不在,直接略过for (size_t inIndex = 0; inIndex < inCount; inIndex++) {const MultiTouchMotionAccumulator::Slot* inSlot =mMultiTouchMotionAccumulator.getSlot(inIndex);if (!inSlot->isInUse()) {continue;}if (outCount >= MAX_POINTERS) {
#if DEBUG_POINTERSALOGD("MultiTouch device %s emitted more than maximum of %d pointers; ""ignoring the rest.",getDeviceName().string(), MAX_POINTERS);
#endifbreak; // too many fingers!}//构建一个触点对象,数据来自slotRawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];outPointer.x = inSlot->getX();outPointer.y = inSlot->getY();outPointer.pressure = inSlot->getPressure();outPointer.touchMajor = inSlot->getTouchMajor();outPointer.touchMinor = inSlot->getTouchMinor();outPointer.toolMajor = inSlot->getToolMajor();outPointer.toolMinor = inSlot->getToolMinor();outPointer.orientation = inSlot->getOrientation();outPointer.distance = inSlot->getDistance();outPointer.tiltX = 0;outPointer.tiltY = 0;outPointer.toolType = inSlot->getToolType();if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {outPointer.toolType = mTouchButtonAccumulator.getToolType();if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;}}bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE&& (mTouchButtonAccumulator.isHovering()|| (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0));outPointer.isHovering = isHovering;// Assign pointer id using tracking id if available.if (mHavePointerIds) {int32_t trackingId = inSlot->getTrackingId();int32_t id = -1;if (trackingId >= 0) {for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty(); ) {//查找并清除第一个被标记的位(即值为 1 的位),然后返回该位的索引uint32_t n = idBits.clearFirstMarkedBit();if (mPointerTrackingIdMap[n] == trackingId) {id = n;}}//第一次在这里id小于0,并且mPointerIdBits没有满if (id < 0 && !mPointerIdBits.isFull()) {id = mPointerIdBits.markFirstUnmarkedBit();//第一次这里返回0,标记第一个没有被标记过的下标,并返回索引//保存trackingIdmPointerTrackingIdMap[id] = trackingId;}}if (id < 0) {mHavePointerIds = false;outState->rawPointerData.clearIdBits();newPointerIdBits.clear();} else {//记录idoutPointer.id = id;//第一个触点outState->rawPointerData.idToIndex[id] = outCount;outState->rawPointerData.markIdBit(id, isHovering);newPointerIdBits.markBit(id);}}outCount += 1;}outState->deviceTimestamp = mMultiTouchMotionAccumulator.getDeviceTimestamp();outState->rawPointerData.pointerCount = outCount;mPointerIdBits = newPointerIdBits;mMultiTouchMotionAccumulator.finishSync();
}void TouchInputMapper::processRawTouches(bool timeout) {//这里判断是不是某个状态抛弃所有触摸数据if (mDeviceMode == DEVICE_MODE_DISABLED) {// Drop all input if the device is disabled.mCurrentRawState.clear();mRawStatesPending.clear();return;}// Drain any pending touch states. The invariant here is that the mCurrentRawState is always// valid and must go through the full cook and dispatch cycle. This ensures that anything// touching the current state will only observe the events that have been dispatched to the// rest of the pipeline.const size_t N = mRawStatesPending.size();size_t count;for(count = 0; count < N; count++) {//取出RawState对象的数据,数据在syncTouch时填入const RawState& next = mRawStatesPending[count];// A failure to assign the stylus id means that we're waiting on stylus data// and so should defer the rest of the pipeline.if (assignExternalStylusId(next, timeout)) {break;}// All ready to go.clearStylusDataPendingFlags();//拷贝一份数据mCurrentRawState.copyFrom(next);if (mCurrentRawState.when < mLastRawState.when) {mCurrentRawState.when = mLastRawState.when;}//关键方法cookAndDispatch(mCurrentRawState.when);}if (count != 0) {mRawStatesPending.removeItemsAt(0, count);}if (mExternalStylusDataPending) {if (timeout) {nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY;clearStylusDataPendingFlags();mCurrentRawState.copyFrom(mLastRawState);
#if DEBUG_STYLUS_FUSIONALOGD("Timeout expired, synthesizing event with new stylus data");
#endifcookAndDispatch(when);} else if (mExternalStylusFusionTimeout == LLONG_MAX) {mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT;getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);}}
}
分析cookAndDispatch
void TouchInputMapper::cookAndDispatch(nsecs_t when) {// Always start with a clean state.//初始化变量mCurrentCookedState.clear();// Apply stylus buttons to current raw state.applyExternalStylusButtonState(when);// Handle policy on initial down or hover events.bool initialDown = mLastRawState.rawPointerData.pointerCount == 0&& mCurrentRawState.rawPointerData.pointerCount != 0;uint32_t policyFlags = 0;bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState;if (initialDown || buttonsPressed) {// If this is a touch screen, hide the pointer on an initial down.if (mDeviceMode == DEVICE_MODE_DIRECT) {getContext()->fadePointer();}if (mParameters.wake) {policyFlags |= POLICY_FLAG_WAKE;}}// Consume raw off-screen touches before cooking pointer data.// If touches are consumed, subsequent code will not receive any pointer data.if (consumeRawTouches(when, policyFlags)) {//mCurrentRawState.rawPointerData.clear();}// Cook pointer data. This call populates the mCurrentCookedState.cookedPointerData structure// with cooked pointer data that has the same ids and indices as the raw data.// The following code can use either the raw or cooked data, as needed.//关键方法cookPointerData();// Apply stylus pressure to current cooked state.applyExternalStylusTouchState(when);// Synthesize key down from raw buttons if needed.synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,policyFlags, mLastCookedState.buttonState, mCurrentCookedState.buttonState);// Dispatch the touches either directly or by translation through a pointer on screen.if (mDeviceMode == DEVICE_MODE_POINTER) {for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits);!idBits.isEmpty(); ) {uint32_t id = idBits.clearFirstMarkedBit();const RawPointerData::Pointer& pointer =mCurrentRawState.rawPointerData.pointerForId(id);if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS|| pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {mCurrentCookedState.stylusIdBits.markBit(id);} else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER|| pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {mCurrentCookedState.fingerIdBits.markBit(id);} else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) {mCurrentCookedState.mouseIdBits.markBit(id);}}for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits);!idBits.isEmpty(); ) {uint32_t id = idBits.clearFirstMarkedBit();const RawPointerData::Pointer& pointer =mCurrentRawState.rawPointerData.pointerForId(id);if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS|| pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {mCurrentCookedState.stylusIdBits.markBit(id);}}// Stylus takes precedence over all tools, then mouse, then finger.PointerUsage pointerUsage = mPointerUsage;if (!mCurrentCookedState.stylusIdBits.isEmpty()) {mCurrentCookedState.mouseIdBits.clear();mCurrentCookedState.fingerIdBits.clear();pointerUsage = POINTER_USAGE_STYLUS;} else if (!mCurrentCookedState.mouseIdBits.isEmpty()) {mCurrentCookedState.fingerIdBits.clear();pointerUsage = POINTER_USAGE_MOUSE;} else if (!mCurrentCookedState.fingerIdBits.isEmpty() ||isPointerDown(mCurrentRawState.buttonState)) {pointerUsage = POINTER_USAGE_GESTURES;}dispatchPointerUsage(when, policyFlags, pointerUsage);} else {if (mDeviceMode == DEVICE_MODE_DIRECT&& mConfig.showTouches && mPointerController != NULL) {mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);mPointerController->setButtonState(mCurrentRawState.buttonState);mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,mCurrentCookedState.cookedPointerData.idToIndex,mCurrentCookedState.cookedPointerData.touchingIdBits);}if (!mCurrentMotionAborted) {dispatchButtonRelease(when, policyFlags);dispatchHoverExit(when, policyFlags);//触摸点派发dispatchTouches(when, policyFlags);dispatchHoverEnterAndMove(when, policyFlags);dispatchButtonPress(when, policyFlags);}if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {mCurrentMotionAborted = false;}}// Synthesize key up from raw buttons if needed.synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,policyFlags, mLastCookedState.buttonState, mCurrentCookedState.buttonState);// Clear some transient state.mCurrentRawState.rawVScroll = 0;mCurrentRawState.rawHScroll = 0;// Copy current touch to last touch in preparation for the next cycle.mLastRawState.copyFrom(mCurrentRawState);mLastCookedState.copyFrom(mCurrentCookedState);
}void TouchInputMapper::cookPointerData() {//获取有多少个触点uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount;mCurrentCookedState.cookedPointerData.clear();mCurrentCookedState.deviceTimestamp =mCurrentRawState.deviceTimestamp;//按下点的个数mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount;mCurrentCookedState.cookedPointerData.hoveringIdBits =mCurrentRawState.rawPointerData.hoveringIdBits;//按下id的集合mCurrentCookedState.cookedPointerData.touchingIdBits =mCurrentRawState.rawPointerData.touchingIdBits;//判断按下点的个数是否为0,正常不会为0if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {mCurrentCookedState.buttonState = 0;} else {mCurrentCookedState.buttonState = mCurrentRawState.buttonState;}// Walk through the the active pointers and map device coordinates onto// surface coordinates and adjust for display orientation.//遍历触点for (uint32_t i = 0; i < currentPointerCount; i++) {//取出触点数据const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i];// Sizefloat touchMajor, touchMinor, toolMajor, toolMinor, size;//size的校准 长轴短轴的校准switch (mCalibration.sizeCalibration) {case Calibration::SIZE_CALIBRATION_GEOMETRIC:case Calibration::SIZE_CALIBRATION_DIAMETER:case Calibration::SIZE_CALIBRATION_BOX:case Calibration::SIZE_CALIBRATION_AREA:if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) {touchMajor = in.touchMajor;touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor;toolMajor = in.toolMajor;toolMinor = mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor;size = mRawPointerAxes.touchMinor.valid? avg(in.touchMajor, in.touchMinor) : in.touchMajor;} else if (mRawPointerAxes.touchMajor.valid) {toolMajor = touchMajor = in.touchMajor;toolMinor = touchMinor = mRawPointerAxes.touchMinor.valid? in.touchMinor : in.touchMajor;size = mRawPointerAxes.touchMinor.valid? avg(in.touchMajor, in.touchMinor) : in.touchMajor;} else if (mRawPointerAxes.toolMajor.valid) {touchMajor = toolMajor = in.toolMajor;touchMinor = toolMinor = mRawPointerAxes.toolMinor.valid? in.toolMinor : in.toolMajor;size = mRawPointerAxes.toolMinor.valid? avg(in.toolMajor, in.toolMinor) : in.toolMajor;} else {ALOG_ASSERT(false, "No touch or tool axes. ""Size calibration should have been resolved to NONE.");touchMajor = 0;touchMinor = 0;toolMajor = 0;toolMinor = 0;size = 0;}if (mCalibration.haveSizeIsSummed && mCalibration.sizeIsSummed) {uint32_t touchingCount =mCurrentRawState.rawPointerData.touchingIdBits.count();if (touchingCount > 1) {touchMajor /= touchingCount;touchMinor /= touchingCount;toolMajor /= touchingCount;toolMinor /= touchingCount;size /= touchingCount;}}if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_GEOMETRIC) {touchMajor *= mGeometricScale;touchMinor *= mGeometricScale;toolMajor *= mGeometricScale;toolMinor *= mGeometricScale;} else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_AREA) {touchMajor = touchMajor > 0 ? sqrtf(touchMajor) : 0;touchMinor = touchMajor;toolMajor = toolMajor > 0 ? sqrtf(toolMajor) : 0;toolMinor = toolMajor;} else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DIAMETER) {touchMinor = touchMajor;toolMinor = toolMajor;}mCalibration.applySizeScaleAndBias(&touchMajor);mCalibration.applySizeScaleAndBias(&touchMinor);mCalibration.applySizeScaleAndBias(&toolMajor);mCalibration.applySizeScaleAndBias(&toolMinor);size *= mSizeScale;break;default:touchMajor = 0;touchMinor = 0;toolMajor = 0;toolMinor = 0;size = 0;break;}// Pressurefloat pressure;switch (mCalibration.pressureCalibration) {case Calibration::PRESSURE_CALIBRATION_PHYSICAL:case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:pressure = in.pressure * mPressureScale;break;default:pressure = in.isHovering ? 0 : 1;break;}//方向校准// Tilt and Orientationfloat tilt;float orientation;if (mHaveTilt) {float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale;float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale;orientation = atan2f(-sinf(tiltXAngle), sinf(tiltYAngle));tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle));} else {tilt = 0;switch (mCalibration.orientationCalibration) {case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:orientation = in.orientation * mOrientationScale;break;case Calibration::ORIENTATION_CALIBRATION_VECTOR: {int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4);int32_t c2 = signExtendNybble(in.orientation & 0x0f);if (c1 != 0 || c2 != 0) {orientation = atan2f(c1, c2) * 0.5f;float confidence = hypotf(c1, c2);float scale = 1.0f + confidence / 16.0f;touchMajor *= scale;touchMinor /= scale;toolMajor *= scale;toolMinor /= scale;} else {orientation = 0;}break;}default:orientation = 0;}}// Distance 距离校准float distance;switch (mCalibration.distanceCalibration) {case Calibration::DISTANCE_CALIBRATION_SCALED:distance = in.distance * mDistanceScale;break;default:distance = 0;}// Coverage 覆盖面校准int32_t rawLeft, rawTop, rawRight, rawBottom;switch (mCalibration.coverageCalibration) {case Calibration::COVERAGE_CALIBRATION_BOX:rawLeft = (in.toolMinor & 0xffff0000) >> 16;rawRight = in.toolMinor & 0x0000ffff;rawBottom = in.toolMajor & 0x0000ffff;rawTop = (in.toolMajor & 0xffff0000) >> 16;break;default:rawLeft = rawTop = rawRight = rawBottom = 0;break;}//x y 坐标校准// Adjust X,Y coords for device calibration// TODO: Adjust coverage coords?float xTransformed = in.x, yTransformed = in.y;mAffineTransform.applyTo(xTransformed, yTransformed);// Adjust X, Y, and coverage coords for surface orientation.float x, y;float left, top, right, bottom;switch (mSurfaceOrientation) {case DISPLAY_ORIENTATION_90:x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate;left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;right = float(rawBottom- mRawPointerAxes.y.minValue) * mYScale + mYTranslate;bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;orientation -= M_PI_2;if (mOrientedRanges.haveOrientation && orientation < mOrientedRanges.orientation.min) {orientation += (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);}break;case DISPLAY_ORIENTATION_180:x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate;y = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate;left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate;orientation -= M_PI;if (mOrientedRanges.haveOrientation && orientation < mOrientedRanges.orientation.min) {orientation += (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);}break;case DISPLAY_ORIENTATION_270:x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate;y = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate;right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;orientation += M_PI_2;if (mOrientedRanges.haveOrientation && orientation > mOrientedRanges.orientation.max) {orientation -= (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);}break;default:x = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;y = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;break;}// Write output coords.PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i];out.clear();out.setAxisValue(AMOTION_EVENT_AXIS_X, x);out.setAxisValue(AMOTION_EVENT_AXIS_Y, y);out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size);out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor);out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor);out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation);out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt);out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance);if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) {out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_1, left);out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_2, top);out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_3, right);out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_4, bottom);} else {out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor);out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor);}// Write output properties.PointerProperties& properties =mCurrentCookedState.cookedPointerData.pointerProperties[i];uint32_t id = in.id;properties.clear();properties.id = id;properties.toolType = in.toolType;// Write id index.mCurrentCookedState.cookedPointerData.idToIndex[id] = i;}
}void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {//触点的个数BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;//上一次触点BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits;int32_t metaState = getContext()->getGlobalMetaState();int32_t buttonState = mCurrentCookedState.buttonState;//本次触点和上一次触点是相等的 没有手指的变化if (currentIdBits == lastIdBits) {if (!currentIdBits.isEmpty()) { // 当前这次不为空// No pointer id changes so this is a move event.// The listener takes care of batching moves so we don't have to deal with that here.//没有触点的变化并且不为null,还有事件的变化,所以判定为触点的移动dispatchMotion(when, policyFlags, mSource,AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,AMOTION_EVENT_EDGE_FLAG_NONE,mCurrentCookedState.deviceTimestamp,mCurrentCookedState.cookedPointerData.pointerProperties,mCurrentCookedState.cookedPointerData.pointerCoords,mCurrentCookedState.cookedPointerData.idToIndex,currentIdBits, -1,mOrientedXPrecision, mOrientedYPrecision, mDownTime);}} else {// There may be pointers going up and pointers going down and pointers moving// all at the same time.//如果有触点的变化,那么判定是否有触点的按下或抬起或者同时有触点的移动BitSet32 upIdBits(lastIdBits.value & ~currentIdBits.value);BitSet32 downIdBits(currentIdBits.value & ~lastIdBits.value);BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value);BitSet32 dispatchedIdBits(lastIdBits.value);// Update last coordinates of pointers that have moved so that we observe the new// pointer positions at the same time as other pointers that have just gone up.//比较last触点坐标的变化来判定是否有触点的移动bool moveNeeded = updateMovedPointers(mCurrentCookedState.cookedPointerData.pointerProperties,mCurrentCookedState.cookedPointerData.pointerCoords,mCurrentCookedState.cookedPointerData.idToIndex,mLastCookedState.cookedPointerData.pointerProperties,mLastCookedState.cookedPointerData.pointerCoords,mLastCookedState.cookedPointerData.idToIndex,moveIdBits);if (buttonState != mLastCookedState.buttonState) {moveNeeded = true;}//是否有up事件// Dispatch pointer up events.while (!upIdBits.isEmpty()) {uint32_t upId = upIdBits.clearFirstMarkedBit();dispatchMotion(when, policyFlags, mSource,AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState, 0,mCurrentCookedState.deviceTimestamp,mLastCookedState.cookedPointerData.pointerProperties,mLastCookedState.cookedPointerData.pointerCoords,mLastCookedState.cookedPointerData.idToIndex,dispatchedIdBits, upId, mOrientedXPrecision, mOrientedYPrecision, mDownTime);dispatchedIdBits.clearBit(upId);}// Dispatch move events if any of the remaining pointers moved from their old locations.// Although applications receive new locations as part of individual pointer up// events, they do not generally handle them except when presented in a move event.//是否有move事件if (moveNeeded && !moveIdBits.isEmpty()) {ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);dispatchMotion(when, policyFlags, mSource,AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, 0,mCurrentCookedState.deviceTimestamp,mCurrentCookedState.cookedPointerData.pointerProperties,mCurrentCookedState.cookedPointerData.pointerCoords,mCurrentCookedState.cookedPointerData.idToIndex,dispatchedIdBits, -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime);}// Dispatch pointer down events using the new pointer locations.//是否有按下事件while (!downIdBits.isEmpty()) {uint32_t downId = downIdBits.clearFirstMarkedBit();dispatchedIdBits.markBit(downId);if (dispatchedIdBits.count() == 1) {// First pointer is going down. Set down time.mDownTime = when;}dispatchMotion(when, policyFlags, mSource,AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState, 0,mCurrentCookedState.deviceTimestamp,mCurrentCookedState.cookedPointerData.pointerProperties,mCurrentCookedState.cookedPointerData.pointerCoords,mCurrentCookedState.cookedPointerData.idToIndex,dispatchedIdBits, downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime);}}
}
相关文章:

Android input系统原理二
1.inputmanager启动源码分析 在SystemServer.java中构造了 inputmanagerservice的对象,在其构造函数中,最重要的是这个nativeInit函数。 下面是核心代码 inputManager new InputManagerService(context);public InputManagerService(Context context)…...

Oracle登录报错-ORA-01017: invalid username/password;logon denied
接上文:Oracle创建用户报错-ORA-65096: invalid common user or role name 我以为 按照上文在PDB里创建了用户,我以为就可以用PLSQL远程连接了,远程服务器上也安装了对应版本的Oracle客户端,但是我想多了,客户只是新建…...

JavaScript 获取浏览器本地数据的4种方式
JavaScript 获取浏览器本地数据的方式 我们在做Web开发中,客户端存储机制对于在浏览器中持久化数据至关重要。这些机制允许开发者存储用户偏好设置、应用状态以及其他关键信息,从而增强用户体验。本文将介绍几种常用的JavaScript获取浏览器本地数据的方…...

77寸OLED透明触摸屏有哪些应用场景
说到77寸OLED透明触摸屏,那可真是市场营销中的一大亮点,应用场景多到数不清!我这就给你细数几个热门的: 商业展示:这可是77寸OLED透明触摸屏的拿手好戏!在高端零售店铺里,它可以作为陈列窗口&am…...

二分解题的奇技淫巧都有哪些,你还不会吗?
先说一下我为什么要写这篇文章。 “二分“ 查找 or ”二分“ 答案的思想大家想必都知道吧(如果不懂,可以看一下我之前写的一篇文章)。 二分求解 可是呢?思想都会,做题的时候,就懵圈了。 这个题竟然考的是…...

LeetCode-871 最低加油次数
重启力扣每日一题系列! 因为过去两个月里掉粉掉的好严重,我想大抵是因为更新的频率不如上半年了,如果我重启了每日一题系列那岂不是至少是每日一更☝🤓? 也不是每天都更,我有两不更,特难的就不…...

OpenCV-OCR
文章目录 一、OCR技术的基本原理二、OpenCV在OCR识别中的应用1.图像预处理2.文字区域检测3.OCR识别:4.后处理: 三、OCR识别示例代码四、注意事项 OpenCV-OCR主要涉及使用OpenCV库进行光学字符识别(OCR)的技术。OCR技术可以识别图像…...

Linux卸载mysql
一、查看当前安装mysql情况,查找以前是否装有mysql rpm -qa|grep -i mysql二、停止MySQL服务 三、删除mysql库和文件 查找MySQL库 # 查找命令 find / -name mysql# 显示结果 /var/lib/mysql/var/lib/mysql/mysql/usr/lib64/mysql删除对应的mysql目录 rm -rf /v…...

【大语言模型-论文精读】用于医疗领域摘要任务的大型语言模型评估综述
【大语言模型-论文精读】用于医疗领域摘要任务的大型语言模型评估综述 论文信息: 用于医疗领域摘要任务的大型语言模型评估:一篇叙述性综述, 文章是由 Emma Croxford , Yanjun Gao 博士 , Nicholas Pellegrino , Karen K. Wong 等人近期合作…...

图吧工具箱
图吧工具箱202309绿色版自动解压程序R2.exe,永久有效 链接:https://pan.baidu.com/s/1M6TI7Git8bXOzZX_qZ3LJw?pwdzked 提取码:zked...

vue2 + View design 使用inputNumber设置默认值为undefined但展示数据为1且表单校验不通过的原因
文章目录 一、背景二、操作步骤1.复现前的准备工作(1)vue版本和view design 版本(2)创建一个组件(组件中根据类型渲染不同的组件)(3)在list.vue页面中引入组件,传入配置&…...

【SpringSecurity】基本流程
【中文文档: Spring Security 中文文档 :: Spring Security Reference】 【英文文档:Spring Security】 以下内容只是记录springsecurity最简单的一种验证流程,所有配置基本都是默认的配置。 引入依赖 <dependency><groupId>org.springf…...
算法-汉诺塔问题(Hanoi tower)
介绍 汉诺塔是源于印度的一个古老传说的小游戏,简单来说就是有三根柱子,开始的时候,第一根柱子上圆盘由大到小,自下往上排列。这个小游戏要实现的目的呢,就是要把第一根柱子上的圆盘移到第三根的柱子上去;…...

HarmonyOS鸿蒙 Next 实现协调布局效果
HarmonyOS鸿蒙 Next 实现协调布局效果 假期愉快! 最近大A 的涨势实在是红的让人晕头转向,不知道各位收益如何,这会是在路上,还是已经到目的地了? 言归正传,最近有些忙,关于鸿蒙的实践系列有些脱节了,…...

【自然语言处理】(1) --语言转换方法
文章目录 语言转换方法一、统计语言模型1. 词向量转换2. 统计模型问题 二、神经语言模型1. 词向量化2. 维度灾难3. 解决维度灾难4. embedding词嵌入5. Word2Vec技术5.1 连续词袋模型(CBOW)5.2 跳字模型(Skip-gram) 总结 语言转换方…...

叉车防撞系统方案,引领安全作业新时代
在现代工业的舞台上,叉车如同忙碌的“搬运工”,在仓储和制造环境中发挥着不可或缺的作用。然而,随着叉车使用频率的不断攀升,安全事故也如影随形,给企业带来经济损失的同时,更严重威胁着操作人员的生命安全…...

Nginx的核心架构和设计原理
Nginx 是一个免费的、开源的、高性能 Http 服务器和反向代理。Nginx 的架构设计是为了提供高性能、稳定性和可扩展性。 Nginx 的主要架构组件和工作原理: 1、Master 进程:Nginx 的运行始于一个 master 进程,它负责管理所有的工作进程。mast…...

leetcode35--搜索插入位置--二分查找刷题
搜索插入位置 一共会出现下面四种情况: 目标值在数组所有元素之前 目标值等于数组中某一个元素 目标值插入数组中的位置 目标值在数组所有元素之后 首先在二分查找的代码之前处理掉目标值在数组所有元素之前和之后的情况如果目标值在数组中的某个位置,…...

Django对接支付宝沙箱环境(2024年9月新测有效)
1、申请沙箱环境 #需要填一些个人信息 https://opendocs.alipay.com/ 2、使用支付宝登入,并进入控制台,进入开发者工具推荐-->沙箱 3、获取基本信息 主要是APPID,和支付宝网关地址 4、生成应用私钥和应用公钥和支付宝公钥 上面的接口加签方式选择…...

【MySQL】-- 库的操作
文章目录 1. 查看数据库1.1 语法 2. 创建数据库2.1 语法2.2 示例2.2.1 创建一个名为java114的数据库2.2.2 创建数据库java114,如果数据库不存在则创建2.2.3 查看警告信息 3. 字符集编码和校验(排序)规则3.1 查看数据库支持的字符集编码3.2 查…...

linux桌面软件(wps)内嵌到主窗口后的关闭问题
程序测试环境是:slackware系统,属于linux系统,有桌面(Xface Session)。系统镜像是:slackware64-15.0-install-dvd.iso。qt、c代码实现。 问题描述:延续上一篇文章,将wps软件窗口内嵌…...

WindowsTerminal 美化-壁纸随机更换
目录 一. 相关网址二. 壁纸随机更换思路三. 指定 WindowsTermina 壁纸路径四. 编写脚本,随机替换壁纸4.1 powershell脚本4.2 .bat批处理脚本 四. 配置定时任务,添加触发器五. 效果 一. 相关网址 官方下载 Windows Terminal 官方Github微软商店 美化 Oh …...

iOS 多次获取图片主题色不一样
一个需求中,要求获取图片的主题色 代码如下 -(void)kk_getImage:(UIImage *)image fetchthemeColor:(void(^)(UIColor *color))callBack {dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{// 第一步 先把图片缩小 加快计算速度.…...

UE5 武器IK瞄准系统
创建空项目 创建基础蓝图类My_GameMode,My_HUD,My_PlayChar,My_PlayController 项目设置地图模式 近裁平面 0.1 My_PlayChar蓝图中添加摄像机,角色骨骼网格体,武器骨骼网格体 编辑角色骨骼,预览控制器使用特定动画,动画选择ANM_ark-47-Idle hand_r 添加插槽WeaponMes…...

①EtherCAT转ModbusTCP, EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关
EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关https://item.taobao.com/item.htm?ftt&id822721028899 协议转换通信网关 EtherCAT 转 ModbusTCP GW系列型号 MS-GW15 简介 MS-GW15 是 EtherCAT 和 Modbus TCP 协议转换网关,为用户提供一种 …...

在macOS上进行开发环境配置与应用开发详细的配置指南
在macOS上进行开发环境配置与应用开发,需要遵循一系列步骤来确保你的开发环境既高效又稳定。以下是一个详细的配置指南,涵盖了从安装基本工具到创建应用的整个过程。 1. 安装和更新macOS 首先,确保你的macOS是最新版本。更新系统可以提供更…...

JavaScript 事件处理基础
在网页中添加事件监听器,可以通过JavaScript代码来实现。 要处理用户的交互事件,需要先选择要添加事件监听器的元素,可以使用document.querySelector()或document.getElementById()等方法来获取元素。 然后,使用addEventListene…...

WordPress响应式Git主题响应式CMS主题模板
兼容 IE9、谷歌 Chrome 、火狐 Firefox 等主流浏览器 扁平化的设计加响应式布局,兼容电脑、和各个尺寸手机的完美响应 主题设置面板新增多种AD位,PC端和移动设备各不相同 在主题设置选项中就可以进行基本的SEO设置:首页、分类、文章等页面…...

Solidity 设计模式:实现灵活与可扩展的智能合约架构
Solidity 作为以太坊智能合约的主要编程语言,拥有许多独特的设计模式,这些模式帮助开发者实现更加灵活、可扩展和安全的合约架构。设计模式不仅能够简化开发过程,还能减少常见的编程错误,并提高智能合约的可维护性和可升级性。本文…...

房屋水电费:重新布局,重构JS代码
<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>房租水电费</title><script type"…...