【android 9】【input】【11.发送普通motion事件1——touch设备的加载——MultiTouchInputMapper】

系列文章目录

 可跳转到下面链接查看下表所有内容https://blog.youkuaiyun.com/handsomethefirst/article/details/138226266?spm=1001.2014.3001.5501文章浏览阅读2次。系列文章大全https://blog.youkuaiyun.com/handsomethefirst/article/details/138226266?spm=1001.2014.3001.5501


目录

系列文章目录

1.简介

1.1流程介绍

1.2 时序图

2.touch设备的加载和配置流程分析

2.1 InputReader::loopOnce

2.2 EventHub::getEvents

2.3 scanDevicesLocked

2.4 scanDirLocked

2.5 openDeviceLocked

2.6 EventHub::loadConfigurationLocked

 2.7 getInputDeviceConfigurationFilePathByDeviceIdentifier

 2.8 getInputDeviceConfigurationFilePathByName

 2.9 appendInputDeviceConfigurationFileRelativePath

 2.10 PropertyMap::load

2.11 loadVirtualKeyMapLocked

2.12 registerDeviceForEpollLocked

2.13 processEventsLocked

2.14 addDeviceLocked

2.15 createDeviceLocked

2.16 MultiTouchInputMapper

 2.17 InputDevice::configure

2.18 TouchInputMapper::configure

2.19 TouchInputMapper::configureParameters

2.20 CursorScrollAccumulator::configure

2.21 TouchButtonAccumulator::configure

2.22 MultiTouchInputMapper::configureRawPointerAxes

2.23 InputMapper::getAbsoluteAxisInfo

2.24 EventHub::getAbsoluteAxisInfo

2.25 MultiTouchMotionAccumulator::configure

2.26 TouchInputMapper::parseCalibration

2.27 TouchInputMapper::resolveCalibration

2.28 TouchInputMapper::configureSurface

2.29 InputReaderConfiguration::getDisplayViewport

2.30 TouchInputMapper::configureVirtualKeys

3.重要的类型

3.1 Parameters

3.2 Calibration

3.3 RawPointerAxes

3.4 RawAbsoluteAxisInfo

3.5 InputReaderConfiguration


1.简介

     从之前的篇幅我们知道了,事件分为设备增删事件和原始输入事件,而原始输入事件主要有两种,一种是key按键事件的派发,一种是触摸事件的派发。Key事件的派发我们已经分析过了,本篇主要针对motion事件的派发。想要了解motion事件的派发,首先需要了解到touch设备的是如何加载的,本篇和前文的input设备流程相同,只是对于触摸屏的配置进行了更详细的描述。如MultiTouchInputMapper的创建过程。

1.1流程介绍

第一步:经过前面几篇文章我们知道,在开机启动后,input系统也在systemserver进程中被拉起,InputReader线程中的loopOnce会执行,loopOnce会执行getEvents函数,此函数在设备刚启动时,会调用scanDevicesLocked函数扫描/dev/input下的所有输入设备。
 
第二步:当扫描到/dev/input/xxx的设备时,会调用openDeviceLocked函数打开设备节点,并从中读取设备的厂商信息,如触摸屏配置文件路径等。

第三步:然后会调用loadConfigurationLocked去加载指定路径的配置文件将里面的内容加载到对应的map中,如触摸屏的Vendor_xxxx_Product_xxxx.idc文件。

第四步:会此设备注册到epoll中监听设备的原始输入事件。

第五步:生成设备添加事件。

第六步:processEventsLocked处理设备添加事件。此时会根据设备创建多个mapper对象,以触摸屏为例子,则会创建MultiTouchInputMapper对象。

第七步:然后便是对MultiTouchInputMapper进行配置,会调用configure函数。
              此函数有以下几个作用:
              1.对触摸屏判断多触摸还是单触摸,触摸设备是触摸屏还是触摸平板等
              2.读取屏幕的信息,构建物理坐标系,如x和y的范围等信息。
              3.准备输入设备的校准,即mConfiguration中读取大小,方向,压力等值。
              4. 配置device source、Surface尺寸、方向和缩放比例。完成物理坐标系到屏幕坐标系的转化。

第八步:等待触摸事件的到来。

1.2 时序图

 (为了完整的画出时序图,较为模糊,可保存到本地放大查看)

2.touch设备的加载和配置流程分析

首先我们从前文知道当input系统第一次启动时,会扫描/dev/input下的所有设备节点,然后读取所有设备节点的信息,并生成对应的mapper对象,那么本篇便来分析常用的触摸屏的加载具体会做些什么。

