MTK MMI event

来源:互联网 发布:阿里算法工程师 工资 编辑:程序博客网 时间:2024/05/17 03:54

转自:http://blog.csdn.net/yanwuxufeng/article/details/5808309

MTK MMI里面有各种event,最常见的跟交互相关按键event触摸屏event跟各种具体事件相关的比如电话event,短信event,电量event,信号量eventtimer event等等。MTK都有相应的处理方式,跟交互相关的按键(KeyBrd.c)和触摸屏(TouchScreen.c)各有一套自己的处理机制,timer event已经在MTK time小结3介绍过。还有一套机制就是处理各种其他事件,它主要分为普通事件interrupt eventinterrupt event是主要用于需要弹出的对话框的event

下面分析常用事件机制。做过MTK开发的童鞋应该知道要接收 MTK L4层(L4层即OSI的网络层)的消息,用一个注册函数 SetProtocolEventHandler,注册一个event的处理函数。比如短信,电话,电池电量,信号量等等都是通过这个函数来注册消息。当L4层,处理完封装事件后,就会把这个event发送到MMI task里来(消息发送可以看MTK task 小结 5 ),然后MMI task通过这个event机制,找到相应的处理函数,进行处理。实现这种机制,也是考虑到灵活性和扩张性。如果都在MMI task里面,用 switch case来处理,那就很疯狂了,长度不说,写个应用,定义个消息,都要去改MMI task,所以这个机制实现虽然比较简单,但是还是很有必要的。

这个机制的 event主要有两种,普通eventintrrupt event(中断事件),这些主要是一些需要中断当前应用的事件(主要是看那些弹出框),比如电话,有些应用需要提前处理这个消息,还要处理这个事件处理完毕后的处理。还是看代码吧,

