资讯专栏INFORMATION COLUMN

Input系统之键值映射

jerryloveemily / 1167人阅读

摘要:一概述系统的输入事件来源在内核提供的的设备节点下当该设备下及诶点有数据刻度时将数据独处并进行一系列的翻译和加工然后在所有的窗口中寻找合适的接受者并派发给它输入系统总体流程如下引之深入理解卷开发环境系统运行环境版本二准备工作模拟输入事件为

一. 概述

android系统的输入事件来源在linux内核提供的/dev/input的设备节点下, 当该设备下及诶点有数据刻度时,将数据独处并进行一系列的翻译和加工,然后在所有的窗口中寻找合适的接受者,并派发给它;

输入系统总体流程如下(引之深入理解android卷3 ):

1.1 开发环境

系统: ubuntu 16.04

运行环境: firefly-rk3288

android版本: arm-5.1.0

二. 准备工作 2.1 模拟输入事件

为了接下来讲解原理方便, 在这里模拟一个输入设备, 方法-写一个驱动;

驱动源码

/* 参考driversinputkeyboardgpio_keys.c */

#include 
#include 
#include 
#include 
#include 

static struct input_dev *input_emulator_dev;

static int input_emulator_init(void)
{
    int i;
    int ret;

    /* 1. 分配一个input_dev结构体 */
    input_emulator_dev = input_allocate_device();

    /* 2. 设置 */
    /* 2.1 能产生哪类事件 */
    set_bit(EV_KEY, input_emulator_dev->evbit);
    set_bit(EV_REP, input_emulator_dev->evbit);
    
    /* 2.2 能产生所有的按键 */
    for (i = 0; i < BITS_TO_LONGS(KEY_CNT); i++)
        input_emulator_dev->keybit[i] = ~0UL;

    /* 2.3 为android构造一些设备信息 */
    input_emulator_dev->name = "smart remote";
    input_emulator_dev->id.bustype = 1;
    input_emulator_dev->id.vendor  = 0x0001;
    input_emulator_dev->id.product = 0x0010;
    input_emulator_dev->id.version = 1;

    /* 3. 注册 */
    ret = input_register_device(input_emulator_dev);
    if (ret < 0) {
        return -1;
    }
    
    return 0;
}

static void input_emulator_exit(void)
{
    input_unregister_device(input_emulator_dev);
    input_free_device(input_emulator_dev);    
}

module_init(input_emulator_init);
module_exit(input_emulator_exit);
MODULE_LICENSE("GPL");

makefile

KERN_DIR = /media/sourcelink/Backups/5.CompileCode/firefly3288/kernel

all:
    make -C $(KERN_DIR) M=`pwd` modules 

clean:
    make -C $(KERN_DIR) M=`pwd` modules clean
    rm -rf modules.order

obj-m    += smart_remote.o

编译完后,放到板子上加载,使用命令cat /proc/bus/input/devices查看设备加载情况如下:

root@firefly:/data/nfs # ins
insmod    installd  
root@firefly:/data/nfs # insmod smart_remote.ko
root@firefly:/data/nfs # cat /proc/bus/input/devices

....
I: Bus=0001 Vendor=0001 Product=0010 Version=0001
N: Name="smart remote"
P: Phys=
S: Sysfs=/devices/virtual/input/input3
U: Uniq=
H: Handlers=sysrq event3 ddr_freq keychord 
B: PROP=0
B: EV=100003
B: KEY=ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe

如上是我板子加载完驱动的情况,原默认的信息我已经删除, 也可以看下在/dev/input, 节点下多了个event3;

root@firefly:/data/nfs # ls /dev/input/
event0
event1
event2
event3

这样就完成一个输入设备的模拟,接下来介绍两个工具模拟事件的产生;

2.2 工具使用

android系统提供了geteventsendevent两个工具供开发从设备节点中读取和写入事件;

getevent

语法:

getevent [-opera]  [节点路径]

可以使用getevent -help 查看一些具体的operation;

如果不带指定设备节点, 这样会监控所有的设备节点:

root@firefly:/data/nfs # getevent
add device 1: /dev/input/event3
  name:     "smart remote"
add device 2: /dev/input/event2
  name:     "RK_ES8323 Headphone Jack"
add device 3: /dev/input/event1
  name:     "rk29-keypad"
add device 4: /dev/input/event0
  name:     "ff680000.pwm"

现在我将自己的键盘插到开发板上了,使用getevent命令来监控事件;

当我按下并松开键盘上的数字1键获取到的数据如下:

root@firefly:/data/nfs # getevent /dev/input/event4 
0004 0004 0007001e
0001 0002 00000001
0000 0000 00000000
0004 0004 0007001e
0001 0002 00000000
0000 0000 00000000

数据意义依次是: 事件类型, 事件代码, 事件值

