Merge branch 'revamp/qt-input'

This commit is contained in:
Vicki Pfau 2023-01-22 19:38:31 -08:00
commit 7824a317ae
42 changed files with 1428 additions and 537 deletions

View File

@ -55,6 +55,7 @@ int mInputMapKeyBits(const struct mInputMap* map, uint32_t type, uint32_t bits,
void mInputBindKey(struct mInputMap*, uint32_t type, int key, int input);
int mInputQueryBinding(const struct mInputMap*, uint32_t type, int input);
void mInputUnbindKey(struct mInputMap*, uint32_t type, int input);
void mInputUnbindAllKeys(struct mInputMap*, uint32_t type);
int mInputMapAxis(const struct mInputMap*, uint32_t type, int axis, int value);
int mInputClearAxis(const struct mInputMap*, uint32_t type, int axis, int keys);
@ -69,6 +70,7 @@ void mInputBindHat(struct mInputMap*, uint32_t type, int id, const struct mInput
bool mInputQueryHat(const struct mInputMap*, uint32_t type, int id, struct mInputHatBindings* bindings);
void mInputUnbindHat(struct mInputMap*, uint32_t type, int id);
void mInputUnbindAllHats(struct mInputMap*, uint32_t type);
void mInputEnumerateHats(const struct mInputMap*, uint32_t type, void (handler(int hat, const struct mInputHatBindings* bindings, void* user)), void* user);
bool mInputMapLoad(struct mInputMap*, uint32_t type, const struct Configuration*);
void mInputMapSave(const struct mInputMap*, uint32_t type, struct Configuration*);

View File

@ -38,6 +38,11 @@ struct mInputAxisEnumerate {
void* user;
};
struct mInputHatEnumerate {
void (*handler)(int axis, const struct mInputHatBindings* bindings, void* user);
void* user;
};
static void _makeSectionName(const char* platform, char* sectionName, size_t len, uint32_t type) {
snprintf(sectionName, len, "%s.input.%c%c%c%c", platform, type >> 24, type >> 16, type >> 8, type);
sectionName[len - 1] = '\0';
@ -304,6 +309,12 @@ void _unbindAxis(uint32_t axis, void* dp, void* user) {
}
}
void _enumerateHat(uint32_t axis, void* dp, void* ep) {
struct mInputHatEnumerate* enumUser = ep;
const struct mInputHatBindings* description = dp;
enumUser->handler(axis, description, enumUser->user);
}
static bool _loadAll(struct mInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config) {
if (!ConfigurationHasSection(config, sectionName)) {
return false;
@ -415,6 +426,16 @@ void mInputUnbindKey(struct mInputMap* map, uint32_t type, int input) {
}
}
void mInputUnbindAllKeys(struct mInputMap* map, uint32_t type) {
struct mInputMapImpl* impl = _lookupMap(map, type);
if (impl) {
size_t i;
for (i = 0; i < map->info->nKeys; ++i) {
impl->map[i] = -1;
}
}
}
int mInputQueryBinding(const struct mInputMap* map, uint32_t type, int input) {
if (input < 0 || (size_t) input >= map->info->nKeys) {
return -1;
@ -578,6 +599,18 @@ void mInputUnbindAllHats(struct mInputMap* map, uint32_t type) {
}
}
void mInputEnumerateHats(const struct mInputMap* map, uint32_t type, void (handler(int hat, const struct mInputHatBindings* bindings, void* user)), void* user) {
const struct mInputMapImpl* impl = _lookupMapConst(map, type);
if (!impl) {
return;
}
struct mInputHatEnumerate enumUser = {
handler,
user
};
TableEnumerate(&impl->axes, _enumerateHat, &enumUser);
}
bool mInputMapLoad(struct mInputMap* map, uint32_t type, const struct Configuration* config) {
char sectionName[SECTION_NAME_MAX];
_makeSectionName(map->info->platformName, sectionName, SECTION_NAME_MAX, type);

View File

@ -103,10 +103,15 @@ set(SOURCE_FILES
GBAApp.cpp
GBAKeyEditor.cpp
GIFView.cpp
GamepadAxisEvent.cpp
GamepadButtonEvent.cpp
GamepadHatEvent.cpp
IOViewer.cpp
input/Gamepad.cpp
input/GamepadAxisEvent.cpp
input/GamepadButtonEvent.cpp
input/GamepadHatEvent.cpp
input/InputDriver.cpp
input/InputSource.cpp
input/InputMapper.cpp
input/KeySource.cpp
InputController.cpp
InputProfile.cpp
KeyEditor.cpp
@ -198,6 +203,7 @@ set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libqt5widgets5
set(AUDIO_SRC)
if(BUILD_SDL)
list(APPEND SOURCE_FILES input/SDLInputDriver.cpp)
list(APPEND AUDIO_SRC AudioProcessorSDL.cpp)
endif()

View File

@ -32,6 +32,10 @@
#include "DiscordCoordinator.h"
#endif
#ifdef BUILD_SDL
#include "input/SDLInputDriver.h"
#endif
using namespace QGBA;
static GBAApp* g_app = nullptr;
@ -309,6 +313,25 @@ bool GBAApp::waitOnJob(qint64 jobId, QObject* context, std::function<void ()> ca
return true;
}
void GBAApp::suspendScreensaver() {
#ifdef BUILD_SDL
SDL::suspendScreensaver();
#endif
}
void GBAApp::resumeScreensaver() {
#ifdef BUILD_SDL
SDL::resumeScreensaver();
#endif
}
void GBAApp::setScreensaverSuspendable(bool suspendable) {
UNUSED(suspendable);
#ifdef BUILD_SDL
SDL::setScreensaverSuspendable(suspendable);
#endif
}
void GBAApp::cleanupAfterUpdate() {
// Remove leftover updater if there's one present
QDir configDir(ConfigController::configDir());

View File

@ -86,6 +86,10 @@ public slots:
void restartForUpdate();
Window* newWindow();
void suspendScreensaver();
void resumeScreensaver();
void setScreensaverSuspendable(bool);
signals:
void jobFinished(qint64 jobId);

View File

@ -13,6 +13,7 @@
#include <QPushButton>
#include <QVBoxLayout>
#include "input/InputMapper.h"
#include "InputController.h"
#include "KeyEditor.h"
@ -214,8 +215,9 @@ void GBAKeyEditor::setNext() {
void GBAKeyEditor::save() {
#ifdef BUILD_SDL
m_controller->unbindAllAxes(m_type);
m_controller->unbindAllHats(m_type);
InputMapper mapper = m_controller->mapper(m_type);
mapper.unbindAllAxes();
mapper.unbindAllHats();
#endif
bindKey(m_keyDU, GBA_KEY_UP);
@ -260,7 +262,7 @@ void GBAKeyEditor::refresh() {
#endif
}
void GBAKeyEditor::lookupBinding(const mInputMap* map, KeyEditor* keyEditor, GBAKey key) {
void GBAKeyEditor::lookupBinding(const mInputMap* map, KeyEditor* keyEditor, int key) {
#ifdef BUILD_SDL
if (m_type == SDL_BINDING_BUTTON) {
int value = mInputQueryBinding(map, m_type, key);
@ -275,14 +277,14 @@ void GBAKeyEditor::lookupBinding(const mInputMap* map, KeyEditor* keyEditor, GBA
void GBAKeyEditor::lookupAxes(const mInputMap* map) {
mInputEnumerateAxes(map, m_type, [](int axis, const mInputAxis* description, void* user) {
GBAKeyEditor* self = static_cast<GBAKeyEditor*>(user);
if (description->highDirection != GBA_KEY_NONE) {
KeyEditor* key = self->keyById(static_cast<enum GBAKey>(description->highDirection));
if (description->highDirection != -1) {
KeyEditor* key = self->keyById(description->highDirection);
if (key) {
key->setValueAxis(axis, GamepadAxisEvent::POSITIVE);
}
}
if (description->lowDirection != GBA_KEY_NONE) {
KeyEditor* key = self->keyById(static_cast<enum GBAKey>(description->lowDirection));
if (description->lowDirection != -1) {
KeyEditor* key = self->keyById(description->lowDirection);
if (key) {
key->setValueAxis(axis, GamepadAxisEvent::NEGATIVE);
}
@ -295,25 +297,25 @@ void GBAKeyEditor::lookupHats(const mInputMap* map) {
int i = 0;
while (mInputQueryHat(map, m_type, i, &bindings)) {
if (bindings.up >= 0) {
KeyEditor* key = keyById(static_cast<enum GBAKey>(bindings.up));
KeyEditor* key = keyById(bindings.up);
if (key) {
key->setValueHat(i, GamepadHatEvent::UP);
}
}
if (bindings.right >= 0) {
KeyEditor* key = keyById(static_cast<enum GBAKey>(bindings.right));
KeyEditor* key = keyById(bindings.right);
if (key) {
key->setValueHat(i, GamepadHatEvent::RIGHT);
}
}
if (bindings.down >= 0) {
KeyEditor* key = keyById(static_cast<enum GBAKey>(bindings.down));
KeyEditor* key = keyById(bindings.down);
if (key) {
key->setValueHat(i, GamepadHatEvent::DOWN);
}
}
if (bindings.left >= 0) {
KeyEditor* key = keyById(static_cast<enum GBAKey>(bindings.left));
KeyEditor* key = keyById(bindings.left);
if (key) {
key->setValueHat(i, GamepadHatEvent::LEFT);
}
@ -323,16 +325,17 @@ void GBAKeyEditor::lookupHats(const mInputMap* map) {
}
#endif
void GBAKeyEditor::bindKey(const KeyEditor* keyEditor, GBAKey key) {
void GBAKeyEditor::bindKey(const KeyEditor* keyEditor, int key) {
InputMapper mapper = m_controller->mapper(m_type);
#ifdef BUILD_SDL
if (m_type == SDL_BINDING_BUTTON && keyEditor->axis() >= 0) {
m_controller->bindAxis(m_type, keyEditor->axis(), keyEditor->direction(), key);
mapper.bindAxis(keyEditor->axis(), keyEditor->direction(), key);
}
if (m_type == SDL_BINDING_BUTTON && keyEditor->hat() >= 0) {
m_controller->bindHat(m_type, keyEditor->hat(), keyEditor->hatDirection(), key);
mapper.bindHat(keyEditor->hat(), keyEditor->hatDirection(), key);
}
#endif
m_controller->bindKey(m_type, keyEditor->value(), key);
mapper.bindKey(keyEditor->value(), key);
}
bool GBAKeyEditor::findFocus(KeyEditor* needle) {
@ -358,7 +361,7 @@ void GBAKeyEditor::selectGamepad(int index) {
}
#endif
KeyEditor* GBAKeyEditor::keyById(GBAKey key) {
KeyEditor* GBAKeyEditor::keyById(int key) {
switch (key) {
case GBA_KEY_UP:
return m_keyDU;
@ -395,14 +398,13 @@ void GBAKeyEditor::setLocation(QWidget* widget, qreal x, qreal y) {
#ifdef BUILD_SDL
void GBAKeyEditor::updateJoysticks() {
m_controller->updateJoysticks();
m_controller->recalibrateAxes();
m_controller->update();
// Block the currentIndexChanged signal while rearranging the combo box
auto wasBlocked = m_profileSelect->blockSignals(true);
m_profileSelect->clear();
m_profileSelect->addItems(m_controller->connectedGamepads(m_type));
int activeGamepad = m_controller->gamepad(m_type);
int activeGamepad = m_controller->gamepadIndex(m_type);
m_profileSelect->setCurrentIndex(activeGamepad);
m_profileSelect->blockSignals(wasBlocked);

View File

@ -54,8 +54,8 @@ private:
void setLocation(QWidget* widget, qreal x, qreal y);
void lookupBinding(const mInputMap*, KeyEditor*, GBAKey);
void bindKey(const KeyEditor*, GBAKey);
void lookupBinding(const mInputMap*, KeyEditor*, int key);
void bindKey(const KeyEditor*, int key);
bool findFocus(KeyEditor* needle = nullptr);
@ -64,7 +64,7 @@ private:
void lookupHats(const mInputMap*);
#endif
KeyEditor* keyById(GBAKey);
KeyEditor* keyById(int);
QComboBox* m_profileSelect = nullptr;
QWidget* m_clear = nullptr;

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2014 Jeffrey Pfau
/* Copyright (c) 2013-2023 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
@ -6,8 +6,8 @@
#include "InputController.h"
#include "ConfigController.h"
#include "GamepadAxisEvent.h"
#include "GamepadButtonEvent.h"
#include "input/Gamepad.h"
#include "input/GamepadButtonEvent.h"
#include "InputProfile.h"
#include "LogController.h"
#include "utils.h"
@ -25,11 +25,6 @@
using namespace QGBA;
#ifdef BUILD_SDL
int InputController::s_sdlInited = 0;
mSDLEvents InputController::s_sdlEvents;
#endif
InputController::InputController(int playerId, QWidget* topLevel, QObject* parent)
: QObject(parent)
, m_playerId(playerId)
@ -38,23 +33,17 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren
{
mInputMapInit(&m_inputMap, &GBAInputInfo);
#ifdef BUILD_SDL
if (s_sdlInited == 0) {
mSDLInitEvents(&s_sdlEvents);
}
++s_sdlInited;
m_sdlPlayer.bindings = &m_inputMap;
updateJoysticks();
#endif
#ifdef BUILD_SDL
connect(&m_gamepadTimer, &QTimer::timeout, [this]() {
testGamepad(SDL_BINDING_BUTTON);
for (auto& driver : m_inputDrivers) {
if (driver->supportsPolling() && driver->supportsGamepads()) {
testGamepad(driver->type());
}
}
if (m_playerId == 0) {
updateJoysticks();
update();
}
});
#endif
m_gamepadTimer.setInterval(50);
m_gamepadTimer.start();
@ -141,43 +130,32 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren
InputController::~InputController() {
mInputMapDeinit(&m_inputMap);
}
#ifdef BUILD_SDL
if (m_playerAttached) {
mSDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
void InputController::addInputDriver(std::shared_ptr<InputDriver> driver) {
m_inputDrivers[driver->type()] = driver;
if (!m_sensorDriver && driver->supportsSensors()) {
m_sensorDriver = driver->type();
}
--s_sdlInited;
if (s_sdlInited == 0) {
mSDLDeinitEvents(&s_sdlEvents);
}
#endif
}
void InputController::setConfiguration(ConfigController* config) {
m_config = config;
loadConfiguration(KEYBOARD);
#ifdef BUILD_SDL
mSDLEventsLoadConfig(&s_sdlEvents, config->input());
if (!m_playerAttached) {
m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
for (auto& driver : m_inputDrivers) {
driver->loadConfiguration(config);
}
if (!loadConfiguration(SDL_BINDING_BUTTON)) {
mSDLInitBindingsGBA(&m_inputMap);
}
loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
#endif
}
bool InputController::loadConfiguration(uint32_t type) {
if (!mInputMapLoad(&m_inputMap, type, m_config->input())) {
return false;
}
#ifdef BUILD_SDL
if (m_playerAttached) {
mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input());
auto driver = m_inputDrivers.value(type);
if (!driver) {
return false;
}
#endif
driver->loadConfiguration(m_config);
return true;
}
@ -186,7 +164,6 @@ bool InputController::loadProfile(uint32_t type, const QString& profile) {
return false;
}
bool loaded = mInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
recalibrateAxes();
if (!loaded) {
const InputProfile* ip = InputProfile::findProfile(profile);
if (ip) {
@ -200,18 +177,18 @@ bool InputController::loadProfile(uint32_t type, const QString& profile) {
void InputController::saveConfiguration() {
saveConfiguration(KEYBOARD);
#ifdef BUILD_SDL
saveConfiguration(SDL_BINDING_BUTTON);
saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
if (m_playerAttached) {
mSDLPlayerSaveConfig(&m_sdlPlayer, m_config->input());
for (auto& driver : m_inputDrivers) {
driver->saveConfiguration(m_config);
}
#endif
m_config->write();
}
void InputController::saveConfiguration(uint32_t type) {
mInputMapSave(&m_inputMap, type, m_config->input());
auto driver = m_inputDrivers.value(type);
if (driver) {
driver->saveConfiguration(m_config);
}
m_config->write();
}
@ -223,372 +200,242 @@ void InputController::saveProfile(uint32_t type, const QString& profile) {
m_config->write();
}
const char* InputController::profileForType(uint32_t type) {
UNUSED(type);
#ifdef BUILD_SDL
if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
return SDL_JoystickName(m_sdlPlayer.joystick->joystick);
#else
return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick));
#endif
QString InputController::profileForType(uint32_t type) {
auto driver = m_inputDrivers.value(type);
if (!driver) {
return {};
}
#endif
return 0;
return driver->currentProfile();
}
void InputController::setGamepadDriver(uint32_t type) {
auto driver = m_inputDrivers.value(type);
if (!driver || !driver->supportsGamepads()) {
return;
}
m_gamepadDriver = type;
}
QStringList InputController::connectedGamepads(uint32_t type) const {
UNUSED(type);
#ifdef BUILD_SDL
if (type == SDL_BINDING_BUTTON) {
QStringList pads;
for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
const char* name;
#if SDL_VERSION_ATLEAST(2, 0, 0)
name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick);
#else
name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick));
#endif
if (name) {
pads.append(QString(name));
} else {
pads.append(QString());
}
}
return pads;
if (!type) {
type = m_gamepadDriver;
}
auto driver = m_inputDrivers.value(type);
if (!driver) {
return {};
}
#endif
return QStringList();
QStringList pads;
for (auto pad : driver->connectedGamepads()) {
pads.append(pad->visibleName());
}
return pads;
}
int InputController::gamepad(uint32_t type) const {
#ifdef BUILD_SDL
if (type == SDL_BINDING_BUTTON) {
return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0;
int InputController::gamepadIndex(uint32_t type) const {
if (!type) {
type = m_gamepadDriver;
}
#endif
return 0;
auto driver = m_inputDrivers.value(type);
if (!driver) {
return -1;
}
return driver->activeGamepad();
}
void InputController::setGamepad(uint32_t type, int index) {
#ifdef BUILD_SDL
if (type == SDL_BINDING_BUTTON) {
mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index);
if (!type) {
type = m_gamepadDriver;
}
#endif
auto driver = m_inputDrivers.value(type);
if (!driver) {
return;
}
driver->setActiveGamepad(index);
}
void InputController::setGamepad(int index) {
setGamepad(0, index);
}
void InputController::setPreferredGamepad(uint32_t type, int index) {
if (!m_config) {
return;
}
#ifdef BUILD_SDL
#if SDL_VERSION_ATLEAST(2, 0, 0)
char name[34] = {0};
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick), name, sizeof(name));
#else
const char* name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick));
if (!name) {
if (!type) {
type = m_gamepadDriver;
}
auto driver = m_inputDrivers.value(type);
if (!driver) {
return;
}
#endif
mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, name);
#else
UNUSED(type);
UNUSED(index);
#endif
auto pads = driver->connectedGamepads();
if (index >= pads.count()) {
return;
}
QString name = pads[index]->name();
if (name.isEmpty()) {
return;
}
mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, name.toUtf8().constData());
}
mRumble* InputController::rumble() {
#ifdef BUILD_SDL
#if SDL_VERSION_ATLEAST(2, 0, 0)
if (m_playerAttached) {
return &m_sdlPlayer.rumble.d;
void InputController::setPreferredGamepad(int index) {
setPreferredGamepad(0, index);
}
InputMapper InputController::mapper(uint32_t type) {
return InputMapper(&m_inputMap, type);
}
InputMapper InputController::mapper(InputDriver* driver) {
return InputMapper(&m_inputMap, driver->type());
}
InputMapper InputController::mapper(InputSource* source) {
return InputMapper(&m_inputMap, source->type());
}
void InputController::setSensorDriver(uint32_t type) {
auto driver = m_inputDrivers.value(type);
if (!driver || !driver->supportsSensors()) {
return;
}
m_sensorDriver = type;
}
mRumble* InputController::rumble() {
auto driver = m_inputDrivers.value(m_sensorDriver);
if (driver) {
return driver->rumble();
}
#endif
#endif
return nullptr;
}
mRotationSource* InputController::rotationSource() {
#ifdef BUILD_SDL
if (m_playerAttached) {
return &m_sdlPlayer.rotation.d;
auto driver = m_inputDrivers.value(m_sensorDriver);
if (driver) {
return driver->rotationSource();
}
#endif
return nullptr;
}
void InputController::registerTiltAxisX(int axis) {
#ifdef BUILD_SDL
if (m_playerAttached) {
m_sdlPlayer.rotation.axisX = axis;
}
#endif
int InputController::mapKeyboard(int key) const {
return mInputMapKey(&m_inputMap, KEYBOARD, key);
}
void InputController::registerTiltAxisY(int axis) {
#ifdef BUILD_SDL
if (m_playerAttached) {
m_sdlPlayer.rotation.axisY = axis;
}
#endif
}
void InputController::registerGyroAxisX(int axis) {
#ifdef BUILD_SDL
if (m_playerAttached) {
m_sdlPlayer.rotation.gyroX = axis;
if (m_sdlPlayer.rotation.gyroY == axis) {
m_sdlPlayer.rotation.gyroZ = axis;
} else {
m_sdlPlayer.rotation.gyroZ = -1;
void InputController::update() {
for (auto& driver : m_inputDrivers) {
QString profile = profileForType(driver->type());
driver->update();
QString newProfile = profileForType(driver->type());
if (profile != newProfile) {
loadProfile(driver->type(), newProfile);
}
}
#endif
}
void InputController::registerGyroAxisY(int axis) {
#ifdef BUILD_SDL
if (m_playerAttached) {
m_sdlPlayer.rotation.gyroY = axis;
if (m_sdlPlayer.rotation.gyroX == axis) {
m_sdlPlayer.rotation.gyroZ = axis;
} else {
m_sdlPlayer.rotation.gyroZ = -1;
}
}
#endif
}
float InputController::gyroSensitivity() const {
#ifdef BUILD_SDL
if (m_playerAttached) {
return m_sdlPlayer.rotation.gyroSensitivity;
}
#endif
return 0;
}
void InputController::setGyroSensitivity(float sensitivity) {
#ifdef BUILD_SDL
if (m_playerAttached) {
m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
}
#endif
}
GBAKey InputController::mapKeyboard(int key) const {
return static_cast<GBAKey>(mInputMapKey(&m_inputMap, KEYBOARD, key));
}
void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
return mInputBindKey(&m_inputMap, type, key, gbaKey);
}
void InputController::updateJoysticks() {
#ifdef BUILD_SDL
QString profile = profileForType(SDL_BINDING_BUTTON);
mSDLUpdateJoysticks(&s_sdlEvents, m_config->input());
QString newProfile = profileForType(SDL_BINDING_BUTTON);
if (profile != newProfile) {
loadProfile(SDL_BINDING_BUTTON, newProfile);
}
#endif
}
int InputController::pollEvents() {
int activeButtons = 0;
#ifdef BUILD_SDL
if (m_playerAttached && m_sdlPlayer.joystick) {
SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
SDL_JoystickUpdate();
int numButtons = SDL_JoystickNumButtons(joystick);
int i;
QReadLocker l(&m_eventsLock);
for (i = 0; i < numButtons; ++i) {
GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i));
if (key == GBA_KEY_NONE) {
continue;
}
if (hasPendingEvent(key)) {
continue;
}
if (SDL_JoystickGetButton(joystick, i)) {
activeButtons |= 1 << key;
}
}
l.unlock();
int numHats = SDL_JoystickNumHats(joystick);
for (i = 0; i < numHats; ++i) {
int hat = SDL_JoystickGetHat(joystick, i);
activeButtons |= mInputMapHat(&m_inputMap, SDL_BINDING_BUTTON, i, hat);
}
int numAxes = SDL_JoystickNumAxes(joystick);
for (i = 0; i < numAxes; ++i) {
int value = SDL_JoystickGetAxis(joystick, i);
enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value));
if (key != GBA_KEY_NONE) {
activeButtons |= 1 << key;
}
for (auto pad : gamepads()) {
InputMapper im(mapper(pad));
activeButtons |= im.mapKeys(pad->currentButtons());
activeButtons |= im.mapAxes(pad->currentAxes());
activeButtons |= im.mapHats(pad->currentHats());
}
for (int i = 0; i < GBA_KEY_MAX; ++i) {
if ((activeButtons & (1 << i)) && hasPendingEvent(i)) {
activeButtons ^= 1 << i;
}
}
#endif
return activeButtons;
}
Gamepad* InputController::gamepad(uint32_t type) {
auto driver = m_inputDrivers.value(type);
if (!driver) {
return nullptr;
}
if (!driver->supportsGamepads()) {
return nullptr;
}
QList<Gamepad*> driverPads(driver->connectedGamepads());
int activeGamepad = driver->activeGamepad();
if (activeGamepad < 0 || activeGamepad >= driverPads.count()) {
return nullptr;
}
return driverPads[activeGamepad];
}
QList<Gamepad*> InputController::gamepads() {
QList<Gamepad*> pads;
for (auto& driver : m_inputDrivers) {
if (!driver->supportsGamepads()) {
continue;
}
QList<Gamepad*> driverPads(driver->connectedGamepads());
int activeGamepad = driver->activeGamepad();
if (activeGamepad >= 0 && activeGamepad < driverPads.count()) {
pads.append(driverPads[activeGamepad]);
}
}
return pads;
}
QSet<int> InputController::activeGamepadButtons(int type) {
QSet<int> activeButtons;
#ifdef BUILD_SDL
if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
SDL_JoystickUpdate();
int numButtons = SDL_JoystickNumButtons(joystick);
int i;
for (i = 0; i < numButtons; ++i) {
if (SDL_JoystickGetButton(joystick, i)) {
activeButtons.insert(i);
}
Gamepad* pad = gamepad(type);
if (!pad) {
return {};
}
auto allButtons = pad->currentButtons();
for (int i = 0; i < allButtons.size(); ++i) {
if (allButtons[i]) {
activeButtons.insert(i);
}
}
#endif
return activeButtons;
}
void InputController::recalibrateAxes() {
#ifdef BUILD_SDL
if (m_playerAttached && m_sdlPlayer.joystick) {
SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
SDL_JoystickUpdate();
int numAxes = SDL_JoystickNumAxes(joystick);
if (numAxes < 1) {
return;
}
m_deadzones.resize(numAxes);
int i;
for (i = 0; i < numAxes; ++i) {
m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
}
}
#endif
}
QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
#ifdef BUILD_SDL
if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
SDL_JoystickUpdate();
int numAxes = SDL_JoystickNumAxes(joystick);
if (numAxes < 1) {
return activeAxes;
Gamepad* pad = gamepad(type);
if (!pad) {
return {};
}
InputMapper im(mapper(type));
auto allAxes = pad->currentAxes();
for (int i = 0; i < allAxes.size(); ++i) {
if (allAxes[i] - im.axisCenter(i) >= im.axisThreshold(i)) {
activeAxes.insert(qMakePair(i, GamepadAxisEvent::POSITIVE));
continue;
}
m_deadzones.resize(numAxes);
int i;
for (i = 0; i < numAxes; ++i) {
int32_t axis = SDL_JoystickGetAxis(joystick, i);
axis -= m_deadzones[i];
if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
}
if (allAxes[i] - im.axisCenter(i) <= -im.axisThreshold(i)) {
activeAxes.insert(qMakePair(i, GamepadAxisEvent::NEGATIVE));
continue;
}
}
#endif
return activeAxes;
}
void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
const mInputAxis* old = mInputQueryAxis(&m_inputMap, type, axis);
mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
if (old) {
description = *old;
}
int deadzone = 0;
if (axis > 0 && m_deadzones.size() > axis) {
deadzone = m_deadzones[axis];
}
switch (direction) {
case GamepadAxisEvent::NEGATIVE:
description.lowDirection = key;
description.deadLow = deadzone - AXIS_THRESHOLD;
break;
case GamepadAxisEvent::POSITIVE:
description.highDirection = key;
description.deadHigh = deadzone + AXIS_THRESHOLD;
break;
default:
return;
}
mInputBindAxis(&m_inputMap, type, axis, &description);
}
void InputController::unbindAllAxes(uint32_t type) {
mInputUnbindAllAxes(&m_inputMap, type);
}
QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {
QSet<QPair<int, GamepadHatEvent::Direction>> activeHats;
#ifdef BUILD_SDL
if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
SDL_JoystickUpdate();
int numHats = SDL_JoystickNumHats(joystick);
if (numHats < 1) {
return activeHats;
}
int i;
for (i = 0; i < numHats; ++i) {
int hat = SDL_JoystickGetHat(joystick, i);
if (hat & GamepadHatEvent::UP) {
activeHats.insert(qMakePair(i, GamepadHatEvent::UP));
}
if (hat & GamepadHatEvent::RIGHT) {
activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT));
}
if (hat & GamepadHatEvent::DOWN) {
activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN));
}
if (hat & GamepadHatEvent::LEFT) {
activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT));
}
Gamepad* pad = gamepad(type);
if (!pad) {
return {};
}
auto allHats = pad->currentHats();
for (int i = 0; i < allHats.size(); ++i) {
if (allHats[i] != GamepadHatEvent::CENTER) {
activeHats.insert(qMakePair(i, allHats[i]));
}
}
#endif
return activeHats;
}
void InputController::bindHat(uint32_t type, int hat, GamepadHatEvent::Direction direction, GBAKey gbaKey) {
mInputHatBindings bindings{ -1, -1, -1, -1 };
mInputQueryHat(&m_inputMap, type, hat, &bindings);
switch (direction) {
case GamepadHatEvent::UP:
bindings.up = gbaKey;
break;
case GamepadHatEvent::RIGHT:
bindings.right = gbaKey;
break;
case GamepadHatEvent::DOWN:
bindings.down = gbaKey;
break;
case GamepadHatEvent::LEFT:
bindings.left = gbaKey;
break;
default:
return;
}
mInputBindHat(&m_inputMap, type, hat, &bindings);
}
void InputController::unbindAllHats(uint32_t type) {
mInputUnbindAllHats(&m_inputMap, type);
}
void InputController::testGamepad(int type) {
QWriteLocker l(&m_eventsLock);
auto activeAxes = activeGamepadAxes(type);
@ -614,16 +461,16 @@ void InputController::testGamepad(int type) {
bool newlyAboveThreshold = activeAxes.contains(axis);
if (newlyAboveThreshold) {
GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
postPendingEvent(event->gbaKey());
postPendingEvent(event->platformKey());
sendGamepadEvent(event);
if (!event->isAccepted()) {
clearPendingEvent(event->gbaKey());
clearPendingEvent(event->platformKey());
}
}
}
for (auto axis : oldAxes) {
GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
clearPendingEvent(event->gbaKey());
clearPendingEvent(event->platformKey());
sendGamepadEvent(event);
}
@ -636,15 +483,15 @@ void InputController::testGamepad(int type) {
for (int button : activeButtons) {
GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
postPendingEvent(event->gbaKey());
postPendingEvent(event->platformKey());
sendGamepadEvent(event);
if (!event->isAccepted()) {
clearPendingEvent(event->gbaKey());
clearPendingEvent(event->platformKey());
}
}
for (int button : oldButtons) {
GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
clearPendingEvent(event->gbaKey());
clearPendingEvent(event->platformKey());
sendGamepadEvent(event);
}
@ -653,15 +500,15 @@ void InputController::testGamepad(int type) {
for (auto& hat : activeHats) {
GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this);
postPendingEvent(event->gbaKey());
postPendingEvent(event->platformKey());
sendGamepadEvent(event);
if (!event->isAccepted()) {
clearPendingEvent(event->gbaKey());
clearPendingEvent(event->platformKey());
}
}
for (auto& hat : oldHats) {
GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this);
clearPendingEvent(event->gbaKey());
clearPendingEvent(event->platformKey());
sendGamepadEvent(event);
}
}
@ -679,42 +526,18 @@ void InputController::sendGamepadEvent(QEvent* event) {
QApplication::postEvent(focusWidget, event, Qt::HighEventPriority);
}
void InputController::postPendingEvent(GBAKey key) {
void InputController::postPendingEvent(int key) {
m_pendingEvents.insert(key);
}
void InputController::clearPendingEvent(GBAKey key) {
void InputController::clearPendingEvent(int key) {
m_pendingEvents.remove(key);
}
bool InputController::hasPendingEvent(GBAKey key) const {
bool InputController::hasPendingEvent(int key) const {
return m_pendingEvents.contains(key);
}
void InputController::suspendScreensaver() {
#ifdef BUILD_SDL
#if SDL_VERSION_ATLEAST(2, 0, 0)
mSDLSuspendScreensaver(&s_sdlEvents);
#endif
#endif
}
void InputController::resumeScreensaver() {
#ifdef BUILD_SDL
#if SDL_VERSION_ATLEAST(2, 0, 0)
mSDLResumeScreensaver(&s_sdlEvents);
#endif
#endif
}
void InputController::setScreensaverSuspendable(bool suspendable) {
#ifdef BUILD_SDL
#if SDL_VERSION_ATLEAST(2, 0, 0)
mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
#endif
#endif
}
void InputController::stealFocus(QWidget* focus) {
m_focusParent = focus;
}

View File

@ -1,13 +1,16 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
/* Copyright (c) 2013-2023 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/. */
#pragma once
#include "GamepadAxisEvent.h"
#include "GamepadHatEvent.h"
#include "input/GamepadAxisEvent.h"
#include "input/GamepadHatEvent.h"
#include "input/InputDriver.h"
#include "input/InputMapper.h"
#include <QHash>
#include <QImage>
#include <QMutex>
#include <QReadWriteLock>
@ -18,13 +21,8 @@
#include <memory>
#include <mgba/core/input.h>
#include <mgba/gba/interface.h>
#include <mgba/internal/gba/input.h>
#ifdef BUILD_SDL
#include "platform/sdl/sdl-events.h"
#endif
#ifdef BUILD_QT_MULTIMEDIA
#include "VideoDumper.h"
@ -37,6 +35,8 @@ struct mRumble;
namespace QGBA {
class ConfigController;
class Gamepad;
class InputSource;
class InputController : public QObject {
Q_OBJECT
@ -54,46 +54,43 @@ public:
InputController(int playerId = 0, QWidget* topLevel = nullptr, QObject* parent = nullptr);
~InputController();
void addInputDriver(std::shared_ptr<InputDriver>);
void setConfiguration(ConfigController* config);
void saveConfiguration();
bool loadConfiguration(uint32_t type);
bool loadProfile(uint32_t type, const QString& profile);
void saveConfiguration(uint32_t type);
void saveProfile(uint32_t type, const QString& profile);
const char* profileForType(uint32_t type);
QString profileForType(uint32_t type);
GBAKey mapKeyboard(int key) const;
void bindKey(uint32_t type, int key, GBAKey);
int mapKeyboard(int key) const;
mInputMap* map() { return &m_inputMap; }
const mInputMap* map() const { return &m_inputMap; }
int pollEvents();
static const int32_t AXIS_THRESHOLD = 0x3000;
QSet<int> activeGamepadButtons(int type);
QSet<QPair<int, GamepadAxisEvent::Direction>> activeGamepadAxes(int type);
QSet<QPair<int, GamepadHatEvent::Direction>> activeGamepadHats(int type);
void recalibrateAxes();
void bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction, GBAKey);
void unbindAllAxes(uint32_t type);
void setGamepadDriver(uint32_t type);
const InputDriver* gamepadDriver() const { return m_inputDrivers.value(m_sensorDriver).get(); }
InputDriver* gamepadDriver() { return m_inputDrivers.value(m_sensorDriver).get(); }
void bindHat(uint32_t type, int hat, GamepadHatEvent::Direction, GBAKey);
void unbindAllHats(uint32_t type);
QStringList connectedGamepads(uint32_t type) const;
int gamepad(uint32_t type) const;
QStringList connectedGamepads(uint32_t type = 0) const;
int gamepadIndex(uint32_t type = 0) const;
void setGamepad(uint32_t type, int index);
void setGamepad(int index);
void setPreferredGamepad(uint32_t type, int index);
void setPreferredGamepad(int index);
void registerTiltAxisX(int axis);
void registerTiltAxisY(int axis);
void registerGyroAxisX(int axis);
void registerGyroAxisY(int axis);
InputMapper mapper(uint32_t type);
InputMapper mapper(InputDriver*);
InputMapper mapper(InputSource*);
float gyroSensitivity() const;
void setGyroSensitivity(float sensitivity);
void setSensorDriver(uint32_t type);
const InputDriver* sensorDriver() const { return m_inputDrivers.value(m_sensorDriver).get(); }
InputDriver* sensorDriver() { return m_inputDrivers.value(m_sensorDriver).get(); }
void stealFocus(QWidget* focus);
void releaseFocus(QWidget* focus);
@ -111,12 +108,7 @@ signals:
public slots:
void testGamepad(int type);
void updateJoysticks();
// TODO: Move these to somewhere that makes sense
void suspendScreensaver();
void resumeScreensaver();
void setScreensaverSuspendable(bool);
void update();
void increaseLuminanceLevel();
void decreaseLuminanceLevel();
@ -136,11 +128,18 @@ private slots:
void teardownCam();
private:
void postPendingEvent(GBAKey);
void clearPendingEvent(GBAKey);
bool hasPendingEvent(GBAKey) const;
void postPendingEvent(int);
void clearPendingEvent(int);
bool hasPendingEvent(int) const;
void sendGamepadEvent(QEvent*);
Gamepad* gamepad(uint32_t type);
QList<Gamepad*> gamepads();
QSet<int> activeGamepadButtons(int type);
QSet<QPair<int, GamepadAxisEvent::Direction>> activeGamepadAxes(int type);
QSet<QPair<int, GamepadHatEvent::Direction>> activeGamepadHats(int type);
struct InputControllerLux : GBALuminanceSource {
InputController* p;
uint8_t value;
@ -170,21 +169,16 @@ private:
QWidget* m_topLevel;
QWidget* m_focusParent;
#ifdef BUILD_SDL
static int s_sdlInited;
static mSDLEvents s_sdlEvents;
mSDLPlayer m_sdlPlayer{};
bool m_playerAttached = false;
#endif
QVector<int> m_deadzones;
QHash<uint32_t, std::shared_ptr<InputDriver>> m_inputDrivers;
uint32_t m_gamepadDriver;
uint32_t m_sensorDriver;
QSet<int> m_activeButtons;
QSet<QPair<int, GamepadAxisEvent::Direction>> m_activeAxes;
QSet<QPair<int, GamepadHatEvent::Direction>> m_activeHats;
QTimer m_gamepadTimer{nullptr};
QSet<GBAKey> m_pendingEvents;
QSet<int> m_pendingEvents;
QReadWriteLock m_eventsLock;
};

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "InputProfile.h"
#include "input/InputMapper.h"
#include "InputController.h"
#include <QRegExp>
@ -210,17 +211,23 @@ const InputProfile* InputProfile::findProfile(const QString& name) {
}
void InputProfile::apply(InputController* controller) const {
for (size_t i = 0; i < GBA_KEY_MAX; ++i) {
#ifdef BUILD_SDL
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));
#endif
auto gamepadDriver = controller->gamepadDriver();
if (gamepadDriver) {
InputMapper mapper = controller->mapper(gamepadDriver);
for (size_t i = 0; i < GBA_KEY_MAX; ++i) {
mapper.bindKey(m_keys[i], i);
mapper.bindAxis(m_axes[i].axis, m_axes[i].direction, i);
}
}
InputDriver* sensorDriver = controller->sensorDriver();
if (sensorDriver) {
sensorDriver->registerTiltAxisX(m_tiltAxis.x);
sensorDriver->registerTiltAxisY(m_tiltAxis.y);
sensorDriver->registerGyroAxisX(m_gyroAxis.x);
sensorDriver->registerGyroAxisY(m_gyroAxis.y);
sensorDriver->setGyroSensitivity(m_gyroSensitivity);
}
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 {

View File

@ -5,9 +5,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#pragma once
#include "GamepadAxisEvent.h"
#include "input/GamepadAxisEvent.h"
#include <mgba/gba/interface.h>
#include <mgba/internal/gba/input.h>
namespace QGBA {

View File

@ -5,8 +5,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "KeyEditor.h"
#include "GamepadAxisEvent.h"
#include "GamepadButtonEvent.h"
#include "input/GamepadAxisEvent.h"
#include "input/GamepadButtonEvent.h"
#include "ShortcutController.h"
#include <QCoreApplication>

View File

@ -5,8 +5,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#pragma once
#include "GamepadAxisEvent.h"
#include "GamepadHatEvent.h"
#include "input/GamepadAxisEvent.h"
#include "input/GamepadHatEvent.h"
#include <QLineEdit>
#include <QTimer>

View File

@ -6,8 +6,8 @@
#include "LoadSaveState.h"
#include "CoreController.h"
#include "GamepadAxisEvent.h"
#include "GamepadButtonEvent.h"
#include "input/GamepadAxisEvent.h"
#include "input/GamepadButtonEvent.h"
#include "VFileDevice.h"
#include "utils.h"
@ -17,6 +17,7 @@
#include <QPainter>
#include <mgba/core/serialize.h>
#include <mgba/internal/gba/input.h>
#include <mgba-util/memory.h>
#include <mgba-util/vfs.h>
@ -131,13 +132,13 @@ bool LoadSaveState::eventFilter(QObject* object, QEvent* event) {
if (event->type() == GamepadButtonEvent::Down() || event->type() == GamepadAxisEvent::Type()) {
int column = m_currentFocus % 3;
int row = m_currentFocus - column;
GBAKey key = GBA_KEY_NONE;
int key = -1;
if (event->type() == GamepadButtonEvent::Down()) {
key = static_cast<GamepadButtonEvent*>(event)->gbaKey();
key = static_cast<GamepadButtonEvent*>(event)->platformKey();
} else if (event->type() == GamepadAxisEvent::Type()) {
GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event);
if (gae->isNew()) {
key = gae->gbaKey();
key = gae->platformKey();
} else {
return false;
}

View File

@ -314,10 +314,8 @@ void ReportView::generateReport() {
} else {
windowReport << QString("ROM open: No");
}
#ifdef BUILD_SDL
InputController* input = window->inputController();
windowReport << QString("Active gamepad: %1").arg(input->gamepad(SDL_BINDING_BUTTON));
#endif
windowReport << QString("Active gamepad: %1").arg(input->gamepadIndex());
windowReport << QString("Configuration: %1").arg(configs.indexOf(config) + 1);
addReport(QString("Window %1").arg(winId), windowReport.join('\n'));
}
@ -477,9 +475,8 @@ void ReportView::addGLInfo(QStringList& report) {
}
void ReportView::addGamepadInfo(QStringList& report) {
#ifdef BUILD_SDL
InputController* input = GBAApp::app()->windows()[0]->inputController();
QStringList gamepads = input->connectedGamepads(SDL_BINDING_BUTTON);
QStringList gamepads = input->connectedGamepads();
report << QString("Connected gamepads: %1").arg(gamepads.size());
int i = 0;
for (const auto& gamepad : gamepads) {
@ -490,10 +487,9 @@ void ReportView::addGamepadInfo(QStringList& report) {
i = 0;
for (Window* window : GBAApp::app()->windows()) {
++i;
report << QString("Window %1 gamepad: %2").arg(i).arg(window->inputController()->gamepad(SDL_BINDING_BUTTON));
report << QString("Window %1 gamepad: %2").arg(i).arg(window->inputController()->gamepadIndex());
}
}
#endif
}
void ReportView::addROMInfo(QStringList& report, CoreController* controller) {

View File

@ -6,7 +6,8 @@
#include "SensorView.h"
#include "CoreController.h"
#include "GamepadAxisEvent.h"
#include "input/GamepadAxisEvent.h"
#include "input/InputDriver.h"
#include "InputController.h"
#include "utils.h"
@ -47,14 +48,20 @@ SensorView::SensorView(InputController* input, QWidget* parent)
m_timer.start();
}
jiggerer(m_ui.tiltSetX, &InputController::registerTiltAxisX);
jiggerer(m_ui.tiltSetY, &InputController::registerTiltAxisY);
jiggerer(m_ui.gyroSetX, &InputController::registerGyroAxisX);
jiggerer(m_ui.gyroSetY, &InputController::registerGyroAxisY);
jiggerer(m_ui.tiltSetX, &InputDriver::registerTiltAxisX);
jiggerer(m_ui.tiltSetY, &InputDriver::registerTiltAxisY);
jiggerer(m_ui.gyroSetX, &InputDriver::registerGyroAxisX);
jiggerer(m_ui.gyroSetY, &InputDriver::registerGyroAxisY);
m_ui.gyroSensitivity->setValue(m_input->gyroSensitivity() / 1e8f);
InputDriver* sensorDriver = m_input->sensorDriver();
if (sensorDriver) {
m_ui.gyroSensitivity->setValue(sensorDriver->gyroSensitivity() / 1e8f);
}
connect(m_ui.gyroSensitivity, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), [this](double value) {
m_input->setGyroSensitivity(value * 1e8f);
InputDriver* sensorDriver = m_input->sensorDriver();
if (sensorDriver) {
sensorDriver->setGyroSensitivity(value * 1e8f);
}
});
m_input->stealFocus(this);
connect(m_input, &InputController::luminanceValueChanged, this, &SensorView::luminanceValueChanged);
@ -84,7 +91,7 @@ void SensorView::setController(std::shared_ptr<CoreController> controller) {
});
}
void SensorView::jiggerer(QAbstractButton* button, void (InputController::*setter)(int)) {
void SensorView::jiggerer(QAbstractButton* button, void (InputDriver::*setter)(int)) {
connect(button, &QAbstractButton::toggled, [this, button, setter](bool checked) {
if (!checked) {
m_button = nullptr;
@ -115,7 +122,10 @@ bool SensorView::eventFilter(QObject*, QEvent* event) {
m_button->removeEventFilter(this);
m_button->clearFocus();
m_button->setChecked(false);
(m_input->*m_setter)(gae->axis());
InputDriver* sensorDriver = m_input->sensorDriver();
if (sensorDriver) {
(sensorDriver->*m_setter)(gae->axis());
}
m_button = nullptr;
}
return true;

View File

@ -21,6 +21,7 @@ class ConfigController;
class CoreController;
class GamepadAxisEvent;
class InputController;
class InputDriver;
class SensorView : public QDialog {
Q_OBJECT
@ -43,14 +44,14 @@ private:
Ui::SensorView m_ui;
QAbstractButton* m_button = nullptr;
void (InputController::*m_setter)(int);
void (InputDriver::*m_setter)(int);
std::shared_ptr<CoreController> m_controller;
InputController* m_input;
mRotationSource* m_rotation;
QTimer m_timer;
void jiggerer(QAbstractButton*, void (InputController::*)(int));
void jiggerer(QAbstractButton*, void (InputDriver::*)(int));
};
}

View File

@ -25,6 +25,10 @@
#include <mgba/core/version.h>
#include <mgba/internal/gba/gba.h>
#ifdef BUILD_SDL
#include "platform/sdl/sdl-events.h"
#endif
using namespace QGBA;
SettingsView::SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, LogController* logController, QWidget* parent)
@ -326,8 +330,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
GBAKeyEditor* buttonEditor = nullptr;
#ifdef BUILD_SDL
inputController->recalibrateAxes();
const char* profile = inputController->profileForType(SDL_BINDING_BUTTON);
QString profile = inputController->profileForType(SDL_BINDING_BUTTON);
buttonEditor = new GBAKeyEditor(inputController, SDL_BINDING_BUTTON, profile);
addPage(tr("Controllers"), buttonEditor, Page::CONTROLLERS);
connect(m_ui.buttonBox, &QDialogButtonBox::accepted, buttonEditor, &GBAKeyEditor::save);

View File

@ -6,7 +6,7 @@
#include "ShortcutController.h"
#include "ConfigController.h"
#include "GamepadButtonEvent.h"
#include "input/GamepadButtonEvent.h"
#include "InputProfile.h"
#include <QAction>

View File

@ -6,7 +6,7 @@
#pragma once
#include "ActionMapper.h"
#include "GamepadAxisEvent.h"
#include "input/GamepadAxisEvent.h"
#include <QHash>
#include <QMap>

View File

@ -5,8 +5,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ShortcutView.h"
#include "GamepadButtonEvent.h"
#include "InputController.h"
#include "input/GamepadButtonEvent.h"
#include "ShortcutController.h"
#include "ShortcutModel.h"

View File

@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#pragma once
#include "GamepadAxisEvent.h"
#include "input/GamepadAxisEvent.h"
#include <QWidget>

View File

@ -36,6 +36,9 @@
#include "GDBController.h"
#include "GDBWindow.h"
#include "GIFView.h"
#ifdef BUILD_SDL
#include "input/SDLInputDriver.h"
#endif
#include "IOViewer.h"
#include "LoadSaveState.h"
#include "LogView.h"
@ -76,6 +79,7 @@
#include <mgba/internal/gba/gba.h>
#endif
#include <mgba/feature/commandline.h>
#include <mgba/internal/gba/input.h>
#include <mgba-util/vfs.h>
#include <mgba-util/convolve.h>
@ -170,6 +174,10 @@ Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWi
m_mustReset.setInterval(MUST_RESTART_TIMEOUT);
m_mustReset.setSingleShot(true);
#ifdef BUILD_SDL
m_inputController.addInputDriver(std::make_shared<SDLInputDriver>(&m_inputController));
#endif
m_shortcutController->setConfigController(m_config);
m_shortcutController->setActionMapper(&m_actions);
setupMenu(menuBar());
@ -297,7 +305,7 @@ void Window::reloadConfig() {
m_display->resizeContext();
}
m_inputController.setScreensaverSuspendable(opts->suspendScreensaver);
GBAApp::app()->setScreensaverSuspendable(opts->suspendScreensaver);
}
void Window::saveConfig() {
@ -631,8 +639,8 @@ void Window::keyPressEvent(QKeyEvent* event) {
QWidget::keyPressEvent(event);
return;
}
GBAKey key = m_inputController.mapKeyboard(event->key());
if (key == GBA_KEY_NONE) {
int key = m_inputController.mapKeyboard(event->key());
if (key == -1) {
QWidget::keyPressEvent(event);
return;
}
@ -647,8 +655,8 @@ void Window::keyReleaseEvent(QKeyEvent* event) {
QWidget::keyReleaseEvent(event);
return;
}
GBAKey key = m_inputController.mapKeyboard(event->key());
if (key == GBA_KEY_NONE) {
int key = m_inputController.mapKeyboard(event->key());
if (key == -1) {
QWidget::keyPressEvent(event);
return;
}
@ -1999,7 +2007,6 @@ void Window::setController(CoreController* controller, const QString& fname) {
}
m_controller = std::shared_ptr<CoreController>(controller);
m_inputController.recalibrateAxes();
m_controller->setInputController(&m_inputController);
m_controller->setLogger(&m_log);
@ -2012,14 +2019,14 @@ void Window::setController(CoreController* controller, const QString& fname) {
});
connect(m_controller.get(), &CoreController::started, this, &Window::gameStarted);
connect(m_controller.get(), &CoreController::started, &m_inputController, &InputController::suspendScreensaver);
connect(m_controller.get(), &CoreController::started, GBAApp::app(), &GBAApp::suspendScreensaver);
connect(m_controller.get(), &CoreController::stopping, this, &Window::gameStopped);
{
connect(m_controller.get(), &CoreController::stopping, [this]() {
m_controller.reset();
});
}
connect(m_controller.get(), &CoreController::stopping, &m_inputController, &InputController::resumeScreensaver);
connect(m_controller.get(), &CoreController::stopping, GBAApp::app(), &GBAApp::resumeScreensaver);
connect(m_controller.get(), &CoreController::paused, this, &Window::updateFrame);
#ifndef Q_OS_MAC
@ -2031,14 +2038,14 @@ void Window::setController(CoreController* controller, const QString& fname) {
});
#endif
connect(m_controller.get(), &CoreController::paused, &m_inputController, &InputController::resumeScreensaver);
connect(m_controller.get(), &CoreController::paused, GBAApp::app(), &GBAApp::resumeScreensaver);
connect(m_controller.get(), &CoreController::paused, [this]() {
emit paused(true);
});
connect(m_controller.get(), &CoreController::unpaused, [this]() {
emit paused(false);
});
connect(m_controller.get(), &CoreController::unpaused, &m_inputController, &InputController::suspendScreensaver);
connect(m_controller.get(), &CoreController::unpaused, GBAApp::app(), &GBAApp::suspendScreensaver);
connect(m_controller.get(), &CoreController::frameAvailable, this, &Window::recordFrame);
connect(m_controller.get(), &CoreController::crashed, this, &Window::gameCrashed);
connect(m_controller.get(), &CoreController::failed, this, &Window::gameFailed);

View File

@ -0,0 +1,13 @@
/* Copyright (c) 2013-2023 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 "input/Gamepad.h"
using namespace QGBA;
Gamepad::Gamepad(InputDriver* driver, QObject* parent)
: InputSource(driver, parent)
{
}

View File

@ -0,0 +1,30 @@
/* Copyright (c) 2013-2023 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/. */
#pragma once
#include "GamepadHatEvent.h"
#include "input/InputSource.h"
namespace QGBA {
class InputDriver;
class Gamepad : public InputSource {
Q_OBJECT
public:
Gamepad(InputDriver* driver, QObject* parent = nullptr);
virtual QList<bool> currentButtons() = 0;
virtual QList<int16_t> currentAxes() = 0;
virtual QList<GamepadHatEvent::Direction> currentHats() = 0;
virtual int buttonCount() const = 0;
virtual int axisCount() const = 0;
virtual int hatCount() const = 0;
};
}

View File

@ -1,9 +1,9 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
/* Copyright (c) 2013-2023 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 "GamepadAxisEvent.h"
#include "input/GamepadAxisEvent.h"
#include "InputController.h"
@ -16,11 +16,11 @@ GamepadAxisEvent::GamepadAxisEvent(int axis, Direction direction, bool isNew, in
, m_axis(axis)
, m_direction(direction)
, m_isNew(isNew)
, m_key(GBA_KEY_NONE)
, m_key(-1)
{
ignore();
if (controller) {
m_key = static_cast<GBAKey>(mInputMapAxis(controller->map(), type, axis, direction * INT_MAX));
m_key = mInputMapAxis(controller->map(), type, axis, direction * INT_MAX);
}
}

View File

@ -7,8 +7,6 @@
#include <QEvent>
#include <mgba/internal/gba/input.h>
namespace QGBA {
class InputController;
@ -26,7 +24,7 @@ public:
int axis() const { return m_axis; }
Direction direction() const { return m_direction; }
bool isNew() const { return m_isNew; }
GBAKey gbaKey() const { return m_key; }
int platformKey() const { return m_key; }
static enum Type Type();
@ -36,7 +34,7 @@ private:
int m_axis;
Direction m_direction;
bool m_isNew;
GBAKey m_key;
int m_key;
};
}

View File

@ -1,9 +1,9 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
/* Copyright (c) 2013-2023 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 "GamepadButtonEvent.h"
#include "input/GamepadButtonEvent.h"
#include "InputController.h"
@ -15,11 +15,11 @@ QEvent::Type GamepadButtonEvent::s_upType = QEvent::None;
GamepadButtonEvent::GamepadButtonEvent(QEvent::Type pressType, int button, int type, InputController* controller)
: QEvent(pressType)
, m_button(button)
, m_key(GBA_KEY_NONE)
, m_key(-1)
{
ignore();
if (controller) {
m_key = static_cast<GBAKey>(mInputMapKey(controller->map(), type, button));
m_key = mInputMapKey(controller->map(), type, button);
}
}

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
/* Copyright (c) 2013-2023 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
@ -7,8 +7,6 @@
#include <QEvent>
#include <mgba/internal/gba/input.h>
namespace QGBA {
class InputController;
@ -18,7 +16,7 @@ public:
GamepadButtonEvent(Type pressType, int button, int type, InputController* controller = nullptr);
int value() const { return m_button; }
GBAKey gbaKey() const { return m_key; }
int platformKey() const { return m_key; }
static Type Down();
static Type Up();
@ -28,7 +26,7 @@ private:
static Type s_upType;
int m_button;
GBAKey m_key;
int m_key;
};
}

View File

@ -1,9 +1,9 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
/* Copyright (c) 2013-2023 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 "GamepadHatEvent.h"
#include "input/GamepadHatEvent.h"
#include "InputController.h"
@ -16,11 +16,11 @@ GamepadHatEvent::GamepadHatEvent(QEvent::Type pressType, int hatId, Direction di
: QEvent(pressType)
, m_hatId(hatId)
, m_direction(direction)
, m_key(GBA_KEY_NONE)
, m_key(-1)
{
ignore();
if (controller) {
m_key = static_cast<GBAKey>(mInputMapHat(controller->map(), type, hatId, direction));
m_key = mInputMapHat(controller->map(), type, hatId, direction);
}
}

View File

@ -7,8 +7,6 @@
#include <QEvent>
#include <mgba/internal/gba/input.h>
namespace QGBA {
class InputController;
@ -27,7 +25,7 @@ public:
int hatId() const { return m_hatId; }
Direction direction() const { return m_direction; }
GBAKey gbaKey() const { return m_key; }
int platformKey() const { return m_key; }
static Type Down();
static Type Up();
@ -38,7 +36,7 @@ private:
int m_hatId;
Direction m_direction;
GBAKey m_key;
int m_key;
};
}

View File

@ -0,0 +1,85 @@
/* Copyright (c) 2013-2023 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 "input/InputDriver.h"
using namespace QGBA;
InputDriver::InputDriver(QObject* parent)
: QObject(parent)
{}
void InputDriver::loadConfiguration(ConfigController*) {
}
void InputDriver::saveConfiguration(ConfigController*) {
}
bool InputDriver::supportsPolling() const {
return false;
}
bool InputDriver::supportsGamepads() const {
return false;
}
bool InputDriver::supportsSensors() const {
return false;
}
void InputDriver::bindDefaults(InputController*) {
}
QList<KeySource*> InputDriver::connectedKeySources() const {
return {};
}
QList<Gamepad*> InputDriver::connectedGamepads() const {
return {};
}
int InputDriver::activeKeySource() const {
return -1;
}
int InputDriver::activeGamepad() const {
return -1;
}
void InputDriver::setActiveKeySource(int) {
}
void InputDriver::setActiveGamepad(int) {
}
void InputDriver::registerTiltAxisX(int) {
}
void InputDriver::registerTiltAxisY(int) {
}
void InputDriver::registerGyroAxisX(int) {
}
void InputDriver::registerGyroAxisY(int) {
}
void InputDriver::registerGyroAxisZ(int) {
}
float InputDriver::gyroSensitivity() const {
return 0;
}
void InputDriver::setGyroSensitivity(float) {
}
mRumble* InputDriver::rumble() {
return nullptr;
}
mRotationSource* InputDriver::rotationSource() {
return nullptr;
}

View File

@ -0,0 +1,66 @@
/* Copyright (c) 2013-2023 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/. */
#pragma once
#include <QList>
#include <QString>
#include <QObject>
struct mRotationSource;
struct mRumble;
namespace QGBA {
class ConfigController;
class Gamepad;
class InputController;
class KeySource;
class InputDriver : public QObject {
Q_OBJECT
public:
InputDriver(QObject* parent = nullptr);
virtual ~InputDriver() = default;
virtual uint32_t type() const = 0;
virtual QString visibleName() const = 0;
virtual QString currentProfile() const = 0;
virtual bool supportsPolling() const;
virtual bool supportsGamepads() const;
virtual bool supportsSensors() const;
virtual void loadConfiguration(ConfigController*);
virtual void saveConfiguration(ConfigController*);
virtual void bindDefaults(InputController*);
virtual bool update() = 0;
virtual QList<KeySource*> connectedKeySources() const;
virtual QList<Gamepad*> connectedGamepads() const;
virtual int activeKeySource() const;
virtual int activeGamepad() const;
virtual void setActiveKeySource(int);
virtual void setActiveGamepad(int);
virtual void registerTiltAxisX(int axis);
virtual void registerTiltAxisY(int axis);
virtual void registerGyroAxisX(int axis);
virtual void registerGyroAxisY(int axis);
virtual void registerGyroAxisZ(int axis);
virtual float gyroSensitivity() const;
virtual void setGyroSensitivity(float sensitivity);
virtual mRumble* rumble();
virtual mRotationSource* rotationSource();
};
}

View File

@ -0,0 +1,184 @@
/* Copyright (c) 2013-2023 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 "input/InputMapper.h"
#include <mgba/core/input.h>
using namespace QGBA;
InputMapper::InputMapper(mInputMap* map, uint32_t type)
: m_map(map)
, m_type(type)
{
}
int InputMapper::mapKey(int key) const {
return mInputMapKey(m_map, m_type, key);
}
int InputMapper::mapAxis(int axis, int16_t value) const {
return mInputMapAxis(m_map, m_type, axis, value);
}
int InputMapper::mapHat(int hat, GamepadHatEvent::Direction direction) const {
return mInputMapHat(m_map, m_type, hat, direction);
}
int InputMapper::mapKeys(QList<bool> keys) const {
int platformKeys = 0;
for (int i = 0; i < keys.count(); ++i) {
if (!keys[i]) {
continue;
}
int platformKey = mInputMapKey(m_map, m_type, i);
if (platformKey >= 0) {
platformKeys |= 1 << platformKey;
}
}
return platformKeys;
}
int InputMapper::mapKeys(QSet<int> keys) const {
int platformKeys = 0;
for (int key : keys) {
int platformKey = mInputMapKey(m_map, m_type, key);
if (platformKey >= 0) {
platformKeys |= 1 << platformKey;
}
}
return platformKeys;
}
int InputMapper::mapAxes(QList<int16_t> axes) const {
int platformKeys = 0;
for (int i = 0; i < axes.count(); ++i) {
int platformKey = mInputMapAxis(m_map, m_type, i, axes[i]);
if (platformKey >= 0) {
platformKeys |= 1 << platformKey;
}
}
return platformKeys;
}
int InputMapper::mapHats(QList<GamepadHatEvent::Direction> hats) const {
int platformKeys = 0;
for (int i = 0; i < hats.count(); ++i) {
int platformKey = mInputMapHat(m_map, m_type, i, hats[i]);
if (platformKey >= 0) {
platformKeys |= 1 << platformKey;
}
}
return platformKeys;
}
void InputMapper::bindKey(int key, int platformKey) {
mInputBindKey(m_map, m_type, key, platformKey);
}
void InputMapper::bindAxis(int axis, GamepadAxisEvent::Direction direction, int platformKey) {
const mInputAxis* old = mInputQueryAxis(m_map, m_type, axis);
mInputAxis description = { -1, -1, -axisThreshold(axis), axisThreshold(axis) };
if (old) {
description = *old;
}
switch (direction) {
case GamepadAxisEvent::NEGATIVE:
description.lowDirection = platformKey;
description.deadLow = axisCenter(axis) - axisThreshold(axis);
break;
case GamepadAxisEvent::POSITIVE:
description.highDirection = platformKey;
description.deadHigh = axisCenter(axis) + axisThreshold(axis);
break;
default:
return;
}
mInputBindAxis(m_map, m_type, axis, &description);
}
void InputMapper::bindHat(int hat, GamepadHatEvent::Direction direction, int platformKey) {
mInputHatBindings bindings{ -1, -1, -1, -1 };
mInputQueryHat(m_map, m_type, hat, &bindings);
switch (direction) {
case GamepadHatEvent::UP:
bindings.up = platformKey;
break;
case GamepadHatEvent::RIGHT:
bindings.right = platformKey;
break;
case GamepadHatEvent::DOWN:
bindings.down = platformKey;
break;
case GamepadHatEvent::LEFT:
bindings.left = platformKey;
break;
default:
return;
}
mInputBindHat(m_map, m_type, hat, &bindings);
}
QSet<int> InputMapper::queryKeyBindings(int platformKey) const {
return {mInputQueryBinding(m_map, m_type, platformKey)};
}
QSet<QPair<int, GamepadAxisEvent::Direction>> InputMapper::queryAxisBindings(int platformKey) const {
QPair<int, QSet<QPair<int, GamepadAxisEvent::Direction>>> userdata;
userdata.first = platformKey;
mInputEnumerateAxes(m_map, m_type, [](int axis, const struct mInputAxis* description, void* user) {
auto userdata = static_cast<QPair<int, QSet<QPair<int, GamepadAxisEvent::Direction>>>*>(user);
int platformKey = userdata->first;
auto& bindings = userdata->second;
if (description->lowDirection == platformKey) {
bindings.insert({axis, GamepadAxisEvent::NEGATIVE});
}
if (description->highDirection == platformKey) {
bindings.insert({axis, GamepadAxisEvent::POSITIVE});
}
}, &userdata);
return userdata.second;
}
QSet<QPair<int, GamepadHatEvent::Direction>> InputMapper::queryHatBindings(int platformKey) const {
QPair<int, QSet<QPair<int, GamepadHatEvent::Direction>>> userdata;
userdata.first = platformKey;
mInputEnumerateHats(m_map, m_type, [](int hat, const struct mInputHatBindings* description, void* user) {
auto userdata = static_cast<QPair<int, QSet<QPair<int, GamepadHatEvent::Direction>>>*>(user);
int platformKey = userdata->first;
auto& bindings = userdata->second;
if (description->up == platformKey) {
bindings.insert({hat, GamepadHatEvent::UP});
}
if (description->right == platformKey) {
bindings.insert({hat, GamepadHatEvent::RIGHT});
}
if (description->down == platformKey) {
bindings.insert({hat, GamepadHatEvent::DOWN});
}
if (description->left == platformKey) {
bindings.insert({hat, GamepadHatEvent::LEFT});
}
}, &userdata);
return userdata.second;
}
void InputMapper::unbindAllKeys() {
mInputUnbindAllKeys(m_map, m_type);
}
void InputMapper::unbindAllAxes() {
mInputUnbindAllAxes(m_map, m_type);
}
void InputMapper::unbindAllHats() {
mInputUnbindAllHats(m_map, m_type);
}

View File

@ -0,0 +1,56 @@
/* Copyright (c) 2013-2023 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/. */
#pragma once
#include <QPair>
#include <QSet>
#include "GamepadAxisEvent.h"
#include "GamepadHatEvent.h"
struct mInputMap;
namespace QGBA {
class InputMapper final {
public:
static const int32_t DEFAULT_AXIS_THRESHOLD = 0x3000;
InputMapper(mInputMap* map, uint32_t type);
mInputMap* inputMap() const { return m_map; }
uint32_t type() const { return m_type; }
int mapKey(int key) const;
int mapAxis(int axis, int16_t value) const;
int mapHat(int hat, GamepadHatEvent::Direction) const;
int mapKeys(QList<bool> keys) const;
int mapKeys(QSet<int> keys) const;
int mapAxes(QList<int16_t> axes) const;
int mapHats(QList<GamepadHatEvent::Direction> hats) const;
void bindKey(int key, int platformKey);
void bindAxis(int axis, GamepadAxisEvent::Direction, int platformKey);
void bindHat(int hat, GamepadHatEvent::Direction, int platformKey);
QSet<int> queryKeyBindings(int platformKey) const;
QSet<QPair<int, GamepadAxisEvent::Direction>> queryAxisBindings(int platformKey) const;
QSet<QPair<int, GamepadHatEvent::Direction>> queryHatBindings(int platformKey) const;
int16_t axisThreshold(int) const { return DEFAULT_AXIS_THRESHOLD; }
int16_t axisCenter(int) const { return 0; }
void unbindAllKeys();
void unbindAllAxes();
void unbindAllHats();
private:
mInputMap* m_map;
uint32_t m_type;
};
}

View File

@ -0,0 +1,14 @@
/* Copyright (c) 2013-2023 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 "input/InputSource.h"
using namespace QGBA;
InputSource::InputSource(InputDriver* driver, QObject* parent)
: QObject(parent)
, m_driver(driver)
{
}

View File

@ -0,0 +1,32 @@
/* Copyright (c) 2013-2023 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/. */
#pragma once
#include <QObject>
#include <QString>
#include "input/InputDriver.h"
namespace QGBA {
class InputSource : public QObject {
Q_OBJECT
public:
InputSource(InputDriver* driver, QObject* parent = nullptr);
virtual ~InputSource() = default;
InputDriver* driver() { return m_driver; }
uint32_t type() { return m_driver->type(); }
virtual QString name() const = 0;
virtual QString visibleName() const = 0;
protected:
InputDriver* const m_driver;
};
}

View File

@ -0,0 +1,13 @@
/* Copyright (c) 2013-2023 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 "input/KeySource.h"
using namespace QGBA;
KeySource::KeySource(InputDriver* driver, QObject* parent)
: InputSource(driver, parent)
{
}

View File

@ -0,0 +1,23 @@
/* Copyright (c) 2013-2023 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/. */
#pragma once
#include "input/InputSource.h"
namespace QGBA {
class InputDriver;
class KeySource : public InputSource {
Q_OBJECT
public:
KeySource(InputDriver* driver, QObject* parent = nullptr);
virtual QSet<int> currentKeys() = 0;
};
}

View File

@ -0,0 +1,357 @@
/* Copyright (c) 2013-2023 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 "input/SDLInputDriver.h"
#include "ConfigController.h"
#include "InputController.h"
#include <algorithm>
using namespace QGBA;
int s_sdlInited = 0;
mSDLEvents s_sdlEvents;
void SDL::suspendScreensaver() {
#if SDL_VERSION_ATLEAST(2, 0, 0)
if (s_sdlInited) {
mSDLSuspendScreensaver(&s_sdlEvents);
}
#endif
}
void SDL::resumeScreensaver() {
#if SDL_VERSION_ATLEAST(2, 0, 0)
if (s_sdlInited) {
mSDLResumeScreensaver(&s_sdlEvents);
}
#endif
}
void SDL::setScreensaverSuspendable(bool suspendable) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
if (s_sdlInited) {
mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
}
#endif
}
SDLInputDriver::SDLInputDriver(InputController* controller, QObject* parent)
: InputDriver(parent)
, m_controller(controller)
{
if (s_sdlInited == 0) {
mSDLInitEvents(&s_sdlEvents);
}
++s_sdlInited;
m_sdlPlayer.bindings = m_controller->map();
for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
m_gamepads.append(std::make_shared<SDLGamepad>(this, i));
}
}
SDLInputDriver::~SDLInputDriver() {
if (m_playerAttached) {
mSDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
}
--s_sdlInited;
if (s_sdlInited == 0) {
mSDLDeinitEvents(&s_sdlEvents);
}
}
bool SDLInputDriver::supportsPolling() const {
return true;
}
bool SDLInputDriver::supportsGamepads() const {
return true;
}
bool SDLInputDriver::supportsSensors() const {
return true;
}
QString SDLInputDriver::currentProfile() const {
if (m_sdlPlayer.joystick) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
return SDL_JoystickName(m_sdlPlayer.joystick->joystick);
#else
return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick));
#endif
}
return {};
}
void SDLInputDriver::loadConfiguration(ConfigController* config) {
mSDLEventsLoadConfig(&s_sdlEvents, config->input());
if (!m_playerAttached) {
m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
}
if (m_playerAttached) {
mSDLPlayerLoadConfig(&m_sdlPlayer, config->input());
}
}
void SDLInputDriver::saveConfiguration(ConfigController* config) {
if (m_playerAttached) {
mSDLPlayerSaveConfig(&m_sdlPlayer, config->input());
}
}
void SDLInputDriver::bindDefaults(InputController* controller) {
mSDLInitBindingsGBA(controller->map());
}
mRumble* SDLInputDriver::rumble() {
#if SDL_VERSION_ATLEAST(2, 0, 0)
if (m_playerAttached) {
return &m_sdlPlayer.rumble.d;
}
#endif
return nullptr;
}
mRotationSource* SDLInputDriver::rotationSource() {
if (m_playerAttached) {
return &m_sdlPlayer.rotation.d;
}
return nullptr;
}
bool SDLInputDriver::update() {
if (!m_playerAttached || !m_sdlPlayer.joystick) {
return false;
}
SDL_JoystickUpdate();
#if SDL_VERSION_ATLEAST(2, 0, 0)
updateGamepads();
#endif
return true;
}
QList<Gamepad*> SDLInputDriver::connectedGamepads() const {
QList<Gamepad*> pads;
for (auto& pad : m_gamepads) {
pads.append(pad.get());
}
return pads;
}
#if SDL_VERSION_ATLEAST(2, 0, 0)
void SDLInputDriver::updateGamepads() {
for (int i = 0; i < m_gamepads.size(); ++i) {
if (m_gamepads.at(i)->updateIndex()) {
continue;
}
m_gamepads.removeAt(i);
--i;
}
std::sort(m_gamepads.begin(), m_gamepads.end(), [](const auto& a, const auto b) {
return a->m_index < b->m_index;
});
for (size_t i = 0, j = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
std::shared_ptr<SDLGamepad> gamepad = m_gamepads.at(j);
if (gamepad->m_index == i) {
++j;
continue;
}
m_gamepads.append(std::make_shared<SDLGamepad>(this, i));
}
std::sort(m_gamepads.begin(), m_gamepads.end(), [](const auto& a, const auto b) {
return a->m_index < b->m_index;
});
}
#endif
int SDLInputDriver::activeGamepad() const {
return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0;
}
void SDLInputDriver::setActiveGamepad(int index) {
mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index);
}
void SDLInputDriver::registerTiltAxisX(int axis) {
if (m_playerAttached) {
m_sdlPlayer.rotation.axisX = axis;
}
}
void SDLInputDriver::registerTiltAxisY(int axis) {
if (m_playerAttached) {
m_sdlPlayer.rotation.axisY = axis;
}
}
void SDLInputDriver::registerGyroAxisX(int axis) {
if (m_playerAttached) {
m_sdlPlayer.rotation.gyroX = axis;
if (m_sdlPlayer.rotation.gyroY == axis) {
m_sdlPlayer.rotation.gyroZ = axis;
} else {
m_sdlPlayer.rotation.gyroZ = -1;
}
}
}
void SDLInputDriver::registerGyroAxisY(int axis) {
if (m_playerAttached) {
m_sdlPlayer.rotation.gyroY = axis;
if (m_sdlPlayer.rotation.gyroX == axis) {
m_sdlPlayer.rotation.gyroZ = axis;
} else {
m_sdlPlayer.rotation.gyroZ = -1;
}
}
}
void SDLInputDriver::registerGyroAxisZ(int axis) {
if (m_playerAttached) {
m_sdlPlayer.rotation.gyroZ = axis;
m_sdlPlayer.rotation.gyroX = -1;
m_sdlPlayer.rotation.gyroY = -1;
}
}
float SDLInputDriver::gyroSensitivity() const {
if (m_playerAttached) {
return m_sdlPlayer.rotation.gyroSensitivity;
}
return 0;
}
void SDLInputDriver::setGyroSensitivity(float sensitivity) {
if (m_playerAttached) {
m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
}
}
SDLGamepad::SDLGamepad(SDLInputDriver* driver, int index, QObject* parent)
: Gamepad(driver, parent)
, m_index(index)
{
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick;
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), m_guid, sizeof(m_guid));
#endif
}
QList<bool> SDLGamepad::currentButtons() {
if (!verify()) {
return {};
}
SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick;
QList<bool> buttons;
int numButtons = SDL_JoystickNumButtons(joystick);
for (int i = 0; i < numButtons; ++i) {
buttons.append(SDL_JoystickGetButton(joystick, i));
}
return buttons;
}
QList<int16_t> SDLGamepad::currentAxes() {
if (!verify()) {
return {};
}
SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick;
QList<int16_t> axes;
int numAxes = SDL_JoystickNumAxes(joystick);
for (int i = 0; i < numAxes; ++i) {
axes.append(SDL_JoystickGetAxis(joystick, i));
}
return axes;
}
QList<GamepadHatEvent::Direction> SDLGamepad::currentHats() {
if (!verify()) {
return {};
}
SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick;
QList<GamepadHatEvent::Direction> hats;
int numHats = SDL_JoystickNumHats(joystick);
for (int i = 0; i < numHats; ++i) {
hats.append(static_cast<GamepadHatEvent::Direction>(SDL_JoystickGetHat(joystick, i)));
}
return hats;
}
int SDLGamepad::buttonCount() const {
if (!verify()) {
return -1;
}
SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick;
return SDL_JoystickNumButtons(joystick);
}
int SDLGamepad::axisCount() const {
if (!verify()) {
return -1;
}
SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick;
return SDL_JoystickNumAxes(joystick);
}
int SDLGamepad::hatCount() const {
if (!verify()) {
return -1;
}
SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick;
return SDL_JoystickNumHats(joystick);
}
QString SDLGamepad::name() const {
#if SDL_VERSION_ATLEAST(2, 0, 0)
return m_guid;
#else
return visibleName();
#endif
}
QString SDLGamepad::visibleName() const {
#if SDL_VERSION_ATLEAST(2, 0, 0)
return SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick);
#else
return SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick));
#endif
}
#if SDL_VERSION_ATLEAST(2, 0, 0)
bool SDLGamepad::updateIndex() {
char guid[34];
for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick;
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), guid, sizeof(guid));
if (memcmp(guid, m_guid, 33) == 0) {
m_index = i;
return true;
}
}
return false;
}
#endif
bool SDLGamepad::verify() const {
return m_index < SDL_JoystickListSize(&s_sdlEvents.joysticks);
}

View File

@ -0,0 +1,107 @@
/* Copyright (c) 2013-2023 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/. */
#pragma once
#include "input/Gamepad.h"
#include "input/InputDriver.h"
#include "platform/sdl/sdl-events.h"
#include <memory>
namespace QGBA {
class SDLGamepad;
namespace SDL {
void suspendScreensaver();
void resumeScreensaver();
void setScreensaverSuspendable(bool);
}
class SDLInputDriver : public InputDriver {
Q_OBJECT
public:
SDLInputDriver(InputController*, QObject* parent = nullptr);
~SDLInputDriver();
uint32_t type() const override { return SDL_BINDING_BUTTON; }
QString visibleName() const override { return QLatin1String("SDL"); }
QString currentProfile() const override;
bool supportsPolling() const override;
bool supportsGamepads() const override;
bool supportsSensors() const override;
void loadConfiguration(ConfigController* config) override;
void saveConfiguration(ConfigController* config) override;
void bindDefaults(InputController*) override;
bool update() override;
QList<Gamepad*> connectedGamepads() const override;
int activeGamepad() const override;
void setActiveGamepad(int) override;
void registerTiltAxisX(int axis) override;
void registerTiltAxisY(int axis) override;
void registerGyroAxisX(int axis) override;
void registerGyroAxisY(int axis) override;
void registerGyroAxisZ(int axis) override;
float gyroSensitivity() const override;
void setGyroSensitivity(float sensitivity) override;
mRumble* rumble() override;
mRotationSource* rotationSource() override;
private:
InputController* m_controller;
mSDLPlayer m_sdlPlayer{};
bool m_playerAttached = false;
QList<std::shared_ptr<SDLGamepad>> m_gamepads;
#if SDL_VERSION_ATLEAST(2, 0, 0)
void updateGamepads();
#endif
};
class SDLGamepad : public Gamepad {
Q_OBJECT
public:
SDLGamepad(SDLInputDriver*, int index, QObject* parent = nullptr);
QList<bool> currentButtons() override;
QList<int16_t> currentAxes() override;
QList<GamepadHatEvent::Direction> currentHats() override;
int buttonCount() const override;
int axisCount() const override;
int hatCount() const override;
QString name() const override;
QString visibleName() const override;
#if SDL_VERSION_ATLEAST(2, 0, 0)
bool updateIndex();
#endif
private:
friend class SDLInputDriver;
size_t m_index;
#if SDL_VERSION_ATLEAST(2, 0, 0)
char m_guid[34]{};
#endif
bool verify() const;
};
}

View File

@ -14,6 +14,10 @@
#include <mgba/core/version.h>
#include <mgba/gba/interface.h>
#ifdef BUILD_SDL
#include "platform/sdl/sdl-events.h"
#endif
#include <QLibraryInfo>
#include <QTranslator>