MMI task最后,会调用函数ProtocolEventHandler,这个函数是就是找相应的event的相应处理函数(平台不一样,可能实际的函数名字有些区别,但是流程基本上一样的,我这里被 #define mmi_frm_execute_current_protocol_handler)

// MsgStruct是具体事件的消息体
void mmi_frm_execute_current_protocol_handler(U16 eventID, void *MsgStruct, int mod_src, void *peerBuf)
{
    U16 count = 0;
    PsExtPeerFuncPtr currFuncPtr = NULL;
    U8 interrup_result = MMI_FALSE; /* False not handle interrupt, True will handle */
    MMI_BOOL query_result = MMI_FALSE, execute_result = MMI_FALSE;
    interrupt_event_hdlr int_func = NULL, post_int_func = NULL;
    mmi_frm_int_event_type current_frm_int_event;
    {
        // 遍历 protocolEventHandler 查找是否有event注册了回调函数
        // 虽然这个方法感觉比较笨,就一个数组,实际上对速度没有什么影响
        for (count = 0; count < maxProtocolEvent; count++)
        {
            if (protocolEventHandler[count].eventID == eventID)
            {
                //找到处理函数 
                currFuncPtr = (PsExtPeerFuncPtr) protocolEventHandler[count].entryFuncPtr;
                // 这个地方时,一个优化处理的事情就是把刚才现在处理的 event id 往前移动一个单位
                // 这是考虑到 程序的局部性原理, 这个优化还是很有必要的
                if (count > 0)
                {   
                    protocolEventHandler[count].eventID = protocolEventHandler[count - 1].eventID;
                    protocolEventHandler[count].entryFuncPtr = protocolEventHandler[count - 1].entryFuncPtr;
                    protocolEventHandler[count - 1].eventID = eventID;
                    protocolEventHandler[count - 1].entryFuncPtr = (PsFuncPtr) currFuncPtr;
                }
                break;
            }
        }
    }
    // 这个就是查找是否是中断事件 
    current_frm_int_event = mmi_frm_interrupt_event_converter(eventID, MsgStruct);
    if (current_frm_int_event > 0)
    {
        // 查看该中断事件是否有注册-提前处理函数
        query_result = mmi_frm_query_interrupt_event_information(current_frm_int_event, &int_func, &post_int_func);
    }
    // 如果有,调用该函数
    if (query_result && int_func)
    {
        /* New interruption mechanism */
        execute_result = (*int_func)(current_frm_int_event);
    }
    // 根据 event 相应的回调函数 以及 前面处理的结果,来觉得是否处理该事件 
    // 要注意的是,如果如果 interrput 提前处理函数 返回 true,那么 这里就不会执行
    if ((currFuncPtr) && (!interrup_result) && (!execute_result))
    {
        (*currFuncPtr)(MsgStruct, mod_src, peerBuf);
    }
    else
    {
        MMI_TRACE(MMI_FW_TRC_G1_FRM, MMI_FRM_INFO_EVENT_EXECURPTO_NO_HDLR, eventID);
    }
    // interrupt event 最后处理函数
    if (query_result && post_int_func)
    {
        execute_result = (*post_int_func)(current_frm_int_event);
    }
}
看完这个,其实觉得挺简单的,对吧。就是一个数组,数组里关联了 event id 和 对应的处理函数。

还有就是增加了一种 intrrupt event。可以注册这种event pre_handler  and post_handler。这样如果需要,可以提前做处理或者不响应该事件。

// 这个函数就是简单把L4c(L4层)的interrupt消息,转换成 MMI 层interrupt的消息
static mmi_frm_int_event_type mmi_frm_interrupt_event_converter(U16 event_id, void *msg)
{
    mmi_frm_int_event_type frm_interrupt_event = 0;
    switch (event_id)
    {   
         // GPIO 消息
        case PRT_EQ_GPIO_DETECT_IND:
        {
            mmi_eq_gpio_detect_ind_struct *gpio_detect_ind = (mmi_eq_gpio_detect_ind_struct *)msg;
            switch (gpio_detect_ind->gpio_device)
            {
                // 翻盖关闭
                case EXT_DEV_CLAM_CLOSE:
                {
                    frm_interrupt_event = MMI_FRM_INT_CLAM_CLOSE;
                    break;
                }
                // 翻盖打开
                case EXT_DEV_CLAM_OPEN:
                {
                    frm_interrupt_event = MMI_FRM_INT_CLAM_OPEN;
                    break;
                }
                // 耳机插入\拔出
                case EXT_DEV_EARPHONE:
                {
                    if (gpio_detect_ind->on_off == 1)
                    {
                        frm_interrupt_event = MMI_FRM_INT_EARPHONE_PLUG_IN;
                    }
                    else
                    {
                        frm_interrupt_event = MMI_FRM_INT_EARPHONE_PLUG_OUT;
                    }
                    break;
                }
            }
            break;
        }
        // 充电信息,
        case PRT_BATTERY_STATUS_IND:
        {
            mmi_eq_battery_status_ind_struct *battery_status_ind = (mmi_eq_battery_status_ind_struct*)msg;
            frm_interrupt_event = mmi_frm_get_frm_int_event_macro(
                                    battery_status_ind->battery_status,
                                      mmi_frm_int_event_battery_table,
                                      sizeof(mmi_frm_int_event_battery_table) / sizeof(mmi_frm_int_event_battery_table[0]));
            /* For low battery indication */
            if (battery_status_ind->battery_status == PMIC_VBAT_STATUS)
            {
                if (battery_status_ind->battery_voltage == BATTERY_LOW_WARNING)
                {
                    frm_interrupt_event = MMI_FRM_INT_BATTERY_LOW_WARNING;
                }
                elseif (battery_status_ind->battery_voltage == BATTERY_LOW_TX_PROHIBIT)
                {
                    frm_interrupt_event = MMI_FRM_INT_BATTERY_LOW_TX_PROHIBIT;
                }
            }
            break;
        }
        case MSG_ID_TIMER_EXPIRY:
        case MSG_ID_MMI_EQ_POWER_ON_IND:
        case MSG_ID_MMI_EQ_KEYPAD_DETECT_IND:
        {
            break;
        }
        default:
        { 
            //其他 interrupt event,主要是弹出框相关,短信报告等等
            frm_interrupt_event = mmi_frm_get_frm_int_event_macro(
                                      event_id, 
                                      mmi_frm_int_event_convert_table,
                                      sizeof(mmi_frm_int_event_convert_table) / sizeof(mmi_frm_int_event_convert_table[0]));
            break;
        }
    }
    return frm_interrupt_event;
}


这个函数就是把 L4C的消息转换到 MMI的消息,然后通过 mmi_frm_query_interrupt_event_information这个函数去查找,是否有相应的注册。这个函数比较简单,就是数组里去比较。

还有一个常用的函数是 SetProtocolEventHandler,也就是注册一个消息的地回调函数。

具体也没有什么可说的,无非就是循环查找是否有这个 event

1,如果有,看func是否为空,为空表示去掉这个event的注册,不为空,就直接修改这个event func

2,没有,就增加一个event

这里注意,由一个小小的优化,就是判断,event数组的最后是否为无效的 event,如果无效,那么减少总注册event的个数。

这个样可以减少循环查询的次数。

说说key eventMTK key envent管理其实跟MTK MMI event 小结 2 说的protocol event机制也差不了多少,简单来说,就是app通过注册某一个key的回调函数,当这个key事件产生是,该机制就去找到相应的回调函数,进行回调。

先说说keyevent有哪些:

1 key down按键按下 KEY_EVENT_DOWN

2 key up   按键弹起 KEY_EVENT_UP

3 key long press按键长按  KEY_LONG_PRESS默认好像是长按1.5记不清楚了,可以自己设置,函数Kbd_SetLongPressTime,不过除非必不得已,不要去随便修改,可能会引起其他应用出问题,要修改,也要先获得当前值,退出应用时设置回原来的值。

4 key repeat按键按下一直不放,发送该消息 KEY_REPEAT举个简单例子,pc上当输入时,如果长按 a键,那么回不停的增加a,这个就是repeat动作。这个的时间好像是0.5s,也就是按住键 0.5 s 之后,每隔0.5s,就会发送一个repeat消息。设置函数Kbd_SetRepeatTime

还有两个不常用的 KEY_HALF_PRESS_DOWNKEY_HALF_PRESS_UP,也就是说的 2step 可以。

先讲一下按键的处理流程。当按键下按键之后,MMI task会收到MSG_ID_MMI_EQ_KEYPAD_DETECT_IND这个消息,在系统初始化的时候,已经注册了这个消息的处理函数 mmi_frm_key_handle。也就是说 mmi_frm_key_handle是处理按键过程的函数。这个函数 mmi_frm_key_handle收到这个消息后,回去一个通过该消息带的函数,去取当前发生的按键事件(实际这个函数就是一个按键缓存队列里去第一个,用一个按键缓存队列,可以防止按键大量的丢失,可以起到同步驱动和MMI层,驱动产生按键事件很快而MMI有可能比较慢,可以简单的理解为生产者和消费者的关系,跑题了)取到当前按键后,看当前是否可以进行操作,比如是否有触摸笔按下等等。

先看执行key event函数 mmi_frm_key_handle 

void mmi_frm_key_handle(void *paraBuff)

{

   kbd_data k;

   U32 msg_count;

   mmi_eq_keypad_detect_ind_struct *p;

   //判断参数是否为空

   //在初始化时,系统就注册了 MSG_ID_MMI_EQ_KEYPAD_DETECT_IND的回调函数为mmi_frm_key_handle,当收到这个消息是,表示有按键事件要处理。

   //这个paraBuff 是 keyTask 传过来的参数,里面的内容是获取按键信息的函数指针

   if (paraBuff != NULL)

   {

       drv_get_key_func new_key_ptr;

       p = (mmi_eq_keypad_detect_ind_struct*) paraBuff;

       new_key_ptr = (drv_get_key_func) (p->func);

       //判断是否是新的函数指针,如果是的话,需要进行一些清理工作         

       if (new_key_ptr != keypad_ptr)

       {

           // 新的函数指针,则进行清理,并且判断是否有按键需要处理

           if (!mmi_kbd_process_keyptr_change((void *)new_key_ptr))

           {            

               return;

           }

           //清理 按键 事件缓存buffer      

           ClearKeyEvents();

           keypad_ptr = new_key_ptr;

       }

   }

   //通过 while 1 来不停的从按键缓存里获取按键信息

   //当然这里不会无限死循环,后面会进行相应处理

   while (1)

   {

       //1)判读是否是挂起,2)是否key需要处理

       if ((g_mmi_suspend_key_flag == MMI_FALSE) && keypad_ptr &&((*(keypad_ptr))(&k) == MMI_TRUE))

       {

           if (k.Keydata[0] != KEY_INVALID)

           {

           #if defined(__MMI_TOUCH_SCREEN__) || defined(__MMI_HANDWRITING_PAD__)

               kal_bool is_pen_enabled, is_pen_down;

               //获得触摸屏状态

               mmi_pen_get_state(&is_pen_enabled, &is_pen_down);

           #endif /* defined(__MMI_TOUCH_SCREEN__) || defined(__MMI_HANDWRITING_PAD__) */

           #if defined(__MMI_TOUCH_SCREEN__) || defined(__MMI_HANDWRITING_PAD__)

               //如果触摸屏按下,那么保存状态

               if (is_pen_down && (k.Keyevent == WM_KEYPRESS))

               {

                   U16 KeyMapIndex;

                   //不允许 按键处理

                   is_allow_key_action = MMI_FALSE;

                   //把驱动的按键和MMI 按键进行转换

                   KeyMapIndex = mmi_frm_get_idx_from_device_key_code(k.Keydata[0]);

                   if (nKeyPadStatus[KeyMapIndex] == KEY_EVENT_UP)

                   {

                       KEYBRD_MESSAGE KeyBrdMsg;

                       KeyBrdMsg.nKeyCode = nKeyPadMap[KeyMapIndex].nMMIKeyCode;

                       if (mmi_frm_is_2step_keyCode(KeyBrdMsg.nKeyCode))

                       {

                           nKeyPadStatus[KeyMapIndex] = KEY_HALF_PRESS_DOWN;

                           pressKey = HALF_DOWN_STATUS;

                           key_is_pressing_count++;

                       }

                       else

                       {

                           nKeyPadStatus[KeyMapIndex] = KEY_EVENT_DOWN;

                           pressKey = FULL_DOWN_STATUS;

                           key_is_pressing_count++;

                       }

                   }

               }

               else

           #endif /* defined(__MMI_TOUCH_SCREEN__) || defined(__MMI_HANDWRITING_PAD__) */

               //如果按键是弹起,那么还原原来按键状态,也就是弹起状态

               //

               if ((k.Keyevent == WM_KEYRELEASE) && (is_allow_key_action == MMI_FALSE))

               {

               #if defined(__MMI_TOUCH_SCREEN__) || defined(__MMI_HANDWRITING_PAD__)

                   /* Reset allow key flag and Update key status even if pen is down*/

                   U16 KeyMapIndex;

                   KeyMapIndex = mmi_frm_get_idx_from_device_key_code(k.Keydata[0]);

                   if ((nKeyPadStatus[KeyMapIndex] == KEY_EVENT_DOWN)

                       || (nKeyPadStatus[KeyMapIndex] == KEY_HALF_PRESS_DOWN)

                       || (nKeyPadStatus[KeyMapIndex] == KEY_LONG_PRESS)

                       || (nKeyPadStatus[KeyMapIndex] == KEY_REPEAT))

                   {

                        nKeyPadStatus[KeyMapIndex] = KEY_EVENT_UP;

                       key_is_pressing_count--;

                   }

                   prevKeyMapIndex = prevKeyMapIndex;

               #endif/* defined(__MMI_TOUCH_SCREEN__) || defined(__MMI_HANDWRITING_PAD__) */

                   is_allow_key_action = MMI_TRUE;

               }

               //按键是否可以处理

               if (is_allow_key_action == MMI_TRUE)

               {

                   if (((k.Keyevent == WM_KEYPRESS) && (mmi_kbd_get_key_is_pressing_count() == 0))

                       || k.Keyevent == DRV_WM_KEYLONGPRESS || k.Keyevent == DRV_WM_KEYREPEATED || k.Keyevent == DRV_WM_KEYFULLPRESS)

                   {

                   #if defined(__MMI_TOUCH_SCREEN__) || defined(__MMI_HANDWRITING_PAD__)

                       //关闭触摸屏

                       mmi_pen_disable();

                   #endif

                   }

                   //真正处理按键事件

                   mmi_frm_convert_process_key_event(k.Keyevent, k.Keydata[0]);

                   if ((k.Keyevent == WM_KEYRELEASE) && (mmi_kbd_get_key_is_pressing_count() == 0))

                   {

                   #if defined(__MMI_TOUCH_SCREEN__) || defined(__MMI_HANDWRITING_PAD__)

                       //重新打开触摸屏

                       mmi_pen_enable();

                   #endif

                   }

               }

           }

           // 获得MMI task 消息队列消息个数

           msg_get_ext_queue_info(mmi_ext_qid, &msg_count);

           //如果外部有消息或者内部消息,则跳出该循环

           if (msg_count > 0 || OslNumOfCircularQMsgs() > 0)

           {

               //表示还有key需要处理,在MMI task 里会直接调用这个函数进行再处理

               g_keypad_flag = MMI_TRUE;

               break;

           }

       }

       else

       {  

           // 没有key消息要处理

           g_keypad_flag = MMI_FALSE;

           break;

       }

   }  /* while(1) */

   MMI_TRACE(MMI_FW_TRC_G1_FRM, MMI_FRM_KEY_HDLR_END);

}

1这里keywhile (1)来处理,不停的从key消息buffer里取出按键信息,然后处理,这么做可以防止task中不停的发送消息,可以理解为共享内存。同样当有其他消息来时,需要跳出来处理其他消息,处理完毕后还需要再来处理。

2)这里跟触摸屏的事件进行了冲突处理,也就是按键和触摸屏不能同时工作,这两者也没有优先级。