事件值的1表示按下, 0表示抬起, 观察数据可以发现每次按下或抬起都会获取到0000 0000 00000000的数据, 表示同步事件,通知此次事件已经结束可以进行处理了;
这里的事件代码是linux端发来的原始数据, 在android端还会进行一次键值布局,下面笔者会讲解这个映射关系;

setevent

语法:

setevent [节点路径] [事件类型] [事件代码] [事件值]

现在操作下往设备几点写入一个事件, 打开我开发板的浏览器:

依次输入如下指令:

sendevent /dev/input/event3 1 2 1
sendevent /dev/input/event3 1 2 0
sendevent /dev/input/event3 0 0 0

效果如下:

屏幕上出现了一个数字1, 最后发送的0 0 0表示同步事件,通知此次事件已经结束可以进行处理了.

三. 按键布局和键值映射 3.1 Key Layout

按键事件来源于linux内核, 但是在android端对按键的值有重新做一个布局,即将linux key code转换为android key code, 这个布局依赖于个.kl文件, 全称: Key Layout Files;

该文件搜索路径如下:

/odm/usr/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl  
/vendor/usr/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl
/system/usr/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl
/data/system/devices/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl
/odm/usr/keylayout/Vendor_XXXX_Product_XXXX.kl
/vendor/usr/keylayout/Vendor_XXXX_Product_XXXX.kl
/system/usr/keylayout/Vendor_XXXX_Product_XXXX.kl
/data/system/devices/keylayout/Vendor_XXXX_Product_XXXX.kl
/odm/usr/keylayout/DEVICE_NAME.kl
/vendor/usr/keylayout/DEVICE_NAME.kl
/system/usr/keylayout/DEVICE_NAME.kl
/data/system/devices/keylayout/DEVICE_NAME.kl
/odm/usr/keylayout/Generic.kl
/vendor/usr/keylayout/Generic.kl
/system/usr/keylayout/Generic.kl
/data/system/devices/keylayout/Generic.kl

从上面路径信息可以看出Key Layout Files文件的命名和厂家信息有关, 具体为供应商id, 产品id和设备名有关;
比如我们的模拟输入设备的需要的.kl文件可以命名为Vendor_0001_Product_0010.klsmart remote.kl;

输入系统在检测到有新设备接入时, 在上述路径查找对应符合规则的.kl文件并加载它,如果没有找到则加载Generic.kl文件;

修改测试

拷贝一个Generic.kl和我们模拟按键设备名字一样, 并加上权限,如果你的板子上没有该目录的话则创建它;

cp /system/usr/keylayout/Generic.kl /data/system/devices/keylayout/smart_remote.kl
chmod 777 /data/system/devices/keylayout/smart_remote.kl

我们打开这个文件看下里面的内容:

key 1     ESCAPE
key 2     1
key 3     2
key 4     3
key 5     4
key 6     5
key 7     6
key 8     7
key 9     8
key 10    9
....

看到这就明白了为什么我们前面输入的键值2,最后在浏览器上看到了1; 我们修改下这个文件:

key 1     ESCAPE
key 2     3
...

卸载驱动, 再重新加载驱动后,再依次输入如下指令:

sendevent /dev/input/event3 1 2 1
sendevent /dev/input/event3 1 2 0
sendevent /dev/input/event3 0 0 0

效果如下:

这样就达到了输入同样按键却得到不同之的效果了;

3.2 Key Character Map

负责将android key code与修饰符的组合映射到Unicode字符。

/odm/usr/keychars/Vendor_XXXX_Product_XXXX_Version_XXXX.kcm  
/vendor/usr/keychars/Vendor_XXXX_Product_XXXX_Version_XXXX.kcm
/system/usr/keychars/Vendor_XXXX_Product_XXXX_Version_XXXX.kcm
/data/system/devices/keychars/Vendor_XXXX_Product_XXXX_Version_XXXX.kcm
/odm/usr/keychars/Vendor_XXXX_Product_XXXX.kcm
/vendor/usr/keychars/Vendor_XXXX_Product_XXXX.kcm
/system/usr/keychars/Vendor_XXXX_Product_XXXX.kcm
/data/system/devices/keychars/Vendor_XXXX_Product_XXXX.kcm
/odm/usr/keychars/DEVICE_NAME.kcm
/vendor/usr/keychars/DEVICE_NAME.kcm
/system/usr/keychars/DEVICE_NAME.kcm
/data/system/devices/keychars/DEVICE_NAME.kcm
/odm/usr/keychars/Generic.kcm
/vendor/usr/keychars/Generic.kcm
/system/usr/keychars/Generic.kcm
/data/system/devices/keychars/Generic.kcm
/odm/usr/keychars/Virtual.kcm
/vendor/usr/keychars/Virtual.kcm
/system/usr/keychars/Virtual.kcm
/data/system/devices/keychars/Virtual.kcm

输入系统在创建设备时会在这些路径下查找对应的.kl.kcm文件进行加载, 如果没有找到对应的文件将会加载Generic.klGeneric.kcm文件