首先从启动篇我们知道,启动后,inputreader线程会执行loopOnce函数。前面的内容在启动篇已经介绍过了,所以本篇我们便从此处开始分析。

2.1 InputReader::loopOnce

主要作用为:

1. 通过EventHub的getEvents函数获取add设备事件。

2. processEventsLocked处理设备事件。

3.发布事件,通知inputdispatcher线程去处理消息。

//第一次启动时,执行的代码块
void InputReader::loopOnce() {

 
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);//通过EventHub的getEvents函数获取事件,
	//并存放在mEventBuffer中。此时可以知道刚启动时,会生成所有已经扫描的设备的add类型的事件
	//参数分析:timeoutMillis=-1,如果数字为0代表立即执行 -1为一直阻塞
	//mEventBuffer是一个存放从eventhub中读取的rawevent结构体类型的数组,源代码是:RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
	//EVENT_BUFFER_SIZE值是256,代表最大可以读取256个原始事件,RawEvent结构体如下
	从EventHub检索到的原始事件
	//struct RawEvent {
    //nsecs_t when;//时间
    //int32_t deviceId;//事件发生的设备id
    //int32_t type;//类型,例如按键事件等
    //int32_t code;//扫描码,按键对应的扫描码
    //int32_t value;//值,表示按键按下,或者抬起等
	//};
 

 
        if (count)
		{//返回的事件数量大于,则调用processEventsLocked处理事件
            processEventsLocked(mEventBuffer, count);
		}
		
 
        if (oldGeneration != mGeneration) 
		{//此处是不等于的,因为当有add设备事件时processEventsLocked函数中调用addDeviceLocked,addDeviceLocked在完成设备配置后
		//会调用bumpGenerationLocked函数,使得mGeneration+1,从而不相等
            inputDevicesChanged = true;
            getInputDevicesLocked(inputDevices);//获取所有的新的设备信息到inputDevices容器中
        }
 
 

    //发布事件。 一般此处只会处理加工后的原始输入事件,而此时开机启动只有触摸屏幕add设备事件
    mQueuedListener->flush();
}

2.2 EventHub::getEvents

主要作用为:
1.设备刚启动,调用scanDevicesLocked函数扫描/dev/input下的所有输入设备,用Device类保存其信息,并生成DEVICE_ADDED类型的事件(设备增加类型事件)放入消息数组中
2.返回存在设备增加事件的数组,让inputreader进行处理。

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
 
 
    for (;;) {//死循环
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);//获取当前时间
 
 
 
        if (mNeedToScanDevices) {//默认值为true,即第一次调用getevent时,会扫描/dev/input下的设备
            mNeedToScanDevices = false;//设置为false,避免重复扫描设备
            scanDevicesLocked();//执行扫描设备的函数。扫描/dev/input下的设备
            mNeedToSendFinishedDeviceScan = true;//mNeedToSendFinishedDeviceScan值为true,用于生成FINISHED_DEVICE_SCAN事件
        }
 
        while (mOpeningDevices != NULL) {
            Device* device = mOpeningDevices;//此时mOpeningDevices值是刚打开过的device
            mOpeningDevices = device->next;//device->next值为0,指向下一个设备
			//生成DEVICE_ADDED类型的事件,并通过event指针存放于buffer数组中
            event->when = now;
            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;//如果是内置键盘,则其id为0,如果不是则是device->id
            event->type = DEVICE_ADDED;//类型
            event += 1;//event的定义是RawEvent* event = buffer,event指针指向传入的buffer数组的首地址,即第0个元素,
			//每存入一个事件,event指针向后移动一个元素
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) {//buffer数组可容量-1
                break;
            }
        }
 
        if (mNeedToSendFinishedDeviceScan) {
            mNeedToSendFinishedDeviceScan = false;
			//生成FINISHED_DEVICE_SCAN类型的事件,并通过event指针存放于buffer数组中
            event->when = now;
            event->type = FINISHED_DEVICE_SCAN;
            event += 1;
            if (--capacity == 0) {
                break;
            }
        }
 
        
        bool deviceChanged = false;
		
 
 
        // 当event的指针不再指向buffer的首地址时,代表里面有数据,或者被唤醒时,立即退出循环
        if (event != buffer || awoken) {//此时生成了两种事件,一个是扫描的所有打开设备的事件,还有一个是完成设备扫描报告的事件,故退出循环
            break;
        }
    }
    // 全部完成后,返回我们读取的事件数。
    return event - buffer;
}

2.3 scanDevicesLocked