3)实际真正处理按键是在 mmi_frm_convert_process_key_event里面,下次在分析

MTK MMI event 小结 5 中,提到了event处理函数 mmi_frm_key_handle,这个函数主要作用是判断是否需要处理按键,从按键缓存里面持续的读取按键信息,然后调用 mmi_frm_convert_process_key_event 进行处理。这个函数没有什么可说的,最多是在屏幕旋转的情况下,把导航键转换一下,接着它调用了 ProcessKeyEvent这个函数主要是对于一些状态的处理,防止key downup不成对,出现混乱。

void ProcessKeyEvent(U32 MsgType, U16 DeviceKeyCode)
{
    MMI_BOOL isKeyPaired;
    U16      KeyMapIndex;
   // 按键影射,把驱动的按键码,转换成MMI 的 按键消息
    KeyMapIndex = mmi_frm_get_idx_from_device_key_code(DeviceKeyCode);
    if (KeyMapIndex >= MAX_KEYS)
    {
        return;
    }
    // 处理各种按键事件,没有什么可以多说的,
    // 主体结构都一样,
    // 1 判断状态是否正常
    // 2 正常 则调用 KeyEventHandler 处理, 否则忽略该事件
    if (MsgType == WM_KEYPRESS)
    {
        // 这里处理 多案件同时按下的情况。这里需要硬件支持
        if ((KeyMapIndex != prevKeyMapIndex) && (g_kbd_concurrent_key_mode == CONCURRENT_KEY_MODE_1_KEY))
        {
            isKeyPaired = (nKeyPadStatus[prevKeyMapIndex] == KEY_EVENT_UP);
            prevKeyMapIndex = KeyMapIndex;
        }
        //判断案件状态是否正常,防止不匹配
        if (nKeyPadStatus[KeyMapIndex] == KEY_EVENT_UP)
        {
            KEYBRD_MESSAGE KeyBrdMsg;
            KeyBrdMsg.nKeyCode = nKeyPadMap[KeyMapIndex].nMMIKeyCode;
            if (mmi_frm_is_2step_keyCode(KeyBrdMsg.nKeyCode))
            {
                nKeyPadStatus[KeyMapIndex] = KEY_HALF_PRESS_DOWN;
                key_is_pressing_count++;
                KeyBrdMsg.nMsgType = KEY_HALF_PRESS_DOWN;
            }
            else
            {
                nKeyPadStatus[KeyMapIndex] = KEY_EVENT_DOWN;    /* same with KEY_FULL_PRESS_DOWN */
                key_is_pressing_count++;
                KeyBrdMsg.nMsgType = KEY_EVENT_DOWN;
            }
            // 处理按键事件
            KeyEventHandler((KEYBRD_MESSAGE*) & KeyBrdMsg);
        }
        else
        {
            /* Ignore the event */
        }
    }
    elseif (MsgType == WM_KEYRELEASE)
    {
        if ((nKeyPadStatus[KeyMapIndex] == KEY_EVENT_DOWN)
            || (nKeyPadStatus[KeyMapIndex] == KEY_LONG_PRESS)
            || (nKeyPadStatus[KeyMapIndex] == KEY_REPEAT) || (nKeyPadStatus[KeyMapIndex] == KEY_HALF_PRESS_DOWN))
        {
            KEYBRD_MESSAGE KeyBrdMsg;
            nKeyPadStatus[KeyMapIndex] = KEY_EVENT_UP;
            key_is_pressing_count--;
            KeyBrdMsg.nMsgType = KEY_EVENT_UP;
            KeyBrdMsg.nKeyCode = nKeyPadMap[KeyMapIndex].nMMIKeyCode;
            KeyEventHandler((KEYBRD_MESSAGE*) & KeyBrdMsg);
        }
        else
        {
            /* Ignore the event */
        }
    }
    /* ++Robin, modified by Max Chen */
    elseif (MsgType == DRV_WM_KEYLONGPRESS)
    {
        if (nKeyPadStatus[KeyMapIndex] == KEY_EVENT_DOWN)
        {
            KEYBRD_MESSAGE KeyBrdMsg;
            nKeyPadStatus[KeyMapIndex] = KEY_LONG_PRESS;
            KeyBrdMsg.nMsgType = KEY_LONG_PRESS;
            KeyBrdMsg.nKeyCode = nKeyPadMap[KeyMapIndex].nMMIKeyCode;
            KeyEventHandler((KEYBRD_MESSAGE*) & KeyBrdMsg);
        }
        else
        {
            /* Ignore the event */
        }
    }
    elseif (MsgType == DRV_WM_KEYREPEATED)
    {
        if ((nKeyPadStatus[KeyMapIndex] == KEY_LONG_PRESS) || (nKeyPadStatus[KeyMapIndex] == KEY_REPEAT))
        {
            KEYBRD_MESSAGE KeyBrdMsg;
            nKeyPadStatus[KeyMapIndex] = KEY_REPEAT;
            KeyBrdMsg.nMsgType = KEY_REPEAT;
            KeyBrdMsg.nKeyCode = nKeyPadMap[KeyMapIndex].nMMIKeyCode;
            KeyEventHandler((KEYBRD_MESSAGE*) & KeyBrdMsg);
        }
        else
        {
            /* Ignore the event */
        }
    }
    elseif (MsgType == DRV_WM_KEYFULLPRESS)
    {
        /*
         * Only in two-stage key will have KEY_FULL_PRESS_DOWN, and it followed after KEY_HALF_PRESS_DOWN
         */
        if (nKeyPadStatus[KeyMapIndex] == KEY_HALF_PRESS_DOWN)
        {
            KEYBRD_MESSAGE KeyBrdMsg;
            nKeyPadStatus[KeyMapIndex] = KEY_EVENT_DOWN;
            KeyBrdMsg.nMsgType = KEY_EVENT_DOWN;
            KeyBrdMsg.nKeyCode = nKeyPadMap[KeyMapIndex].nMMIKeyCode;
            KeyEventHandler((struct KEYBRD_MESSAGE*)&KeyBrdMsg);
        }
        else
        {
            /* Ignore the event */
        }
    }
    elseif ((MsgType == DRV_WM_ENABLE_TWOKEY_DETECTION) || (MsgType == DRV_WM_ENABLE_THREEKEY_DETECTION) ||
                (MsgType == DRV_WM_DISABLE_MULTIKEY_DETECTION))
    {
        /* Ignore the event */
    }
    else
    {
        MMI_TRACE(MMI_FW_TRC_G6_FRM_DETAIL, MMI_FRM_ERROR_PROC_KEYEVENT_HDLR);
        MMI_ASSERT(0);
    }
 
}

KeyEventHandler函数主要判断是否要真的处理该事件,可以看成是一个按键事件的拦截,比如应用切换过程中,需要一个切换动画,而这个动画工程中,需要处理忽略这些按键。就需要特殊的处理。

staticvoid KeyEventHandler(KEYBRD_MESSAGE *eventKey)
{
    MMI_BOOL is_hdlr_enabled = MMI_TRUE;
   // 主要处理一些特殊相应:屏幕背光,屏幕锁定,按键声音
    mmi_kbd_app_key_hdlr(eventKey);
    // 判断是否有前置处理函数
    if (g_mmi_frm_cntx.kbd_pre_func)
    {
        is_hdlr_enabled = g_mmi_frm_cntx.kbd_pre_func(eventKey);
    }
    // 根据前置处理函数结果,判断是否要处理该 key event
    if (is_hdlr_enabled)
    {
        //处理 案件事件
        ExecuteCurrKeyHandler((S16) eventKey->nKeyCode, (S16) eventKey->nMsgType);
    }
    // 是否有后置处理函数,可以进行一些监视
    if (g_mmi_frm_cntx.kbd_post_func)
    {
        g_mmi_frm_cntx.kbd_post_func(eventKey);
    }
}

接下来是 ExecuteCurrKeyHandler这个函数就是根据按键事件,获得处理函数,进行处理。

void ExecuteCurrKeyHandler(S16 keyCode, S16 keyType)
{
        FuncPtr currFuncPtr = NULL;
        // 重新设定 键盘锁和屏保timer
        mmi_idle_restart_keypad_lock_timer();
        mmi_idle_restart_screensaver_timer();
        frm_p->currKeyCode = keyCode;
        frm_p->currKeyType = keyType;
        // 对电话状态下,挂电话键的特殊处理.
        if (frm_p->currKeyType == KEY_EVENT_DOWN && 
            isInCall() && !GetWapCallPresent() && 
            IsBitReset(g_mmi_frm_cntx.end_key_flag, frm_p->currKeyType) )
        {
                      RegisterEndKeyHandlerInCall();
        }
        // 获得按键处理函数
        currFuncPtr = currKeyFuncPtrs[keyCode][keyType];
       // 导航的选择键,默认和左功能键的效果一样。
        if (keyCode == KEY_ENTER && currFuncPtr == NULL)
        {
               if (currKeyFuncPtrs[KEY_ENTER][KEY_EVENT_UP] == NULL &&
                currKeyFuncPtrs[KEY_ENTER][KEY_EVENT_DOWN] == NULL &&
                currKeyFuncPtrs[KEY_ENTER][KEY_HALF_PRESS_DOWN] == NULL &&
                currKeyFuncPtrs[KEY_ENTER][KEY_REPEAT] == NULL && currKeyFuncPtrs[KEY_ENTER][KEY_LONG_PRESS] == NULL)
            {
                currFuncPtr = currKeyFuncPtrs[KEY_LSK][keyType];
            }
        }
        // 处理按键 消息
        if (currFuncPtr)
        {
            (*currFuncPtr)();
        }
    } 
    // 重置状态
    if (keyType == KEY_EVENT_UP)
    {    
        frm_p->currKeyCode = KEY_INVALID;
        frm_p->currKeyType = MAX_KEY_TYPE;
    }
}

