在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文件。