鼠標鍵盤xml動作文件的加載過程

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章