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 "GamepadAxisEvent.h"
10#include "GamepadButtonEvent.h"
11
12#include <QApplication>
13#include <QTimer>
14#include <QWidget>
15
16extern "C" {
17#include "util/configuration.h"
18}
19
20using namespace QGBA;
21
22#ifdef BUILD_SDL
23int InputController::s_sdlInited = 0;
24GBASDLEvents InputController::s_sdlEvents;
25#endif
26
27InputController::InputController(int playerId, QObject* parent)
28 : QObject(parent)
29 , m_playerId(playerId)
30 , m_config(nullptr)
31 , m_gamepadTimer(nullptr)
32#ifdef BUILD_SDL
33 , m_playerAttached(false)
34#endif
35 , m_allowOpposing(false)
36{
37 GBAInputMapInit(&m_inputMap);
38
39#ifdef BUILD_SDL
40 if (s_sdlInited == 0) {
41 GBASDLInitEvents(&s_sdlEvents);
42 }
43 ++s_sdlInited;
44 m_sdlPlayer.bindings = &m_inputMap;
45 GBASDLInitBindings(&m_inputMap);
46
47 m_gamepadTimer = new QTimer(this);
48 connect(m_gamepadTimer, SIGNAL(timeout()), this, SLOT(testGamepad()));
49 m_gamepadTimer->setInterval(50);
50 m_gamepadTimer->start();
51#endif
52
53 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
54 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
55 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
56 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
57 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
58 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
59 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
60 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
61 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
62 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
63}
64
65InputController::~InputController() {
66 GBAInputMapDeinit(&m_inputMap);
67
68#ifdef BUILD_SDL
69 --s_sdlInited;
70 if (s_sdlInited == 0) {
71 GBASDLDeinitEvents(&s_sdlEvents);
72 }
73#endif
74}
75
76void InputController::setConfiguration(ConfigController* config) {
77 m_config = config;
78 setAllowOpposing(config->getOption("allowOpposingDirections").toInt());
79 loadConfiguration(KEYBOARD);
80#ifdef BUILD_SDL
81 GBASDLEventsLoadConfig(&s_sdlEvents, config->input());
82 if (!m_playerAttached) {
83 GBASDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
84 m_playerAttached = true;
85 }
86 loadConfiguration(SDL_BINDING_BUTTON);
87 loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
88#endif
89}
90
91void InputController::loadConfiguration(uint32_t type) {
92 GBAInputMapLoad(&m_inputMap, type, m_config->input());
93}
94
95void InputController::loadProfile(uint32_t type, const QString& profile) {
96 GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toLocal8Bit().constData());
97#ifdef BUILD_SDL
98 recalibrateAxes();
99#endif
100}
101
102void InputController::saveConfiguration(uint32_t type) {
103 GBAInputMapSave(&m_inputMap, type, m_config->input());
104 m_config->write();
105}
106
107void InputController::saveProfile(uint32_t type, const QString& profile) {
108 GBAInputProfileSave(&m_inputMap, type, m_config->input(), profile.toLocal8Bit().constData());
109 m_config->write();
110}
111
112const char* InputController::profileForType(uint32_t type) {
113 UNUSED(type);
114#ifdef BUILD_SDL
115 if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
116#if SDL_VERSION_ATLEAST(2, 0, 0)
117 return SDL_JoystickName(m_sdlPlayer.joystick);
118#else
119 return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick));
120#endif
121 }
122#endif
123 return 0;
124}
125
126#ifdef BUILD_SDL
127QStringList InputController::connectedGamepads(uint32_t type) const {
128 UNUSED(type);
129 if (type != SDL_BINDING_BUTTON) {
130 return QStringList();
131 }
132
133 QStringList pads;
134 for (size_t i = 0; i < s_sdlEvents.nJoysticks; ++i) {
135 const char* name;
136#if SDL_VERSION_ATLEAST(2, 0, 0)
137 name = SDL_JoystickName(s_sdlEvents.joysticks[i]);
138#else
139 name = SDL_JoystickName(SDL_JoystickIndex(s_sdlEvents.joysticks[i]));
140#endif
141 if (name) {
142 pads.append(QString(name));
143 } else {
144 pads.append(QString());
145 }
146 }
147 return pads;
148}
149
150void InputController::setPreferredGamepad(uint32_t type, const QString& device) {
151 if (!m_config) {
152 return;
153 }
154 GBAInputSetPreferredDevice(m_config->input(), type, m_sdlPlayer.playerId, device.toLocal8Bit().constData());
155}
156
157GBARumble* InputController::rumble() {
158 return &m_sdlPlayer.rumble.d;
159}
160#endif
161
162GBAKey InputController::mapKeyboard(int key) const {
163 return GBAInputMapKey(&m_inputMap, KEYBOARD, key);
164}
165
166void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
167 return GBAInputBindKey(&m_inputMap, type, key, gbaKey);
168}
169
170#ifdef BUILD_SDL
171int InputController::testSDLEvents() {
172 SDL_Joystick* joystick = m_sdlPlayer.joystick;
173 SDL_JoystickUpdate();
174 int numButtons = SDL_JoystickNumButtons(joystick);
175 int activeButtons = 0;
176 int i;
177 for (i = 0; i < numButtons; ++i) {
178 GBAKey key = GBAInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i);
179 if (key == GBA_KEY_NONE) {
180 continue;
181 }
182 if (hasPendingEvent(key)) {
183 continue;
184 }
185 if (SDL_JoystickGetButton(joystick, i)) {
186 activeButtons |= 1 << key;
187 }
188 }
189 int numHats = SDL_JoystickNumHats(joystick);
190 for (i = 0; i < numHats; ++i) {
191 int hat = SDL_JoystickGetHat(joystick, i);
192 if (hat & SDL_HAT_UP) {
193 activeButtons |= 1 << GBA_KEY_UP;
194 }
195 if (hat & SDL_HAT_LEFT) {
196 activeButtons |= 1 << GBA_KEY_LEFT;
197 }
198 if (hat & SDL_HAT_DOWN) {
199 activeButtons |= 1 << GBA_KEY_DOWN;
200 }
201 if (hat & SDL_HAT_RIGHT) {
202 activeButtons |= 1 << GBA_KEY_RIGHT;
203 }
204 }
205
206 int numAxes = SDL_JoystickNumAxes(joystick);
207 for (i = 0; i < numAxes; ++i) {
208 int value = SDL_JoystickGetAxis(joystick, i);
209
210 enum GBAKey key = GBAInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value);
211 if (key != GBA_KEY_NONE) {
212 activeButtons |= 1 << key;
213 }
214 }
215 return activeButtons;
216}
217
218QSet<int> InputController::activeGamepadButtons() {
219 SDL_Joystick* joystick = m_sdlPlayer.joystick;
220 SDL_JoystickUpdate();
221 int numButtons = SDL_JoystickNumButtons(joystick);
222 QSet<int> activeButtons;
223 int i;
224 for (i = 0; i < numButtons; ++i) {
225 if (SDL_JoystickGetButton(joystick, i)) {
226 activeButtons.insert(i);
227 }
228 }
229 return activeButtons;
230}
231
232void InputController::recalibrateAxes() {
233 SDL_Joystick* joystick = m_sdlPlayer.joystick;
234 SDL_JoystickUpdate();
235 int numAxes = SDL_JoystickNumAxes(joystick);
236 if (numAxes < 1) {
237 return;
238 }
239 m_deadzones.resize(numAxes);
240 int i;
241 for (i = 0; i < numAxes; ++i) {
242 m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
243 }
244}
245
246QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes() {
247 SDL_Joystick* joystick = m_sdlPlayer.joystick;
248 SDL_JoystickUpdate();
249 int numAxes = SDL_JoystickNumAxes(joystick);
250 QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
251 if (numAxes < 1) {
252 return activeAxes;
253 }
254 m_deadzones.resize(numAxes);
255 int i;
256 for (i = 0; i < numAxes; ++i) {
257 int32_t axis = SDL_JoystickGetAxis(joystick, i);
258 axis -= m_deadzones[i];
259 if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
260 activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
261 }
262 }
263 return activeAxes;
264}
265
266void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
267 const GBAAxis* old = GBAInputQueryAxis(&m_inputMap, SDL_BINDING_BUTTON, axis);
268 GBAAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
269 if (old) {
270 description = *old;
271 }
272 switch (direction) {
273 case GamepadAxisEvent::NEGATIVE:
274 description.lowDirection = key;
275 description.deadLow = m_deadzones[axis] - AXIS_THRESHOLD;
276 break;
277 case GamepadAxisEvent::POSITIVE:
278 description.highDirection = key;
279 description.deadHigh = m_deadzones[axis] + AXIS_THRESHOLD;
280 break;
281 default:
282 return;
283 }
284 GBAInputBindAxis(&m_inputMap, SDL_BINDING_BUTTON, axis, &description);
285}
286#endif
287
288void InputController::testGamepad() {
289#ifdef BUILD_SDL
290 auto activeAxes = activeGamepadAxes();
291 auto oldAxes = m_activeAxes;
292 m_activeAxes = activeAxes;
293
294 auto activeButtons = activeGamepadButtons();
295 auto oldButtons = m_activeButtons;
296 m_activeButtons = activeButtons;
297
298 if (!QApplication::focusWidget()) {
299 return;
300 }
301
302 activeAxes.subtract(oldAxes);
303 oldAxes.subtract(m_activeAxes);
304
305 for (auto& axis : m_activeAxes) {
306 bool newlyAboveThreshold = activeAxes.contains(axis);
307 if (newlyAboveThreshold) {
308 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, this);
309 postPendingEvent(event->gbaKey());
310 QApplication::sendEvent(QApplication::focusWidget(), event);
311 if (!event->isAccepted()) {
312 clearPendingEvent(event->gbaKey());
313 }
314 }
315 }
316 for (auto axis : oldAxes) {
317 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, this);
318 clearPendingEvent(event->gbaKey());
319 QApplication::sendEvent(QApplication::focusWidget(), event);
320 }
321
322 if (!QApplication::focusWidget()) {
323 return;
324 }
325
326 activeButtons.subtract(oldButtons);
327 oldButtons.subtract(m_activeButtons);
328
329 for (int button : activeButtons) {
330 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, this);
331 postPendingEvent(event->gbaKey());
332 QApplication::sendEvent(QApplication::focusWidget(), event);
333 if (!event->isAccepted()) {
334 clearPendingEvent(event->gbaKey());
335 }
336 }
337 for (int button : oldButtons) {
338 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, this);
339 clearPendingEvent(event->gbaKey());
340 QApplication::sendEvent(QApplication::focusWidget(), event);
341 }
342#endif
343}
344
345void InputController::postPendingEvent(GBAKey key) {
346 m_pendingEvents.insert(key);
347}
348
349void InputController::clearPendingEvent(GBAKey key) {
350 m_pendingEvents.remove(key);
351}
352
353bool InputController::hasPendingEvent(GBAKey key) const {
354 return m_pendingEvents.contains(key);
355}