从整个按键事件的处理流程来看,也没有什么特殊的地方,就是收到消息后,从按键buffer里取出按键事件,然后处理。如果有有别的事件要处理,那么就break出来,等到处理完这个消息后,在 MMI task 调用 mmi_frm_key_handle继续处理剩下的按键事件。

这里无非多了很多的判断,是否要进行按键处理,MTK的代码函数名字上虽然看不出什么东西,但是一层一层函数,每一层函数的功能还是比较独立的,所以也不是很难看懂。

到这,key event的处理基本上就算完成了。其他几个常用的函数注册key处理函数了。了解了按键event的处理过程之后(其实也是比较简单的),注册函数就很好了解了,就是把相应key event的数组里放入一个回调函数的指针。比如

void SetKeyHandler(FuncPtr funcPtr, U16 keyCode, U16 keyType)抛开其他语句,就是一句简单的实现currKeyFuncPtrs[keyCode][keyType] = funcPtr;还有可以设置一组按键的处理函数,

void SetGroupKeyHandler(FuncPtr funcPtr, PU16 keyCodes, U8 len, U16 keyType),这个很好理解,keyCodes就是一个按键数组,len是这个数组的长度。实现也很简单,就是一个for循环,把这些keyCodes的回调函数都写成 funcPtr。这里就不再说了(其实也没有什么可以说得了)

其他还有:

void mmi_frm_kbd_reg_pre_key_hdlr(PsKeyProcessCBPtr func) ; //注册按键预处理函数,MTK MMI event 小结 6 解释过

void mmi_frm_kbd_set_tone_state(mmi_frm_kbd_tone_state_enum state); //设置按键音。需要说明一点,在开发一个应用程序的时候,有些手机状态一般先保存一下进入程序前的状态,在自己的app退出后还原状态。比如这个按键音,背光长亮等等

接下来简单说一下触摸屏事件,其实触摸屏的实现跟key event几乎一样。也是通过pen event缓存队列来获得pen事件。来看几个代码.

先看一下初始化函数:mmi_pen_init

void mmi_pen_init(void)
{
    g_pen_initialized = KAL_TRUE;
    // 看到这两个函数指针,是不是感觉很熟悉?对,就是跟key 一样,前置处理和后置处理函数
    // 这个看多了,不用细看代码就能猜到了。
    g_pen_cntx.pre_pen_callback_fp = NULL;
    g_pen_cntx.post_pen_callback_fp = NULL;
    // 设置 手写输入法的区域,手写输入法在 触摸屏这里需要特殊处理。
    mmi_pen_stop_capture_strokes();
    // 设置触摸屏采样时间间隔,参数一是在普通状态下,采样时间间隔,参数二是手写输入法情况下采样时间间隔
    mmi_pen_config_sampling_period(MMI_PEN_SAMPLING_PERIOD_1, MMI_PEN_SAMPLING_PERIOD_2);
    // 设置超时时间间隔
    mmi_pen_config_timeout_period(MMI_PEN_LONGTAP_TIME, MMI_PEN_REPEAT_TIME, MMI_PEN_STROKE_LONGTAP_TIME);
   // 设置move 的间距,也就是两个点之间多少个pixel 算move 事件。
    mmi_pen_config_move_offset(
        MMI_PEN_SKIP_MOVE_OFFSET,
        MMI_PEN_SKIP_STROKE_MOVE_OFFSET,
        MMI_PEN_SKIP_LONGTAP_OFFSET,
        MMI_PEN_SKIP_STROKE_LONGTAP_OFFSET);
    // 设置drv 的回调函数,也就是当产生事件时,会回调这个函数
    touch_panel_cb_registration(mmi_pen_touch_panel_sendilm, NULL);
    // 设置MMI 层消息 MSG_ID_TP_EVENT_IND 的处理函数
    SetProtocolEventHandler(mmi_pen_touch_panel_event_ind, MSG_ID_TP_EVENT_IND);
}

在驱动drv这一层,触摸屏有一个task-- MOD_TP_TASK,在处理事件,MOD_TP_TASK会回调用 touch_panel_cb_registration注册的函数,有pen event事件产生了,这个去pen event缓存里去,这里注册的是:mmi_pen_touch_panel_sendilm函数,这个函数就是简单的向MMI task发送一个消息 MSG_ID_TP_EVENT_IND,让MMI task来处理。这里这么做的原因也很简单,drvtask优先级是很高的,要处理的东西也很多,所以希望回调函数能很快处理完这个事情(发送一个消息很快),这里可以想一下 pc上的中断,这两者差不多。当然中断还有更多其他原因,比如内核态和用户态等等,需要回调函数尽可能快处理,发送消息到消息队列(扯远了。。。)

         还有一点需要说明的是,现在触摸屏的手机越来越普遍,那么该善触摸屏的体验是非常重要的,可惜的是MTK的又不是多点触摸,处理起来很费劲。这个时候就需要充分利用好这里的几个设置,采样时间间隔,以及move事件距离。需要时,让自己app可以捕获尽可能多的点,然后自己进行计算,来判断用户行为。这对改善体验很有帮助。当然这个也是比较耗电的,不需要时,一定要设置回普通状态。

MTK MMI event 小结 7 中,说到,init时候,注册了触摸屏消息MSG_ID_TP_EVENT_IND的处理函数 mmi_pen_touch_panel_event_ind

staticvoid mmi_pen_touch_panel_event_ind(void *param )

{

    // 这个用于控制 只有一个 MSG_ID_TP_EVENT_IND在 消息队列里

    g_pen_is_driver_indication_in_queue = 0;

   //判断是否启动了timer 在定时读取 pen event 缓存

   if (!g_pen_polling_timer_started)

   {

      //真正的处理函数

       mmi_pen_poll_hdlr();

   }

}

这个函数其实没有做什么真正的处理,只是简单的判断的了一下是否要处理这个消息。这里需要注意的是,跟按键事件不同的是,这里通过timer来控制处理 pen event 消息缓存里剩余的消息,按键事件是在 MMI task消息循环里,直接调用按键事件的处理函数。从要实现的效果上没有什么太多的区别。

staticvoid mmi_pen_poll_hdlr(void)

{

   TouchPanelEventStruct data;

   MMI_BOOL is_stroke_move = MMI_FALSE;

   MMI_BOOL has_incoming_event = MMI_FALSE;

   MMI_BOOL delay_polling_timer = MMI_FALSE;

   MMI_BOOL pen_abort_happen = MMI_FALSE;

   U16      interval = 0;

   g_pen_polling_timer_started = MMI_FALSE;

   ResetBit(g_input_msg_in_queue, MMI_DEVICE_PEN);

   //判断 pen event 是否开启,按键和pen 是冲突的。

   if (!g_pen_cntx.is_enabled)

   {

       return;

   }

   //判断是否还有数据要处理

   // delay_polling_timer表示是否启动了timer,来稍后处理这些pen event 事件,开始时初始化为 false

   while (!delay_polling_timer && mmi_pen_lookahead_buffer_get_event(&data))

   {      

       mmi_pen_point_struct pos;

       // 检测背光情况,是否要开启

       mmi_idle_key_event_backlight_check();

       //重置 屏幕保护和键盘锁的时间

       mmi_idle_restart_screensaver_timer();

       mmi_idle_restart_keypad_lock_timer();

       // 获取 x,y。坐标点

       pos.x = (S16) data.x_adc;

       pos.y = (S16) data.y_adc;

       has_incoming_event = MMI_TRUE;

   #ifdef __MMI_TOUCH_PANEL_SHORTCUT__

       if(!mmi_pen_check_tp_shortcut(&pos, data.event))

   #endif/* __MMI_TOUCH_PANEL_SHORTCUT__ */

       {

           // 判断 是否是 stroke move事件结束

           // is_stroke_move 表示前一个pen event事件是否是 stroke move

           if (is_stroke_move && (data.event != STROKE_MOVE))

           {

               is_stroke_move = MMI_FALSE;

               //这个函数眼熟吧

               if (g_pen_stroke_post_move)

               {

                   g_pen_stroke_post_move();

               }

           }

           // 保存现在状态

           g_pen_cntx.pen_current_pos = pos;

           g_pen_cntx.pen_event = data.event;

           switch (data.event)

           {

               case PEN_DOWN:

                   g_pen_cntx.is_pen_down = 1;

                   g_pen_cntx.is_in_pen_handler = 1;

                   //计算最近两次 点击事件的 事件间隔

                   //这里这么判断是当时间溢出 short的时候,需要特殊处理

                   if (g_pen_cntx.previous_pen_down_time > data.time_stamp)

                   {

                       interval = 0XFFFF - g_pen_cntx.previous_pen_down_time + data.time_stamp;

                   }

                   else

                   {

                       interval = data.time_stamp - g_pen_cntx.previous_pen_down_time;

                   }

                   //判断两次pen down的时间是否足够短,间隔距离足够短,从而判断是否是 双击

                   if ((interval <= MMI_PEN_DOUBLE_CLICK_THRESHOLD) &&

                       (mmi_pen_get_distance_square(g_pen_cntx.pen_down_pos, pos) < MMI_PEN_SKIP_DOUBLE_CLICK_OFFSET_SQUARE))

                   {                       

                       if (g_pen_event_table[MMI_PEN_EVENT_DOUBLE_CLICK] && g_pen_cntx.is_enabled)

                       {

                           // 前置处理函数

                           MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_EVENT_DOUBLE_CLICK);

                           //回调double click注册函数

                           (g_pen_event_table[MMI_PEN_EVENT_DOUBLE_CLICK]) (pos);

                           //后置处理函数

                           MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_EVENT_DOUBLE_CLICK);

                          

                       }

                       g_pen_cntx.previous_pen_down_time = 0;

                   }

                   else

                   {

                       g_pen_cntx.previous_pen_down_time = data.time_stamp;

                   }

                   g_pen_cntx.pen_down_pos = pos;

                   if (g_pen_event_table[MMI_PEN_EVENT_DOWN] && g_pen_cntx.is_enabled)

                   {

                      //前置处理函数

                       MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_EVENT_DOWN);

                       //回调pen down注册函数

                       (g_pen_event_table[MMI_PEN_EVENT_DOWN]) (pos);

                       //后置处理函数

                       MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_EVENT_DOWN);

                   }

                   break;

               case PEN_UP:

                   g_pen_cntx.is_pen_down = 0;

                   if (g_pen_event_table[MMI_PEN_EVENT_UP] && g_pen_cntx.is_enabled)

                   {

                       MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_EVENT_UP);

                       (g_pen_event_table[MMI_PEN_EVENT_UP]) (pos);

                       MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_EVENT_UP);

                   }

                   //判断是否要进行pen event reset

                   if (g_pen_cntx.reset_stroke_on_pen_up)

                   {

                       //判读是否有 stroke event

                       if (g_pen_stroke_max_points > 0)

                       

                           //结束 stroke event注意,stroke event 会把所有的点push到一个数组里,供注册的程序使用,当然这个数组大小也是程序设定的。这里设置结束标志。

                           mmi_pen_end_strokes_of_character();

                           mmi_pen_reset();

                           mmi_pen_begin_strokes_of_character();

                       }

                       else

                       {

                           mmi_pen_reset();

                       }

                   }

                   //一对 down move up后,即时还有pen事件,通过timer 来取下一组信息

                   delay_polling_timer = MMI_TRUE;

                   g_pen_cntx.is_in_pen_handler = 0;

                   g_pen_cntx.pen_event = TP_UNKNOWN_EVENT;

                   break;

               case PEN_MOVE:

                   if (g_pen_event_table[MMI_PEN_EVENT_MOVE] && g_pen_cntx.is_enabled)

                   {

                       MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_EVENT_MOVE);

                       (g_pen_event_table[MMI_PEN_EVENT_MOVE]) (pos);

                       MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_EVENT_MOVE);

                   }

                   break;

               case PEN_LONGTAP:

                   if (g_pen_event_table[MMI_PEN_EVENT_LONG_TAP] && g_pen_cntx.is_enabled)

                   {

                       MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_EVENT_LONG_TAP);

                       (g_pen_event_table[MMI_PEN_EVENT_LONG_TAP]) (pos);

                       MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_EVENT_LONG_TAP);

                   }

                   break;

               case PEN_REPEAT:     

                   if (g_pen_event_table[MMI_PEN_EVENT_REPEAT])

                   {

                       MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_EVENT_REPEAT);

                       (g_pen_event_table[MMI_PEN_EVENT_REPEAT]) (pos);

                       MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_EVENT_REPEAT);

                   }

                   break;

               case PEN_ABORT:

                   pen_abort_happen = MMI_TRUE;

                   g_pen_drvq_full_abort = MMI_TRUE;

                   mmi_pen_reset();

                   break;

               case TP_UNKNOWN_EVENT:

                   MMI_ASSERT(0);

                   break;

               case STROKE_MOVE:

                   //这个主要是用于当只有一个手写区域时使用

                   if (g_pen_cntx.is_pending_stroke_event)

                   {

                       //判断是第一stroke move点是否符合标准(最小距离)

                       if (mmi_pen_get_distance_square(g_pen_cntx.pen_down_pos, pos) > g_pen_stroke_min_offset_square)

                       {

                           g_pen_cntx.is_pending_stroke_event = 0;

                           g_pen_stroke_min_offset_square = 0;

                           g_pen_cntx.reset_stroke_on_pen_up = 0;

                           g_pen_cntx.pen_event = PEN_ABORT;

                           // 先产生 pen_abort事件,告诉应用普通的pen 事件被中断,也就是光有pen down 没有pen up事件了

                           if (g_pen_event_table[MMI_PEN_EVENT_ABORT]&& g_pen_cntx.is_enabled)

                           {

                               MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_EVENT_ABORT);

                               (g_pen_event_table[MMI_PEN_EVENT_ABORT]) (g_pen_cntx.pen_down_pos);

                               MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_EVENT_ABORT);

                           }

                           g_pen_cntx.pen_event = STROKE_DOWN;

                           //把 stroke down时间事件 保存到数组里

                           mmi_pen_push_stroke_point(g_pen_cntx.pen_down_pos.x, g_pen_cntx.pen_down_pos.y);

                           //发送 stroke down事件

                           if (g_pen_stroke_table[MMI_PEN_STROKE_DOWN] && g_pen_cntx.is_enabled)

                           {

                               MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_STROKE_DOWN);

                               (g_pen_stroke_table[MMI_PEN_STROKE_DOWN]) (g_pen_cntx.pen_down_pos);

                               MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_STROKE_DOWN);

                           }

                           g_pen_cntx.pen_event = STROKE_MOVE;

                           //预处理

                           is_stroke_move = MMI_TRUE;

                           if (g_pen_stroke_pre_move)

                           {

                               g_pen_stroke_pre_move();

                           }

                           //发送 stroke move

                           mmi_pen_push_stroke_point(pos.x, pos.y);

                           if (g_pen_stroke_table[MMI_PEN_STROKE_MOVE] && g_pen_cntx.is_enabled)

                           {

                               MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_STROKE_MOVE);

                               (g_pen_stroke_table[MMI_PEN_STROKE_MOVE]) (pos);

                               MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_STROKE_MOVE);

                           }

                       }

                   }

                   else

                   {

                       //判断在哪个区域里面

                       if (g_pen_num_stroke_area > 1)

                       {

                           mmi_pen_fix_multi_block_pen_position(&pos);

                       }

                       

                       //预处理

                       if (!is_stroke_move)

                       {

                           is_stroke_move = MMI_TRUE;

                           if (g_pen_stroke_pre_move)

                           {

                               g_pen_stroke_pre_move();

                           }

                       }

                       //保存 stroke move并发送

                       mmi_pen_push_stroke_point(pos.x, pos.y);

                       if (g_pen_stroke_table[MMI_PEN_STROKE_MOVE] && g_pen_cntx.is_enabled)

                       {

                           MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_STROKE_MOVE);

                           (g_pen_stroke_table[MMI_PEN_STROKE_MOVE]) (pos);

                           MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_STROKE_MOVE);

                       }

                   }

                   break;

               case STROKE_DOWN:

                   g_pen_cntx.is_stroke_down = 1;

                   g_pen_cntx.is_pen_down = 1;

                   g_pen_cntx.pen_down_pos = pos;

                   g_pen_cntx.stroke_down_block_index = mmi_pen_lookup_handwriting_block(pos.x, pos.y);

                   //单个区域特殊处理

                   if (g_pen_stroke_min_offset_square > 0)

                   {

                       g_pen_cntx.is_pending_stroke_event = 1;

                       g_pen_cntx.pen_event = PEN_DOWN;

                       if (g_pen_event_table[MMI_PEN_EVENT_DOWN] && g_pen_cntx.is_enabled)

                       {

                           MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_EVENT_DOWN);

                           (g_pen_event_table[MMI_PEN_EVENT_DOWN]) (g_pen_cntx.pen_down_pos);

                           MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_EVENT_DOWN);

                       }

                   }

                   else

                   {

                       g_pen_cntx.is_pending_stroke_event = 0;

                       g_pen_cntx.pen_event = STROKE_DOWN;

                       if (g_pen_stroke_table[MMI_PEN_STROKE_DOWN] && g_pen_cntx.is_enabled)

                       {

                           MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_STROKE_DOWN);

                           (g_pen_stroke_table[MMI_PEN_STROKE_DOWN]) (pos);

                           MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_STROKE_DOWN);

                       }

                       if (g_pen_cntx.is_enabled && g_pen_cntx.is_pen_down && g_pen_stroke_max_points > 0)

                       {

                           mmi_pen_push_stroke_point(pos.x, pos.y);

                       }                   

                   }

                   break;

               case STROKE_UP:

                   MMI_DBG_ASSERT(g_pen_stroke_max_points > 0);

                   g_pen_cntx.is_stroke_down = 0;

                   g_pen_cntx.is_pen_down = 0;

                   if (g_pen_cntx.is_pending_stroke_event)

                   {

                       g_pen_cntx.is_pending_stroke_event = 0;

                       g_pen_cntx.reset_stroke_on_pen_up = 1;

                       g_pen_cntx.pen_event = PEN_UP;

                       if (g_pen_event_table[MMI_PEN_EVENT_UP] && g_pen_cntx.is_enabled)

                       {

                           MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_EVENT_UP);

                           (g_pen_event_table[MMI_PEN_EVENT_UP]) (g_pen_cntx.pen_down_pos);

                           MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_EVENT_UP);

                       }

                       delay_polling_timer = MMI_TRUE;

                   }

                   else

                   {

                       g_pen_cntx.pen_event = STROKE_UP;

                       if (g_pen_num_stroke_area > 1)

                       {

                          mmi_pen_fix_multi_block_pen_position(&pos);

                       }

                       mmi_pen_push_stroke_end();

                       if (g_pen_stroke_table[MMI_PEN_STROKE_UP] && g_pen_cntx.is_enabled)

                       {

                           MMI_PEN_EXECUTE_PRE_CALLBACK_FUNC(MMI_PEN_STROKE_UP);

                           (g_pen_stroke_table[MMI_PEN_STROKE_UP]) (pos);

                           MMI_PEN_EXECUTE_POST_CALLBACK_FUNC(MMI_PEN_STROKE_UP);

                       }

                   }

                   if (g_pen_cntx.reset_stroke_on_pen_up)

                   {

                       if (g_pen_stroke_max_points > 0)

                       {

                           mmi_pen_end_strokes_of_character();

                           mmi_pen_reset();

                           mmi_pen_begin_strokes_of_character();

                       }

                       else

                       {

                           mmi_pen_reset();

                       }

                   }

                   /* leave pen handler procedure */

                   g_pen_cntx.is_in_pen_handler = 0;

                   g_pen_cntx.pen_event = TP_UNKNOWN_EVENT;

                   break;

               default:

                   MMI_ASSERT(0);

           }

       } /* if(!mmi_pen_check_tp_shortcut(&pos, data.event)) */

       // 把外部消息放入到内部循环消息队列

       mmi_frm_fetch_msg_from_extQ_to_circularQ();

       OslCircularDump();

       // 如果内部消息积压大于 MMI_PENDING_MSG_THRESHOLD,就跳出循环,进行处理

       if (OslNumOfCircularQMsgs() > MMI_PENDING_MSG_THRESHOLD)

       {

           break;

       }

   }

   if (is_stroke_move)

   {

       if (g_pen_stroke_post_move)

       {

           g_pen_stroke_post_move();

       }

   }

   if (has_incoming_event)

   {

       if (!mmi_shutdown_is_in_power_off_period())

       {

           mmi_idle_key_event_backlight_check();

       }

   }

   if (g_pen_cntx.is_in_pen_handler == 0)

   {

       g_pen_cntx.pen_event = TP_UNKNOWN_EVENT;

   }

   //启动定时期

   if (delay_polling_timer)

   {

       StartTimer(PEN_POLLING_TIMER, MMI_PEN_DEBOUNCE_POLLING_DELAY, mmi_pen_poll_hdlr);

       g_pen_polling_timer_started = MMI_TRUE;

       SetBit(g_input_msg_in_queue, MMI_DEVICE_PEN);

   }

   elseif (g_pen_cntx.is_pen_down || pen_abort_happen)

   {

       StartTimer(PEN_POLLING_TIMER, MMI_PEN_POLLING_PERIOD, mmi_pen_poll_hdlr);

       g_pen_polling_timer_started = MMI_TRUE;

       SetBit(g_input_msg_in_queue, MMI_DEVICE_PEN);

       if(pen_abort_happen)

       {

               pen_abort_happen = MMI_FALSE;

       }

   }

}

