在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, 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());
}
}
}
}
}
}
接下来我们分析具体的加载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, pButton->FirstChild()->Value(), 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, CButtonAction>(buttonCode, button));
以此类推读取整个keyboard.xml,乃至其他的输入设备控制的xml文件。