summaryrefslogtreecommitdiffstats
path: root/xbmc/input/JoystickMapper.cpp
blob: 233238dbd3de3a142304f6688d5f8d921c788ec2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/*
 *  Copyright (C) 2017-2018 Team Kodi
 *  This file is part of Kodi - https://kodi.tv
 *
 *  SPDX-License-Identifier: GPL-2.0-or-later
 *  See LICENSES/README.md for more information.
 */

#include "JoystickMapper.h"

#include "input/WindowKeymap.h"
#include "input/actions/ActionIDs.h"
#include "input/actions/ActionTranslator.h"
#include "input/joysticks/JoystickTranslator.h"
#include "input/joysticks/JoystickUtils.h"
#include "utils/StringUtils.h"
#include "utils/XBMCTinyXML.h"

#include <algorithm>
#include <sstream>
#include <utility>

using namespace KODI;

#define JOYSTICK_XML_NODE_PROFILE "profile"
#define JOYSTICK_XML_ATTR_DIRECTION "direction"
#define JOYSTICK_XML_ATTR_HOLDTIME "holdtime"
#define JOYSTICK_XML_ATTR_HOTKEY "hotkey"

void CJoystickMapper::MapActions(int windowID, const TiXmlNode* pDevice)
{
  std::string controllerId;
  DeserializeJoystickNode(pDevice, controllerId);
  if (controllerId.empty())
    return;

  // Update Controller IDs
  if (std::find(m_controllerIds.begin(), m_controllerIds.end(), controllerId) ==
      m_controllerIds.end())
    m_controllerIds.emplace_back(controllerId);

  // Create/overwrite keymap
  auto& keymap = m_joystickKeymaps[controllerId];
  if (!keymap)
    keymap.reset(new CWindowKeymap(controllerId));

  const TiXmlElement* pButton = pDevice->FirstChildElement();
  while (pButton != nullptr)
  {
    std::string feature;
    JOYSTICK::ANALOG_STICK_DIRECTION dir;
    unsigned int holdtimeMs;
    std::set<std::string> hotkeys;
    std::string actionString;
    if (DeserializeButton(pButton, feature, dir, holdtimeMs, hotkeys, actionString))
    {
      // Update keymap
      unsigned int actionId = ACTION_NONE;
      if (CActionTranslator::TranslateString(actionString, actionId))
      {
        JOYSTICK::KeymapAction action = {
            actionId,
            std::move(actionString),
            holdtimeMs,
            std::move(hotkeys),
        };
        keymap->MapAction(windowID, JOYSTICK::CJoystickUtils::MakeKeyName(feature, dir),
                          std::move(action));
      }
    }
    pButton = pButton->NextSiblingElement();
  }
}

void CJoystickMapper::Clear()
{
  m_joystickKeymaps.clear();
  m_controllerIds.clear();
}

std::vector<std::shared_ptr<const IWindowKeymap>> CJoystickMapper::GetJoystickKeymaps() const
{
  std::vector<std::shared_ptr<const IWindowKeymap>> keymaps;

  for (const auto& controllerId : m_controllerIds)
  {
    auto it = m_joystickKeymaps.find(controllerId);
    if (it != m_joystickKeymaps.end())
      keymaps.emplace_back(it->second);
  }

  return keymaps;
}

void CJoystickMapper::DeserializeJoystickNode(const TiXmlNode* pDevice, std::string& controllerId)
{
  const TiXmlElement* deviceElem = pDevice->ToElement();
  if (deviceElem != nullptr)
    deviceElem->QueryValueAttribute(JOYSTICK_XML_NODE_PROFILE, &controllerId);
}

bool CJoystickMapper::DeserializeButton(const TiXmlElement* pButton,
                                        std::string& feature,
                                        JOYSTICK::ANALOG_STICK_DIRECTION& dir,
                                        unsigned int& holdtimeMs,
                                        std::set<std::string>& hotkeys,
                                        std::string& actionStr)
{
  const char* szButton = pButton->Value();
  if (szButton != nullptr)
  {
    const char* szAction = nullptr;

    const TiXmlNode* actionNode = pButton->FirstChild();
    if (actionNode != nullptr)
      szAction = actionNode->Value();

    if (szAction != nullptr)
    {
      feature = szButton;
      StringUtils::ToLower(feature);
      actionStr = szAction;
    }
  }

  if (!feature.empty() && !actionStr.empty())
  {
    // Handle direction
    dir = JOYSTICK::ANALOG_STICK_DIRECTION::NONE;
    const char* szDirection = pButton->Attribute(JOYSTICK_XML_ATTR_DIRECTION);
    if (szDirection != nullptr)
      dir = JOYSTICK::CJoystickTranslator::TranslateAnalogStickDirection(szDirection);

    // Process holdtime parameter
    holdtimeMs = 0;
    std::string strHoldTime;
    if (pButton->QueryValueAttribute(JOYSTICK_XML_ATTR_HOLDTIME, &strHoldTime) == TIXML_SUCCESS)
    {
      std::istringstream ss(strHoldTime);
      ss >> holdtimeMs;
    }

    // Process hotkeys
    hotkeys.clear();
    std::string strHotkeys;
    if (pButton->QueryValueAttribute(JOYSTICK_XML_ATTR_HOTKEY, &strHotkeys) == TIXML_SUCCESS)
    {
      std::vector<std::string> vecHotkeys = StringUtils::Split(strHotkeys, ",");
      for (auto& hotkey : vecHotkeys)
        hotkeys.insert(std::move(hotkey));
    }

    return true;
  }

  return false;
}