void EventHub::scanDevicesLocked() {
    status_t res = scanDirLocked(DEVICE_PATH);//调用scanDirLocked方法,DEVICE_PATH=/dev/input,返回0代表成功
 
    if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {//
	//mDevices的定义是一个map容器,KeyedVector<int32_t, Device*> mDevices;mDevices中存放的是已经扫描完成的设备,即
	//已经完成设备厂商信息的获取和注册进epoll监听的设备。VIRTUAL_KEYBOARD_ID的值等于-1
	//此处如果从mDevice中没有找到虚拟键盘,则调用createVirtualKeyboardLocked方法创建虚拟键盘。
        createVirtualKeyboardLocked();
    }
}

2.4 scanDirLocked

1.循环打开/dev/input下的所有设备节点。

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;
}

2.5 openDeviceLocked

针对触摸屏的主要作用为:

主要作用为:

1.打开/dev/input/xxxx的设备,然后获取设备的驱动版本,设备产品,设备供应商,设备物理地址,设备标识符将信息保存到InputDeviceIdentifier类的identifier对象中,此对象主要用于存储设备厂商的所有信息。

2.假设此时有一个设备/dev/input/event0,根据其dentifier对象生成对应的Device类对象。然后加载设备的配置文件到device类对象的PropertyMap中。

      配置文件主要有三类。

       一.idc文件,主要用于触摸屏配置。

       二.kl文件,主要用于键盘的扫描码和keycode的转化。

       三.kcm文件,主要作用是将 Android按键代码与修饰符的组合映射到 Unicode。

3.从设备的fd中读取数据判断其设备类型为EV_KEY、EV_ABS、EV_REL、EV_SW、EV_LED、EV_FF类型。

            一:EV_KEY,按键类型的事件。能够上报这类事件的设备有键盘、鼠标、手柄、手写板
            等一切拥有按钮的设备(包括手机上的实体按键)。在Device结构体中,对应的事件
            位掩码keyBitmask描述了设备可以产生的按键事件的集合。按键事件的全集包括字
            符按键、方向键、控制键、鼠标键、游戏按键等。


            二:EV_ABS,绝对坐标类型的事件。这类事件描述了在空间中的一个点,触控板、触
            摸屏等使用绝对坐标的输入设备可以上报这类事件。事件位掩码absBitmask描述了
            设备可以上报的事件的维度信息(ABS_X、ABS_Y、ABS_Z),以及是否支持多点
            事件。


            三:EV_REL,相对坐标类型的事件。这类事件描述了事件在空间中相对于上次事件
            的偏移量。鼠标、轨迹球等基于游标指针的设备可以上报此类事件。事件位掩码
            relBitmask描述了设备可以上报的事件的维度信息(REL_X、REL_Y、REL_Z)。


            四:EV_SW,开关类型的事件。这类事件描述了若干固定状态之间的切换。手机上的静
            音模式开关按钮、模式切换拨盘等设备可以上报此类事件。事件位掩码swBitmask表
            示了设备可切换的状态列表。

            五:EV_LED,光反馈类型事件。ledBitmask描述了设备是否支持光
            六:EV_FF,力反馈类型,ffBitmask则描述了设备是否支持力反馈。

4.根据上诉大类型,生成更细分的类型——INPUT_DEVICE_CLASS_xxx类型。如L按键事件可再通过有无X,y比特位,区分是键盘还是蓝牙手柄。

            一:INPUT_DEVICE_CLASS_KEYBOARD,可以上报鼠标按键以外的EV_KEY类型事件的设备都属于此类。如键盘、机身按钮(音量键、电源键等)。


            二:INPUT_DEVICE_CLASS_ALPHAKEY,可以上报字符按键的设备,例如键盘。此类   型的设备必定同时属于KEYBOARD。


           三:INPUT_DEVICE_CLASS_DPAD,可以上报方向键的设备。例如键盘、手机导航键

等。这类设备同时也属于KEYBOARD。


            四:INPUT_DEVICE_CLASS_GAMEPAD,可以上报游戏按键的设备,如游戏手柄。这类设备同时也属于KEYBOARD。


            五:INPUT_DEVICE_CLASS_TOUCH,可以上报EV_ABS类型事件的设备都属于此类,如触摸屏和触控板。


            六:INPUT_DEVICE_CLASS_TOUCH_MT,可以上报EV_ABS类型事件,并且其事件位掩码指示其支持多点事件的设备属于此类。例如多点触摸屏。这类设备同时也属于TOUCH类型。
            七:INPUT_DEVICE_CLASS_CURSOR,可以上报EV_REL类型的事件,并且可以上报BTN_MOUSE子类的EV_KEY事件的设备属于此类,例如鼠标和轨迹球。


            八:INPUT_DEVICE_CLASS_SWITCH,可以上报EV_SW类型事件的设备。


            九:INPUT_DEVICE_CLASS_JOYSTICK,属于GAMEPAD类型,并且属于TOUCH类型的设备。


            十:INPUT_DEVICE_CLASS_VIBRATOR,支持力反馈的设备。


            十一:INPUT_DEVICE_CLASS_VIRTUAL,虚拟设备。


            十二:INPUT_DEVICE_CLASS_EXTERNAL,外部设备,即非内建设备。如外接鼠标、键盘、游戏手柄等。

