2020-09-21 17:17:34 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2020-06-23 18:07:00 +00:00
|
|
|
//
|
2020-09-21 17:17:34 +00:00
|
|
|
// EmulationStation Desktop Edition
|
2020-06-23 18:07:00 +00:00
|
|
|
// InputConfig.cpp
|
|
|
|
//
|
|
|
|
// Input device configuration functions.
|
|
|
|
//
|
|
|
|
|
2013-04-08 14:27:38 +00:00
|
|
|
#include "InputConfig.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
|
2013-06-14 12:34:12 +00:00
|
|
|
#include "Log.h"
|
2020-06-26 16:03:55 +00:00
|
|
|
|
2020-06-24 15:38:41 +00:00
|
|
|
#include <pugixml.hpp>
|
2013-04-08 14:27:38 +00:00
|
|
|
|
2020-06-23 18:07:00 +00:00
|
|
|
// Some utility functions.
|
2013-04-11 22:27:27 +00:00
|
|
|
std::string inputTypeToString(InputType type)
|
|
|
|
{
|
2020-06-23 18:07:00 +00:00
|
|
|
switch (type) {
|
|
|
|
case TYPE_AXIS:
|
|
|
|
return "axis";
|
|
|
|
case TYPE_BUTTON:
|
|
|
|
return "button";
|
|
|
|
case TYPE_HAT:
|
|
|
|
return "hat";
|
|
|
|
case TYPE_KEY:
|
|
|
|
return "key";
|
|
|
|
case TYPE_CEC_BUTTON:
|
|
|
|
return "cec-button";
|
|
|
|
default:
|
|
|
|
return "error";
|
|
|
|
}
|
2013-04-11 22:27:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
InputType stringToInputType(const std::string& type)
|
|
|
|
{
|
2020-06-23 18:07:00 +00:00
|
|
|
if (type == "axis")
|
|
|
|
return TYPE_AXIS;
|
|
|
|
if (type == "button")
|
|
|
|
return TYPE_BUTTON;
|
|
|
|
if (type == "hat")
|
|
|
|
return TYPE_HAT;
|
|
|
|
if (type == "key")
|
|
|
|
return TYPE_KEY;
|
|
|
|
if (type == "cec-button")
|
|
|
|
return TYPE_CEC_BUTTON;
|
|
|
|
return TYPE_COUNT;
|
2013-04-11 22:27:27 +00:00
|
|
|
}
|
|
|
|
|
2013-04-08 14:27:38 +00:00
|
|
|
std::string toLower(std::string str)
|
|
|
|
{
|
2020-06-23 18:07:00 +00:00
|
|
|
for (unsigned int i = 0; i < str.length(); i++)
|
|
|
|
str[i] = (char)tolower(str[i]);
|
2013-04-08 14:27:38 +00:00
|
|
|
|
2020-06-23 18:07:00 +00:00
|
|
|
return str;
|
2013-04-08 14:27:38 +00:00
|
|
|
}
|
2020-06-23 18:07:00 +00:00
|
|
|
// End of utility functions.
|
|
|
|
|
|
|
|
InputConfig::InputConfig(
|
|
|
|
int deviceId,
|
|
|
|
const std::string& deviceName,
|
|
|
|
const std::string& deviceGUID)
|
|
|
|
: mDeviceId(deviceId),
|
|
|
|
mDeviceName(deviceName),
|
2020-07-14 17:16:21 +00:00
|
|
|
mDeviceGUID(deviceGUID),
|
|
|
|
mDefaultConfigFlag(false)
|
2013-04-08 14:27:38 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void InputConfig::clear()
|
|
|
|
{
|
2020-06-23 18:07:00 +00:00
|
|
|
mNameMap.clear();
|
2013-04-08 14:27:38 +00:00
|
|
|
}
|
|
|
|
|
2014-03-22 01:12:57 +00:00
|
|
|
bool InputConfig::isConfigured()
|
|
|
|
{
|
2020-06-23 18:07:00 +00:00
|
|
|
return mNameMap.size() > 0;
|
2014-03-22 01:12:57 +00:00
|
|
|
}
|
|
|
|
|
2013-04-08 14:27:38 +00:00
|
|
|
void InputConfig::mapInput(const std::string& name, Input input)
|
|
|
|
{
|
2020-06-23 18:07:00 +00:00
|
|
|
mNameMap[toLower(name)] = input;
|
2013-04-08 14:27:38 +00:00
|
|
|
}
|
|
|
|
|
2014-04-13 02:09:54 +00:00
|
|
|
void InputConfig::unmapInput(const std::string& name)
|
|
|
|
{
|
2020-06-23 18:07:00 +00:00
|
|
|
auto it = mNameMap.find(toLower(name));
|
|
|
|
if (it != mNameMap.cend())
|
|
|
|
mNameMap.erase(it);
|
2014-04-13 02:09:54 +00:00
|
|
|
}
|
|
|
|
|
2014-05-31 23:00:42 +00:00
|
|
|
bool InputConfig::getInputByName(const std::string& name, Input* result)
|
2013-04-08 14:27:38 +00:00
|
|
|
{
|
2020-06-23 18:07:00 +00:00
|
|
|
auto it = mNameMap.find(toLower(name));
|
|
|
|
if (it != mNameMap.cend()) {
|
|
|
|
*result = it->second;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2013-04-08 14:27:38 +00:00
|
|
|
}
|
|
|
|
|
2020-07-24 16:24:04 +00:00
|
|
|
int InputConfig::getInputIDByName(const std::string& name)
|
|
|
|
{
|
|
|
|
auto it = mNameMap.find(toLower(name));
|
|
|
|
if (it != mNameMap.cend()) {
|
|
|
|
return it->second.id;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-04-08 14:27:38 +00:00
|
|
|
bool InputConfig::isMappedTo(const std::string& name, Input input)
|
|
|
|
{
|
2020-06-23 18:07:00 +00:00
|
|
|
Input comp;
|
|
|
|
if (!getInputByName(name, &comp))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (comp.configured && comp.type == input.type && comp.id == input.id) {
|
|
|
|
if (comp.type == TYPE_HAT)
|
|
|
|
return (input.value == 0 || input.value & comp.value);
|
|
|
|
|
|
|
|
if (comp.type == TYPE_AXIS)
|
|
|
|
return input.value == 0 || comp.value == input.value;
|
|
|
|
else
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2013-04-08 14:27:38 +00:00
|
|
|
}
|
|
|
|
|
2018-11-15 16:38:20 +00:00
|
|
|
bool InputConfig::isMappedLike(const std::string& name, Input input)
|
|
|
|
{
|
2020-06-23 18:07:00 +00:00
|
|
|
if (name == "left") {
|
|
|
|
return isMappedTo("left", input) || isMappedTo("leftanalogleft", input) ||
|
|
|
|
isMappedTo("rightanalogleft", input);
|
|
|
|
}
|
|
|
|
else if (name == "right") {
|
|
|
|
return isMappedTo("right", input) || isMappedTo("leftanalogright", input) ||
|
|
|
|
isMappedTo("rightanalogright", input);
|
|
|
|
}
|
|
|
|
else if (name == "up") {
|
|
|
|
return isMappedTo("up", input) || isMappedTo("leftanalogup", input) ||
|
|
|
|
isMappedTo("rightanalogup", input);
|
|
|
|
}
|
|
|
|
else if (name == "down") {
|
|
|
|
return isMappedTo("down", input) || isMappedTo("leftanalogdown", input) ||
|
|
|
|
isMappedTo("rightanalogdown", input);
|
|
|
|
}
|
|
|
|
else if (name == "leftshoulder") {
|
|
|
|
return isMappedTo("leftshoulder", input) || isMappedTo("pageup", input);
|
|
|
|
}
|
|
|
|
else if (name == "rightshoulder") {
|
|
|
|
return isMappedTo("rightshoulder", input) || isMappedTo("pagedown", input);
|
|
|
|
}
|
|
|
|
else if (name == "lefttrigger") {
|
|
|
|
return isMappedTo("lefttrigger", input) || isMappedTo("home", input);
|
|
|
|
}
|
|
|
|
else if (name == "righttrigger") {
|
|
|
|
return isMappedTo("righttrigger", input) || isMappedTo("end", input);
|
|
|
|
}
|
|
|
|
return isMappedTo(name, input);
|
2018-11-15 16:38:20 +00:00
|
|
|
}
|
|
|
|
|
2013-04-08 14:27:38 +00:00
|
|
|
std::vector<std::string> InputConfig::getMappedTo(Input input)
|
|
|
|
{
|
2020-06-23 18:07:00 +00:00
|
|
|
std::vector<std::string> maps;
|
|
|
|
|
|
|
|
typedef std::map<std::string, Input>::const_iterator it_type;
|
|
|
|
for (it_type iterator = mNameMap.cbegin(); iterator != mNameMap.cend(); iterator++) {
|
|
|
|
Input chk = iterator->second;
|
|
|
|
|
|
|
|
if (!chk.configured)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (chk.device == input.device && chk.type == input.type && chk.id == input.id) {
|
|
|
|
if (chk.type == TYPE_HAT) {
|
|
|
|
if (input.value == 0 || input.value & chk.value)
|
|
|
|
maps.push_back(iterator->first);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (input.type == TYPE_AXIS) {
|
|
|
|
if (input.value == 0 || chk.value == input.value)
|
|
|
|
maps.push_back(iterator->first);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
maps.push_back(iterator->first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return maps;
|
2013-04-08 14:27:38 +00:00
|
|
|
}
|
|
|
|
|
2017-11-10 19:16:42 +00:00
|
|
|
void InputConfig::loadFromXML(pugi::xml_node& node)
|
2013-04-11 22:27:27 +00:00
|
|
|
{
|
2020-06-23 18:07:00 +00:00
|
|
|
clear();
|
2013-04-11 22:27:27 +00:00
|
|
|
|
2020-06-23 18:07:00 +00:00
|
|
|
for (pugi::xml_node input = node.child("input"); input; input = input.next_sibling("input")) {
|
|
|
|
std::string name = input.attribute("name").as_string();
|
|
|
|
std::string type = input.attribute("type").as_string();
|
|
|
|
InputType typeEnum = stringToInputType(type);
|
2013-04-11 22:27:27 +00:00
|
|
|
|
2020-06-23 18:07:00 +00:00
|
|
|
if (typeEnum == TYPE_COUNT) {
|
2020-07-26 21:30:45 +00:00
|
|
|
LOG(LogError) << "InputConfig load error - input of type \"" << type <<
|
2020-06-23 18:07:00 +00:00
|
|
|
"\" is invalid! Skipping input \"" << name << "\".\n";
|
|
|
|
continue;
|
|
|
|
}
|
2013-04-11 22:27:27 +00:00
|
|
|
|
2020-06-23 18:07:00 +00:00
|
|
|
int id = input.attribute("id").as_int();
|
|
|
|
int value = input.attribute("value").as_int();
|
2013-04-11 22:27:27 +00:00
|
|
|
|
2020-06-25 17:52:38 +00:00
|
|
|
if (value == 0) {
|
2020-07-26 21:30:45 +00:00
|
|
|
LOG(LogWarning) << "InputConfig value is 0 for " <<
|
2020-06-23 18:07:00 +00:00
|
|
|
type << " " << id << "!\n";
|
2020-06-25 17:52:38 +00:00
|
|
|
}
|
2013-04-11 22:27:27 +00:00
|
|
|
|
2020-06-23 18:07:00 +00:00
|
|
|
mNameMap[toLower(name)] = Input(mDeviceId, typeEnum, id, value, true);
|
|
|
|
}
|
2013-04-11 22:27:27 +00:00
|
|
|
}
|
|
|
|
|
2017-11-10 19:16:42 +00:00
|
|
|
void InputConfig::writeToXML(pugi::xml_node& parent)
|
2013-04-11 22:27:27 +00:00
|
|
|
{
|
2020-06-23 18:07:00 +00:00
|
|
|
pugi::xml_node cfg = parent.append_child("inputConfig");
|
|
|
|
|
|
|
|
if (mDeviceId == DEVICE_KEYBOARD) {
|
|
|
|
cfg.append_attribute("type") = "keyboard";
|
|
|
|
cfg.append_attribute("deviceName") = "Keyboard";
|
|
|
|
}
|
|
|
|
else if (mDeviceId == DEVICE_CEC) {
|
|
|
|
cfg.append_attribute("type") = "cec";
|
|
|
|
cfg.append_attribute("deviceName") = "CEC";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
cfg.append_attribute("type") = "joystick";
|
|
|
|
cfg.append_attribute("deviceName") = mDeviceName.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg.append_attribute("deviceGUID") = mDeviceGUID.c_str();
|
|
|
|
|
|
|
|
typedef std::map<std::string, Input>::const_iterator it_type;
|
|
|
|
for (it_type iterator = mNameMap.cbegin(); iterator != mNameMap.cend(); iterator++) {
|
|
|
|
if (!iterator->second.configured)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
pugi::xml_node input = cfg.append_child("input");
|
|
|
|
input.append_attribute("name") = iterator->first.c_str();
|
|
|
|
input.append_attribute("type") = inputTypeToString(iterator->second.type).c_str();
|
|
|
|
input.append_attribute("id").set_value(iterator->second.id);
|
|
|
|
input.append_attribute("value").set_value(iterator->second.value);
|
|
|
|
}
|
2013-04-11 22:27:27 +00:00
|
|
|
}
|