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
|
||||
- Analog inputs can be used for shortcuts
|
||||
- Menu items for specific solar sensor brightness levels
|
||||
- Remappable controls for tilt and gyroscope sensors
|
||||
Bugfixes:
|
||||
- GBA: Fix timers not updating timing when writing to only the reload register
|
||||
- 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);
|
||||
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);
|
||||
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
|
||||
|
|
|
@ -110,6 +110,11 @@ void InputController::loadProfile(uint32_t type, const QString& profile) {
|
|||
|
||||
void InputController::saveConfiguration(uint32_t type) {
|
||||
GBAInputMapSave(&m_inputMap, type, m_config->input());
|
||||
#ifdef BUILD_SDL
|
||||
if (m_playerAttached) {
|
||||
GBASDLPlayerSaveConfig(&m_sdlPlayer, m_config->input());
|
||||
}
|
||||
#endif
|
||||
m_config->write();
|
||||
}
|
||||
|
||||
|
@ -170,6 +175,27 @@ GBARumble* InputController::rumble() {
|
|||
GBARotationSource* InputController::rotationSource() {
|
||||
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
|
||||
GBARumble* InputController::rumble() {
|
||||
return nullptr;
|
||||
|
|
|
@ -65,6 +65,14 @@ public:
|
|||
int gamepad(uint32_t type) const { return m_sdlPlayer.joystickIndex; }
|
||||
void setGamepad(uint32_t type, int index) { GBASDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index); }
|
||||
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
|
||||
|
||||
GBARumble* rumble();
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "SensorView.h"
|
||||
|
||||
#include "GameController.h"
|
||||
#include "GamepadAxisEvent.h"
|
||||
#include "InputController.h"
|
||||
|
||||
using namespace QGBA;
|
||||
|
@ -50,6 +51,42 @@ SensorView::SensorView(GameController* controller, InputController* input, QWidg
|
|||
} else {
|
||||
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() {
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "ui_SensorView.h"
|
||||
|
||||
struct GBARotationSource;
|
||||
|
@ -17,6 +19,7 @@ namespace QGBA {
|
|||
|
||||
class ConfigController;
|
||||
class GameController;
|
||||
class GamepadAxisEvent;
|
||||
class InputController;
|
||||
|
||||
class SensorView : public QWidget {
|
||||
|
@ -25,6 +28,9 @@ Q_OBJECT
|
|||
public:
|
||||
SensorView(GameController* controller, InputController* input, QWidget* parent = nullptr);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject*, QEvent* event) override;
|
||||
|
||||
private slots:
|
||||
void updateSensors();
|
||||
void setLuminanceValue(int);
|
||||
|
@ -33,10 +39,13 @@ private slots:
|
|||
private:
|
||||
Ui::SensorView m_ui;
|
||||
|
||||
std::function<void(int)> m_jiggered;
|
||||
GameController* m_controller;
|
||||
InputController* m_input;
|
||||
GBARotationSource* m_rotation;
|
||||
QTimer m_timer;
|
||||
|
||||
void jiggerer(QAbstractButton*, void (InputController::*)(int));
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>508</width>
|
||||
<height>258</height>
|
||||
<width>434</width>
|
||||
<height>288</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
|
@ -157,6 +157,9 @@
|
|||
<property name="text">
|
||||
<string>Set Y</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
|
@ -164,6 +167,9 @@
|
|||
<property name="text">
|
||||
<string>Set X</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" alignment="Qt::AlignHCenter">
|
||||
|
@ -240,6 +246,9 @@
|
|||
<property name="text">
|
||||
<string>Set Y</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
|
@ -247,6 +256,9 @@
|
|||
<property name="text">
|
||||
<string>Set X</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "gba/video.h"
|
||||
#include "gba/renderers/video-software.h"
|
||||
#include "util/configuration.h"
|
||||
#include "util/formatting.h"
|
||||
#include "util/vfs.h"
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0) && defined(__APPLE__)
|
||||
|
@ -233,10 +234,71 @@ void GBASDLPlayerLoadConfig(struct GBASDLPlayer* context, const struct Configura
|
|||
if (context->joystick) {
|
||||
GBAInputMapLoad(context->bindings, SDL_BINDING_BUTTON, config);
|
||||
#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
|
||||
GBAInputProfileLoad(context->bindings, SDL_BINDING_BUTTON, config, SDL_JoystickName(SDL_JoystickIndex(context->joystick)));
|
||||
const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick));
|
||||
#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 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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue