mirror of https://github.com/mgba-emu/mgba.git
Qt: Default controller profiles, with a few included already
This commit is contained in:
parent
b9c276ee30
commit
963758c348
1
CHANGES
1
CHANGES
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -61,6 +61,7 @@ set(SOURCE_FILES
|
|||
GamepadAxisEvent.cpp
|
||||
GamepadButtonEvent.cpp
|
||||
InputController.cpp
|
||||
InputProfile.cpp
|
||||
KeyEditor.cpp
|
||||
LoadSaveState.cpp
|
||||
LogController.cpp
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue