MTK机制,适合新学者

MTK MMI event 小结 1
在MTK MMI 里面有各种event,最常见的有跟交互相关的 按键event,触摸屏event。跟各种具体事件比如电话event,短信event,电量event,信号量event,timer event等等。

MTK 都有相应的处理方式,跟交互相关的 按键(KeyBrd.c)和触摸屏(TouchScreen.c) 各有一套自己的机子,timer event 已经在 MTK timer 小结 3 介绍过,还有一套机制就是处理各种其他事件,它主要分为普通事件和interrupt event,interrupt event 主要用于需要弹出的对话框的event。
M T K  MMI event 小结 2
今天主要看分析,常用事件机制。做过MTK开发的童鞋应该知道要接受 MTK L4 层的消息,用一个注册函数 SetProtocolEventHandler,注册一个event 的处理函数。比如短信,电话,电池电量,信号量等等都是通过这个函数来注册消息。当L4 层处理完封装事件后,就会把这个event 发送到MMI task 里来(消息发送可以看 MTK task 小结 5 ),然后MMI task 通过这个event 机制,找到相应的处理函数,进行处理。实现这种机制,也是考虑到零活性和扩张性。如果都在MMI task 里面,用 switch case 来处理,那就很疯狂了,长度不说,写个应用,定义个消息,都要去改MMI task,所以这个机制实现虽然比较简单,但是还是很有必要的。

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

在MMI task 最后,会调用函数 ProtocolEventHandler,这个函数就是找到 相应event 的 相应 处理函数(平台不一样,可能实际的函数名字有些区别,但是流程基本上一样的,我这里被 #define 到 mmi_frm_execute_current_protocol_handler)
注意:event的处理函数使用SetProtocolEventHandler来注册。
// 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;

idx = mmi_frm_search_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。这样如果需要,可以提前做处理或者不响应该事件。
M T K  MMI event 小结 3
昨天大概的说了下,普通 event 的执行过程。中间还有几个函数没有介绍,这里简单介绍一下
// 这个函数就是简单把 L4c 的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;
                }
                else if (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 的个数。这个样可以减少 循环查询的次数。
MTK MMI event 小结 4
今天打算说说key event。MTK的key envent 管理其实跟 MTK (MMI event小结2) 说的protocol event 机制也差不了多少,简单来说,就是app 通过注册某一个key 的回调函数,当这个key事件产生是,该机制就去找到相应的回调函数,进行回调。
先说说key 的 event 有哪些:
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_DOWN和KEY_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有可能比较慢,可以简单的理解为生产者和消费者的关系,跑题了  )。 取到当前按键后,看当前是否可以进行操作,比如是否有触摸笔按下等等。
MTK MMI event 小结 5
昨天说了一下key event 的基本情况,今天直接从代码开始吧
先看执行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) 这里key 用while (1)来处理,不停的从key 消息buffer里取出按键信息,然后处理,这么做可以防止task中不停的发送消息,可以理解为共享内存。同样当有其他消息来时,需要跳出来处理其他消息,处理完毕后还需要再来处理。
2)这里跟触摸屏的事件进行了冲突处理,也就是按键和触摸屏不能同时工作,这两者也没有优先级。
3)实际真正处理按键 是在 mmi_frm_convert_process_key_event里面,下次在分析
MTK MMI event 小结 6
在 MTK MMI event 小结 5 中,提到了event 处理函数 mmi_frm_key_handle,这个函数主要作用是判断是否需要处理按键,从按键缓存里面持续的读取按键信息,然后调用 mmi_frm_convert_process_key_event 进行处理。这个函数没有什么可说的,最多是在屏幕旋转的情况下,把 导航键 转换一下,接着它调用了 ProcessKeyEvent, 这个函数主要是对于一些状态的处理,防止key down 和up 不成对,出现混乱。
void ProcessKeyEvent(U32 MsgType, U16 DeviceKeyCode)
{
    MMI_BOOL isKeyPaired;
    U16      KeyMapIndex;
    /*----------------------------------------------------------------*/
    /* Code Body                                 */
    /*----------------------------------------------------------------*/
   // 按键影射,把驱动的按键码,转换成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 */
        }
}
    else if (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 */
    else if (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 */
        }
    }
    else if (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 */
        }
    }
    else if (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 */
        }
    }
    else if ((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 函数主要判断是否要真的处理该事件,可以看成是一个按键事件的拦截,比如应用切换过程中,需要一个切换动画,而这个动画工程中,需要处理忽略这些按键。就需要特殊的处理。
static void 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的代码函数名字上虽然看不出什么东西,但是一层一层函数,每一层函数的功能还是比较独立的,所以也不是很难看懂。
MTK MMI event 小结 7
到这,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 来处理。这里这么做的原因也很简单,drv 的task 优先级是很高的,要处理的东西也很多,所以希望回调函数能很快处理完这个事情(发送一个消息很快),这里可以想一下 pc 上的中断,这两者差不多。当然中断还有更多其他原因,比如内核态和用户态等等,需要回调函数尽可能快处理,发送消息到消息队列(扯远了。。。)。还有一点需要说明的是,现在触摸屏的手机越来越普遍,那么该善触摸屏的体验是非常重要的,可惜的是MTK 的又不是多点触摸,处理起来很费劲。这个时候就需要充分利用好这里的几个设置,采样时间间隔,以及move 事件距离。需要时,让自己app可以捕获尽可能多的点,然后自己进行计算,来判断用户行为。这对改善体验很有帮助。当然这个也是比较耗电的,不需要时,一定要设置回普通状态。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值