Qt: Default controller profiles, with a few included already

This commit is contained in:
Jeffrey Pfau 2015-07-16 23:45:55 -07:00
parent b9c276ee30
commit 963758c348
11 changed files with 297 additions and 13 deletions

View File

@ -26,6 +26,7 @@ Features:
- Configurable display driver, between software and OpenGL
- Undo-able savestate loading and saving
- Controller profiles now store shortcut settings
- Default controller profiles for several common controllers
Bugfixes:
- ARM7: Fix SWI and IRQ timings
- GBA Audio: Force audio FIFOs to 32-bit

View File

@ -257,7 +257,10 @@ void _unbindAxis(uint32_t axis, void* dp, void* user) {
}
}
static void _loadAll(struct GBAInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config) {
static bool _loadAll(struct GBAInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config) {
if (!ConfigurationHasSection(config, sectionName)) {
return false;
}
_loadKey(map, type, sectionName, config, GBA_KEY_A, "A");
_loadKey(map, type, sectionName, config, GBA_KEY_B, "B");
_loadKey(map, type, sectionName, config, GBA_KEY_L, "L");
@ -279,6 +282,7 @@ static void _loadAll(struct GBAInputMap* map, uint32_t type, const char* section
_loadAxis(map, type, sectionName, config, GBA_KEY_DOWN, "Down");
_loadAxis(map, type, sectionName, config, GBA_KEY_LEFT, "Left");
_loadAxis(map, type, sectionName, config, GBA_KEY_RIGHT, "Right");
return true;
}
static void _saveAll(const struct GBAInputMap* map, uint32_t type, const char* sectionName, struct Configuration* config) {
@ -469,11 +473,11 @@ void GBAInputMapSave(const struct GBAInputMap* map, uint32_t type, struct Config
_saveAll(map, type, sectionName, config);
}
void GBAInputProfileLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config, const char* profile) {
bool GBAInputProfileLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config, const char* profile) {
char sectionName[SECTION_NAME_MAX];
snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
sectionName[SECTION_NAME_MAX - 1] = '\0';
_loadAll(map, type, sectionName, config);
return _loadAll(map, type, sectionName, config);
}
void GBAInputProfileSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config, const char* profile) {

View File

@ -45,7 +45,7 @@ void GBAInputEnumerateAxes(const struct GBAInputMap*, uint32_t type, void (handl
void GBAInputMapLoad(struct GBAInputMap*, uint32_t type, const struct Configuration*);
void GBAInputMapSave(const struct GBAInputMap*, uint32_t type, struct Configuration*);
void GBAInputProfileLoad(struct GBAInputMap*, uint32_t type, const struct Configuration*, const char* profile);
bool GBAInputProfileLoad(struct GBAInputMap*, uint32_t type, const struct Configuration*, const char* profile);
void GBAInputProfileSave(const struct GBAInputMap*, uint32_t type, struct Configuration*, const char* profile);
const char* GBAInputGetPreferredDevice(const struct Configuration*, uint32_t type, int playerId);

View File

@ -61,6 +61,7 @@ set(SOURCE_FILES
GamepadAxisEvent.cpp
GamepadButtonEvent.cpp
InputController.cpp
InputProfile.cpp
KeyEditor.cpp
LoadSaveState.cpp
LogController.cpp

View File

@ -8,6 +8,7 @@
#include "ConfigController.h"
#include "GamepadAxisEvent.h"
#include "GamepadButtonEvent.h"
#include "InputProfile.h"
#include <QApplication>
#include <QTimer>
@ -106,8 +107,14 @@ void InputController::loadConfiguration(uint32_t type) {
}
void InputController::loadProfile(uint32_t type, const QString& profile) {
GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
bool loaded = GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
recalibrateAxes();
if (!loaded) {
const InputProfile* ip = InputProfile::findProfile(profile);
if (ip) {
ip->apply(this);
}
}
emit profileLoaded(profile);
}

View File

@ -0,0 +1,170 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "InputProfile.h"
#include "InputController.h"
#include <QRegExp>
using namespace QGBA;
const InputProfile InputProfile::s_defaultMaps[] = {
{
"PLAYSTATION\\(R\\)3 Controller", // DualShock 3 (OS X)
(int[GBA_KEY_MAX]) {
/*keyA */ 13,
/*keyB */ 14,
/*keySelect */ 0,
/*keyStart */ 3,
/*keyRight */ -1,
/*keyLeft */ -1,
/*keyUp */ -1,
/*keyDown */ -1,
/*keyR */ 11,
/*keyL */ 10
},
(ShortcutButton[]) {
{"loadState", 15},
{"saveState", 12},
{"holdFastForward", 9},
{"holdRewind", 8},
{}
}
},
{
"Wiimote \\(..-..-..-..-..-..\\)", // WJoy (OS X)
(int[GBA_KEY_MAX]) {
/*keyA */ 15,
/*keyB */ 16,
/*keySelect */ 7,
/*keyStart */ 6,
/*keyRight */ -1,
/*keyLeft */ -1,
/*keyUp */ -1,
/*keyDown */ -1,
/*keyR */ 20,
/*keyL */ 19
},
(ShortcutButton[]) {
{"loadState", 18},
{"saveState", 17},
{"holdFastForward", 22},
{"holdRewind", 21},
{}
}
},
{
"Controller", // The Xbox 360 controller drivers on OS X are vague...
(int[GBA_KEY_MAX]) {
/*keyA */ 1,
/*keyB */ 0,
/*keySelect */ 9,
/*keyStart */ 8,
/*keyRight */ -1,
/*keyLeft */ -1,
/*keyUp */ -1,
/*keyDown */ -1,
/*keyR */ 5,
/*keyL */ 4
},
(ShortcutButton[]) {
{"loadState", 2},
{"saveState", 3},
{}
},
(ShortcutAxis[]) {
{"holdFastForward", GamepadAxisEvent::Direction::POSITIVE, 5},
{"holdRewind", GamepadAxisEvent::Direction::POSITIVE, 2},
{}
}
},
};
constexpr InputProfile::InputProfile(const char* name,
int keys[GBA_KEY_MAX],
const ShortcutButton* shortcutButtons,
const ShortcutAxis* shortcutAxes,
AxisValue axes[GBA_KEY_MAX],
const struct Coord& tiltAxis,
const struct Coord& gyroAxis,
float gyroSensitivity)
: m_profileName(name)
, m_keys {
keys[GBA_KEY_A],
keys[GBA_KEY_B],
keys[GBA_KEY_SELECT],
keys[GBA_KEY_START],
keys[GBA_KEY_RIGHT],
keys[GBA_KEY_LEFT],
keys[GBA_KEY_UP],
keys[GBA_KEY_DOWN],
keys[GBA_KEY_R],
keys[GBA_KEY_L]
}
, m_shortcutButtons(shortcutButtons)
, m_shortcutAxes(shortcutAxes)
, m_axes {
axes[GBA_KEY_A],
axes[GBA_KEY_B],
axes[GBA_KEY_SELECT],
axes[GBA_KEY_START],
axes[GBA_KEY_RIGHT],
axes[GBA_KEY_LEFT],
axes[GBA_KEY_UP],
axes[GBA_KEY_DOWN],
axes[GBA_KEY_R],
axes[GBA_KEY_L]
}
, m_tiltAxis(tiltAxis)
, m_gyroAxis(gyroAxis)
, m_gyroSensitivity(gyroSensitivity)
{
}
const InputProfile* InputProfile::findProfile(const QString& name) {
for (size_t i = 0; i < sizeof(s_defaultMaps) / sizeof(*s_defaultMaps); ++i) {
QRegExp re(s_defaultMaps[i].m_profileName);
if (re.exactMatch(name)) {
return &s_defaultMaps[i];
}
}
return nullptr;
}
void InputProfile::apply(InputController* controller) const {
for (size_t i = 0; i < GBA_KEY_MAX; ++i) {
controller->bindKey(SDL_BINDING_BUTTON, m_keys[i], static_cast<GBAKey>(i));
controller->bindAxis(SDL_BINDING_BUTTON, m_axes[i].axis, m_axes[i].direction, static_cast<GBAKey>(i));
}
controller->registerTiltAxisX(m_tiltAxis.x);
controller->registerTiltAxisY(m_tiltAxis.y);
controller->registerGyroAxisX(m_gyroAxis.x);
controller->registerGyroAxisY(m_gyroAxis.y);
controller->setGyroSensitivity(m_gyroSensitivity);
}
bool InputProfile::lookupShortcutButton(const QString& shortcutName, int* button) const {
for (size_t i = 0; m_shortcutButtons[i].shortcut; ++i) {
const ShortcutButton& shortcut = m_shortcutButtons[i];
if (QLatin1String(shortcut.shortcut) == shortcutName) {
*button = shortcut.button;
return true;
}
}
return false;
}
bool InputProfile::lookupShortcutAxis(const QString& shortcutName, int* axis, GamepadAxisEvent::Direction* direction) const {
for (size_t i = 0; m_shortcutAxes[i].shortcut; ++i) {
const ShortcutAxis& shortcut = m_shortcutAxes[i];
if (QLatin1String(shortcut.shortcut) == shortcutName) {
*axis = shortcut.axis;
*direction = shortcut.direction;
return true;
}
}
return false;
}

View File

@ -0,0 +1,78 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef QGBA_INPUT_PROFILE
#define QGBA_INPUT_PROFILE
#include "GamepadAxisEvent.h"
extern "C" {
#include "gba/interface.h"
}
namespace QGBA {
class InputController;
class InputProfile {
public:
static const InputProfile* findProfile(const QString& name);
void apply(InputController*) const;
bool lookupShortcutButton(const QString& shortcut, int* button) const;
bool lookupShortcutAxis(const QString& shortcut, int* axis, GamepadAxisEvent::Direction* direction) const;
private:
struct Coord {
int x;
int y;
};
struct AxisValue {
GamepadAxisEvent::Direction direction;
int axis;
};
struct ShortcutButton {
const char* shortcut;
int button;
};
struct ShortcutAxis {
const char* shortcut;
GamepadAxisEvent::Direction direction;
int axis;
};
constexpr InputProfile(const char* name,
int keys[GBA_KEY_MAX],
const ShortcutButton* shortcutButtons = (ShortcutButton[]) {{}},
const ShortcutAxis* shortcutAxes = (ShortcutAxis[]) {{}},
AxisValue axes[GBA_KEY_MAX] = (AxisValue[GBA_KEY_MAX]) {
{}, {}, {}, {},
{ GamepadAxisEvent::Direction::POSITIVE, 0 },
{ GamepadAxisEvent::Direction::NEGATIVE, 0 },
{ GamepadAxisEvent::Direction::NEGATIVE, 1 },
{ GamepadAxisEvent::Direction::POSITIVE, 1 },
{}, {}},
const struct Coord& tiltAxis = { 2, 3 },
const struct Coord& gyroAxis = { 0, 1 },
float gyroSensitivity = 2e+09f);
static const InputProfile s_defaultMaps[];
const char* m_profileName;
const int m_keys[GBA_KEY_MAX];
const AxisValue m_axes[GBA_KEY_MAX];
const ShortcutButton* m_shortcutButtons;
const ShortcutAxis* m_shortcutAxes;
Coord m_tiltAxis;
Coord m_gyroAxis;
float m_gyroSensitivity;
};
}
#endif

View File

@ -7,6 +7,7 @@
#include "ConfigController.h"
#include "GamepadButtonEvent.h"
#include "InputProfile.h"
#include <QAction>
#include <QKeyEvent>
@ -18,6 +19,7 @@ ShortcutController::ShortcutController(QObject* parent)
: QAbstractItemModel(parent)
, m_rootMenu(nullptr)
, m_config(nullptr)
, m_profile(nullptr)
{
}
@ -239,8 +241,8 @@ void ShortcutController::updateButton(const QModelIndex& index, int button) {
}
if (m_config) {
m_config->setQtOption(item->name(), button, BUTTON_SECTION);
if (!m_profile.isNull()) {
m_config->setQtOption(item->name(), button, BUTTON_PROFILE_SECTION + m_profile);
if (!m_profileName.isNull()) {
m_config->setQtOption(item->name(), button, BUTTON_PROFILE_SECTION + m_profileName);
}
}
emit dataChanged(createIndex(index.row(), 0, index.internalPointer()),
@ -275,8 +277,8 @@ void ShortcutController::updateAxis(const QModelIndex& index, int axis, GamepadA
d = '-';
}
m_config->setQtOption(item->name(), QString("%1%2").arg(d).arg(axis), AXIS_SECTION);
if (!m_profile.isNull()) {
m_config->setQtOption(item->name(), QString("%1%2").arg(d).arg(axis), AXIS_PROFILE_SECTION + m_profile);
if (!m_profileName.isNull()) {
m_config->setQtOption(item->name(), QString("%1%2").arg(d).arg(axis), AXIS_PROFILE_SECTION + m_profileName);
}
}
emit dataChanged(createIndex(index.row(), 0, index.internalPointer()),
@ -393,24 +395,37 @@ void ShortcutController::loadGamepadShortcuts(ShortcutItem* item) {
if (item->name().isNull()) {
return;
}
QVariant button = m_config->getQtOption(item->name(), !m_profile.isNull() ? BUTTON_PROFILE_SECTION + m_profile : BUTTON_SECTION);
QVariant button = m_config->getQtOption(item->name(), !m_profileName.isNull() ? BUTTON_PROFILE_SECTION + m_profileName : BUTTON_SECTION);
int oldButton = item->button();
if (oldButton >= 0) {
m_buttons.take(oldButton);
item->setButton(-1);
}
if (button.isNull() && m_profile) {
int buttonInt;
if (m_profile->lookupShortcutButton(item->name(), &buttonInt)) {
button = buttonInt;
}
}
if (!button.isNull()) {
item->setButton(button.toInt());
m_buttons[button.toInt()] = item;
}
QVariant axis = m_config->getQtOption(item->name(), !m_profile.isNull() ? AXIS_PROFILE_SECTION + m_profile : AXIS_SECTION);
QVariant axis = m_config->getQtOption(item->name(), !m_profileName.isNull() ? AXIS_PROFILE_SECTION + m_profileName : AXIS_SECTION);
int oldAxis = item->axis();
GamepadAxisEvent::Direction oldDirection = item->direction();
if (oldAxis >= 0) {
m_axes.take(qMakePair(oldAxis, oldDirection));
item->setAxis(-1, GamepadAxisEvent::NEUTRAL);
}
if (axis.isNull() && m_profile) {
int axisInt;
GamepadAxisEvent::Direction direction;
if (m_profile->lookupShortcutAxis(item->name(), &axisInt, &direction)) {
axis = QLatin1String(direction == GamepadAxisEvent::Direction::NEGATIVE ? "-" : "+") + QString::number(axisInt);
}
}
if (!axis.isNull()) {
QString axisDesc = axis.toString();
if (axisDesc.size() >= 2) {
@ -452,7 +467,8 @@ QKeySequence ShortcutController::keyEventToSequence(const QKeyEvent* event) {
}
void ShortcutController::loadProfile(const QString& profile) {
m_profile = profile;
m_profileName = profile;
m_profile = InputProfile::findProfile(profile);
onSubitems(&m_rootMenu, [this](ShortcutItem* item) {
loadGamepadShortcuts(item);
});

View File

@ -21,6 +21,7 @@ class QString;
namespace QGBA {
class ConfigController;
class InputProfile;
class ShortcutController : public QAbstractItemModel {
Q_OBJECT
@ -133,7 +134,8 @@ private:
QMap<QPair<int, GamepadAxisEvent::Direction>, ShortcutItem*> m_axes;
QMap<QKeySequence, ShortcutItem*> m_heldKeys;
ConfigController* m_config;
QString m_profile;
QString m_profileName;
const InputProfile* m_profile;
};
}

View File

@ -100,6 +100,10 @@ void ConfigurationClearValue(struct Configuration* configuration, const char* se
HashTableRemove(currentSection, key);
}
bool ConfigurationHasSection(const struct Configuration* configuration, const char* section) {
return HashTableLookup(&configuration->sections, section);
}
const char* ConfigurationGetValue(const struct Configuration* configuration, const char* section, const char* key) {
const struct Table* currentSection = &configuration->root;
if (section) {

View File

@ -23,6 +23,7 @@ void ConfigurationSetIntValue(struct Configuration*, const char* section, const
void ConfigurationSetUIntValue(struct Configuration*, const char* section, const char* key, unsigned value);
void ConfigurationSetFloatValue(struct Configuration*, const char* section, const char* key, float value);
bool ConfigurationHasSection(const struct Configuration*, const char* section);
const char* ConfigurationGetValue(const struct Configuration*, const char* section, const char* key);
void ConfigurationClearValue(struct Configuration*, const char* section, const char* key);