Created
February 5, 2012 14:08
-
-
Save SunRain/1745737 to your computer and use it in GitHub Desktop.
android:添加usb键盘+按键布局和映射的修改
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
android下的按键布局和映射 | |
这个笔记整理一下使用usb键盘遇到的问题,比如添加usb键盘的keylayout,添加按键,修改按键映射等。 | |
本文参考了: | |
http://blog.csdn.net/kieven2008/archive/2011/03/26/6279975.aspx | |
http://blog.csdn.net/skdev/archive/2010/03/08/5355542.aspx | |
Android的用户输入系统,自下而上,分成如下部分: | |
1.驱动程序:/dev/input目录。负责report键值到上层。其中键值定义在input.h中; | |
2.根文件系统中,KeyLayout(按键布局)和KeyCharacterMap(按键字符映射):后缀名称分别为kl和kcm;如果使用usb全键盘,则使用/system/usr/keylayout/qwerty.kl. | |
3.EventHub: libui的一部分,实现了对驱动程序的控制。目录 android/frameworks/base/libs/ui/,读取RawEvent事件。 | |
4.Java框架层的处理:有KeyInputDevice等类来处理EventHub传递上来的信息,这些信息通过RawInputEvent和KeyEvent来表示。 | |
一般情况下,对于按键事件,以后者的形式传送给应用程序,而触摸屏和轨迹球事件以前者的形式转换形成MotionEvent事件传送给应用程序; | |
5.Android应用程序层:通过重载onKeyDown()和onkeyUp()等方法接收KeyEvent(按键事件),通过重载onTouchEvent()和onTrackballEvent()等方法接收MotionEvent(运动事件); | |
keyEvent的处理流程: | |
1、生成 | |
存在这样一个线程,它不断地从driver读取Event,并把它放到RawEvent队列中。这个队列中的RawEvent既有按键,也有触摸、轨迹球等事件。 | |
RawEvent队列中的每个RawEvent最后都会通过一系列转化,最终变为KeyEvent被发送给另外一个线程,即输入线程,也就是一个Activity的主线程。 | |
2、传递 | |
KeyEvent传递过程主要可以划分为三步:过滤器、View树、Activity | |
过滤器部分 | |
主要对应着PhoneWindowManager.java中的interceptKeyTq和interceptKeyTi这两个方法。 | |
它们的代码可以在frameworks/base/policy/base/phone/com/android/internal/policy/impl/PhoneWindowManager.java中看到。 | |
这两个过滤器最大的不同就是interceptKeyTq用于RawEvent,而interceptKeyTi用于KeyEvent。 | |
在一个没有实体键盘的机器上,Power键会被interceptKeyTq这个过滤器吃掉用来调用关机对话框或者使机器休眠。而Home键会被interceptKeyTi这个过滤器吃掉,用来把当前Activity切换到后台并把桌面程序切换到前台。所以,应用程序在View和Activity的onKeyDown/Up中是监听不到这两个按键的。除了这两个键以外的按键,都有机会继续前进。接下来,KeyEvent会先经过interceptKeyTi过滤器。 | |
如果这个过滤器不吃掉的话,就会继续前进,进入View树。如果没有被哪个View吃掉的话,最后进入到Activity的onKeyDown/Up方法中。 | |
当一个KeyEvent经过上面的过程还没有被吃掉的话,系统就会利用它做一些定制的功能。比如音量键被系统用来调整声音,多媒体按键用来控制媒体播放,搜索键用来快速打开搜索功能,返回键用来退出当前Activity等。 | |
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | |
如果你的键盘是全键盘(包括了ALT、SHIFT、CAPS_LOCK功能键),基本上用Android默认的键盘映射文件qwerty.kcm,qwerty.kl就可以了。 | |
如果你需要添加新的按键,或者需要修改按键的映射,就需要修改这两个文件了。 | |
添加新的键盘 | |
比如支持usb键盘,需要导入keylayout. | |
file: frameworks/base/libs/ui/EventHub.cpp | |
int EventHub::open_device(const char *deviceName) | |
{ | |
//................. | |
// find the .kl file we need for this device | |
const char* root = getenv("ANDROID_ROOT"); | |
snprintf(keylayoutFilename, sizeof(keylayoutFilename), | |
"%s/usr/keylayout/%s.kl", root, tmpfn); | |
bool defaultKeymap = false; | |
if (access(keylayoutFilename, R_OK)) { | |
snprintf(keylayoutFilename, sizeof(keylayoutFilename), | |
"%s/usr/keylayout/%s", root, "qwerty.kl"); | |
defaultKeymap = true; | |
} | |
device->layoutMap->load(keylayoutFilename); | |
} | |
*********问题:这里在导入keylayout文件的时候,是指定路径,通过后缀获取的,所以kl文件名字不重要??? | |
比如使用usb键盘,添加一个按键,物理按键是F1,对应android上的功能是SEARCH: | |
1 按下物理按键后,驱动通过接口input_report_key向上层input子系统report一个按键SCANCODE:#define KEY_F159 (定义在内核input.h中) | |
2 查找static const KeycodeLabel KEYCODES[](在文件frameworks/base/include/ui/KeycodeLabels.h中),得知我们需要的功能键的KEYCODE是“SEARCH” | |
3 在文件/system/usr/keylayout/qwerty.kl中添加下面一行, | |
key 59 SEARCH | |
重新启动即可: | |
总结:android按键消息通过二次转换: | |
1 将驱动report上来的SCANCODE,通过文件qwerty.kl的映射,得到KEYCODE字串; | |
2 通过二维表static const KeycodeLabel KEYCODES[],将上面的字符串转换成android 需要的键值信息。 | |
如果是输入键,还需要查询keymap,得到相应的字符。 | |
相关文件: | |
key scancode定义: | |
文件:include/linux/input.h | |
...... | |
#define KEY_SPACE 57 | |
#define KEY_CAPSLOCK 58 | |
#define KEY_F1 59 | |
#define KEY_F2 60 | |
#define KEY_F3 61 | |
#define KEY_F4 62 | |
#define KEY_F5 63 | |
#define KEY_F6 64 | |
#define KEY_F7 65 | |
#define KEY_F8 66 | |
#define KEY_F9 67 | |
#define KEY_F10 68 | |
........ | |
KEYLAYOUT 按键布局 | |
文件:/system/usr/keylayout/qwerty.kl | |
文件格式: | |
key SCANCODE KEYCODE [FLAGS...] | |
第一列:key | |
第二列: SCANCODE是一个整数,是驱动里面定义的,在文件 | |
第三列: KEYCODE 是一个字串,定义在你描述的布局文件frameworks/base/include/ui/KeycodeLabels.h | |
另外可以设置相关的FLAGS: | |
SHIFT: 当按下,自动加上SHIFT键值 | |
ALT:当按下,自动加上ALT | |
CAPS:当按下,自动带上CAPS大写 | |
WAKE:当按下,当设备进入睡眠的时候,按下这个键将唤醒,而且发送消息给应用层。 | |
WAKE_DROPPED:当按下,且设备正处于睡眠,设备被唤醒,但是不发送消息给应用层。 | |
关于SCANCODE,也无需查找驱动代码。在字符界面下可以使用命令getevent获取按键的scan code: | |
# getevent | |
/ $ getevent | |
add device 1: /dev/input/event4 | |
name: "Fake Touchscreen" | |
add device 2: /dev/input/event0 | |
name: "gpio-keys" | |
add device 3: /dev/input/event3 | |
name: "Dell Dell USB Keyboard" | |
add device 4: /dev/input/event2 | |
name: "PixArt USB Optical Mouse" | |
add device 5: /dev/input/event1 | |
name: "twl4030_pwrbutton" | |
支持5种输入设备:触摸屏 GPIOkey usb键盘 usb鼠标 powerkey | |
点击usb键盘的按键“A”的打印信息: | |
/dev/input/event3: 0004 0004 00070004 | |
/dev/input/event3: 0001 001e 00000001 | |
/dev/input/event3: 0000 0000 00000000 | |
/dev/input/event3: 0004 0004 00070004 | |
/dev/input/event3: 0001 001e 00000000 | |
/dev/input/event3: 0000 0000 00000000 | |
按下一个GPIO按键的打印信息: | |
/dev/input/event0: 0001 0114 00000001 | |
/dev/input/event0: 0000 0000 00000000 | |
/dev/input/event0: 0001 0114 00000000 | |
/dev/input/event0: 0000 0000 00000000 | |
其中: | |
0001实际上是输入设备的类型。(USB键盘上的 0004 0004 00070004不太清楚是什么信息,大概是这个USB设备的各种ID信息吧) | |
0114是按键的SCANCODE, | |
00000001和00000000分别是按下和抬起的附加信息。 | |
此文件部分内容如下: | |
..... | |
key 30 A | |
key 31 S | |
key 32 D | |
key 33 F | |
key 34 G | |
key 35 H | |
key 36 J | |
key 79 1 | |
key 80 2 | |
key 81 3 | |
key 96 ENTER | |
key 69 NUM | |
key 56 ALT_LEFT | |
key 100 ALT_RIGHT | |
key 1 POWER | |
key 58 NUM | |
key 111 MUTE | |
key 1 BACK | |
key 59 SEARCH | |
..... | |
按键码:KEYCODE | |
frameworks/base/include/ui/KeycodeLabels.h | |
struct KeycodeLabel { | |
const char *literal; // KEYCODE | |
int value; //用于在keymap索引字符的value | |
}; | |
static const KeycodeLabel KEYCODES[] = { | |
{ "SOFT_LEFT", 1 }, | |
{ "SOFT_RIGHT", 2 }, | |
{ "HOME", 3 }, | |
{ "BACK", 4 }, | |
{ "CALL", 5 }, | |
{ "ENDCALL", 6 }, | |
{ "0", 7 }, | |
{ "1", 8 }, | |
{ "2", 9 }, | |
{ "3", 10 }, | |
{ "4", 11 }, | |
{ "A", 29 }, | |
{ "B", 30 }, | |
{ "C", 31 }, | |
{ "SEARCH", 84 }, | |
............. | |
} | |
字符映射 | |
键字符映射位于:/system/usr/keychars/qwerty.kcm.bin,源文件是android/device/ti/omap3evm/qwerty.kcm | |
首先,需要理解kcm文件的格式: | |
# keycode display number base caps fn caps_fn | |
keycode:由kernel层发出,经*.kl键盘映射文件得到keycode; | |
base:META_KEY没有被激活时的状态,即MetaState==0时映射的字符; | |
caps:毫无疑问,是SHIFT或CAPS_LOCK被激活时的状态,此时MetaState==1时,映射的字符; | |
fn:ALT被激活,对应MetaState==2时映射的字符; | |
caps_fn:ALT,SHIFT或CAPS_LOCK同时被激活时映射的字符;此时MetaState==3;通过这种方式,实现了一键对应多个字符的输出, | |
How To修改字符映射: | |
比如现在的字符映射,is_shitf时按下“,”,映射的字符是“;”,见下面这行: | |
COMMA ',' ',' ',' ';' ';' '|' | |
但在usb全键盘里面,这里应该是字符“<”,所以修改这里即可。 | |
注:META Keys:就是ALT、SHIFT、CAPS_LOCK。 | |
[type=QWERTY] | |
# keycode display number base caps fn caps_fn | |
A 'A' '2' 'a' 'A' '#' 0x00 | |
B 'B' '2' 'b' 'B' '<' 0x00 | |
C 'C' '2' 'c' 'C' '9' 0x00E7 | |
D 'D' '3' 'd' 'D' '5' 0x00 | |
E 'E' '3' 'e' 'E' '2' 0x0301 | |
F 'F' '3' 'f' 'F' '6' 0x00A5 | |
G 'G' '4' 'g' 'G' '-' '_' | |
H 'H' '4' 'h' 'H' '[' '{' | |
I 'I' '4' 'i' 'I' '$' 0x0302 | |
J 'J' '5' 'j' 'J' ']' '}' | |
K 'K' '5' 'k' 'K' '"' '~' | |
L 'L' '5' 'l' 'L' ''' '`' | |
M 'M' '6' 'm' 'M' '!' 0x00 | |
N 'N' '6' 'n' 'N' '>' 0x0303 | |
O 'O' '6' 'o' 'O' '(' 0x00 | |
P 'P' '7' 'p' 'P' ')' 0x00 | |
Q 'Q' '7' 'q' 'Q' '*' 0x0300 | |
R 'R' '7' 'r' 'R' '3' 0x20AC | |
S 'S' '7' 's' 'S' '4' 0x00DF | |
T 'T' '8' 't' 'T' '+' 0x00A3 | |
U 'U' '8' 'u' 'U' '&' 0x0308 | |
V 'V' '8' 'v' 'V' '=' '^' | |
W 'W' '9' 'w' 'W' '1' 0x00 | |
X 'X' '9' 'x' 'X' '8' 0xEF00 | |
Y 'Y' '9' 'y' 'Y' '%' 0x00A1 | |
Z 'Z' '9' 'z' 'Z' '7' 0x00 | |
# on pc keyboards | |
COMMA ',' ',' ',' ';' ';' '|' | |
PERIOD '.' '.' '.' ':' ':' 0x2026 | |
AT '@' '0' '@' '0' '0' 0x2022 | |
SLASH '/' '/' '/' '?' '?' '\' | |
SPACE 0x20 0x20 0x20 0x20 0xEF01 0xEF01 | |
ENTER 0xa 0xa 0xa 0xa 0xa 0xa | |
TAB 0x9 0x9 0x9 0x9 0x9 0x9 | |
0 '0' '0' '0' ')' ')' ')' | |
1 '1' '1' '1' '!' '!' '!' | |
2 '2' '2' '2' '@' '@' '@' | |
3 '3' '3' '3' '#' '#' '#' | |
4 '4' '4' '4' '$' '$' '$' | |
5 '5' '5' '5' '%' '%' '%' | |
6 '6' '6' '6' '^' '^' '^' | |
7 '7' '7' '7' '&' '&' '&' | |
8 '8' '8' '8' '*' '*' '*' | |
9 '9' '9' '9' '(' '(' '(' | |
GRAVE '`' '`' '`' '~' '`' '~' | |
MINUS '-' '-' '-' '_' '-' '_' | |
EQUALS '=' '=' '=' '+' '=' '+' | |
LEFT_BRACKET '[' '[' '[' '{' '[' '{' | |
RIGHT_BRACKET ']' ']' ']' '}' ']' '}' | |
BACKSLASH '\' '\' '\' '|' '\' '|' | |
SEMICOLON ';' ';' ';' ':' ';' ':' | |
APOSTROPHE ''' ''' ''' '"' ''' '"' | |
STAR '*' '*' '*' '*' '*' '*' | |
POUND '#' '#' '#' '#' '#' '#' | |
PLUS '+' '+' '+' '+' '+' '+' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment