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可以捕获尽可能多的点,然后自己进行计算,来判断用户行为。这对改善体验很有帮助。当然这个也是比较耗电的,不需要时,一定要设置回普通状态。