mirror of https://github.com/mgba-emu/mgba.git
Qt: Remappable controls for tilt and gyroscope sensors
This commit is contained in:
parent
3eb4c01515
commit
6248e44e47
1
CHANGES
1
CHANGES
|
@ -11,6 +11,7 @@ Features:
|
||||||
- Deadzone estimation for game controllers
|
- Deadzone estimation for game controllers
|
||||||
- Analog inputs can be used for shortcuts
|
- Analog inputs can be used for shortcuts
|
||||||
- Menu items for specific solar sensor brightness levels
|
- Menu items for specific solar sensor brightness levels
|
||||||
|
- Remappable controls for tilt and gyroscope sensors
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
- GBA: Fix timers not updating timing when writing to only the reload register
|
- GBA: Fix timers not updating timing when writing to only the reload register
|
||||||
- All: Fix sanitize-deb script not cleaning up after itself
|
- All: Fix sanitize-deb script not cleaning up after itself
|
||||||
|
|
|
@ -500,3 +500,23 @@ void GBAInputSetPreferredDevice(struct Configuration* config, uint32_t type, int
|
||||||
snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
|
snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
|
||||||
return ConfigurationSetValue(config, sectionName, deviceId, deviceName);
|
return ConfigurationSetValue(config, sectionName, deviceId, deviceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* GBAInputGetCustomValue(const struct Configuration* config, uint32_t type, const char* key, const char* profile) {
|
||||||
|
char sectionName[SECTION_NAME_MAX];
|
||||||
|
if (profile) {
|
||||||
|
snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
|
||||||
|
} else {
|
||||||
|
_makeSectionName(sectionName, SECTION_NAME_MAX, type);
|
||||||
|
}
|
||||||
|
return ConfigurationGetValue(config, sectionName, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAInputSetCustomValue(struct Configuration* config, uint32_t type, const char* key, const char* value, const char* profile) {
|
||||||
|
char sectionName[SECTION_NAME_MAX];
|
||||||
|
if (profile) {
|
||||||
|
snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
|
||||||
|
} else {
|
||||||
|
_makeSectionName(sectionName, SECTION_NAME_MAX, type);
|
||||||
|
}
|
||||||
|
ConfigurationSetValue(config, sectionName, key, value);
|
||||||
|
}
|
||||||
|
|
|
@ -51,4 +51,7 @@ void GBAInputProfileSave(const struct GBAInputMap*, uint32_t type, struct Config
|
||||||
const char* GBAInputGetPreferredDevice(const struct Configuration*, uint32_t type, int playerId);
|
const char* GBAInputGetPreferredDevice(const struct Configuration*, uint32_t type, int playerId);
|
||||||
void GBAInputSetPreferredDevice(struct Configuration*, uint32_t type, int playerId, const char* deviceName);
|
void GBAInputSetPreferredDevice(struct Configuration*, uint32_t type, int playerId, const char* deviceName);
|
||||||
|
|
||||||
|
const char* GBAInputGetCustomValue(const struct Configuration* config, uint32_t type, const char* key, const char* profile);
|
||||||
|
void GBAInputSetCustomValue(struct Configuration* config, uint32_t type, const char* key, const char* value, const char* profile);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -110,6 +110,11 @@ void InputController::loadProfile(uint32_t type, const QString& profile) {
|
||||||
|
|
||||||
void InputController::saveConfiguration(uint32_t type) {
|
void InputController::saveConfiguration(uint32_t type) {
|
||||||
GBAInputMapSave(&m_inputMap, type, m_config->input());
|
GBAInputMapSave(&m_inputMap, type, m_config->input());
|
||||||
|
#ifdef BUILD_SDL
|
||||||
|
if (m_playerAttached) {
|
||||||
|
GBASDLPlayerSaveConfig(&m_sdlPlayer, m_config->input());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
m_config->write();
|
m_config->write();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,6 +175,27 @@ GBARumble* InputController::rumble() {
|
||||||
GBARotationSource* InputController::rotationSource() {
|
GBARotationSource* InputController::rotationSource() {
|
||||||
return &m_sdlPlayer.rotation.d;
|
return &m_sdlPlayer.rotation.d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputController::registerTiltAxisX(int axis) {
|
||||||
|
m_sdlPlayer.rotation.axisX = axis;
|
||||||
|
}
|
||||||
|
void InputController::registerTiltAxisY(int axis) {
|
||||||
|
m_sdlPlayer.rotation.axisY = axis;
|
||||||
|
}
|
||||||
|
void InputController::registerGyroAxisX(int axis) {
|
||||||
|
m_sdlPlayer.rotation.gyroX = axis;
|
||||||
|
}
|
||||||
|
void InputController::registerGyroAxisY(int axis) {
|
||||||
|
m_sdlPlayer.rotation.gyroY = axis;
|
||||||
|
}
|
||||||
|
|
||||||
|
float InputController::gyroSensitivity() const {
|
||||||
|
return m_sdlPlayer.rotation.gyroSensitivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputController::setGyroSensitivity(float sensitivity) {
|
||||||
|
m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
GBARumble* InputController::rumble() {
|
GBARumble* InputController::rumble() {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -65,6 +65,14 @@ public:
|
||||||
int gamepad(uint32_t type) const { return m_sdlPlayer.joystickIndex; }
|
int gamepad(uint32_t type) const { return m_sdlPlayer.joystickIndex; }
|
||||||
void setGamepad(uint32_t type, int index) { GBASDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index); }
|
void setGamepad(uint32_t type, int index) { GBASDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index); }
|
||||||
void setPreferredGamepad(uint32_t type, const QString& device);
|
void setPreferredGamepad(uint32_t type, const QString& device);
|
||||||
|
|
||||||
|
void registerTiltAxisX(int axis);
|
||||||
|
void registerTiltAxisY(int axis);
|
||||||
|
void registerGyroAxisX(int axis);
|
||||||
|
void registerGyroAxisY(int axis);
|
||||||
|
|
||||||
|
float gyroSensitivity() const;
|
||||||
|
void setGyroSensitivity(float sensitivity);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
GBARumble* rumble();
|
GBARumble* rumble();
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "SensorView.h"
|
#include "SensorView.h"
|
||||||
|
|
||||||
#include "GameController.h"
|
#include "GameController.h"
|
||||||
|
#include "GamepadAxisEvent.h"
|
||||||
#include "InputController.h"
|
#include "InputController.h"
|
||||||
|
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
@ -50,6 +51,42 @@ SensorView::SensorView(GameController* controller, InputController* input, QWidg
|
||||||
} else {
|
} else {
|
||||||
m_timer.start();
|
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);
|
||||||
|
|
||||||
|
m_ui.gyroSensitivity->setValue(m_input->gyroSensitivity() / 1e8f);
|
||||||
|
connect(m_ui.gyroSensitivity, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), [this](double value) {
|
||||||
|
m_input->setGyroSensitivity(value * 1e8f);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SensorView::jiggerer(QAbstractButton* button, void (InputController::*setter)(int)) {
|
||||||
|
connect(button, &QAbstractButton::toggled, [this, button, setter](bool checked) {
|
||||||
|
if (!checked) {
|
||||||
|
m_jiggered = nullptr;
|
||||||
|
} else {
|
||||||
|
m_jiggered = [this, button, setter](int axis) {
|
||||||
|
(m_input->*setter)(axis);
|
||||||
|
button->setChecked(false);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
button->installEventFilter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SensorView::eventFilter(QObject*, QEvent* event) {
|
||||||
|
if (event->type() == GamepadAxisEvent::Type()) {
|
||||||
|
GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event);
|
||||||
|
gae->accept();
|
||||||
|
if (m_jiggered && gae->direction() != GamepadAxisEvent::NEUTRAL && gae->isNew()) {
|
||||||
|
m_jiggered(gae->axis());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SensorView::updateSensors() {
|
void SensorView::updateSensors() {
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#include "ui_SensorView.h"
|
#include "ui_SensorView.h"
|
||||||
|
|
||||||
struct GBARotationSource;
|
struct GBARotationSource;
|
||||||
|
@ -17,6 +19,7 @@ namespace QGBA {
|
||||||
|
|
||||||
class ConfigController;
|
class ConfigController;
|
||||||
class GameController;
|
class GameController;
|
||||||
|
class GamepadAxisEvent;
|
||||||
class InputController;
|
class InputController;
|
||||||
|
|
||||||
class SensorView : public QWidget {
|
class SensorView : public QWidget {
|
||||||
|
@ -25,6 +28,9 @@ Q_OBJECT
|
||||||
public:
|
public:
|
||||||
SensorView(GameController* controller, InputController* input, QWidget* parent = nullptr);
|
SensorView(GameController* controller, InputController* input, QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool eventFilter(QObject*, QEvent* event) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void updateSensors();
|
void updateSensors();
|
||||||
void setLuminanceValue(int);
|
void setLuminanceValue(int);
|
||||||
|
@ -33,10 +39,13 @@ private slots:
|
||||||
private:
|
private:
|
||||||
Ui::SensorView m_ui;
|
Ui::SensorView m_ui;
|
||||||
|
|
||||||
|
std::function<void(int)> m_jiggered;
|
||||||
GameController* m_controller;
|
GameController* m_controller;
|
||||||
InputController* m_input;
|
InputController* m_input;
|
||||||
GBARotationSource* m_rotation;
|
GBARotationSource* m_rotation;
|
||||||
QTimer m_timer;
|
QTimer m_timer;
|
||||||
|
|
||||||
|
void jiggerer(QAbstractButton*, void (InputController::*)(int));
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>508</width>
|
<width>434</width>
|
||||||
<height>258</height>
|
<height>288</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
@ -157,6 +157,9 @@
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Set Y</string>
|
<string>Set Y</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
|
@ -164,6 +167,9 @@
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Set X</string>
|
<string>Set X</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0" alignment="Qt::AlignHCenter">
|
<item row="1" column="0" alignment="Qt::AlignHCenter">
|
||||||
|
@ -240,6 +246,9 @@
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Set Y</string>
|
<string>Set Y</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
|
@ -247,6 +256,9 @@
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Set X</string>
|
<string>Set X</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="2" column="0">
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "gba/video.h"
|
#include "gba/video.h"
|
||||||
#include "gba/renderers/video-software.h"
|
#include "gba/renderers/video-software.h"
|
||||||
#include "util/configuration.h"
|
#include "util/configuration.h"
|
||||||
|
#include "util/formatting.h"
|
||||||
#include "util/vfs.h"
|
#include "util/vfs.h"
|
||||||
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 0) && defined(__APPLE__)
|
#if SDL_VERSION_ATLEAST(2, 0, 0) && defined(__APPLE__)
|
||||||
|
@ -233,10 +234,71 @@ void GBASDLPlayerLoadConfig(struct GBASDLPlayer* context, const struct Configura
|
||||||
if (context->joystick) {
|
if (context->joystick) {
|
||||||
GBAInputMapLoad(context->bindings, SDL_BINDING_BUTTON, config);
|
GBAInputMapLoad(context->bindings, SDL_BINDING_BUTTON, config);
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
GBAInputProfileLoad(context->bindings, SDL_BINDING_BUTTON, config, SDL_JoystickName(context->joystick));
|
const char* name = SDL_JoystickName(context->joystick);
|
||||||
#else
|
#else
|
||||||
GBAInputProfileLoad(context->bindings, SDL_BINDING_BUTTON, config, SDL_JoystickName(SDL_JoystickIndex(context->joystick)));
|
const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick));
|
||||||
#endif
|
#endif
|
||||||
|
GBAInputProfileLoad(context->bindings, SDL_BINDING_BUTTON, config, name);
|
||||||
|
|
||||||
|
const char* value;
|
||||||
|
char* end;
|
||||||
|
int axis;
|
||||||
|
value = GBAInputGetCustomValue(config, SDL_BINDING_BUTTON, "tiltAxisX", name);
|
||||||
|
if (value) {
|
||||||
|
axis = strtol(value, &end, 0);
|
||||||
|
if (end && !*end) {
|
||||||
|
context->rotation.axisX = axis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value = GBAInputGetCustomValue(config, SDL_BINDING_BUTTON, "tiltAxisY", name);
|
||||||
|
if (value) {
|
||||||
|
axis = strtol(value, &end, 0);
|
||||||
|
if (end && !*end) {
|
||||||
|
context->rotation.axisY = axis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value = GBAInputGetCustomValue(config, SDL_BINDING_BUTTON, "gyroAxisX", name);
|
||||||
|
if (value) {
|
||||||
|
axis = strtol(value, &end, 0);
|
||||||
|
if (end && !*end) {
|
||||||
|
context->rotation.gyroX = axis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value = GBAInputGetCustomValue(config, SDL_BINDING_BUTTON, "gyroAxisY", name);
|
||||||
|
if (value) {
|
||||||
|
axis = strtol(value, &end, 0);
|
||||||
|
if (end && !*end) {
|
||||||
|
context->rotation.gyroY = axis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value = GBAInputGetCustomValue(config, SDL_BINDING_BUTTON, "gyroSensitivity", name);
|
||||||
|
if (value) {
|
||||||
|
float sensitivity = strtof_u(value, &end);
|
||||||
|
if (end && !*end) {
|
||||||
|
context->rotation.gyroSensitivity = sensitivity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBASDLPlayerSaveConfig(const struct GBASDLPlayer* context, struct Configuration* config) {
|
||||||
|
if (context->joystick) {
|
||||||
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
|
const char* name = SDL_JoystickName(context->joystick);
|
||||||
|
#else
|
||||||
|
const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick));
|
||||||
|
#endif
|
||||||
|
char value[12];
|
||||||
|
snprintf(value, sizeof(value), "%i", context->rotation.axisX);
|
||||||
|
GBAInputSetCustomValue(config, SDL_BINDING_BUTTON, "tiltAxisX", value, name);
|
||||||
|
snprintf(value, sizeof(value), "%i", context->rotation.axisY);
|
||||||
|
GBAInputSetCustomValue(config, SDL_BINDING_BUTTON, "tiltAxisY", value, name);
|
||||||
|
snprintf(value, sizeof(value), "%i", context->rotation.gyroX);
|
||||||
|
GBAInputSetCustomValue(config, SDL_BINDING_BUTTON, "gyroAxisX", value, name);
|
||||||
|
snprintf(value, sizeof(value), "%i", context->rotation.gyroY);
|
||||||
|
GBAInputSetCustomValue(config, SDL_BINDING_BUTTON, "gyroAxisY", value, name);
|
||||||
|
snprintf(value, sizeof(value), "%g", context->rotation.gyroSensitivity);
|
||||||
|
GBAInputSetCustomValue(config, SDL_BINDING_BUTTON, "gyroSensitivity", value, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,7 @@ void GBASDLPlayerChangeJoystick(struct GBASDLEvents*, struct GBASDLPlayer*, size
|
||||||
|
|
||||||
void GBASDLInitBindings(struct GBAInputMap* inputMap);
|
void GBASDLInitBindings(struct GBAInputMap* inputMap);
|
||||||
void GBASDLPlayerLoadConfig(struct GBASDLPlayer*, const struct Configuration*);
|
void GBASDLPlayerLoadConfig(struct GBASDLPlayer*, const struct Configuration*);
|
||||||
|
void GBASDLPlayerSaveConfig(const struct GBASDLPlayer*, struct Configuration*);
|
||||||
|
|
||||||
void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLPlayer* sdlContext, const union SDL_Event* event);
|
void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLPlayer* sdlContext, const union SDL_Event* event);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue