src/platform/qt/InputController.cpp (view raw)
1/* Copyright (c) 2013-2014 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "InputController.h"
7
8#include "ConfigController.h"
9#include "GamepadButtonEvent.h"
10
11#include <QApplication>
12#include <QTimer>
13#include <QWidget>
14
15extern "C" {
16#include "util/configuration.h"
17}
18
19using namespace QGBA;
20
21InputController::InputController(QObject* parent)
22 : QObject(parent)
23 , m_config(nullptr)
24 , m_gamepadTimer(nullptr)
25{
26 GBAInputMapInit(&m_inputMap);
27
28#ifdef BUILD_SDL
29 m_sdlEvents.bindings = &m_inputMap;
30 GBASDLInitEvents(&m_sdlEvents);
31 GBASDLInitBindings(&m_inputMap);
32 SDL_JoystickEventState(SDL_QUERY);
33
34 m_gamepadTimer = new QTimer(this);
35 connect(m_gamepadTimer, SIGNAL(timeout()), this, SLOT(testGamepad()));
36 m_gamepadTimer->setInterval(50);
37 m_gamepadTimer->start();
38#endif
39
40 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
41 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
42 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
43 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
44 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
45 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
46 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
47 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
48 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
49 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
50}
51
52InputController::~InputController() {
53 GBAInputMapDeinit(&m_inputMap);
54
55#ifdef BUILD_SDL
56 GBASDLDeinitEvents(&m_sdlEvents);
57#endif
58}
59
60void InputController::setConfiguration(ConfigController* config) {
61 m_config = config;
62 loadConfiguration(KEYBOARD);
63#ifdef BUILD_SDL
64 loadConfiguration(SDL_BINDING_BUTTON);
65#endif
66}
67
68void InputController::loadConfiguration(uint32_t type) {
69 GBAInputMapLoad(&m_inputMap, type, m_config->configuration());
70}
71
72void InputController::saveConfiguration(uint32_t type) {
73 GBAInputMapSave(&m_inputMap, type, m_config->configuration());
74 m_config->write();
75}
76
77GBAKey InputController::mapKeyboard(int key) const {
78 return GBAInputMapKey(&m_inputMap, KEYBOARD, key);
79}
80
81void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
82 return GBAInputBindKey(&m_inputMap, type, key, gbaKey);
83}
84
85#ifdef BUILD_SDL
86int InputController::testSDLEvents() {
87 SDL_Joystick* joystick = m_sdlEvents.joystick;
88 SDL_JoystickUpdate();
89 int numButtons = SDL_JoystickNumButtons(joystick);
90 int activeButtons = 0;
91 int i;
92 for (i = 0; i < numButtons; ++i) {
93 GBAKey key = GBAInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i);
94 if (key == GBA_KEY_NONE) {
95 continue;
96 }
97 if (hasPendingEvent(key)) {
98 continue;
99 }
100 if (SDL_JoystickGetButton(joystick, i)) {
101 activeButtons |= 1 << key;
102 }
103 }
104 int numHats = SDL_JoystickNumHats(joystick);
105 for (i = 0; i < numHats; ++i) {
106 int hat = SDL_JoystickGetHat(joystick, i);
107 if (hat & SDL_HAT_UP) {
108 activeButtons |= 1 << GBA_KEY_UP;
109 }
110 if (hat & SDL_HAT_LEFT) {
111 activeButtons |= 1 << GBA_KEY_LEFT;
112 }
113 if (hat & SDL_HAT_DOWN) {
114 activeButtons |= 1 << GBA_KEY_DOWN;
115 }
116 if (hat & SDL_HAT_RIGHT) {
117 activeButtons |= 1 << GBA_KEY_RIGHT;
118 }
119 }
120
121 int numAxes = SDL_JoystickNumAxes(joystick);
122 for (i = 0; i < numAxes; ++i) {
123 int value = SDL_JoystickGetAxis(joystick, i);
124
125 enum GBAKey key = GBAInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value);
126 if (key != GBA_KEY_NONE) {
127 activeButtons |= 1 << key;
128 }
129 }
130 return activeButtons;
131}
132
133QSet<int> InputController::activeGamepadButtons() {
134 SDL_Joystick* joystick = m_sdlEvents.joystick;
135 SDL_JoystickUpdate();
136 int numButtons = SDL_JoystickNumButtons(joystick);
137 QSet<int> activeButtons;
138 int i;
139 for (i = 0; i < numButtons; ++i) {
140 if (SDL_JoystickGetButton(joystick, i)) {
141 activeButtons.insert(i);
142 }
143 }
144 return activeButtons;
145}
146
147QSet<QPair<int, int32_t>> InputController::activeGamepadAxes() {
148 SDL_Joystick* joystick = m_sdlEvents.joystick;
149 SDL_JoystickUpdate();
150 int numButtons = SDL_JoystickNumAxes(joystick);
151 QSet<QPair<int, int32_t>> activeAxes;
152 int i;
153 for (i = 0; i < numButtons; ++i) {
154 int32_t axis = SDL_JoystickGetAxis(joystick, i);
155 if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
156 activeAxes.insert(qMakePair(i, axis > 0 ? 1 : -1));
157 }
158 }
159 return activeAxes;
160}
161
162void InputController::bindAxis(uint32_t type, int axis, Direction direction, GBAKey key) {
163 const GBAAxis* old = GBAInputQueryAxis(&m_inputMap, SDL_BINDING_BUTTON, axis);
164 GBAAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
165 if (old) {
166 description = *old;
167 }
168 switch (direction) {
169 case NEGATIVE:
170 description.lowDirection = key;
171 description.deadLow = -AXIS_THRESHOLD;
172 break;
173 case POSITIVE:
174 description.highDirection = key;
175 description.deadHigh = AXIS_THRESHOLD;
176 break;
177 default:
178 return;
179 }
180 GBAInputBindAxis(&m_inputMap, SDL_BINDING_BUTTON, axis, &description);
181}
182#endif
183
184void InputController::testGamepad() {
185#ifdef BUILD_SDL
186 auto activeAxes = activeGamepadAxes();
187 auto oldAxes = m_activeAxes;
188 m_activeAxes = activeAxes;
189 activeAxes.subtract(oldAxes);
190 if (!activeAxes.empty()) {
191 emit axisChanged(activeAxes.begin()->first, activeAxes.begin()->second);
192 }
193
194 auto activeButtons = activeGamepadButtons();
195 auto oldButtons = m_activeButtons;
196 m_activeButtons = activeButtons;
197 activeButtons.subtract(oldButtons);
198 oldButtons.subtract(m_activeButtons);
199 for (int button : activeButtons) {
200 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, this);
201 postPendingEvent(event->gbaKey());
202 QApplication::sendEvent(QApplication::focusWidget(), event);
203 if (!event->isAccepted()) {
204 clearPendingEvent(event->gbaKey());
205 }
206 }
207 for (int button : oldButtons) {
208 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, this);
209 clearPendingEvent(event->gbaKey());
210 QApplication::sendEvent(QApplication::focusWidget(), event);
211 }
212#endif
213}
214
215void InputController::postPendingEvent(GBAKey key) {
216 m_pendingEvents.insert(key);
217}
218
219void InputController::clearPendingEvent(GBAKey key) {
220 m_pendingEvents.remove(key);
221}
222
223bool InputController::hasPendingEvent(GBAKey key) const {
224 return m_pendingEvents.contains(key);
225}