mirror of
https://github.com/vcmi/vcmi.git
synced 2025-07-15 01:24:45 +02:00
Unified game controller input with keyboard/mouse
This commit is contained in:
@ -35,7 +35,6 @@ set(client_SRCS
|
|||||||
eventsSDL/InputSourceText.cpp
|
eventsSDL/InputSourceText.cpp
|
||||||
eventsSDL/InputSourceTouch.cpp
|
eventsSDL/InputSourceTouch.cpp
|
||||||
eventsSDL/InputSourceGameController.cpp
|
eventsSDL/InputSourceGameController.cpp
|
||||||
eventsSDL/GameControllerConfig.cpp
|
|
||||||
|
|
||||||
gui/CGuiHandler.cpp
|
gui/CGuiHandler.cpp
|
||||||
gui/CIntObject.cpp
|
gui/CIntObject.cpp
|
||||||
@ -214,8 +213,7 @@ set(client_HEADERS
|
|||||||
eventsSDL/InputSourceMouse.h
|
eventsSDL/InputSourceMouse.h
|
||||||
eventsSDL/InputSourceText.h
|
eventsSDL/InputSourceText.h
|
||||||
eventsSDL/InputSourceTouch.h
|
eventsSDL/InputSourceTouch.h
|
||||||
eventsSDL/InputSourceGameController.h
|
eventsSDL/InputSourceGameController.h
|
||||||
eventsSDL/GameControllerConfig.h
|
|
||||||
|
|
||||||
gui/CGuiHandler.h
|
gui/CGuiHandler.h
|
||||||
gui/CIntObject.h
|
gui/CIntObject.h
|
||||||
|
@ -1,212 +0,0 @@
|
|||||||
/*
|
|
||||||
* GameControllerConfig.cpp, part of VCMI engine
|
|
||||||
*
|
|
||||||
* Authors: listed in file AUTHORS in main folder
|
|
||||||
*
|
|
||||||
* License: GNU General Public License v2.0 or later
|
|
||||||
* Full text of license available in license.txt file, in main folder
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#include <SDL.h>
|
|
||||||
|
|
||||||
#include "StdInc.h"
|
|
||||||
#include "GameControllerConfig.h"
|
|
||||||
#include "../gui/CGuiHandler.h"
|
|
||||||
#include "../gui/ShortcutHandler.h"
|
|
||||||
|
|
||||||
|
|
||||||
GameControllerConfig::GameControllerConfig(): leftAxisType(AxisType::NONE), rightAxisType(AxisType::NONE)
|
|
||||||
{
|
|
||||||
load();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GameControllerConfig::load()
|
|
||||||
{
|
|
||||||
const JsonNode config = JsonUtils::assembleFromFiles("config/shortcutsConfig");
|
|
||||||
for(auto const & entry : config["joystick"].Struct())
|
|
||||||
{
|
|
||||||
std::string configName = entry.first;
|
|
||||||
if(configName == "leftaxis")
|
|
||||||
leftAxisType = parseAxis(entry.first, entry.second);
|
|
||||||
else if (configName == "rightaxis")
|
|
||||||
rightAxisType = parseAxis(entry.first, entry.second);
|
|
||||||
else if (configName == "lefttrigger" || configName == "righttrigger")
|
|
||||||
parseTrigger(entry.first, entry.second);
|
|
||||||
else
|
|
||||||
parseButton(entry.first, entry.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AxisType GameControllerConfig::parseAxis(const std::string & key, const JsonNode & value)
|
|
||||||
{
|
|
||||||
if(!value.isString())
|
|
||||||
{
|
|
||||||
logGlobal->error("The value of joystick config key %s should be a string!", key);
|
|
||||||
return AxisType::NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string featureName = value.String();
|
|
||||||
if(featureName == "cursorMotion")
|
|
||||||
return AxisType::CURSOR_MOTION;
|
|
||||||
else if(featureName == "mapScroll")
|
|
||||||
return AxisType::MAP_SCROLL;
|
|
||||||
else if(featureName != "")
|
|
||||||
logGlobal->error("Unknown value %s of joystick config key %s!", featureName, key);
|
|
||||||
return AxisType::NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GameControllerConfig::parseTrigger(const std::string & key, const JsonNode & value)
|
|
||||||
{
|
|
||||||
std::vector<std::string> operations = getOperations(key, value);
|
|
||||||
SDL_GameControllerAxis triggerAxis = key == "lefttrigger" ?
|
|
||||||
SDL_CONTROLLER_AXIS_TRIGGERLEFT : SDL_CONTROLLER_AXIS_TRIGGERRIGHT;
|
|
||||||
std::vector<EShortcut> shortcuts;
|
|
||||||
for(const auto & operation : operations)
|
|
||||||
{
|
|
||||||
if(operation == "mouseLeftClick")
|
|
||||||
{
|
|
||||||
leftClickTriggerSet.insert(triggerAxis);
|
|
||||||
}
|
|
||||||
else if(operation == "mouseRightClick")
|
|
||||||
{
|
|
||||||
rightClickTriggerSet.insert(triggerAxis);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EShortcut shortcut = GH.shortcuts().findShortcut(operation);
|
|
||||||
if(shortcut == EShortcut::NONE)
|
|
||||||
logGlobal->error("Shortcut %s in joystick config key %s is invalid.", operation, key);
|
|
||||||
else
|
|
||||||
shortcuts.push_back(shortcut);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!shortcuts.empty())
|
|
||||||
triggerShortcutsMap.emplace(triggerAxis, std::move(shortcuts));
|
|
||||||
}
|
|
||||||
|
|
||||||
void GameControllerConfig::parseButton(const std::string & key, const JsonNode & value)
|
|
||||||
{
|
|
||||||
std::vector<std::string> operations = getOperations(key, value);
|
|
||||||
SDL_GameControllerButton button = SDL_GameControllerGetButtonFromString(key.c_str());
|
|
||||||
if(button == SDL_CONTROLLER_BUTTON_INVALID)
|
|
||||||
{
|
|
||||||
logGlobal->error("Joystick config key %s is invalid.", key);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<EShortcut> shortcuts;
|
|
||||||
for(const auto & operation : operations)
|
|
||||||
{
|
|
||||||
if(operation == "mouseLeftClick")
|
|
||||||
{
|
|
||||||
leftClickButtonSet.insert(button);
|
|
||||||
}
|
|
||||||
else if(operation == "mouseRightClick")
|
|
||||||
{
|
|
||||||
rightClickButtonSet.insert(button);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EShortcut shortcut = GH.shortcuts().findShortcut(operation);
|
|
||||||
if(shortcut == EShortcut::NONE)
|
|
||||||
logGlobal->error("Shortcut %s in joystick config key %s is invalid.", operation, key);
|
|
||||||
else
|
|
||||||
shortcuts.push_back(shortcut);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!shortcuts.empty())
|
|
||||||
buttonShortcutsMap.emplace(button, std::move(shortcuts));
|
|
||||||
}
|
|
||||||
|
|
||||||
const AxisType & GameControllerConfig::getLeftAxisType()
|
|
||||||
{
|
|
||||||
return leftAxisType;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AxisType & GameControllerConfig::getRightAxisType()
|
|
||||||
{
|
|
||||||
return rightAxisType;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> GameControllerConfig::getOperations(const std::string & key, const JsonNode & value)
|
|
||||||
{
|
|
||||||
std::vector<std::string> operations;
|
|
||||||
if(value.isString())
|
|
||||||
{
|
|
||||||
operations.push_back(value.String());
|
|
||||||
}
|
|
||||||
else if(value.isVector())
|
|
||||||
{
|
|
||||||
for(auto const & entryVector : value.Vector())
|
|
||||||
{
|
|
||||||
if(!entryVector.isString())
|
|
||||||
logGlobal->error("The vector of joystick config key %s can not contain non-string element.", key);
|
|
||||||
else
|
|
||||||
operations.push_back(entryVector.String());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logGlobal->error("The value of joystick config key %s should be string or string vector.", key);
|
|
||||||
}
|
|
||||||
return operations;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GameControllerConfig::isLeftClickButton(int buttonValue)
|
|
||||||
{
|
|
||||||
SDL_GameControllerButton button = static_cast<SDL_GameControllerButton>(buttonValue);
|
|
||||||
return leftClickButtonSet.find(button) != leftClickButtonSet.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GameControllerConfig::isRightClickButton(int buttonValue)
|
|
||||||
{
|
|
||||||
SDL_GameControllerButton button = static_cast<SDL_GameControllerButton>(buttonValue);
|
|
||||||
return rightClickButtonSet.find(button) != rightClickButtonSet.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GameControllerConfig::isShortcutsButton(int buttonValue)
|
|
||||||
{
|
|
||||||
SDL_GameControllerButton button = static_cast<SDL_GameControllerButton>(buttonValue);
|
|
||||||
return buttonShortcutsMap.find(button) != buttonShortcutsMap.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<EShortcut> & GameControllerConfig::getButtonShortcuts(int buttonValue)
|
|
||||||
{
|
|
||||||
SDL_GameControllerButton button = static_cast<SDL_GameControllerButton>(buttonValue);
|
|
||||||
auto it = buttonShortcutsMap.find(button);
|
|
||||||
if(it != buttonShortcutsMap.end())
|
|
||||||
return it->second;
|
|
||||||
static std::vector<EShortcut> emptyVec;
|
|
||||||
return emptyVec;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GameControllerConfig::isLeftClickTrigger(int axisValue)
|
|
||||||
{
|
|
||||||
SDL_GameControllerAxis axis = static_cast<SDL_GameControllerAxis>(axisValue);
|
|
||||||
return leftClickTriggerSet.find(axis) != leftClickTriggerSet.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GameControllerConfig::isRightClickTrigger(int axisValue)
|
|
||||||
{
|
|
||||||
SDL_GameControllerAxis axis = static_cast<SDL_GameControllerAxis>(axisValue);
|
|
||||||
return rightClickTriggerSet.find(axis) != rightClickTriggerSet.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GameControllerConfig::isShortcutsTrigger(int axisValue)
|
|
||||||
{
|
|
||||||
SDL_GameControllerAxis axis = static_cast<SDL_GameControllerAxis>(axisValue);
|
|
||||||
return triggerShortcutsMap.find(axis) != triggerShortcutsMap.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<EShortcut> & GameControllerConfig::getTriggerShortcuts(int axisValue)
|
|
||||||
{
|
|
||||||
SDL_GameControllerAxis axis = static_cast<SDL_GameControllerAxis>(axisValue);
|
|
||||||
auto it = triggerShortcutsMap.find(axis);
|
|
||||||
if(it != triggerShortcutsMap.end())
|
|
||||||
return it->second;
|
|
||||||
static std::vector<EShortcut> emptyVec;
|
|
||||||
return emptyVec;
|
|
||||||
}
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
* GameControllerConfig.h, part of VCMI engine
|
|
||||||
*
|
|
||||||
* Authors: listed in file AUTHORS in main folder
|
|
||||||
*
|
|
||||||
* License: GNU General Public License v2.0 or later
|
|
||||||
* Full text of license available in license.txt file, in main folder
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <SDL.h>
|
|
||||||
|
|
||||||
#include "../gui/Shortcut.h"
|
|
||||||
#include "../../lib/json/JsonUtils.h"
|
|
||||||
|
|
||||||
enum AxisType
|
|
||||||
{
|
|
||||||
CURSOR_MOTION,
|
|
||||||
MAP_SCROLL,
|
|
||||||
NONE
|
|
||||||
};
|
|
||||||
|
|
||||||
class GameControllerConfig {
|
|
||||||
using ButtonShortcutsMap = std::map<SDL_GameControllerButton, std::vector<EShortcut> >;
|
|
||||||
using TriggerShortcutsMap = std::map<SDL_GameControllerAxis, std::vector<EShortcut> >;
|
|
||||||
ButtonShortcutsMap buttonShortcutsMap;
|
|
||||||
TriggerShortcutsMap triggerShortcutsMap;
|
|
||||||
std::set<SDL_GameControllerButton> leftClickButtonSet;
|
|
||||||
std::set<SDL_GameControllerButton> rightClickButtonSet;
|
|
||||||
std::set<SDL_GameControllerAxis> leftClickTriggerSet;
|
|
||||||
std::set<SDL_GameControllerAxis> rightClickTriggerSet;
|
|
||||||
AxisType leftAxisType;
|
|
||||||
AxisType rightAxisType;
|
|
||||||
|
|
||||||
void load();
|
|
||||||
std::vector<std::string> getOperations(const std::string & key, const JsonNode & value);
|
|
||||||
AxisType parseAxis(const std::string & key, const JsonNode & value);
|
|
||||||
void parseTrigger(const std::string & key, const JsonNode & value);
|
|
||||||
void parseButton(const std::string & key, const JsonNode & value);
|
|
||||||
|
|
||||||
public:
|
|
||||||
GameControllerConfig();
|
|
||||||
~GameControllerConfig() = default;
|
|
||||||
|
|
||||||
const AxisType & getLeftAxisType();
|
|
||||||
const AxisType & getRightAxisType();
|
|
||||||
|
|
||||||
bool isLeftClickButton(int buttonValue);
|
|
||||||
bool isRightClickButton(int buttonValue);
|
|
||||||
bool isShortcutsButton(int buttonValue);
|
|
||||||
const std::vector<EShortcut> & getButtonShortcuts(int buttonValue);
|
|
||||||
|
|
||||||
bool isLeftClickTrigger(int axisValue);
|
|
||||||
bool isRightClickTrigger(int axisValue);
|
|
||||||
bool isShortcutsTrigger(int axisValue);
|
|
||||||
const std::vector<EShortcut> & getTriggerShortcuts(int axisValue);
|
|
||||||
};
|
|
@ -18,8 +18,6 @@
|
|||||||
#include "../gui/EventDispatcher.h"
|
#include "../gui/EventDispatcher.h"
|
||||||
#include "../gui/ShortcutHandler.h"
|
#include "../gui/ShortcutHandler.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void InputSourceGameController::gameControllerDeleter(SDL_GameController * gameController)
|
void InputSourceGameController::gameControllerDeleter(SDL_GameController * gameController)
|
||||||
{
|
{
|
||||||
if(gameController)
|
if(gameController)
|
||||||
@ -135,76 +133,56 @@ int InputSourceGameController::getRealAxisValue(int value)
|
|||||||
return (value - base) * AXIS_MAX_ZOOM / (AXIS_MAX_ZOOM - AXIS_DEAD_ZOOM);
|
return (value - base) * AXIS_MAX_ZOOM / (AXIS_MAX_ZOOM - AXIS_DEAD_ZOOM);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputSourceGameController::dispatchTriggerShortcuts(const std::vector<EShortcut> & shortcutsVector, int axisValue)
|
void InputSourceGameController::dispatchAxisShortcuts(const std::vector<EShortcut> & shortcutsVector, SDL_GameControllerAxis axisID, int axisValue)
|
||||||
{
|
{
|
||||||
if(axisValue >= TRIGGER_PRESS_THRESHOLD)
|
if(axisValue >= TRIGGER_PRESS_THRESHOLD)
|
||||||
GH.events().dispatchShortcutPressed(shortcutsVector);
|
{
|
||||||
|
if (!pressedAxes.count(axisID))
|
||||||
|
{
|
||||||
|
GH.events().dispatchShortcutPressed(shortcutsVector);
|
||||||
|
pressedAxes.insert(axisID);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
GH.events().dispatchShortcutReleased(shortcutsVector);
|
{
|
||||||
}
|
if (pressedAxes.count(axisID))
|
||||||
|
{
|
||||||
void InputSourceGameController::dispatchTriggerLeftClick(int axisValue)
|
GH.events().dispatchShortcutReleased(shortcutsVector);
|
||||||
{
|
pressedAxes.erase(axisID);
|
||||||
const Point & position = GH.input().getCursorPosition();
|
}
|
||||||
if(axisValue >= TRIGGER_PRESS_THRESHOLD)
|
}
|
||||||
GH.events().dispatchMouseLeftButtonPressed(position, 0);
|
|
||||||
else
|
|
||||||
GH.events().dispatchMouseLeftButtonReleased(position, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void InputSourceGameController::dispatchTriggerRightClick(int axisValue)
|
|
||||||
{
|
|
||||||
const Point & position = GH.input().getCursorPosition();
|
|
||||||
if(axisValue >= TRIGGER_PRESS_THRESHOLD)
|
|
||||||
GH.events().dispatchShowPopup(position, 0);
|
|
||||||
else
|
|
||||||
GH.events().dispatchClosePopup(position);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputSourceGameController::handleEventAxisMotion(const SDL_ControllerAxisEvent & axis)
|
void InputSourceGameController::handleEventAxisMotion(const SDL_ControllerAxisEvent & axis)
|
||||||
{
|
{
|
||||||
tryToConvertCursor();
|
tryToConvertCursor();
|
||||||
if(axis.axis == SDL_CONTROLLER_AXIS_LEFTX)
|
|
||||||
|
SDL_GameControllerAxis axisID = static_cast<SDL_GameControllerAxis>(axis.axis);
|
||||||
|
std::string axisName = SDL_GameControllerGetStringForAxis(axisID);
|
||||||
|
|
||||||
|
auto axisActions = GH.shortcuts().translateJoystickAxis(axisName);
|
||||||
|
auto buttonActions = GH.shortcuts().translateJoystickButton(axisName);
|
||||||
|
|
||||||
|
for (auto const & action : axisActions)
|
||||||
{
|
{
|
||||||
if(config.getLeftAxisType() == AxisType::CURSOR_MOTION)
|
switch (action)
|
||||||
cursorAxisValueX = getRealAxisValue(axis.value);
|
{
|
||||||
else if(config.getLeftAxisType() == AxisType::MAP_SCROLL)
|
case EShortcut::MOUSE_CURSOR_X:
|
||||||
scrollAxisValueX = getRealAxisValue(axis.value);
|
cursorAxisValueX = getRealAxisValue(axis.value);
|
||||||
}
|
break;
|
||||||
else if(axis.axis == SDL_CONTROLLER_AXIS_LEFTY)
|
case EShortcut::MOUSE_CURSOR_Y:
|
||||||
{
|
cursorAxisValueY = getRealAxisValue(axis.value);
|
||||||
if(config.getLeftAxisType() == AxisType::CURSOR_MOTION)
|
break;
|
||||||
cursorAxisValueY = getRealAxisValue(axis.value);
|
case EShortcut::MOUSE_SWIPE_X:
|
||||||
else if(config.getLeftAxisType() == AxisType::MAP_SCROLL)
|
scrollAxisValueX = getRealAxisValue(axis.value);
|
||||||
scrollAxisValueY = getRealAxisValue(axis.value);
|
break;
|
||||||
}
|
case EShortcut::MOUSE_SWIPE_Y:
|
||||||
if(axis.axis == SDL_CONTROLLER_AXIS_RIGHTX)
|
scrollAxisValueY = getRealAxisValue(axis.value);
|
||||||
{
|
break;
|
||||||
if(config.getRightAxisType() == AxisType::CURSOR_MOTION)
|
}
|
||||||
cursorAxisValueX = getRealAxisValue(axis.value);
|
|
||||||
else if(config.getRightAxisType() == AxisType::MAP_SCROLL)
|
|
||||||
scrollAxisValueX = getRealAxisValue(axis.value);
|
|
||||||
}
|
|
||||||
else if(axis.axis == SDL_CONTROLLER_AXIS_RIGHTY)
|
|
||||||
{
|
|
||||||
if(config.getRightAxisType() == AxisType::CURSOR_MOTION)
|
|
||||||
cursorAxisValueY = getRealAxisValue(axis.value);
|
|
||||||
else if(config.getRightAxisType() == AxisType::MAP_SCROLL)
|
|
||||||
scrollAxisValueY = getRealAxisValue(axis.value);
|
|
||||||
}
|
|
||||||
else if(config.isLeftClickTrigger(axis.axis))
|
|
||||||
{
|
|
||||||
dispatchTriggerLeftClick(axis.value);
|
|
||||||
}
|
|
||||||
else if(config.isRightClickTrigger(axis.axis))
|
|
||||||
{
|
|
||||||
dispatchTriggerRightClick(axis.value);
|
|
||||||
}
|
|
||||||
else if(config.isShortcutsTrigger(axis.axis))
|
|
||||||
{
|
|
||||||
const auto & shortcutsVector = config.getTriggerShortcuts(axis.axis);
|
|
||||||
dispatchTriggerShortcuts(shortcutsVector, axis.value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dispatchAxisShortcuts(buttonActions, axisID, axis.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputSourceGameController::tryToConvertCursor()
|
void InputSourceGameController::tryToConvertCursor()
|
||||||
@ -222,42 +200,16 @@ void InputSourceGameController::tryToConvertCursor()
|
|||||||
|
|
||||||
void InputSourceGameController::handleEventButtonDown(const SDL_ControllerButtonEvent & button)
|
void InputSourceGameController::handleEventButtonDown(const SDL_ControllerButtonEvent & button)
|
||||||
{
|
{
|
||||||
const Point & position = GH.input().getCursorPosition();
|
std::string buttonName = SDL_GameControllerGetStringForButton(static_cast<SDL_GameControllerButton>(button.button));
|
||||||
|
const auto & shortcutsVector = GH.shortcuts().translateJoystickButton(buttonName);
|
||||||
if(config.isLeftClickButton(button.button))
|
GH.events().dispatchShortcutPressed(shortcutsVector);
|
||||||
{
|
|
||||||
GH.events().dispatchMouseLeftButtonPressed(position, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(config.isRightClickButton(button.button))
|
|
||||||
{
|
|
||||||
GH.events().dispatchShowPopup(position, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(config.isShortcutsButton(button.button))
|
|
||||||
{
|
|
||||||
const auto & shortcutsVector = config.getButtonShortcuts(button.button);
|
|
||||||
GH.events().dispatchShortcutPressed(shortcutsVector);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputSourceGameController::handleEventButtonUp(const SDL_ControllerButtonEvent & button)
|
void InputSourceGameController::handleEventButtonUp(const SDL_ControllerButtonEvent & button)
|
||||||
{
|
{
|
||||||
const Point & position = GH.input().getCursorPosition();
|
std::string buttonName = SDL_GameControllerGetStringForButton(static_cast<SDL_GameControllerButton>(button.button));
|
||||||
|
const auto & shortcutsVector = GH.shortcuts().translateJoystickButton(buttonName);
|
||||||
if(config.isLeftClickButton(button.button))
|
GH.events().dispatchShortcutReleased(shortcutsVector);
|
||||||
{
|
|
||||||
GH.events().dispatchMouseLeftButtonReleased(position, 0);
|
|
||||||
}
|
|
||||||
if(config.isRightClickButton(button.button))
|
|
||||||
{
|
|
||||||
GH.events().dispatchClosePopup(position);
|
|
||||||
}
|
|
||||||
if(config.isShortcutsButton(button.button))
|
|
||||||
{
|
|
||||||
const auto & shortcutsVector = config.getButtonShortcuts(button.button);
|
|
||||||
GH.events().dispatchShortcutReleased(shortcutsVector);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputSourceGameController::doCursorMove(int deltaX, int deltaY)
|
void InputSourceGameController::doCursorMove(int deltaX, int deltaY)
|
||||||
|
@ -10,19 +10,24 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <SDL.h>
|
#include <SDL_events.h>
|
||||||
|
#include <SDL_gamecontroller.h>
|
||||||
|
|
||||||
#include "GameControllerConfig.h"
|
|
||||||
#include "../gui/Shortcut.h"
|
#include "../gui/Shortcut.h"
|
||||||
#include "../../lib/Point.h"
|
#include "../../lib/Point.h"
|
||||||
|
|
||||||
|
constexpr int AXIS_DEAD_ZOOM = 6000;
|
||||||
|
constexpr int AXIS_MAX_ZOOM = 32000;
|
||||||
|
constexpr int AXIS_MOVE_SPEED = 500;
|
||||||
|
constexpr int AXIS_CURSOR_MOVE_INTERVAL = 1000;
|
||||||
|
constexpr int TRIGGER_PRESS_THRESHOLD = 8000;
|
||||||
|
|
||||||
const int AXIS_DEAD_ZOOM = 6000;
|
enum class AxisType
|
||||||
const int AXIS_MAX_ZOOM = 32000;
|
{
|
||||||
const int AXIS_MOVE_SPEED = 500;
|
CURSOR_MOTION,
|
||||||
const int AXIS_CURSOR_MOVE_INTERVAL = 1000;
|
MAP_SCROLL,
|
||||||
const int TRIGGER_PRESS_THRESHOLD = 8000;
|
NONE
|
||||||
|
};
|
||||||
|
|
||||||
/// Class that handles game controller input from SDL events
|
/// Class that handles game controller input from SDL events
|
||||||
class InputSourceGameController
|
class InputSourceGameController
|
||||||
@ -31,7 +36,8 @@ class InputSourceGameController
|
|||||||
using GameControllerPtr = std::unique_ptr<SDL_GameController, decltype(&gameControllerDeleter)>;
|
using GameControllerPtr = std::unique_ptr<SDL_GameController, decltype(&gameControllerDeleter)>;
|
||||||
|
|
||||||
std::map<int, GameControllerPtr> gameControllerMap;
|
std::map<int, GameControllerPtr> gameControllerMap;
|
||||||
GameControllerConfig config;
|
std::set<SDL_GameControllerAxis> pressedAxes;
|
||||||
|
|
||||||
long long lastCheckTime;
|
long long lastCheckTime;
|
||||||
int cursorAxisValueX;
|
int cursorAxisValueX;
|
||||||
int cursorAxisValueY;
|
int cursorAxisValueY;
|
||||||
@ -49,9 +55,7 @@ class InputSourceGameController
|
|||||||
void openGameController(int index);
|
void openGameController(int index);
|
||||||
int getJoystickIndex(SDL_GameController * controller);
|
int getJoystickIndex(SDL_GameController * controller);
|
||||||
int getRealAxisValue(int value);
|
int getRealAxisValue(int value);
|
||||||
void dispatchTriggerShortcuts(const std::vector<EShortcut> & shortcutsVector, int axisValue);
|
void dispatchAxisShortcuts(const std::vector<EShortcut> & shortcutsVector, SDL_GameControllerAxis axisID, int axisValue);
|
||||||
void dispatchTriggerLeftClick(int axisValue);
|
|
||||||
void dispatchTriggerRightClick(int axisValue);
|
|
||||||
void tryToConvertCursor();
|
void tryToConvertCursor();
|
||||||
void doCursorMove(int deltaX, int deltaY);
|
void doCursorMove(int deltaX, int deltaY);
|
||||||
int getMoveDis(float planDis);
|
int getMoveDis(float planDis);
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "CGuiHandler.h"
|
#include "CGuiHandler.h"
|
||||||
#include "MouseButton.h"
|
#include "MouseButton.h"
|
||||||
#include "WindowHandler.h"
|
#include "WindowHandler.h"
|
||||||
|
#include "gui/Shortcut.h"
|
||||||
|
|
||||||
#include "../../lib/Rect.h"
|
#include "../../lib/Rect.h"
|
||||||
|
|
||||||
@ -74,6 +75,12 @@ void EventDispatcher::dispatchShortcutPressed(const std::vector<EShortcut> & sho
|
|||||||
{
|
{
|
||||||
bool keysCaptured = false;
|
bool keysCaptured = false;
|
||||||
|
|
||||||
|
if (vstd::contains(shortcutsVector, EShortcut::MOUSE_LEFT))
|
||||||
|
dispatchMouseLeftButtonPressed(GH.getCursorPosition(), 0);
|
||||||
|
|
||||||
|
if (vstd::contains(shortcutsVector, EShortcut::MOUSE_RIGHT))
|
||||||
|
dispatchShowPopup(GH.getCursorPosition(), 0);
|
||||||
|
|
||||||
for(auto & i : keyinterested)
|
for(auto & i : keyinterested)
|
||||||
for(EShortcut shortcut : shortcutsVector)
|
for(EShortcut shortcut : shortcutsVector)
|
||||||
if(i->captureThisKey(shortcut))
|
if(i->captureThisKey(shortcut))
|
||||||
@ -97,6 +104,12 @@ void EventDispatcher::dispatchShortcutReleased(const std::vector<EShortcut> & sh
|
|||||||
{
|
{
|
||||||
bool keysCaptured = false;
|
bool keysCaptured = false;
|
||||||
|
|
||||||
|
if (vstd::contains(shortcutsVector, EShortcut::MOUSE_LEFT))
|
||||||
|
dispatchMouseLeftButtonReleased(GH.getCursorPosition(), 0);
|
||||||
|
|
||||||
|
if (vstd::contains(shortcutsVector, EShortcut::MOUSE_RIGHT))
|
||||||
|
dispatchClosePopup(GH.getCursorPosition());
|
||||||
|
|
||||||
for(auto & i : keyinterested)
|
for(auto & i : keyinterested)
|
||||||
for(EShortcut shortcut : shortcutsVector)
|
for(EShortcut shortcut : shortcutsVector)
|
||||||
if(i->captureThisKey(shortcut))
|
if(i->captureThisKey(shortcut))
|
||||||
|
@ -13,6 +13,14 @@ enum class EShortcut
|
|||||||
{
|
{
|
||||||
NONE,
|
NONE,
|
||||||
|
|
||||||
|
// preudo-shortcuts that trigger mouse events
|
||||||
|
MOUSE_LEFT,
|
||||||
|
MOUSE_RIGHT,
|
||||||
|
MOUSE_CURSOR_X,
|
||||||
|
MOUSE_CURSOR_Y,
|
||||||
|
MOUSE_SWIPE_X,
|
||||||
|
MOUSE_SWIPE_Y,
|
||||||
|
|
||||||
// Global hotkeys that are available in multiple dialogs
|
// Global hotkeys that are available in multiple dialogs
|
||||||
GLOBAL_ACCEPT, // Return - Accept query
|
GLOBAL_ACCEPT, // Return - Accept query
|
||||||
GLOBAL_CANCEL, // Escape - Cancel query
|
GLOBAL_CANCEL, // Escape - Cancel query
|
||||||
|
@ -19,7 +19,16 @@ ShortcutHandler::ShortcutHandler()
|
|||||||
{
|
{
|
||||||
const JsonNode config = JsonUtils::assembleFromFiles("config/shortcutsConfig");
|
const JsonNode config = JsonUtils::assembleFromFiles("config/shortcutsConfig");
|
||||||
|
|
||||||
for (auto const & entry : config["keyboard"].Struct())
|
mappedKeyboardShortcuts = loadShortcuts(config["keyboard"]);
|
||||||
|
mappedJoystickShortcuts = loadShortcuts(config["joystickButtons"]);
|
||||||
|
mappedJoystickAxes = loadShortcuts(config["joystickAxes"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::multimap<std::string, EShortcut> ShortcutHandler::loadShortcuts(const JsonNode & data) const
|
||||||
|
{
|
||||||
|
std::multimap<std::string, EShortcut> result;
|
||||||
|
|
||||||
|
for (auto const & entry : data.Struct())
|
||||||
{
|
{
|
||||||
std::string shortcutName = entry.first;
|
std::string shortcutName = entry.first;
|
||||||
EShortcut shortcutID = findShortcut(shortcutName);
|
EShortcut shortcutID = findShortcut(shortcutName);
|
||||||
@ -32,20 +41,22 @@ ShortcutHandler::ShortcutHandler()
|
|||||||
|
|
||||||
if (entry.second.isString())
|
if (entry.second.isString())
|
||||||
{
|
{
|
||||||
mappedShortcuts.emplace(entry.second.String(), shortcutID);
|
result.emplace(entry.second.String(), shortcutID);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.second.isVector())
|
if (entry.second.isVector())
|
||||||
{
|
{
|
||||||
for (auto const & entryVector : entry.second.Vector())
|
for (auto const & entryVector : entry.second.Vector())
|
||||||
mappedShortcuts.emplace(entryVector.String(), shortcutID);
|
result.emplace(entryVector.String(), shortcutID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<EShortcut> ShortcutHandler::translateKeycode(const std::string & key) const
|
std::vector<EShortcut> ShortcutHandler::translateShortcut(const std::multimap<std::string, EShortcut> & options, const std::string & key) const
|
||||||
{
|
{
|
||||||
auto range = mappedShortcuts.equal_range(key);
|
auto range = options.equal_range(key);
|
||||||
|
|
||||||
// FIXME: some code expects calls to keyPressed / captureThisKey even without defined hotkeys
|
// FIXME: some code expects calls to keyPressed / captureThisKey even without defined hotkeys
|
||||||
if (range.first == range.second)
|
if (range.first == range.second)
|
||||||
@ -59,9 +70,30 @@ std::vector<EShortcut> ShortcutHandler::translateKeycode(const std::string & key
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<EShortcut> ShortcutHandler::translateKeycode(const std::string & key) const
|
||||||
|
{
|
||||||
|
return translateShortcut(mappedKeyboardShortcuts, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<EShortcut> ShortcutHandler::translateJoystickButton(const std::string & key) const
|
||||||
|
{
|
||||||
|
return translateShortcut(mappedJoystickShortcuts, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<EShortcut> ShortcutHandler::translateJoystickAxis(const std::string & key) const
|
||||||
|
{
|
||||||
|
return translateShortcut(mappedJoystickAxes, key);
|
||||||
|
}
|
||||||
|
|
||||||
EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const
|
EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const
|
||||||
{
|
{
|
||||||
static const std::map<std::string, EShortcut> shortcutNames = {
|
static const std::map<std::string, EShortcut> shortcutNames = {
|
||||||
|
{"mouseClickLeft", EShortcut::MOUSE_LEFT },
|
||||||
|
{"mouseClickRight", EShortcut::MOUSE_RIGHT },
|
||||||
|
{"mouseCursorX", EShortcut::MOUSE_CURSOR_X, },
|
||||||
|
{"mouseCursorY", EShortcut::MOUSE_CURSOR_Y, },
|
||||||
|
{"mouseSwipeX", EShortcut::MOUSE_SWIPE_X, },
|
||||||
|
{"mouseSwipeY", EShortcut::MOUSE_SWIPE_Y, },
|
||||||
{"globalAccept", EShortcut::GLOBAL_ACCEPT },
|
{"globalAccept", EShortcut::GLOBAL_ACCEPT },
|
||||||
{"globalCancel", EShortcut::GLOBAL_CANCEL },
|
{"globalCancel", EShortcut::GLOBAL_CANCEL },
|
||||||
{"globalReturn", EShortcut::GLOBAL_RETURN },
|
{"globalReturn", EShortcut::GLOBAL_RETURN },
|
||||||
|
@ -12,15 +12,29 @@
|
|||||||
|
|
||||||
enum class EShortcut;
|
enum class EShortcut;
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
class JsonNode;
|
||||||
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
|
||||||
class ShortcutHandler
|
class ShortcutHandler
|
||||||
{
|
{
|
||||||
std::multimap<std::string, EShortcut> mappedShortcuts;
|
std::multimap<std::string, EShortcut> mappedKeyboardShortcuts;
|
||||||
|
std::multimap<std::string, EShortcut> mappedJoystickShortcuts;
|
||||||
|
std::multimap<std::string, EShortcut> mappedJoystickAxes;
|
||||||
|
|
||||||
|
std::multimap<std::string, EShortcut> loadShortcuts(const JsonNode & data) const;
|
||||||
|
std::vector<EShortcut> translateShortcut(const std::multimap<std::string, EShortcut> & options, const std::string & key) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ShortcutHandler();
|
ShortcutHandler();
|
||||||
|
|
||||||
/// returns list of shortcuts assigned to provided SDL keycode
|
/// returns list of shortcuts assigned to provided SDL keycode
|
||||||
std::vector<EShortcut> translateKeycode(const std::string & key) const;
|
std::vector<EShortcut> translateKeycode(const std::string & key) const;
|
||||||
|
|
||||||
|
std::vector<EShortcut> translateJoystickButton(const std::string & key) const;
|
||||||
|
|
||||||
|
std::vector<EShortcut> translateJoystickAxis(const std::string & key) const;
|
||||||
|
|
||||||
/// attempts to find shortcut by its unique identifier. Returns EShortcut::NONE on failure
|
/// attempts to find shortcut by its unique identifier. Returns EShortcut::NONE on failure
|
||||||
EShortcut findShortcut(const std::string & identifier ) const;
|
EShortcut findShortcut(const std::string & identifier ) const;
|
||||||
};
|
};
|
||||||
|
@ -110,6 +110,7 @@
|
|||||||
"battleConsoleDown": "Down",
|
"battleConsoleDown": "Down",
|
||||||
"battleTacticsNext": "Space",
|
"battleTacticsNext": "Space",
|
||||||
"battleTacticsEnd": [ "Return", "Keypad Enter"],
|
"battleTacticsEnd": [ "Return", "Keypad Enter"],
|
||||||
|
"battleToggleHeroesStats": [],
|
||||||
"battleSelectAction": "S",
|
"battleSelectAction": "S",
|
||||||
"townOpenTavern": "T",
|
"townOpenTavern": "T",
|
||||||
"townSwapArmies": "Space",
|
"townSwapArmies": "Space",
|
||||||
@ -137,24 +138,72 @@
|
|||||||
"heroCostume8": "8",
|
"heroCostume8": "8",
|
||||||
"heroCostume9": "9"
|
"heroCostume9": "9"
|
||||||
},
|
},
|
||||||
"joystick": {
|
|
||||||
"leftaxis": "cursorMotion",
|
"joystickAxes":
|
||||||
"rightaxis": "mapScroll",
|
{
|
||||||
"a": ["globalAccept", "globalReturn", "lobbyBeginStandardGame", "lobbyBeginCampaign", "lobbyLoadGame", "lobbySaveGame", "adventureViewSelected", "adventureExitWorldView", "battleTacticsEnd"],
|
"mouseCursorX" : "leftx",
|
||||||
"b": ["globalCancel", "globalReturn", "adventureExitWorldView"],
|
"mouseCursorY" : "lefty",
|
||||||
"x": "mouseLeftClick",
|
"mouseSwipeX" : "rightx",
|
||||||
"y": "mouseRightClick",
|
"mouseSwipeY" : "righty"
|
||||||
|
},
|
||||||
|
|
||||||
|
"joystickButtons": {
|
||||||
|
"globalAccept" : "a",
|
||||||
|
"globalCancel" : "b",
|
||||||
|
"globalReturn" : [ "a", "b" ],
|
||||||
|
|
||||||
|
"lobbyBeginStandardGame" : "a",
|
||||||
|
"lobbyBeginCampaign" : "a",
|
||||||
|
"lobbyLoadGame" : "a",
|
||||||
|
"lobbySaveGame" : "a",
|
||||||
|
"adventureViewSelected" : "a",
|
||||||
|
"adventureExitWorldView" : [ "a", "b" ],
|
||||||
|
"battleTacticsEnd" : "a",
|
||||||
|
|
||||||
|
"mouseClickLeft": "x",
|
||||||
|
"mouseClickRight": "y",
|
||||||
|
|
||||||
"leftshoulder": ["adventureNextHero", "battleDefend"],
|
"leftshoulder": ["adventureNextHero", "battleDefend"],
|
||||||
"rightshoulder": ["adventureNextTown", "battleWait"],
|
"rightshoulder": ["adventureNextTown", "battleWait"],
|
||||||
"lefttrigger": ["adventureVisitObject", "battleTacticsNext", "battleUseCreatureSpell"],
|
|
||||||
"righttrigger": ["adventureCastSpell", "battleCastSpell"],
|
"adventureNextTown" : "rightshoulder",
|
||||||
"back": ["gameEndTurn", "battleAutocombatEnd"],
|
"battleWait" : "rightshoulder",
|
||||||
"start": ["globalOptions", "adventureGameOptions"],
|
|
||||||
"dpup": ["moveUp", "adventureViewWorld", "recruitmentUpgrade", "recruitmentUpgradeAll", "battleConsoleUp", "recruitmentMax"],
|
"adventureVisitObject" : "lefttrigger",
|
||||||
"dpdown": ["moveDown", "adventureKingdomOverview", "battleConsoleDown","recruitmentMin"],
|
"battleTacticsNext" : "lefttrigger",
|
||||||
"dpleft": ["moveLeft", "adventureViewScenario"],
|
"battleUseCreatureSpell" : "lefttrigger",
|
||||||
"dpright": ["moveRight", "adventureThievesGuild"],
|
|
||||||
"leftstick" : ["adventureToggleMapLevel", "battleToggleHeroesStats"],
|
"adventureCastSpell" : "righttrigger",
|
||||||
"rightstick": ["adventureToggleGrid", "battleToggleQueue"]
|
"battleCastSpell" : "righttrigger",
|
||||||
|
|
||||||
|
"gameEndTurn" : "back",
|
||||||
|
"battleAutocombatEnd" : "back",
|
||||||
|
|
||||||
|
"globalOptions" : "start",
|
||||||
|
"adventureGameOptions" : "start",
|
||||||
|
|
||||||
|
"moveUp" : "dpup",
|
||||||
|
"adventureViewWorld" : "dpup",
|
||||||
|
"recruitmentUpgrade" : "dpup",
|
||||||
|
"recruitmentUpgradeAll" : "dpup",
|
||||||
|
"battleConsoleUp" : "dpup",
|
||||||
|
"recruitmentMax" : "dpup",
|
||||||
|
|
||||||
|
"moveDown" : "dpdown",
|
||||||
|
"adventureKingdomOverview" : "dpdown",
|
||||||
|
"battleConsoleDown" : "dpdown",
|
||||||
|
"recruitmentMin" : "dpdown",
|
||||||
|
|
||||||
|
"moveLeft" : "dpleft",
|
||||||
|
"adventureViewScenario" : "dpleft",
|
||||||
|
|
||||||
|
"moveRight" : "dpright",
|
||||||
|
"adventureThievesGuild" : "dpright",
|
||||||
|
|
||||||
|
"adventureToggleMapLevel" : "leftstick",
|
||||||
|
"battleToggleHeroesStats" : "leftstick",
|
||||||
|
|
||||||
|
"adventureToggleGrid" : "rightstick",
|
||||||
|
"battleToggleQueue" : "rightstick",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user