mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-01-29 19:55:37 +00:00
InputManager mostly redone to handle rolling joystick changes instead of completely deinitializing/reinitializing itself every time a change is detected.
Some other slight changes to better fit with SDL2's joystick improvements. Completely redid GuiDetectDevice and GuiInputConfig. Inching closer and closer to beta.
This commit is contained in:
parent
9a3b0af337
commit
980a2c4ec6
|
@ -49,9 +49,8 @@ std::string toLower(std::string str)
|
|||
}
|
||||
//end util functions
|
||||
|
||||
InputConfig::InputConfig(int deviceId, const std::string& deviceName) : mDeviceId(deviceId), mDeviceName(deviceName)
|
||||
InputConfig::InputConfig(int deviceId, const std::string& deviceName, const std::string& deviceGUID) : mDeviceId(deviceId), mDeviceName(deviceName), mDeviceGUID(deviceGUID)
|
||||
{
|
||||
mPlayerNum = -1;
|
||||
}
|
||||
|
||||
void InputConfig::clear()
|
||||
|
@ -59,6 +58,11 @@ void InputConfig::clear()
|
|||
mNameMap.clear();
|
||||
}
|
||||
|
||||
bool InputConfig::isConfigured()
|
||||
{
|
||||
return mNameMap.size() > 0;
|
||||
}
|
||||
|
||||
void InputConfig::mapInput(const std::string& name, Input input)
|
||||
{
|
||||
mNameMap[toLower(name)] = input;
|
||||
|
@ -126,11 +130,9 @@ std::vector<std::string> InputConfig::getMappedTo(Input input)
|
|||
return maps;
|
||||
}
|
||||
|
||||
void InputConfig::loadFromXML(pugi::xml_node node, int playerNum)
|
||||
void InputConfig::loadFromXML(pugi::xml_node node)
|
||||
{
|
||||
this->clear();
|
||||
|
||||
setPlayerNum(playerNum);
|
||||
clear();
|
||||
|
||||
for(pugi::xml_node input = node.child("input"); input; input = input.next_sibling("input"))
|
||||
{
|
||||
|
@ -161,11 +163,14 @@ void InputConfig::writeToXML(pugi::xml_node parent)
|
|||
if(mDeviceId == DEVICE_KEYBOARD)
|
||||
{
|
||||
cfg.append_attribute("type") = "keyboard";
|
||||
cfg.append_attribute("deviceName") = "Keyboard";
|
||||
}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>::iterator it_type;
|
||||
for(it_type iterator = mNameMap.begin(); iterator != mNameMap.end(); iterator++)
|
||||
{
|
||||
|
@ -179,7 +184,3 @@ void InputConfig::writeToXML(pugi::xml_node parent)
|
|||
input.append_attribute("value").set_value(iterator->second.value);
|
||||
}
|
||||
}
|
||||
|
||||
void InputConfig::setPlayerNum(int num) { mPlayerNum = num; }
|
||||
int InputConfig::getPlayerNum() { return mPlayerNum; }
|
||||
int InputConfig::getDeviceId() { return mDeviceId; }
|
||||
|
|
|
@ -56,9 +56,6 @@ public:
|
|||
|
||||
std::string string()
|
||||
{
|
||||
if(!configured)
|
||||
return "";
|
||||
|
||||
std::stringstream stream;
|
||||
switch(type)
|
||||
{
|
||||
|
@ -86,14 +83,14 @@ public:
|
|||
class InputConfig
|
||||
{
|
||||
public:
|
||||
InputConfig(int deviceId, const std::string& deviceName);
|
||||
InputConfig(int deviceId, const std::string& deviceName, const std::string& deviceGUID);
|
||||
|
||||
void clear();
|
||||
void mapInput(const std::string& name, Input input);
|
||||
void setPlayerNum(int num);
|
||||
|
||||
int getPlayerNum();
|
||||
int getDeviceId();
|
||||
inline int getDeviceId() const { return mDeviceId; };
|
||||
inline const std::string& getDeviceName() { return mDeviceName; }
|
||||
inline const std::string& getDeviceGUIDString() { return mDeviceGUID; }
|
||||
|
||||
//Returns the input mapped to this name.
|
||||
Input getInputByName(const std::string& name);
|
||||
|
@ -104,13 +101,16 @@ public:
|
|||
//Returns a list of names this input is mapped to.
|
||||
std::vector<std::string> getMappedTo(Input input);
|
||||
|
||||
void loadFromXML(pugi::xml_node root, int playerNum);
|
||||
void loadFromXML(pugi::xml_node root);
|
||||
void writeToXML(pugi::xml_node parent);
|
||||
|
||||
bool isConfigured();
|
||||
|
||||
private:
|
||||
std::map<std::string, Input> mNameMap;
|
||||
const int mDeviceId;
|
||||
const std::string mDeviceName;
|
||||
int mPlayerNum;
|
||||
const std::string mDeviceGUID;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -6,11 +6,23 @@
|
|||
#include <boost/filesystem.hpp>
|
||||
#include "platform.h"
|
||||
|
||||
#define KEYBOARD_GUID_STRING "-1"
|
||||
|
||||
// SO HEY POTENTIAL POOR SAP WHO IS TRYING TO MAKE SENSE OF ALL THIS (by which I mean my future self)
|
||||
// There are like four distinct IDs used for joysticks (crazy, right?)
|
||||
// 1. Device index - this is the "lowest level" identifier, and is just the Nth joystick plugged in to the system (like /dev/js#).
|
||||
// It can change even if the device is the same, and is only used to open joysticks (required to receive SDL events).
|
||||
// 2. SDL_JoystickID - this is an ID for each joystick that is supposed to remain consistent between plugging and unplugging.
|
||||
// ES doesn't care if it does, though.
|
||||
// 3. "Device ID" - this is something I made up and is what InputConfig's getDeviceID() returns.
|
||||
// This is actually just an SDL_JoystickID (also called instance ID), but -1 means "keyboard" instead of "error."
|
||||
// 4. Joystick GUID - this is some squashed version of joystick vendor, version, and a bunch of other device-specific things.
|
||||
// It should remain the same across runs of the program/system restarts/device reordering and is what I use to identify which joystick to load.
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
InputManager::InputManager(Window* window) : mWindow(window),
|
||||
mKeyboardInputConfig(NULL),
|
||||
mNumJoysticks(0), mNumPlayers(0)
|
||||
mKeyboardInputConfig(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -25,54 +37,95 @@ void InputManager::init()
|
|||
deinit();
|
||||
|
||||
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
|
||||
SDL_JoystickEventState(SDL_ENABLE);
|
||||
|
||||
mNumJoysticks = SDL_NumJoysticks();
|
||||
|
||||
for(int i = 0; i < mNumJoysticks; i++)
|
||||
// first, open all currently present joysticks
|
||||
int numJoysticks = SDL_NumJoysticks();
|
||||
for(int i = 0; i < numJoysticks; i++)
|
||||
{
|
||||
SDL_Joystick* joy = SDL_JoystickOpen(i);
|
||||
SDL_JoystickID joyId = SDL_JoystickInstanceID(joy);
|
||||
mJoysticks.push_back(joy);
|
||||
mInputConfigs[joyId] = new InputConfig(i, SDL_JoystickName(joy));
|
||||
|
||||
int numAxes = SDL_JoystickNumAxes(joy);
|
||||
mPrevAxisValues[joyId] = new int[numAxes];
|
||||
std::fill(mPrevAxisValues[joyId], mPrevAxisValues[joyId] + numAxes, 0); //initialize array to 0
|
||||
addJoystickByDeviceIndex(i);
|
||||
}
|
||||
|
||||
mKeyboardInputConfig = new InputConfig(DEVICE_KEYBOARD, "Keyboard");
|
||||
mKeyboardInputConfig = new InputConfig(DEVICE_KEYBOARD, "Keyboard", KEYBOARD_GUID_STRING);
|
||||
loadInputConfig(mKeyboardInputConfig);
|
||||
}
|
||||
|
||||
loadConfig();
|
||||
void InputManager::addJoystickByDeviceIndex(int id)
|
||||
{
|
||||
assert(id >= 0 && id < SDL_NumJoysticks());
|
||||
|
||||
// open joystick & add to our list
|
||||
SDL_Joystick* joy = SDL_JoystickOpen(id);
|
||||
assert(joy);
|
||||
|
||||
SDL_JoystickEventState(SDL_ENABLE);
|
||||
// add it to our list so we can close it again later
|
||||
SDL_JoystickID joyId = SDL_JoystickInstanceID(joy);
|
||||
mJoysticks[joyId] = joy;
|
||||
|
||||
char guid[65];
|
||||
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joy), guid, 65);
|
||||
|
||||
// create the InputConfig
|
||||
mInputConfigs[joyId] = new InputConfig(joyId, SDL_JoystickName(joy), guid);
|
||||
if(!loadInputConfig(mInputConfigs[joyId]))
|
||||
{
|
||||
LOG(LogInfo) << "Added unconfigured joystick " << SDL_JoystickName(joy) << " (GUID: " << guid << ", instance ID: " << joyId << ", device index: " << id << ").";
|
||||
}else{
|
||||
LOG(LogInfo) << "Added known joystick " << SDL_JoystickName(joy) << " (instance ID: " << joyId << ", device index: " << id << ")";
|
||||
}
|
||||
|
||||
// set up the prevAxisValues
|
||||
int numAxes = SDL_JoystickNumAxes(joy);
|
||||
mPrevAxisValues[joyId] = new int[numAxes];
|
||||
std::fill(mPrevAxisValues[joyId], mPrevAxisValues[joyId] + numAxes, 0); //initialize array to 0
|
||||
}
|
||||
|
||||
void InputManager::removeJoystickByJoystickID(SDL_JoystickID joyId)
|
||||
{
|
||||
assert(joyId != -1);
|
||||
|
||||
// delete old prevAxisValues
|
||||
auto axisIt = mPrevAxisValues.find(joyId);
|
||||
delete[] axisIt->second;
|
||||
mPrevAxisValues.erase(axisIt);
|
||||
|
||||
// delete old InputConfig
|
||||
auto it = mInputConfigs.find(joyId);
|
||||
delete it->second;
|
||||
mInputConfigs.erase(it);
|
||||
|
||||
// close the joystick
|
||||
auto joyIt = mJoysticks.find(joyId);
|
||||
if(joyIt != mJoysticks.end())
|
||||
{
|
||||
SDL_JoystickClose(joyIt->second);
|
||||
mJoysticks.erase(joyIt);
|
||||
}else{
|
||||
LOG(LogError) << "Could not find joystick to close (instance ID: " << joyId << ")";
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::deinit()
|
||||
{
|
||||
SDL_JoystickEventState(SDL_DISABLE);
|
||||
|
||||
if(!initialized())
|
||||
return;
|
||||
|
||||
for(auto iter = mJoysticks.begin(); iter != mJoysticks.end(); iter++)
|
||||
{
|
||||
SDL_JoystickClose(*iter);
|
||||
SDL_JoystickClose(iter->second);
|
||||
}
|
||||
|
||||
mJoysticks.clear();
|
||||
|
||||
for(auto iter = mInputConfigs.begin(); iter != mInputConfigs.end(); iter++)
|
||||
{
|
||||
delete iter->second;
|
||||
}
|
||||
|
||||
mInputConfigs.clear();
|
||||
|
||||
for(auto iter = mPrevAxisValues.begin(); iter != mPrevAxisValues.end(); iter++)
|
||||
{
|
||||
delete[] iter->second;
|
||||
}
|
||||
|
||||
mPrevAxisValues.clear();
|
||||
|
||||
if(mKeyboardInputConfig != NULL)
|
||||
|
@ -81,11 +134,12 @@ void InputManager::deinit()
|
|||
mKeyboardInputConfig = NULL;
|
||||
}
|
||||
|
||||
SDL_JoystickEventState(SDL_DISABLE);
|
||||
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
|
||||
}
|
||||
|
||||
int InputManager::getNumJoysticks() { return mNumJoysticks; }
|
||||
int InputManager::getButtonCountByDevice(int id)
|
||||
int InputManager::getNumJoysticks() { return mJoysticks.size(); }
|
||||
int InputManager::getButtonCountByDevice(SDL_JoystickID id)
|
||||
{
|
||||
if(id == DEVICE_KEYBOARD)
|
||||
return 120; //it's a lot, okay.
|
||||
|
@ -93,10 +147,7 @@ int InputManager::getButtonCountByDevice(int id)
|
|||
return SDL_JoystickNumButtons(mJoysticks[id]);
|
||||
}
|
||||
|
||||
int InputManager::getNumPlayers() { return mNumPlayers; }
|
||||
void InputManager::setNumPlayers(int num) { mNumPlayers = num; }
|
||||
|
||||
InputConfig* InputManager::getInputConfigByDevice(SDL_JoystickID device)
|
||||
InputConfig* InputManager::getInputConfigByDevice(int device)
|
||||
{
|
||||
if(device == DEVICE_KEYBOARD)
|
||||
return mKeyboardInputConfig;
|
||||
|
@ -104,23 +155,6 @@ InputConfig* InputManager::getInputConfigByDevice(SDL_JoystickID device)
|
|||
return mInputConfigs[device];
|
||||
}
|
||||
|
||||
InputConfig* InputManager::getInputConfigByPlayer(int player)
|
||||
{
|
||||
if(mKeyboardInputConfig->getPlayerNum() == player)
|
||||
return mKeyboardInputConfig;
|
||||
|
||||
for(auto iter = mInputConfigs.begin(); iter != mInputConfigs.end(); iter++)
|
||||
{
|
||||
if(iter->second->getPlayerNum() == player)
|
||||
{
|
||||
return iter->second;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(LogError) << "Could not find input config for player number " << player << "!";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool InputManager::parseEvent(const SDL_Event& ev)
|
||||
{
|
||||
bool causedEvent = false;
|
||||
|
@ -188,102 +222,51 @@ bool InputManager::parseEvent(const SDL_Event& ev)
|
|||
break;
|
||||
|
||||
case SDL_JOYDEVICEADDED:
|
||||
deinit();
|
||||
init();
|
||||
addJoystickByDeviceIndex(ev.jdevice.which); // ev.jdevice.which is a device index
|
||||
return true;
|
||||
|
||||
case SDL_JOYDEVICEREMOVED:
|
||||
removeJoystickByJoystickID(ev.jdevice.which); // ev.jdevice.which is an SDL_JoystickID (instance ID)
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void InputManager::loadConfig()
|
||||
bool InputManager::loadInputConfig(InputConfig* config)
|
||||
{
|
||||
if(!initialized())
|
||||
{
|
||||
LOG(LogError) << "ERROR - cannot load InputManager config without being initialized!";
|
||||
}
|
||||
|
||||
std::string path = getConfigPath();
|
||||
if(!fs::exists(path))
|
||||
return;
|
||||
|
||||
return false;
|
||||
|
||||
pugi::xml_document doc;
|
||||
pugi::xml_parse_result res = doc.load_file(path.c_str());
|
||||
|
||||
if(!res)
|
||||
{
|
||||
LOG(LogError) << "Error loading input config: " << res.description();
|
||||
return;
|
||||
}
|
||||
|
||||
mNumPlayers = 0;
|
||||
|
||||
bool* configuredDevice = new bool[mNumJoysticks];
|
||||
std::fill(configuredDevice, configuredDevice + mNumJoysticks, false);
|
||||
|
||||
for(auto iter = mInputConfigs.begin(); iter != mInputConfigs.end(); iter++)
|
||||
{
|
||||
iter->second->setPlayerNum(-1);
|
||||
LOG(LogError) << "Error parsing input config: " << res.description();
|
||||
return false;
|
||||
}
|
||||
|
||||
pugi::xml_node root = doc.child("inputList");
|
||||
if(!root)
|
||||
return false;
|
||||
|
||||
bool loadedKeyboard = false;
|
||||
for(pugi::xml_node node = root.child("inputConfig"); node; node = node.next_sibling("inputConfig"))
|
||||
{
|
||||
std::string type = node.attribute("type").as_string();
|
||||
pugi::xml_node configNode = root.find_child_by_attribute("inputConfig", "deviceGUID", getDeviceGUIDString(config->getDeviceId()).c_str());
|
||||
if(!configNode)
|
||||
return false;
|
||||
|
||||
if(type == "keyboard")
|
||||
{
|
||||
getInputConfigByDevice(DEVICE_KEYBOARD)->loadFromXML(node, mNumPlayers);
|
||||
loadedKeyboard = true;
|
||||
mNumPlayers++;
|
||||
}else if(type == "joystick")
|
||||
{
|
||||
bool found = false;
|
||||
std::string devName = node.attribute("deviceName").as_string();
|
||||
for(int i = 0; i < mNumJoysticks; i++)
|
||||
{
|
||||
if(!configuredDevice[i] && SDL_JoystickName(mJoysticks[i]) == devName)
|
||||
{
|
||||
SDL_JoystickID joyId = SDL_JoystickInstanceID(mJoysticks[i]);
|
||||
mInputConfigs[joyId]->loadFromXML(node, mNumPlayers);
|
||||
mNumPlayers++;
|
||||
found = true;
|
||||
configuredDevice[i] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!found)
|
||||
{
|
||||
LOG(LogWarning) << "Could not find unconfigured joystick named \"" << devName << "\"! Skipping it.\n";
|
||||
continue;
|
||||
}
|
||||
}else{
|
||||
LOG(LogWarning) << "Device type \"" << type << "\" unknown!\n";
|
||||
}
|
||||
}
|
||||
|
||||
delete[] configuredDevice;
|
||||
|
||||
if(!loadedKeyboard)
|
||||
{
|
||||
LOG(LogInfo) << "No keyboard input found. Loading default keyboard config.";
|
||||
loadDefaultConfig();
|
||||
}
|
||||
|
||||
LOG(LogInfo) << "Loaded InputConfig data for " << getNumPlayers() << " devices.";
|
||||
config->loadFromXML(configNode);
|
||||
return true;
|
||||
}
|
||||
|
||||
//used in an "emergency" where no configs could be loaded from the inputmanager config file
|
||||
//used in an "emergency" where no keyboard config could be loaded from the inputmanager config file
|
||||
//allows the user to select to reconfigure in menus if this happens without having to delete es_input.cfg manually
|
||||
void InputManager::loadDefaultConfig()
|
||||
void InputManager::loadDefaultKBConfig()
|
||||
{
|
||||
InputConfig* cfg = getInputConfigByDevice(DEVICE_KEYBOARD);
|
||||
|
||||
mNumPlayers++;
|
||||
cfg->setPlayerNum(0);
|
||||
cfg->clear();
|
||||
cfg->mapInput("up", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_UP, 1, true));
|
||||
cfg->mapInput("down", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_DOWN, 1, true));
|
||||
cfg->mapInput("left", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_LEFT, 1, true));
|
||||
|
@ -291,35 +274,45 @@ void InputManager::loadDefaultConfig()
|
|||
|
||||
cfg->mapInput("a", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_RETURN, 1, true));
|
||||
cfg->mapInput("b", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_ESCAPE, 1, true));
|
||||
cfg->mapInput("menu", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_F1, 1, true));
|
||||
cfg->mapInput("start", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_F1, 1, true));
|
||||
cfg->mapInput("select", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_F2, 1, true));
|
||||
|
||||
cfg->mapInput("pageup", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_RIGHTBRACKET, 1, true));
|
||||
cfg->mapInput("pagedown", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_LEFTBRACKET, 1, true));
|
||||
|
||||
cfg->mapInput("mastervolup", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_PLUS, 1, true));
|
||||
cfg->mapInput("mastervoldown", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_MINUS, 1, true));
|
||||
}
|
||||
|
||||
void InputManager::writeConfig()
|
||||
void InputManager::writeDeviceConfig(InputConfig* config)
|
||||
{
|
||||
if(!initialized())
|
||||
{
|
||||
LOG(LogError) << "ERROR - cannot write config without being initialized!";
|
||||
return;
|
||||
}
|
||||
assert(initialized());
|
||||
|
||||
std::string path = getConfigPath();
|
||||
|
||||
pugi::xml_document doc;
|
||||
|
||||
pugi::xml_node root = doc.append_child("inputList");
|
||||
|
||||
mKeyboardInputConfig->writeToXML(root);
|
||||
for(int i = 0; i < mNumJoysticks; i++)
|
||||
if(fs::exists(path))
|
||||
{
|
||||
mInputConfigs[i]->writeToXML(root);
|
||||
// merge files
|
||||
pugi::xml_parse_result result = doc.load_file(path.c_str());
|
||||
if(!result)
|
||||
{
|
||||
LOG(LogError) << "Error parsing input config: " << result.description();
|
||||
}else{
|
||||
// successfully loaded, delete the old entry if it exists
|
||||
pugi::xml_node root = doc.child("inputList");
|
||||
if(root)
|
||||
{
|
||||
pugi::xml_node oldEntry = root.find_child_by_attribute("inputConfig", "deviceGUID", config->getDeviceGUIDString().c_str());
|
||||
if(oldEntry)
|
||||
root.remove_child(oldEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pugi::xml_node root = doc.child("inputList");
|
||||
if(!root)
|
||||
root = doc.append_child("inputList");
|
||||
|
||||
config->writeToXML(root);
|
||||
doc.save_file(path.c_str());
|
||||
}
|
||||
|
||||
|
@ -334,3 +327,35 @@ bool InputManager::initialized() const
|
|||
{
|
||||
return mKeyboardInputConfig != NULL;
|
||||
}
|
||||
|
||||
int InputManager::getNumConfiguredDevices()
|
||||
{
|
||||
int num = 0;
|
||||
for(auto it = mInputConfigs.begin(); it != mInputConfigs.end(); it++)
|
||||
{
|
||||
if(it->second->isConfigured())
|
||||
num++;
|
||||
}
|
||||
|
||||
if(mKeyboardInputConfig->isConfigured())
|
||||
num++;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
std::string InputManager::getDeviceGUIDString(int deviceId)
|
||||
{
|
||||
if(deviceId == DEVICE_KEYBOARD)
|
||||
return KEYBOARD_GUID_STRING;
|
||||
|
||||
auto it = mJoysticks.find(deviceId);
|
||||
if(it == mJoysticks.end())
|
||||
{
|
||||
LOG(LogError) << "getDeviceGUIDString - deviceId " << deviceId << " not found!";
|
||||
return "something went horribly wrong";
|
||||
}
|
||||
|
||||
char guid[65];
|
||||
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(it->second), guid, 65);
|
||||
return std::string(guid);
|
||||
}
|
||||
|
|
|
@ -12,19 +12,14 @@ class Window;
|
|||
//you should only ever instantiate one of these, by the way
|
||||
class InputManager
|
||||
{
|
||||
private:
|
||||
static const int DEADZONE = 23000;
|
||||
|
||||
Window* mWindow;
|
||||
|
||||
//non-InputManager classes shouldn't use this, as you can easily miss the keyboard and don't have SDL_JoystickIDs
|
||||
InputConfig* getInputConfigByDevice(SDL_JoystickID device);
|
||||
void loadDefaultKBConfig();
|
||||
|
||||
void loadDefaultConfig();
|
||||
|
||||
int mNumJoysticks;
|
||||
int mNumPlayers;
|
||||
|
||||
std::vector<SDL_Joystick*> mJoysticks;
|
||||
std::map<SDL_JoystickID, SDL_Joystick*> mJoysticks;
|
||||
std::map<SDL_JoystickID, InputConfig*> mInputConfigs;
|
||||
InputConfig* mKeyboardInputConfig;
|
||||
|
||||
|
@ -32,26 +27,29 @@ class InputManager
|
|||
|
||||
bool initialized() const;
|
||||
|
||||
void addJoystickByDeviceIndex(int id);
|
||||
void removeJoystickByJoystickID(SDL_JoystickID id);
|
||||
bool loadInputConfig(InputConfig* config); // returns true if successfully loaded, false if not (or didn't exist)
|
||||
|
||||
public:
|
||||
InputManager(Window* window);
|
||||
~InputManager();
|
||||
|
||||
void loadConfig();
|
||||
void writeConfig();
|
||||
void writeDeviceConfig(InputConfig* config);
|
||||
static std::string getConfigPath();
|
||||
|
||||
void init();
|
||||
void deinit();
|
||||
|
||||
void setNumPlayers(int num);
|
||||
int getNumPlayers();
|
||||
|
||||
int getNumJoysticks();
|
||||
int getButtonCountByDevice(int id);
|
||||
int getButtonCountByDevice(int deviceId);
|
||||
int getNumConfiguredDevices();
|
||||
|
||||
std::string getDeviceGUIDString(int deviceId);
|
||||
|
||||
InputConfig* getInputConfigByDevice(int deviceId);
|
||||
|
||||
bool parseEvent(const SDL_Event& ev);
|
||||
|
||||
InputConfig* getInputConfigByPlayer(int player);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -290,3 +290,10 @@ std::vector<HelpPrompt> ComponentList::getHelpPrompts()
|
|||
|
||||
return mEntries.at(mCursor).data.elements.back().component->getHelpPrompts();
|
||||
}
|
||||
|
||||
bool ComponentList::moveCursor(int amt)
|
||||
{
|
||||
bool ret = listInput(amt);
|
||||
listInput(0);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ public:
|
|||
void onFocusGained() override;
|
||||
void onFocusLost() override;
|
||||
|
||||
bool moveCursor(int amt);
|
||||
inline int getCursorId() const { return mCursor; }
|
||||
|
||||
float getTotalRowHeight() const;
|
||||
|
|
|
@ -12,7 +12,7 @@ static const std::map<std::string, const char*> ICON_PATH_MAP = boost::assign::m
|
|||
("up/down/left/right", ":/help/dpad_all.png")
|
||||
("a", ":/help/a.png")
|
||||
("b", ":/help/b.png")
|
||||
("menu", ":/help/start.png")
|
||||
("start", ":/help/start.png")
|
||||
("select", ":/help/select.png");
|
||||
|
||||
HelpComponent::HelpComponent(Window* window) : GuiComponent(window)
|
||||
|
|
|
@ -3,104 +3,101 @@
|
|||
#include "../Renderer.h"
|
||||
#include "../resources/Font.h"
|
||||
#include "GuiInputConfig.h"
|
||||
#include "../components/TextComponent.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include "../views/ViewController.h"
|
||||
|
||||
GuiDetectDevice::GuiDetectDevice(Window* window) : GuiComponent(window)
|
||||
#define HOLD_TIME 1000
|
||||
|
||||
using namespace Eigen;
|
||||
|
||||
GuiDetectDevice::GuiDetectDevice(Window* window, bool firstRun) : GuiComponent(window),
|
||||
mBackground(window, ":/frame.png"), mGrid(window, Vector2i(1, 4))
|
||||
{
|
||||
//clear any player information from the InputManager
|
||||
for(int i = 0; i < mWindow->getInputManager()->getNumPlayers(); i++)
|
||||
{
|
||||
InputConfig* cfg = mWindow->getInputManager()->getInputConfigByPlayer(i);
|
||||
cfg->setPlayerNum(-1);
|
||||
cfg->clear();
|
||||
}
|
||||
mWindow->getInputManager()->setNumPlayers(0);
|
||||
mHoldingConfig = NULL;
|
||||
mHoldTime = 0;
|
||||
|
||||
mCurrentPlayer = 0;
|
||||
mHoldingFinish = false;
|
||||
addChild(&mBackground);
|
||||
addChild(&mGrid);
|
||||
|
||||
if(firstRun)
|
||||
{
|
||||
mDoneCallback = [window] {
|
||||
window->getViewController()->goToStart();
|
||||
};
|
||||
}
|
||||
|
||||
mTitle = std::make_shared<TextComponent>(mWindow, firstRun ? "WELCOME TO EMULATIONSTATION" : "SELECT A DEVICE",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x666666FF, TextComponent::ALIGN_CENTER);
|
||||
mGrid.setEntry(mTitle, Vector2i(0, 0), false, true);
|
||||
|
||||
std::string msg = (firstRun ? "First, we need to configure your input device!\n" : "");
|
||||
msg += "Hold a button on your input device to configure it.\n"
|
||||
"Press F4 to quit at any time.";
|
||||
mMsg = std::make_shared<TextComponent>(mWindow, msg, Font::get(FONT_SIZE_SMALL), 0x777777FF, TextComponent::ALIGN_CENTER);
|
||||
mGrid.setEntry(mMsg, Vector2i(0, 1), false, true);
|
||||
|
||||
std::stringstream deviceInfo;
|
||||
int numDevices = mWindow->getInputManager()->getNumJoysticks();
|
||||
|
||||
if(numDevices > 0)
|
||||
deviceInfo << numDevices << " joystick" << (numDevices > 1 ? "s" : "") << " detected.";
|
||||
else
|
||||
deviceInfo << "No joysticks detected!";
|
||||
mDeviceInfo = std::make_shared<TextComponent>(mWindow, deviceInfo.str(), Font::get(FONT_SIZE_SMALL), 0x888888FF, TextComponent::ALIGN_CENTER);
|
||||
mGrid.setEntry(mDeviceInfo, Vector2i(0, 2), false, true);
|
||||
|
||||
mDeviceHeld = std::make_shared<TextComponent>(mWindow, "", Font::get(FONT_SIZE_MEDIUM), 0x777777FF, TextComponent::ALIGN_CENTER);
|
||||
mGrid.setEntry(mDeviceHeld, Vector2i(0, 3), false, true);
|
||||
|
||||
setSize(Renderer::getScreenWidth() * 0.6f, Renderer::getScreenHeight() * 0.5f);
|
||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - mSize.y()) / 2);
|
||||
}
|
||||
|
||||
void GuiDetectDevice::onSizeChanged()
|
||||
{
|
||||
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32));
|
||||
|
||||
// grid
|
||||
mGrid.setSize(mSize);
|
||||
mGrid.setRowHeightPerc(0, mTitle->getFont()->getHeight() / mSize.y());
|
||||
mGrid.setRowHeightPerc(2, mDeviceInfo->getFont()->getHeight() / mSize.y());
|
||||
mGrid.setRowHeightPerc(3, mDeviceHeld->getFont()->getHeight() / mSize.y());
|
||||
}
|
||||
|
||||
bool GuiDetectDevice::input(InputConfig* config, Input input)
|
||||
{
|
||||
if((input.type == TYPE_BUTTON || input.type == TYPE_KEY))
|
||||
if(input.type == TYPE_BUTTON || input.type == TYPE_KEY)
|
||||
{
|
||||
if(config->getPlayerNum() != -1)
|
||||
if(input.value && mHoldingConfig == NULL)
|
||||
{
|
||||
if(config->getPlayerNum() == 0)
|
||||
{
|
||||
if(input.value)
|
||||
{
|
||||
mFinishTimer = 0;
|
||||
mHoldingFinish = true;
|
||||
}else{
|
||||
mHoldingFinish = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!input.value)
|
||||
return false;
|
||||
|
||||
config->setPlayerNum(mCurrentPlayer);
|
||||
mWindow->getInputManager()->setNumPlayers(mWindow->getInputManager()->getNumPlayers() + 1); //inc total number of players
|
||||
mCurrentPlayer++;
|
||||
|
||||
//mapped everything we possibly can?
|
||||
if(mCurrentPlayer >= mWindow->getInputManager()->getNumJoysticks() + 1) //+1 for keyboard
|
||||
// started holding
|
||||
mHoldingConfig = config;
|
||||
mHoldTime = HOLD_TIME;
|
||||
mDeviceHeld->setText(config->getDeviceName());
|
||||
}else if(!input.value && mHoldingConfig == config)
|
||||
{
|
||||
done();
|
||||
// cancel
|
||||
mHoldingConfig = NULL;
|
||||
mDeviceHeld->setText("");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GuiDetectDevice::done()
|
||||
{
|
||||
mWindow->pushGui(new GuiInputConfig(mWindow, mWindow->getInputManager()->getInputConfigByPlayer(0)));
|
||||
delete this;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GuiDetectDevice::update(int deltaTime)
|
||||
{
|
||||
if(mHoldingFinish)
|
||||
if(mHoldingConfig)
|
||||
{
|
||||
mFinishTimer += deltaTime;
|
||||
|
||||
if(mFinishTimer > 1000)
|
||||
done();
|
||||
mHoldTime -= deltaTime;
|
||||
if(mHoldTime <= 0)
|
||||
{
|
||||
// picked one!
|
||||
mWindow->pushGui(new GuiInputConfig(mWindow, mHoldingConfig, true, mDoneCallback));
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GuiDetectDevice::render(const Eigen::Affine3f& parentTrans)
|
||||
{
|
||||
Eigen::Affine3f trans = parentTrans * getTransform();
|
||||
Renderer::setMatrix(trans);
|
||||
|
||||
std::shared_ptr<Font> font = Font::get(FONT_SIZE_MEDIUM);
|
||||
|
||||
std::string playerString;
|
||||
std::stringstream stream;
|
||||
stream << (mCurrentPlayer + 1);
|
||||
stream >> playerString;
|
||||
|
||||
font->drawCenteredText("Press a button on the device for", 0, Renderer::getScreenHeight() / 3.0f, 0x000000FF);
|
||||
font->drawCenteredText("PLAYER " + playerString, 0, (Renderer::getScreenHeight()*1.5f) / 3.0f, 0x333333FF);
|
||||
|
||||
if(mWindow->getInputManager()->getNumPlayers() > 0)
|
||||
{
|
||||
font->drawCenteredText("(P1 - hold a button to finish)", 0, (Renderer::getScreenHeight()*2) / 3.0f, (mHoldingFinish ? 0x0000FFFF : 0x000066FF));
|
||||
}
|
||||
|
||||
if(mWindow->getInputManager()->getNumJoysticks() == 0)
|
||||
{
|
||||
font->drawCenteredText("No joysticks detected!", 0, Renderer::getScreenHeight() - (font->getHeight()*2.0f)-10, 0xFF0000FF);
|
||||
}
|
||||
|
||||
font->drawCenteredText("Press F4 to quit.", 0, Renderer::getScreenHeight() - font->getHeight() - 2.0f , 0x000000FF);
|
||||
}
|
||||
|
|
|
@ -1,20 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include "../GuiComponent.h"
|
||||
#include "../components/NinePatchComponent.h"
|
||||
#include "../components/ComponentGrid.h"
|
||||
|
||||
class TextComponent;
|
||||
|
||||
class GuiDetectDevice : public GuiComponent
|
||||
{
|
||||
public:
|
||||
GuiDetectDevice(Window* window);
|
||||
GuiDetectDevice(Window* window, bool firstRun);
|
||||
|
||||
bool input(InputConfig* config, Input input);
|
||||
void update(int deltaTime);
|
||||
void render(const Eigen::Affine3f& parentTrans) override;
|
||||
bool input(InputConfig* config, Input input) override;
|
||||
void update(int deltaTime) override;
|
||||
void onSizeChanged() override;
|
||||
|
||||
private:
|
||||
void done();
|
||||
InputConfig* mHoldingConfig;
|
||||
int mHoldTime;
|
||||
|
||||
bool mHoldingFinish;
|
||||
int mFinishTimer;
|
||||
int mCurrentPlayer;
|
||||
NinePatchComponent mBackground;
|
||||
ComponentGrid mGrid;
|
||||
|
||||
std::shared_ptr<TextComponent> mTitle;
|
||||
std::shared_ptr<TextComponent> mMsg;
|
||||
std::shared_ptr<TextComponent> mDeviceInfo;
|
||||
std::shared_ptr<TextComponent> mDeviceHeld;
|
||||
|
||||
std::function<void()> mDoneCallback;
|
||||
};
|
||||
|
|
|
@ -1,115 +1,157 @@
|
|||
#include "GuiInputConfig.h"
|
||||
#include "../Window.h"
|
||||
#include "../Renderer.h"
|
||||
#include "../resources/Font.h"
|
||||
#include "../Log.h"
|
||||
#include "../views/ViewController.h"
|
||||
#include "../components/TextComponent.h"
|
||||
#include "../components/ImageComponent.h"
|
||||
#include "../components/MenuComponent.h"
|
||||
#include "../components/ButtonComponent.h"
|
||||
#include "../Util.h"
|
||||
|
||||
static const int inputCount = 10;
|
||||
static std::string inputName[inputCount] = { "Up", "Down", "Left", "Right", "A", "B", "Menu", "Select", "PageUp", "PageDown"};
|
||||
static std::string inputDispName[inputCount] = { "Up", "Down", "Left", "Right", "Accept", "Back", "Menu", "Jump to Letter", "Page Up", "Page Down"};
|
||||
static const char* inputName[inputCount] = { "Up", "Down", "Left", "Right", "A", "B", "Start", "Select", "PageUp", "PageDown"};
|
||||
static const char* inputDispName[inputCount] = { "Up", "Down", "Left", "Right", "A", "B", "Start", "Select", "Page Up", "Page Down"};
|
||||
static const char* inputIcon[inputCount] = { ":/help/dpad_up.png", ":/help/dpad_down.png", ":/help/dpad_left.png", ":/help/dpad_right.png",
|
||||
":/help/a.png", ":/help/b.png", ":/help/start.png", ":/help/select.png", ":/help/l.png", ":/help/r.png" };
|
||||
|
||||
//MasterVolUp and MasterVolDown are also hooked up, but do not appear on this screen.
|
||||
//If you want, you can manually add them to es_input.cfg.
|
||||
|
||||
GuiInputConfig::GuiInputConfig(Window* window, InputConfig* target) : GuiComponent(window), mTargetConfig(target), mCanSkip(false)
|
||||
using namespace Eigen;
|
||||
|
||||
GuiInputConfig::GuiInputConfig(Window* window, InputConfig* target, bool reconfigureAll, const std::function<void()>& okCallback) : GuiComponent(window),
|
||||
mBackground(window, ":/frame.png"), mGrid(window, Vector2i(1, 5)),
|
||||
mTargetConfig(target)
|
||||
{
|
||||
mCurInputId = 0;
|
||||
LOG(LogInfo) << "Configuring device " << target->getDeviceId();
|
||||
|
||||
if(reconfigureAll)
|
||||
target->clear();
|
||||
|
||||
mConfiguringAll = reconfigureAll;
|
||||
mConfiguringRow = mConfiguringAll;
|
||||
|
||||
addChild(&mBackground);
|
||||
addChild(&mGrid);
|
||||
|
||||
mTitle = std::make_shared<TextComponent>(mWindow, "PLEASE CONFIGURE INPUT FOR", Font::get(FONT_SIZE_SMALL), 0x555555FF, TextComponent::ALIGN_CENTER);
|
||||
mGrid.setEntry(mTitle, Vector2i(0, 0), false, true);
|
||||
|
||||
mSubtitle1 = std::make_shared<TextComponent>(mWindow, target->getDeviceName(), Font::get(FONT_SIZE_MEDIUM), 0x555555FF, TextComponent::ALIGN_CENTER);
|
||||
mGrid.setEntry(mSubtitle1, Vector2i(0, 1), false, true);
|
||||
|
||||
mSubtitle2 = std::make_shared<TextComponent>(mWindow, "(HOLD ANY BUTTON TO SKIP BUT NOT YET)", Font::get(FONT_SIZE_SMALL), 0x999999FF, TextComponent::ALIGN_CENTER);
|
||||
mGrid.setEntry(mSubtitle2, Vector2i(0, 2), false, true);
|
||||
|
||||
mList = std::make_shared<ComponentList>(mWindow);
|
||||
mGrid.setEntry(mList, Vector2i(0, 3), true, true);
|
||||
for(int i = 0; i < inputCount; i++)
|
||||
{
|
||||
ComponentListRow row;
|
||||
|
||||
// icon
|
||||
auto icon = std::make_shared<ImageComponent>(mWindow);
|
||||
icon->setImage(inputIcon[i]);
|
||||
icon->setResize(0, Font::get(FONT_SIZE_MEDIUM)->getHeight() * 0.8f);
|
||||
row.addElement(icon, false);
|
||||
|
||||
auto text = std::make_shared<TextComponent>(mWindow, inputDispName[i], Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
|
||||
row.addElement(text, true);
|
||||
|
||||
auto mapping = std::make_shared<TextComponent>(mWindow, "-NOT DEFINED-", Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT), 0x999999FF);
|
||||
row.addElement(mapping, true);
|
||||
|
||||
row.input_handler = [this, i, mapping](InputConfig* config, Input input) -> bool
|
||||
{
|
||||
if(!input.value)
|
||||
return false;
|
||||
|
||||
if(mConfiguringRow)
|
||||
{
|
||||
if(!process(config, input, i, mapping)) // button press invalid; try again
|
||||
return true;
|
||||
if(mConfiguringAll)
|
||||
{
|
||||
if(!mList->moveCursor(1)) // try to move to the next one
|
||||
{
|
||||
// at bottom of list
|
||||
mConfiguringAll = false;
|
||||
mConfiguringRow = false;
|
||||
mGrid.moveCursor(Vector2i(0, 1));
|
||||
}
|
||||
}else{
|
||||
mConfiguringRow = false; // we only wanted to configure one row
|
||||
}
|
||||
return true;
|
||||
}else{
|
||||
// not configuring, start configuring when A is pressed
|
||||
if(config->isMappedTo("a", input) && input.value)
|
||||
{
|
||||
mConfiguringRow = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
mList->addRow(row);
|
||||
}
|
||||
|
||||
// buttons
|
||||
std::vector< std::shared_ptr<ButtonComponent> > buttons;
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "OK", "ok", [this, okCallback] {
|
||||
mWindow->getInputManager()->writeDeviceConfig(mTargetConfig); // save
|
||||
if(okCallback)
|
||||
okCallback();
|
||||
delete this;
|
||||
}));
|
||||
mButtonGrid = makeButtonGrid(mWindow, buttons);
|
||||
mGrid.setEntry(mButtonGrid, Vector2i(0, 4), true, false);
|
||||
|
||||
setSize(Renderer::getScreenWidth() * 0.6f, Renderer::getScreenHeight() * 0.7f);
|
||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - mSize.y()) / 2);
|
||||
}
|
||||
|
||||
void GuiInputConfig::update(int deltaTime)
|
||||
void GuiInputConfig::onSizeChanged()
|
||||
{
|
||||
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32));
|
||||
|
||||
// update grid
|
||||
mGrid.setSize(mSize);
|
||||
|
||||
mGrid.setRowHeightPerc(0, mTitle->getFont()->getHeight() / mSize.y());
|
||||
mGrid.setRowHeightPerc(1, mSubtitle1->getFont()->getHeight() / mSize.y());
|
||||
mGrid.setRowHeightPerc(2, mSubtitle2->getFont()->getHeight() / mSize.y());
|
||||
mGrid.setRowHeightPerc(4, mButtonGrid->getSize().y() / mSize.y());
|
||||
}
|
||||
|
||||
bool GuiInputConfig::input(InputConfig* config, Input input)
|
||||
void GuiInputConfig::error(const std::string& msg)
|
||||
{
|
||||
if(config != mTargetConfig || input.value == 0)
|
||||
// TODO
|
||||
LOG(LogWarning) << msg;
|
||||
}
|
||||
|
||||
bool GuiInputConfig::process(InputConfig* config, Input input, int inputId, const std::shared_ptr<TextComponent>& text)
|
||||
{
|
||||
// from some other input source
|
||||
if(config != mTargetConfig)
|
||||
return false;
|
||||
|
||||
if(mCurInputId >= inputCount)
|
||||
// if this input is mapped to something other than "nothing" or the current row, error
|
||||
// (if it's the same as what it was before, allow it)
|
||||
if(config->getMappedTo(input).size() > 0 && !config->isMappedTo(inputName[inputId], input))
|
||||
{
|
||||
//done
|
||||
if(input.type == TYPE_BUTTON || input.type == TYPE_KEY)
|
||||
{
|
||||
if(mTargetConfig->getPlayerNum() < mWindow->getInputManager()->getNumPlayers() - 1)
|
||||
{
|
||||
mWindow->pushGui(new GuiInputConfig(mWindow, mWindow->getInputManager()->getInputConfigByPlayer(mTargetConfig->getPlayerNum() + 1)));
|
||||
}else{
|
||||
mWindow->getInputManager()->writeConfig();
|
||||
mWindow->getViewController()->goToStart();
|
||||
}
|
||||
delete this;
|
||||
return true;
|
||||
}
|
||||
}else{
|
||||
if(mCanSkip && config->isMappedTo("a", input))
|
||||
{
|
||||
mCurInputId++;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(config->getMappedTo(input).size() > 0)
|
||||
{
|
||||
mErrorMsg = "Already mapped!";
|
||||
return true;
|
||||
}
|
||||
|
||||
input.configured = true;
|
||||
LOG(LogInfo) << " [" << input.string() << "] -> " << inputName[mCurInputId];
|
||||
|
||||
config->mapInput(inputName[mCurInputId], input);
|
||||
mCurInputId++;
|
||||
mErrorMsg = "";
|
||||
|
||||
//for buttons with not enough buttons, press A to skip
|
||||
if(mCurInputId >= 7)
|
||||
{
|
||||
mCanSkip = true;
|
||||
}
|
||||
return true;
|
||||
error("Already mapped!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GuiInputConfig::render(const Eigen::Affine3f& parentTrans)
|
||||
{
|
||||
Eigen::Affine3f trans = parentTrans * getTransform();
|
||||
Renderer::setMatrix(trans);
|
||||
|
||||
std::shared_ptr<Font> font = Font::get(FONT_SIZE_MEDIUM);
|
||||
|
||||
std::stringstream stream;
|
||||
stream << "PLAYER " << mTargetConfig->getPlayerNum() + 1 << ", press...";
|
||||
font->drawText(stream.str(), Eigen::Vector2f(10, 10), 0x000000FF);
|
||||
|
||||
int y = 14 + (int)font->getHeight();
|
||||
for(int i = 0; i < mCurInputId; i++)
|
||||
{
|
||||
font->drawText(inputDispName[i], Eigen::Vector2f(10, y), 0x00CC00FF);
|
||||
y += (int)font->getHeight() + 5;
|
||||
}
|
||||
|
||||
if(mCurInputId >= inputCount)
|
||||
{
|
||||
font->drawCenteredText("Basic config done!", 0, Renderer::getScreenHeight() * 0.4f, 0x00CC00FF);
|
||||
font->drawCenteredText("Press any button to continue.", 0, Renderer::getScreenHeight() * 0.4f + font->getHeight() + 4, 0x000000FF);
|
||||
}else{
|
||||
font->drawText(inputDispName[mCurInputId], Eigen::Vector2f(10, y), 0x000000FF);
|
||||
if(mCanSkip)
|
||||
{
|
||||
Eigen::Vector2f textSize = font->sizeText(inputDispName[mCurInputId]);
|
||||
textSize[0] += 14;
|
||||
|
||||
if(Renderer::getScreenWidth() / 2.5f > textSize.x())
|
||||
textSize[0] = Renderer::getScreenWidth() / 2.5f;
|
||||
|
||||
font->drawText("press Accept to skip", Eigen::Vector2f(textSize.x(), y), 0x0000AAFF);
|
||||
}
|
||||
}
|
||||
|
||||
if(!mErrorMsg.empty())
|
||||
font->drawCenteredText(mErrorMsg, 0, (float)Renderer::getScreenHeight() - font->getHeight() - 10, 0xFF0000FF);
|
||||
text->setText(strToUpper(input.string()));
|
||||
text->setColor(0x777777FF);
|
||||
|
||||
input.configured = true;
|
||||
config->mapInput(inputName[inputId], input);
|
||||
|
||||
LOG(LogInfo) << " Mapping [" << input.string() << "] -> " << inputName[inputId];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,20 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include "../GuiComponent.h"
|
||||
#include <string>
|
||||
#include "../components/NinePatchComponent.h"
|
||||
#include "../components/ComponentGrid.h"
|
||||
#include "../components/ComponentList.h"
|
||||
|
||||
class TextComponent;
|
||||
|
||||
class GuiInputConfig : public GuiComponent
|
||||
{
|
||||
public:
|
||||
GuiInputConfig(Window* window, InputConfig* target);
|
||||
GuiInputConfig(Window* window, InputConfig* target, bool reconfigureAll, const std::function<void()>& okCallback);
|
||||
|
||||
bool input(InputConfig* config, Input input);
|
||||
void update(int deltaTime);
|
||||
void render(const Eigen::Affine3f& parentTrans) override;
|
||||
void onSizeChanged() override;
|
||||
|
||||
private:
|
||||
std::string mErrorMsg;
|
||||
void error(const std::string& msg);
|
||||
bool process(InputConfig* config, Input input, int inputId, const std::shared_ptr<TextComponent>& text);
|
||||
|
||||
NinePatchComponent mBackground;
|
||||
ComponentGrid mGrid;
|
||||
|
||||
std::shared_ptr<TextComponent> mTitle;
|
||||
std::shared_ptr<TextComponent> mSubtitle1;
|
||||
std::shared_ptr<TextComponent> mSubtitle2;
|
||||
std::shared_ptr<ComponentList> mList;
|
||||
std::shared_ptr<ComponentGrid> mButtonGrid;
|
||||
|
||||
InputConfig* mTargetConfig;
|
||||
int mCurInputId;
|
||||
bool mCanSkip;
|
||||
bool mConfiguringRow; // next input captured by mList will be interpretted as a remap
|
||||
bool mConfiguringAll; // move the cursor down after configuring a row and start configuring the next row until we reach the bottom
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "GuiMsgBox.h"
|
||||
#include "GuiSettings.h"
|
||||
#include "GuiScraperStart.h"
|
||||
#include "GuiDetectDevice.h"
|
||||
|
||||
#include "../components/ButtonComponent.h"
|
||||
#include "../components/SwitchComponent.h"
|
||||
|
@ -120,6 +121,11 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN
|
|||
mWindow->pushGui(s);
|
||||
});
|
||||
|
||||
addEntry("CONFIGURE INPUT", 0x777777FF, true,
|
||||
[this] {
|
||||
mWindow->pushGui(new GuiDetectDevice(mWindow, false));
|
||||
});
|
||||
|
||||
addEntry("QUIT", 0x777777FF, true,
|
||||
[this] {
|
||||
auto s = new GuiSettings(mWindow, "QUIT");
|
||||
|
@ -191,7 +197,7 @@ void GuiMenu::addEntry(const char* name, unsigned int color, bool add_arrow, con
|
|||
|
||||
bool GuiMenu::input(InputConfig* config, Input input)
|
||||
{
|
||||
if((config->isMappedTo("b", input) || config->isMappedTo("menu", input)) && input.value != 0)
|
||||
if((config->isMappedTo("b", input) || config->isMappedTo("start", input)) && input.value != 0)
|
||||
{
|
||||
delete this;
|
||||
return true;
|
||||
|
|
|
@ -175,11 +175,11 @@ int main(int argc, char* argv[])
|
|||
window.getViewController()->preload();
|
||||
|
||||
//choose which GUI to open depending on if an input configuration already exists
|
||||
if(fs::exists(InputManager::getConfigPath()))
|
||||
if(fs::exists(InputManager::getConfigPath()) && window.getInputManager()->getNumConfiguredDevices() > 0)
|
||||
{
|
||||
window.getViewController()->goToStart();
|
||||
}else{
|
||||
window.pushGui(new GuiDetectDevice(&window));
|
||||
window.pushGui(new GuiDetectDevice(&window, true));
|
||||
}
|
||||
|
||||
//generate joystick events since we're done loading
|
||||
|
|
|
@ -161,7 +161,7 @@ bool ViewController::input(InputConfig* config, Input input)
|
|||
return true;
|
||||
|
||||
// open menu
|
||||
if(config->isMappedTo("menu", input) && input.value != 0)
|
||||
if(config->isMappedTo("start", input) && input.value != 0)
|
||||
{
|
||||
// open menu
|
||||
mWindow->pushGui(new GuiMenu(mWindow));
|
||||
|
@ -255,7 +255,7 @@ std::vector<HelpPrompt> ViewController::getHelpPrompts()
|
|||
return prompts;
|
||||
|
||||
prompts = mCurrentView->getHelpPrompts();
|
||||
prompts.push_back(HelpPrompt("menu", "open menu"));
|
||||
prompts.push_back(HelpPrompt("start", "open menu"));
|
||||
|
||||
return prompts;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue