在xbmc初次启动中我们说到CApplication::CreateGUI()中的加载按键定义文件,这里我们拿keyboard.xml举例
bool CButtonTranslator::Load(bool AlwaysLoad)
{
m_translatorMap.clear();
// Directories to search for keymaps. They're applied in this order,
// so keymaps in profile/keymaps/ override e.g. system/keymaps
static const char* DIRS_TO_CHECK[] = {
"special://xbmc/system/keymaps/","special://masterprofile/keymaps/","special://profile/keymaps/"
};
for (unsigned int dirIndex = 0; dirIndex < ARRAY_SIZE(DIRS_TO_CHECK); ++dirIndex)
{
if (XFILE::CDirectory::Exists(DIRS_TO_CHECK[dirIndex]))
{
CFileItemList files;
XFILE::CDirectory::GetDirectory(DIRS_TO_CHECK[dirIndex],files,".xml");
// Sort the list for filesystem based priorities,e.g. 01-keymap.xml,02-keymap-overrides.xml
files.Sort(SortByFile,SortOrderAscending);
for(int fileIndex = 0; fileIndex<files.Size(); ++fileIndex)
{
if (!files[fileIndex]->m_bIsFolder)
success |= LoadKeymap(files[fileIndex]->GetPath());
}
// Load mappings for any HID devices we have connected
std::list<std::string>::iterator it;
for (it = m_deviceList.begin(); it != m_deviceList.end(); it++)
{
std::string devicedir = DIRS_TO_CHECK[dirIndex];
devicedir.append(*it);
devicedir.append("/");
if( XFILE::CDirectory::Exists(devicedir) )
{
CFileItemList files;
XFILE::CDirectory::GetDirectory(devicedir,".xml");
// Sort the list for filesystem based priorities,02-keymap-overrides.xml
files.Sort(SortByFile,SortOrderAscending);
for(int fileIndex = 0; fileIndex<files.Size(); ++fileIndex)
{
if (!files[fileIndex]->m_bIsFolder)
success |= LoadKeymap(files[fileIndex]->GetPath());
}
}
}
}
}
}
接下来我们分析具体的加载keyboard.xml的加载,我们启动xbmc中会有这样的启动语句Debug Print: Loading special://xbmc/system/keymaps/keyboard.xml,就是在下面这个函数中输出的。
bool CButtonTranslator::LoadKeymap(const std::string &keymapPath)
{
CXBMCTinyXML xmlDoc;
CLog::Log(LOGINFO,"Loading %s",keymapPath.c_str());
...
TiXmlElement* pRoot = xmlDoc.RootElement();
std::string strValue = pRoot->Value(); //这个是xml头节点,应该为keymap
...
// run through our window groups
TiXmlNode* pWindow = pRoot->FirstChild();
while (pWindow)
{
if (pWindow->Type() == TiXmlNode::TINYXML_ELEMENT)
{
int windowID = WINDOW_INVALID;
const char *szWindow = pWindow->Value();
if (szWindow)
{
if (strcmpi(szWindow,"global") == 0) //如果节点名为global,设置windowID即窗口值为-1
windowID = -1;
else
windowID = TranslateWindow(szWindow);
}
MapWindowActions(pWindow,windowID);
}
pWindow = pWindow->NextSibling(); //再读取下一个节点
}
return true;
}
接下来我们看一下MapWindowActions的读取
void CButtonTranslator::MapWindowActions(TiXmlNode *pWindow,int windowID)
{
TiXmlNode* pDevice;
const char* types[] = {"gamepad","remote","universalremote","keyboard","mouse","appcommand",NULL};
for (int i = 0; types[i]; ++i) //我们专门分析keyboard,即types[3]值为情况
{
std::string type(types[i]);
if (HasDeviceType(pWindow,type))
{
buttonMap map;
std::map<int,buttonMap>::iterator it = m_translatorMap.find(windowID); //看translatorMap是否存在windowID的键值定义
if (it != m_translatorMap.end()) //如果存在那么删除它
{
map = it->second;
m_translatorMap.erase(it);
}
pDevice = pWindow->FirstChild(type);//从pWindow中查找keyboard关键词来作为设备标示
TiXmlElement *pButton = pDevice->FirstChildElement();
while (pButton)
{
uint32_t buttonCode=0;
if (type == "gamepad")
buttonCode = TranslateGamepadString(pButton->Value());
...
else if (type == "keyboard")
buttonCode = TranslateKeyboardButton(pButton);
...
else if (type == "appcommand")
buttonCode = TranslateAppCommand(pButton->Value());
if (buttonCode && pButton->FirstChild())
MapAction(buttonCode,pButton->FirstChild()->Value(),map);
pButton = pButton->NextSiblingElement();
}
// add our map to our table
if (!map.empty())
m_translatorMap.insert(pair<int,buttonMap>( windowID,map));
}
}
...
}
我们分析keyboard.xml中的一条VolumeUp
uint32_t CButtonTranslator::TranslateKeyboardButton(TiXmlElement *pButton)
{
uint32_t button_id = 0;
const char *szButton = pButton->Value(); //szButton值为plus
if (!szButton)
return 0;
const std::string strKey = szButton;
if (strKey == "key")
{
...
}
else
button_id = TranslateKeyboardString(szButton);
....
return button_id;
}
到这里szButton是plus
uint32_t CButtonTranslator::TranslateKeyboardString(const char *szButton)
{
uint32_t buttonCode = 0;
XBMCKEYTABLE keytable;
// Look up the key name
if (KeyTableLookupName(szButton,&keytable))
{
buttonCode = keytable.vkey;
}
buttonCode |= KEY_VKEY;
return buttonCode;
}
接下来就是XBMCKeyTable中查找对应keyname匹配
//xbmc/input/XBMC_keytable.cpp
bool KeyTableLookupName(const char* keyname,XBMCKEYTABLE* keytable)
{
...
// We need the button name to be in lowercase变为小写字母
std::string lkeyname = keyname;
StringUtils::ToLower(lkeyname);
// Look up the key name in XBMCKeyTable
for (int i = 0; i < XBMCKeyTableSize; i++)
{ if (XBMCKeyTable[i].keyname)
{ if (strcmp(lkeyname.c_str(),XBMCKeyTable[i].keyname) == 0)
{ *keytable = XBMCKeyTable[i];
return true;
}
}
}
return false;
}
static const XBMCKEYTABLE XBMCKeyTable[] ={
....,{ XBMCK_PLUS,'+',XBMCVK_PLUS,"plus" }
}
XBMCKEYTABLE结构体中第三个值是vkey,这样XBMCVK_VOLUME_UP的值0x2B与KEY_VKEY值0xF000或运算结果0xf02B,
我们回到MapWindowActions方法中的语句MapAction(buttonCode,map);
0xf02B成了buttonCode,这里的pButton->FirstChild()->Value()应为VolumeUp
void CButtonTranslator::MapAction(uint32_t buttonCode,const char *szAction,buttonMap &map)
{
int action = ACTION_NONE;
if (!TranslateActionString(szAction,action) || !buttonCode)
return; // no valid action,or an invalid buttoncode
buttonMap::iterator it = map.find(buttonCode);
if (it == map.end() || (*it).second.id != action || (*it).second.strID != szAction)
{
if (it != map.end())
map.erase(it);
CButtonAction button;
button.id = action;
button.strID = szAction;
map.insert(pair<uint32_t,CButtonAction>(buttonCode,button));
}
}
bool CButtonTranslator::TranslateActionString(const char *szAction,int &action)
{
action = ACTION_NONE;
std::string strAction = szAction;
StringUtils::ToLower(strAction);
if (CBuiltins::HasCommand(strAction))
action = ACTION_BUILT_IN_FUNCTION;
for (unsigned int index=0;index < ARRAY_SIZE(actions);++index)
{
if (strAction == actions[index].name)
{
action = actions[index].action;
break;
}
}
...
return true;
}
上面CBuiltins::HasCommand(strAction)用于判断strAction是否为命令,VolumeUp这里不是。
remote.xml中XBMC.ActivateWindow(MyMusic)就是,那action就是ACTION_BUILT_IN_FUNCTION。
我们分析遥控器音量加找到对应的action定义{“volumeup”,ACTION_VOLUME_UP}
button.name为volumeup,button.action为ACTION_VOLUME_UP
至此就读取了一个xml动作,buttonCode为0xf02B,button.id为ACTION_VOLUME_UP,button.strID为plus
map.insert(pair<uint32_t,button));
以此类推读取整个keyboard.xml,乃至其他的输入设备控制的xml文件。