`
unicorn25
  • 浏览: 67202 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

android键盘映射(转)

阅读更多

原文网址:http://www.cnblogs.com/cnhome/archive/2009/12/22/1629435.html

 

按键事件

对于按键事件,调用mDevices->layoutMap->map进行映射。映射实际是由 KeyLayoutMap::map完成的,KeyLayoutMap类里读取配置文件qwerty.kl,由配置 文件 qwerty.kl 决定键值的映射关系。你可以通过修 改./development/emulator/keymaps/qwerty.kl来改变键值的映射关系。 

JNI 函数 
在frameworks/base/services/jni/com_android_server_KeyInputQueue.cpp文 件中,向 JAVA提供了函数android_server_KeyInputQueue_readEvent,用于读 取输入设备事件。 
C代码: 

 
static jboolean android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz, 
                                           jobject 
event)

    gLock.
lock(); 
    sp hub 
= gHub; 
    
if (hub == NULL) { 
        hub 
= new EventHub; 
        gHub 
= hub; 
    } 
    gLock.unlock(); 
    int32_t deviceId; 
    int32_t type; 
    int32_t scancode, keycode; 
    uint32_t flags; 
    int32_t value; 
    nsecs_t when; 
    
bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode, 
            
&flags, &value, &when); 
    env
->SetIntField(event, gInputOffsets.mDeviceId, (jint)deviceId); 
    env
->SetIntField(event, gInputOffsets.mType, (jint)type); 
    env
->SetIntField(event, gInputOffsets.mScancode, (jint)scancode); 
    env
->SetIntField(event, gInputOffsets.mKeycode, (jint)keycode); 
    env
->SetIntField(event, gInputOffsets.mFlags, (jint)flags); 
    env
->SetIntField(event, gInputOffsets.mValue, value); 
    env
->SetLongField(event, gInputOffsets.mWhen, 
                       (jlong)(nanoseconds_to_milliseconds(when))); 
    
return res; 
}

 readEvent调用hub->getEvent读了取事件,然后转换成JAVA的结构。 

事件中转线程 
在frameworks/base/services/java/com/android/server/KeyInputQueue.java 里创建了一个线程,它循环的读取事件,然后把事件放入事件队列里。 
Java代码:

 

Thread mThread = new Thread("InputDeviceReader") {
        public void run() {
            android.os.Process.setThreadPriority(
                    android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
            
            try {
                RawInputEvent ev = new RawInputEvent();
                while (true) {
                    InputDevice di;

                    // block, doesn't release the monitor
                    readEvent(ev);

                    boolean send = false;
                    boolean configChanged = false;
                    
                    if (false) {
                        Log.i(TAG, "Input event: dev=0x"
                                + Integer.toHexString(ev.deviceId)
                                + " type=0x" + Integer.toHexString(ev.type)
                                + " scancode=" + ev.scancode
                                + " keycode=" + ev.keycode
                                + " value=" + ev.value);
                    }
                    
                    if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
                        synchronized (mFirst) {
                            di = newInputDevice(ev.deviceId);
                            mDevices.put(ev.deviceId, di);
                            configChanged = true;
                        }
                    }

            ......
          }
        }
      }
};

 

按键、触摸屏流、轨迹球程分析

输入事件分发线程 
在frameworks/base/services/java/com/android/server/WindowManagerService.java里创建了一个输入事件分发线程,它负责把事件分发到相应的窗口上去。


按键触摸屏流程分析: 

    WindowManagerService类的构造函数

    WindowManagerService()

    mQueue = new KeyQ(); 

因为 WindowManagerService.java (frameworks\base\services\java\com\android\server)中有:

private class KeyQ extends KeyInputQueue implements KeyInputQueue.FilterCallback

KeyQ 是抽象类 KeyInputQueue 的实现,所以 new KeyQ类的时候实际上在 KeyInputQueue 类中创建了一个线程 InputDeviceReader 专门用来从设备读取按键事件,

代码: 

Thread mThread = new Thread("InputDeviceReader") { 

    
public void run() { 

        // 在循环中调用:
     readEvent(ev); 
        ... 
        send 
= preprocessEvent(di, ev); 
  
        //实际调用的是 KeyQ 类的 preprocessEvent 函数 
         ... 
        
int keycode = rotateKeyCodeLocked(ev.keycode); 
 
        int[] map = mKeyRotationMap; 

        
for (int i=0; i<N; i+=2) { 

            
if (map == keyCode)  

            
return map[i+1]; 

        } 
// 

        addLocked(di, curTime, ev.flags,RawInputEvent.CLASS_KEYBOARD,
                  newKeyEvent(di, di.mDownTime, curTime, down,keycode, 
0, scancode,...)); 

        QueuedEvent ev 
= obtainLocked(device, when, flags, classType, event); 
    } 
}; 

 

readEvent() 实际上调用的是 com_android_server_KeyInputQueue.cpp (frameworks\base\services\jni)中的

static jboolean android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz,jobject event) 来读取事件,

 

bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode,&flags, &value, &when)调用的是EventHub.cpp (frameworks\base\libs\ui)中的:

    bool EventHub::getEvent (int32_t* outDeviceId, int32_t* outType, 

            int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags, 

            int32_t* outValue, nsecs_t* outWhen) 

在函数中调用了读设备操作:res = read(mFDs.fd, &iev, sizeof(iev)); 

在构造函数 WindowManagerService()调用 new KeyQ() 以后接着调用了: 

    mInputThread = new InputDispatcherThread();       
    ...     

    mInputThread.start(); 

来启动一个线程 InputDispatcherThread 

    run() 
    process(); 
    QueuedEvent ev = mQueue.getEvent(...) 

因为WindowManagerService类中: final KeyQ mQueue; 

所以实际上 InputDispatcherThread 线程实际上从 KeyQ 的事件队列中读取按键事件,在process() 方法中进行处理事件。

    switch (ev.classType) 
    case RawInputEvent.CLASS_KEYBOARD: 
        ... 
        dispatchKey((KeyEvent)ev.event, 0, 0); 
        mQueue.recycleEvent(ev); 
        break; 
    case RawInputEvent.CLASS_TOUCHSCREEN: 
        //Log.i(TAG, "Read next event " + ev); 
        dispatchPointer(ev, (MotionEvent)ev.event, 0, 0); 
        break;

  case RawInputEvent.CLASS_TRACKBALL:
        dispatchTrackball(ev, (MotionEvent)ev.event, 0, 0);
        break;

 

===============================================================

补充一些内容:

在写程序时,需要捕获KEYCODE_HOME、KEYCODE_ENDCALL、KEYCODE_POWER这几个按键,但是这几个按键系统做了特殊处理,

在进行dispatch之前做了一些操作,HOME除了Keygaurd之外,不分发给任何其他APP,ENDCALL和POWER也类似,所以需要我们系统

处理之前进行处理。

我的做法是自己定义一个FLAG,在自己的程序中添加此FLAG,然后在WindowManagerServices.java中获取当前窗口的FLAG属性,如果是我

们自己设置的那个FLAG,则不进行特殊处理,直接分发按键消息到我们的APP当中,由APP自己处理。

这部分代码最好添加在

@Override
boolean preprocessEvent(InputDevice device, RawInputEvent event)

方法中,这个方法是KeyInputQueue中的一个虚函数,在处理按键事件之前的一个“预处理”。

PS:对HOME键的处理好像必需要修改PhoneWindowManager.java中的interceptKeyTi方法,具体可以参考对KeyGuard程序的处理。

 

===============================================================

 

系统底层事件处理过程


在系统启动后,android 会通过 

    static const char *device_path = "/dev/input";

    bool EventHub::penPlatformInput(void)

    res = scan_dir(device_path); 

通过下面的函数打开设备。 

 
int EventHub::pen_device(const char *deviceName) 

    ... 
    fd 
= open(deviceName, O_RDWR); 
    ...  
    mFDs[mFDCount].fd 
= fd; 
    mFDs[mFDCount].events 
= POLLIN; 
    ... 
    ioctl(mFDs[mFDCount].fd, EVIOCGNAME(
sizeof(devname)-1), devname); 
    ... 
    
const char* root = getenv("ANDROID_ROOT"); 
    snprintf(keylayoutFilename, 
sizeof(keylayoutFilename), 
                 
"%s/usr/keylayout/%s.kl", root, tmpfn); 
    ... 
    device
->layoutMap->load(keylayoutFilename); 
    ... 

 

打开设备的时候,如果 device->classes&CLASS_KEYBOARD 不等于 0 表明是键盘。 

常用输入设备的定义有: 

enum { 
        CLASS_KEYBOARD      = 0x00000001, //键盘 

        CLASS_ALPHAKEY      = 0x00000002, // 

        CLASS_TOUCHSCREEN   = 0x00000004, //触摸屏 

        CLASS_TRACKBALL     = 0x00000008 //轨迹球 
}; 

打开键盘设备的时候通过上面的 ioctl 获得设备名称,命令字 EVIOCGNAME 的定义在文件: 

kernel/include/linux/input.h 中。 

#define EVIOCGNAME(len)   _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */ 

在内核键盘驱动文件 drivers/input/keyboard/pxa27x_keypad.c 中定义了设备名称:pxa27x-keypad 

static struct platform_driver pxa27x_keypad_driver = { 

    .probe        = pxa27x_keypad_probe, 

    .remove        = __devexit_p(pxa27x_keypad_remove), 

    .suspend    = pxa27x_keypad_suspend, 

    .resume        = pxa27x_keypad_resume, 

    .driver        = { 

        .name    = "pxa27x-keypad", 

        .owner    = THIS_MODULE, 

    }, 

}; 

ANDROID_ROOT 为环境变量,在android的命令模式下通过 printenv 可以知道它为: system 

所以 keylayoutFilename 为:/system/usr/keylayout/pxa27x-keypad.kl 

pxa27x-keypad.kl 定义了按键映射,具体内容如下:

 
# NUMERIC KEYS 3x4 
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 
key 
11 0 
key 
83 POUND 
key 
55 STAR 

# FUNCTIONAL KEYS 
key 
231 MENU        WAKE_DROPPED 
key 
192 BACK           WAKE_DROPPED 
key 
193 HOME       WAKE 
key 
107 DEL        WAKE 
key 
102 CALL        WAKE_DROPPED 
key 
158 ENDCALL     WAKE_DROPPED 
key 
28   DPAD_CENTER     WAKE 
key 
115 VOLUME_UP 
key 
114 VOLUME_DOWN 

 

如果没有定义键盘映射文件,那么默认使用系统的 /system/usr/keylayout/qwerty.kl 可以修改 /system/usr/keylayout/qwerty.kl 文件改变Android公司的按键映射。 

device->layoutMap->load(keylayoutFilename) 调用的是文件 KeyLayoutMap.cpp (frameworks\base\libs\ui)中的函数:

    status_t KeyLayoutMap::load(const char* filename)通过解析 pxa27x-keypad.kl 
把按键的映射关系保存在 :KeyedVector<int32_t,Key> m_keys; 中。 

当获得按键事件以后调用: 
status_t KeyLayoutMap::map(int32_t scancode, int32_t *keycode, uint32_t *flags) 

由映射关系 KeyedVector<int32_t,Key> m_keys 把扫描码转换成andorid上层可以识别的按键。

分享到:
评论

相关推荐

    Android键盘映射

    ### Android键盘映射详解 #### 一、概览 Android系统中的键盘映射是指系统如何将物理键盘(或虚拟键盘)上的按键与操作系统内部表示的键值进行匹配的过程。这一机制对于实现用户输入功能至关重要,同时也为开发者...

    Android 模拟器和PC物理键盘映射

    Android 模拟器和PC物理键盘映射,文档里有的,不要钱

    Android Studio键盘映射设置,也可用于其他intellij创意

    本文将详细讲解如何在Android Studio中进行键盘映射设置,并说明这些设置同样适用于其他基于IntelliJ IDEA的产品。 首先,打开Android Studio,进入“File”菜单,然后选择“Settings”(在Mac系统中是...

    Android 按键映射对应表

    在Android系统中,按键映射是开发者理解和处理设备硬件按键输入的关键部分。这些按键映射对应表详细列举了各种按键的码值,使得程序能够识别并响应不同的用户操作。码值是Android系统用来识别按键的一种编码方式,...

    android屏幕映射,演示

    在Android平台上,屏幕映射(Screen Mirroring)是一种将手机屏幕实时投射到其他设备上显示的技术,这对于演示、教学或者协作非常有用。在你提到的场景中,使用这种工具可以使观众在大屏幕上清晰地看到演讲者手机上...

    android串口键盘

    在Android系统中,"android串口键盘"是指通过串行通信接口(UART)与设备进行交互,实现键盘功能的一种技术。这种技术常用于智能电视、机顶盒等嵌入式设备,因为它们可能没有物理键盘,但需要用户输入指令。在本简例...

    android自定义全键盘随机(包含字母+标点符号+数字)

    在Android平台上,自定义全键盘是一项常见的需求,特别是在开发输入法或者特定应用时。这个“android自定义全键盘随机(包含字母+标点符号+数字)”项目提供了一个解决方案,它能够适应各种Android手机系统,为用户...

    电脑键盘操作android设备

    通过MonkeyRunner,我们可以编写程序,使电脑键盘的按键映射为Android设备上的相应操作,实现远程控制。 Python作为强大的脚本语言,拥有丰富的库和社区支持,使得键盘事件的捕获和解析变得相对简单。我们可以利用...

    如何捕获android系统鼠标轨迹值和键盘值.pdf

    在 Android 系统中,我们可以使用键盘映射表来将事件值映射到对应的键盘值。键盘映射表是一个查表,它将事件值与键盘值之间建立映射关系。通过使用键盘映射表,我们可以将事件值映射到对应的键盘值。 捕获 Android ...

    android 游戏键盘GameKeyboard

    可以实现安卓游戏中的虚拟键盘映射到实体键上,可以编辑映射的虚拟键位置,使其对位在屏幕的虚拟控制键上,实体按钮才能产生作用。然后可以再手机上玩Xperia Play专属游戏,不过进入游戏前和过程中不能点击返回键,...

    Android-AdbKeyMonkey用于从PC键盘控制Android设备的工具

    `AdbKeyMonkey`的核心在于它能将PC键盘输入的键值映射为Android设备上的触摸事件。当你在PC键盘上按下按键时,`AdbKeyMonkey`会捕获这个输入,然后通过`adb`发送相应的触摸事件到连接的Android设备上。这样,你可以...

    Android底层驱动开发.pdf

    Android的PMEM(Physical Memory Block)驱动用于管理内存映射区域,使得用户空间可以直接访问物理内存。PMEM驱动通常与GPU、VPU、CPU等硬件组件相交互。PMEM服务涉及到了linux/android_pmem.h和drivers/android/...

    android车牌号输入法(完美版)

    在Android平台上,开发一款车牌号输入法涉及到许多技术细节和用户体验设计。首先,我们要了解车牌号输入法的主要功能,即为用户提供快速、准确输入车辆识别号码(VIN)或地方规定的车牌号码的方式。以下是一些相关的...

    Android Studio 安装教程.zip

    安装完成后,首次启动Android Studio,会引导你进行一些基本设置,如导入现有设置、选择主题、设置键盘映射等。然后,系统会检查并更新Android SDK组件,这是必要的步骤,以确保能支持最新的Android版本。 接下来,...

    Android开发案例驱动教程 配套代码

    2.3.3 键盘映射与模拟器控制 13 2.3.4 横屏与竖屏切换 14 第3章 第一个Android程序 15 3.1 HelloAndroid 15 3.1.1 在Eclipse中创建项目 15 3.1.2 编写程序项目代码 17 3.1.3 运行HelloAndroid 18 3.1.4 ...

    Android Studio代码高亮插件

    至于"IntelliJ IDEA Global Settings",这是IntelliJ IDEA(Android Studio基于此平台)的全局设置备份,可能包括了你的个性化配置,如插件、代码风格、键盘绑定等。 总的来说,使用代码高亮插件能显著提高Android ...

    GeminiKCM:用户可安装的Gemini PDA键盘映射测试

    标题中的“GeminiKCM”指的是一个专为Gemini PDA设计的键盘映射测试工具。Gemini PDA是一款结合了手机和平板功能的设备,它具有物理键盘,旨在提供类似传统PDA(个人数字助手)的使用体验。这个项目可能是为了优化或...

    android linux 捕获按键

    在Linux系统中,输入设备(如键盘)通常会被映射到`/dev/input`目录下的事件设备文件,例如`/dev/input/event0`。这些事件文件代表了系统中不同的输入设备,你可以通过读取这些文件来获取设备发送的事件信息,包括...

    Android系统添加全局快捷键的方法

    #### 一、Android中键盘驱动及按键映射机制 在深入了解Android系统中如何实现全局快捷键之前,我们需要先理解Android操作系统中的键盘驱动及其工作原理。 ##### 1. Linux内核中的按键定义 在Linux内核层面,按键...

    Android5.1夜神模拟器

    对于游戏爱好者来说,夜神模拟器的一大亮点是支持键盘映射功能。用户可以将键盘按键映射到游戏中的触摸操作,实现更精确、更快速的游戏控制,这对于需要精细操作的手机游戏尤其有帮助。此外,模拟器还支持多开功能,...

Global site tag (gtag.js) - Google Analytics