5.然后注册设备节点到epoll中,开始监听各个设备节点下是否有可读事件,即监听此设备的原始输入事件。

status_t EventHub::openDeviceLocked(const char *devicePath) {
    char buffer[80];
 
    int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK);//以读写,非阻塞,原子方式打开设备
	//打开设备节点的文件描述符,用于获取设备信息以及读取原始输入事件
	
	
 
    InputDeviceIdentifier identifier;//存储厂商的信息
	//接下来的代码通过ioctl()函数从设备节点中获取输入设备的厂商信息
	
    if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {//通过设备的fd,向驱动发送EVIOCGNAME指令,
	//获取设备名称,写入buffer中
    } else {
        buffer[sizeof(buffer) - 1] = '\0';
        identifier.name.setTo(buffer);//从buffer中取出名称信息,赋值给identifier的name属性
    }
 
    // 检查获取的设备名称是否在排除列表中下
    for (size_t i = 0; i < mExcludedDevices.size(); i++) {// 检查获取的设备名称是否在排除列表中下,
	//如果在mExcludedDevices列表中,则关闭此设备,并返回,mExcludedDevices列表在setExcludedDevices方法中赋值的,
	//而setExcludedDevices方法是在inputreader构造函数中的refreshConfigurationLocked方法中调用的,
	//同时mExcludedDevices最后是调用到java层ims中获取的。
        const String8& item = mExcludedDevices.itemAt(i);
        if (identifier.name == item) {
            ALOGI("ignoring event id %s driver %s\n", devicePath, item.string());
            close(fd);
            return -1;
        }
    }
 
    //获取设备驱动版本
    int driverVersion;
    if(ioctl(fd, EVIOCGVERSION, &driverVersion)) {//从驱动中获取的信息赋值给driverVersion
        ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));
        close(fd);
        return -1;
    }
 
    //获取设备标识符
    struct input_id inputId;
    if(ioctl(fd, EVIOCGID, &inputId)) {//赋值给inputId
        ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));
        close(fd);
        return -1;
    }
    identifier.bus = inputId.bustype;
    identifier.product = inputId.product;//设备产品
    identifier.vendor = inputId.vendor;//供应商
    identifier.version = inputId.version;//版本
 
    // 获取设备物理地址
    if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) {//放于buffer中
        //fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno));
    } else {
        buffer[sizeof(buffer) - 1] = '\0';
        identifier.location.setTo(buffer);//赋值给location
    }
 
    //获取设备唯一的id
    if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) {
        //fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno));
    } else {
        buffer[sizeof(buffer) - 1] = '\0';
        identifier.uniqueId.setTo(buffer);//赋值给uniqueId
    }
 
    
    assignDescriptorLocked(identifier);//将identifier信息填充到fd,分配唯一标识设备的设备描述符,主要作用是填写identifier.nonce字段。
 
    // 分配device,device对象获取fd的所有权
    int32_t deviceId = mNextDeviceId++;//mNextDeviceId初始值为1,故deviceId=1
    Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
	//参数分析:
	//此处结合是getevent工具分析的
	//fd是dev/input/event0的设备的描述符
	//deviceid=1
	//devicePath=dev/input/event0
	//identifier存储了此设备所有的厂商信息
	
 
    
    loadConfigurationLocked(device);//加载device的配置文件。主要是例如:device.type和device.internal等信息
 
    // 详细事件报告的类型,从fd对应的device中读取各种信息,并保存到device的keyBitmask或absBitmask中,
	//例如:如果是键盘设备,则device->keyBitmask有数据,而device->absBitmask无数据
    ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);//按键类型
    ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask);//绝对坐标类型
    ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);//相对坐标类型,只要用于motion
    ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);//开关类型
    ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);//led等类型
    ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);//力反馈类型
    ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
	//Device结构体的事件位掩码描述了6种类型的输入事件: 
    //EV_KEY,按键类型的事件。能够上报这类事件的设备有键盘、鼠标、手柄、手写板 
            //等一切拥有按钮的设备(包括手机上的实体按键)。在Device结构体中,对应的事件 
            //位掩码keyBitmask描述了设备可以产生的按键事件的集合。按键事件的全集包括字 
            //符按键、方向键、控制键、鼠标键、游戏按键等。 
    //EV_ABS,绝对坐标类型的事件。这类事件描述了在空间中的一个点,触控板、触 
            //摸屏等使用绝对坐标的输入设备可以上报这类事件。事件位掩码absBitmask描述了 
            //设备可以上报的事件的维度信息(ABS_X、ABS_Y、ABS_Z),以及是否支持多点 
            //事件。 
    //EV_REL,相对坐标类型的事件。这类事件描述了事件在空间中相对于上次事件 
            //的偏移量。鼠标、轨迹球等基于游标指针的设备可以上报此类事件。事件位掩码 
            //relBitmask描述了设备可以上报的事件的维度信息(REL_X、REL_Y、REL_Z)。 
    //EV_SW,开关类型的事件。这类事件描述了若干固定状态之间的切换。手机上的静 
            //音模式开关按钮、模式切换拨盘等设备可以上报此类事件。事件位掩码swBitmask表 
            //示了设备可切换的状态列表。
	//EV_LED,光反馈类型事件。ledBitmask描述了设备是否支持光 
	//EV_FF,力反馈类型,ffBitmask则描述了设备是否支持力反馈。
	
	
	
	//INPUT_DEVICE_CLASS_KEYBOARD,可以上报鼠标按键以外的EV_KEY类型事件的设备都属于此类。如键 
            //盘、机身按钮(音量键、电源键等)。 
    //INPUT_DEVICE_CLASS_ALPHAKEY,可以上报字符按键的设备,例如键盘。此类型的设备必定同时属于KEYBOARD。 
    //INPUT_DEVICE_CLASS_DPAD,可以上报方向键的设备。例如键盘、手机导航键等。这类设备同时也属于KEYBOARD。 
    //INPUT_DEVICE_CLASS_GAMEPAD,可以上报游戏按键的设备,如游戏手柄。这类设备同时也属于KEYBOARD。 
    //INPUT_DEVICE_CLASS_TOUCH,可以上报EV_ABS类型事件的设备都属于此类,如触摸屏和触控板。 
    //INPUT_DEVICE_CLASS_TOUCH_MT,可以上报EV_ABS类型事件,并且其事件位掩码指示其支持多点事件 
            //的设备属于此类。例如多点触摸屏。这类设备同时也属于TOUCH类型。 
    //INPUT_DEVICE_CLASS_CURSOR,可以上报EV_REL类型的事件,并且可以上报BTN_MOUSE子类的 
            //EV_KEY事件的设备属于此类,例如鼠标和轨迹球。 
    //INPUT_DEVICE_CLASS_SWITCH,可以上报EV_SW类型事件的设备。 
    //INPUT_DEVICE_CLASS_JOYSTICK,属于GAMEPAD类型,并且属于TOUCH类型的设备。 
    //INPUT_DEVICE_CLASS_VIBRATOR,支持力反馈的设备。 
    //INPUT_DEVICE_CLASS_VIRTUAL,虚拟设备。 
    //INPUT_DEVICE_CLASS_EXTERNAL,外部设备,即非内建设备。如外接鼠标、键盘、游戏手柄等。
 
 



 
    //通过查看device->absBitmask,查看这是不是触摸板。
    if (test_bit(ABS_MT_POSITION_X, device->absBitmask)
            && test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
        if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {//尝试确认设备确实触摸屏。
            device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
        }
    // 查看这是不是旧式的单触的驱动
    } 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;
    } 
 

    // 如果是触摸屏,则为其配置虚拟键盘
    if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
        //加载触摸屏的虚拟键(如果有)。
        status_t status = loadVirtualKeyMapLocked(device);//loadVirtualKeyMapLocked函数的作用大致为
		//加载系统提供的虚拟键盘的文件,然后将其键值对保存到virtualKeyMap中
        if (!status) {
            device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
        }
    }
 

 
    // 配置键盘、游戏板或虚拟键盘。
    if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {
        
        if (!keyMapStatus
                && mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD//表示此时并未任何内置键盘配置过
                && isEligibleBuiltInKeyboard(device->identifier,
                        device->configuration, &device->keyMap)) //如果符合条件,会将键盘注册为内置键盘,
		//然后device->keyMap保存着键盘映射表信息
		{
            mBuiltInKeyboardId = device->id;//mBuiltInKeyboardId内置键盘id,如果是第一次打开设备,则此时id为1
        }
    }
 


 
 
    if (registerDeviceForEpollLocked(device)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值