修改测试

我们现在根据我们的模拟设备来更改下kcm文件, 操作如下:

mkdir /data/system/devices/keychars
cp /system/usr/keychars/Generic.kcm /data/system/devices/keychars/smart_remote.kcm

打开该文件查看下里面内容:

### Basic QWERTY keys ###

key A {
    label:                              "A"
    base:                               "a"
    shift, capslock:                    "A"
}

key B {
    label:                              "B"
    base:                               "b"
    shift, capslock:                    "B"
}

key C {
    label:                              "C"
    base:                               "c"
    shift, capslock:                    "C"
    alt:                                "u00e7"
    shift+alt:                          "u00c7"
}
....

以按键A为例, 查看.kl文件当输入事件代码为30时, 对应到android的key A,
根据.kcm文件知道此时会映射成字符a, 当按下shift键时再按下A会显示字符A;

输入如下指令看下效果:

sendevent /dev/input/event3 1 30 1
sendevent /dev/input/event3 1 30 0
sendevent /dev/input/event3 0 0 0

效果如下:

为了方便大家查看都使用了文件前面的内容进行修改并演示;

如果想当按下a键时显示字符b, 按下shift+a时显示字符2修改下kcm文件,如下:

key A {
    label:                              "A"
    base:                               "b"
    shift, capslock:                    "2"
}
...

重新卸载驱动并加载驱动, 再次执行:

sendevent /dev/input/event3 1 30 1
sendevent /dev/input/event3 1 30 0
sendevent /dev/input/event3 0 0 0

效果如下:

现在试下同时按下shift键的效果, 依次输入如下:

sendevent /dev/input/event3 1 42 1
sendevent /dev/input/event3 1 30 1
sendevent /dev/input/event3 1 30 0
sendevent /dev/input/event3 0 0 0

效果如下:

果然和我们修改的kcm文件映射的字符保持了一致;

3.3 总结

按键事件的转化流程大致如下图:

如果想个性化定制输入的按键的键值和字符显示只需要修改kl和kcm文件;

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/14679.html

相关文章

  • Java编程基础19——Map集合&斗地主案例

    摘要:使用默认随机源对指定列表进行置换。将集合排序使用二分搜索法搜索指定列表,以获得指定对象根据元素的自然顺序,返回给定的最大元素。 1_Map集合概述和特点 A:Map接口概述 查看API可以知道: 将键映射到值的对象 一个映射不能包含重复的键 每个键最多只能映射到一个值 B:Map接口和Collection接口的不同 Map是双列的,Collection是单列的 Map...

    ygyooo 评论0 收藏0
  • iOS文章

    摘要:在单核时代,使用多线程技术更多时候是为了避免耗时操作堵塞了主线程。而在多核时代,多线程技术才真正完成了提升执行效率的工作。 iOS 监控 - DNS 劫持 DNS 劫持指在劫持的网络范围内拦截域名解析的请求,分析请求的域名,把审查范围以外的请求放行,否则返回假的 IP 地址或者什么都不做使请求失去响应。 JavaScript深入系列15篇正式完结! 写在前面 JavaScript 深入...

    dreamans 评论0 收藏0
  • Hadoop Map/Reduce教程

    摘要:而仅负责执行由指派的任务。随后程序调用了行来提交作业并且监控它的执行。框架为每一个产生一个任务,而每个是由该作业的产生的。应用程序可以使用报告进度,设定应用程序级别的状态消息,更新计数器,或者仅是表明自己运行正常。增加的数目会增加整个 这篇教程从用户的角度出发,全面地介绍了Hadoop Map/Reduce框架的各个方面。 先决条件请先确认Hadoop被正确安装、配置和正常运行中。更多信息见...

    summerpxy 评论0 收藏0
  • Java 集合Hashtable源码深入解析

    摘要:分别获取正序反序的键集。是用来实现机制的第部分源码解析基于为了更了解的原理,下面对源码代码作出分析。实现了迭代器和枚举两个接口获取的迭代器若的实际大小为则返回空迭代器对象否则,返回正常的的对象。 概要 前面,我们已经系统的对List进行了学习。接下来,我们先学习Map,然后再学习Set;因为Set的实现类都是基于Map来实现的(如,HashSet是通过HashMap实现的,TreeSe...

    Turbo 评论0 收藏0
  • vue 2.x 的 v-bind 指令的 .prop 事件修饰符详解

    摘要:例子与各自的属性和方法修饰符用途默认绑定到节点的上,使用修饰符后,会绑定到注意事项使用获取最新的值设置的自定义属性会在渲染后的标签里显示,不会。修饰符用途通过自定义属性存储变量,避免暴露数据防止污染结构例如标签结构的值标签结构 vue 官方文档对 .prop 修饰符的解释是: showImg(https://segmentfault.com/img/bV1Xao?w=381&h=35)...

    philadelphia 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<