这里其实也是比较简单:

1pen event事件

2根据时间类型处理事件

3:是否有额外消息要处理,是退出,否继续 1

这里有点点复杂的是手写输入法,这个事件比较乱,全局变量一堆,分析起来比较头疼。

到这里 pen event其实也就差不多了,说一下几个常用的函数:

mmi_pen_enable /mmi_pen_disable  //打开/关闭 pen 事件

mmi_pen_set_calibration_data / mmi_pen_read_calibration_data  //设置和读取校准数据,校准数据总共四个值 x = x*x_scale + x_offset, y = y*y_scale + y_offset

mmi_pen_start_calibration  //开始校准

mmi_pen_start_capture_strokes / mmi_pen_stop_capture_strokes  //在设定区域里开始和关闭 捕捉输入法轨迹(stroke)点。具体可以参见 EntryEmPenTest里面用法,有机会在分析MTK手写输入法

mmi_pen_config_move_offset / mmi_pen_config_timeout_period / mmi_pen_config_sampling_period  //几个配置函数,对于写一个好的触摸屏应用,还是很有必要的,相关介绍看MTK MMI event 小结

mmi_pen_register_move(up down repeat …)_handler  //注册pen事件处理函数。

对于触摸屏校准,做一个简单的说明:

当调用 void mmi_pen_start_calibration(kal_uint16 num, const mmi_pen_point_struct *points)函数时,就开始进入触摸屏校准状态,一旦调用这个函数,那么收到点将都是没有校准过的,直到校准完成。num表示校准的个数,MTK3个,points分别就是理想点的位置(可能不好理解,就是屏幕上显示需要点击的3个点)

真正执行校准的是在函数 touch_excute_cali里面

void touch_excute_cali(kal_int16 x_adc, kal_int16 y_adc)
{
   ilm_struct *tp_ilm;
   tp_cali_done_struct *local_para;
   kal_bool cali_result;
   module_type owner=0;
   // 校准是否被激活,调用 mmi_pen_start_calibration 后,就为true
   if(tp_cali_mode==KAL_FALSE)
      return;
   // 校准第一个点 
   if(tp_cali_cnt==0)         
   {
      tp_cali_cnt++;
      // 记录获取的adc点(实际物理点坐标)
      cali_point_adc[0].x=x_adc;
      cali_point_adc[0].y=y_adc;      
   }
   // 第二个点         
   elseif(tp_cali_cnt==1)   
   {
      tp_cali_cnt++;
      cali_point_adc[1].x=x_adc;
      cali_point_adc[1].y=y_adc;          
   }
   // 第三个点
   elseif(tp_cali_cnt==2)
   {      
      cali_point_adc[2].x=x_adc;
      cali_point_adc[2].y=y_adc;          
      tp_cali_mode=KAL_FALSE;
      tp_cali_cnt=0;
      // 对校准进行计算
      cali_result=touch_panel_check_cali();
      
      if(cali_result==KAL_TRUE) 
      {
           // 校准ok ,分别计算 x 和 y 的 slope 和 offset
           touch_panel_tuning(cali_point[0].x, cali_point_adc[0].x, 
                         cali_point[1].x, cali_point_adc[1].x, 
                         &TPCali.x_slope, &TPCali.x_offset);
           touch_panel_tuning(cali_point[0].y, cali_point_adc[0].y, 
                         cali_point[1].y, cali_point_adc[1].y, 
                         &TPCali.y_slope, &TPCali.y_offset);      
      }   
      // 发送消息告诉MMI , 校准的结果.
      // 如果失败,MMI 需要重新校准
      local_para = (tp_cali_done_struct *) construct_local_para(sizeof(tp_cali_done_struct),TD_UL);
      local_para->result=cali_result;
      owner=MOD_MMI;
      DRV_BuildPrimitive(tp_ilm,
                    MOD_TP_TASK,
                         owner,
                    MSG_ID_TP_CALI_DONE,
                    local_para);   
      msg_send_ext_queue(tp_ilm);                                
   }               

其实校准是否正确还是主要看 touch_panel_check_cali这个函数的结果。这个函数主要里有  3个校准测试,只有都符合条件才符合。

stage 1比例对比,也就是保证测试点1倾斜一定的角度。不会与x轴或者y轴平行

stage 2保证第1 2 3点的两端

stage 3根据stage 1 stage 2得出的slope offset比较计算获得的第3希望获得第3误差小于设定值。

这几个函数就是几个数学公式,自己研究研究Ok了。

 

原创粉丝点击