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 if (m_playerAttached) {
70 GBASDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
71 }
72
73 --s_sdlInited;
74 if (s_sdlInited == 0) {
75 GBASDLDeinitEvents(&s_sdlEvents);
76 }
77#endif
78}
79
80void InputController::setConfiguration(ConfigController* config) {
81 m_config = config;
82 setAllowOpposing(config->getOption("allowOpposingDirections").toInt());
83 loadConfiguration(KEYBOARD);
84#ifdef BUILD_SDL
85 GBASDLEventsLoadConfig(&s_sdlEvents, config->input());
86 if (!m_playerAttached) {
87 GBASDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
88 m_playerAttached = true;
89 }
90 loadConfiguration(SDL_BINDING_BUTTON);
91 loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
92#endif
93}
94
95void InputController::loadConfiguration(uint32_t type) {
96 GBAInputMapLoad(&m_inputMap, type, m_config->input());
97#ifdef BUILD_SDL
98 if (m_playerAttached) {
99 GBASDLPlayerLoadConfig(&m_sdlPlayer, m_config->input());
100 }
101#endif
102}
103
104void InputController::loadProfile(uint32_t type, const QString& profile) {
105 GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toLocal8Bit().constData());
106#ifdef BUILD_SDL
107 recalibrateAxes();
108#endif
109}
110
111void InputController::saveConfiguration() {
112 saveConfiguration(KEYBOARD);
113#ifdef BUILD_SDL
114 saveConfiguration(SDL_BINDING_BUTTON);
115 saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
116 if (m_playerAttached) {
117 GBASDLPlayerSaveConfig(&m_sdlPlayer, m_config->input());
118 }
119 m_config->write();
120#endif
121}
122
123void InputController::saveConfiguration(uint32_t type) {
124 GBAInputMapSave(&m_inputMap, type, m_config->input());
125 m_config->write();
126}
127
128void InputController::saveProfile(uint32_t type, const QString& profile) {
129 GBAInputProfileSave(&m_inputMap, type, m_config->input(), profile.toLocal8Bit().constData());
130 m_config->write();
131}
132
133const char* InputController::profileForType(uint32_t type) {
134 UNUSED(type);
135#ifdef BUILD_SDL
136 if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
137#if SDL_VERSION_ATLEAST(2, 0, 0)
138 return SDL_JoystickName(m_sdlPlayer.joystick);
139#else
140 return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick));
141#endif
142 }
143#endif
144 return 0;
145}
146
147#ifdef BUILD_SDL
148QStringList InputController::connectedGamepads(uint32_t type) const {
149 UNUSED(type);
150 if (type != SDL_BINDING_BUTTON) {
151 return QStringList();
152 }
153
154 QStringList pads;
155 for (size_t i = 0; i < s_sdlEvents.nJoysticks; ++i) {
156 const char* name;
157#if SDL_VERSION_ATLEAST(2, 0, 0)
158 name = SDL_JoystickName(s_sdlEvents.joysticks[i]);
159#else
160 name = SDL_JoystickName(SDL_JoystickIndex(s_sdlEvents.joysticks[i]));
161#endif
162 if (name) {
163 pads.append(QString(name));
164 } else {
165 pads.append(QString());
166 }
167 }
168 return pads;
169}
170
171void InputController::setPreferredGamepad(uint32_t type, const QString& device) {
172 if (!m_config) {
173 return;
174 }
175 GBAInputSetPreferredDevice(m_config->input(), type, m_sdlPlayer.playerId, device.toLocal8Bit().constData());
176}
177
178GBARumble* InputController::rumble() {
179 return &m_sdlPlayer.rumble.d;
180}
181
182GBARotationSource* InputController::rotationSource() {
183 return &m_sdlPlayer.rotation.d;
184}
185
186void InputController::registerTiltAxisX(int axis) {
187 m_sdlPlayer.rotation.axisX = axis;
188}
189void InputController::registerTiltAxisY(int axis) {
190 m_sdlPlayer.rotation.axisY = axis;
191}
192void InputController::registerGyroAxisX(int axis) {
193 m_sdlPlayer.rotation.gyroX = axis;
194}
195void InputController::registerGyroAxisY(int axis) {
196 m_sdlPlayer.rotation.gyroY = axis;
197}
198
199float InputController::gyroSensitivity() const {
200 return m_sdlPlayer.rotation.gyroSensitivity;
201}
202
203void InputController::setGyroSensitivity(float sensitivity) {
204 m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
205}
206#else
207GBARumble* InputController::rumble() {
208 return nullptr;
209}
210
211GBARotationSource* InputController::rotationSource() {
212 return nullptr;
213}
214#endif
215
216GBAKey InputController::mapKeyboard(int key) const {
217 return GBAInputMapKey(&m_inputMap, KEYBOARD, key);
218}
219
220void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
221 return GBAInputBindKey(&m_inputMap, type, key, gbaKey);
222}
223
224#ifdef BUILD_SDL
225int InputController::testSDLEvents() {
226 SDL_Joystick* joystick = m_sdlPlayer.joystick;
227 SDL_JoystickUpdate();
228 int numButtons = SDL_JoystickNumButtons(joystick);
229 int activeButtons = 0;
230 int i;
231 for (i = 0; i < numButtons; ++i) {
232 GBAKey key = GBAInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i);
233 if (key == GBA_KEY_NONE) {
234 continue;
235 }
236 if (hasPendingEvent(key)) {
237 continue;
238 }
239 if (SDL_JoystickGetButton(joystick, i)) {
240 activeButtons |= 1 << key;
241 }
242 }
243 int numHats = SDL_JoystickNumHats(joystick);
244 for (i = 0; i < numHats; ++i) {
245 int hat = SDL_JoystickGetHat(joystick, i);
246 if (hat & SDL_HAT_UP) {
247 activeButtons |= 1 << GBA_KEY_UP;
248 }
249 if (hat & SDL_HAT_LEFT) {
250 activeButtons |= 1 << GBA_KEY_LEFT;
251 }
252 if (hat & SDL_HAT_DOWN) {
253 activeButtons |= 1 << GBA_KEY_DOWN;
254 }
255 if (hat & SDL_HAT_RIGHT) {
256 activeButtons |= 1 << GBA_KEY_RIGHT;
257 }
258 }
259
260 int numAxes = SDL_JoystickNumAxes(joystick);
261 for (i = 0; i < numAxes; ++i) {
262 int value = SDL_JoystickGetAxis(joystick, i);
263
264 enum GBAKey key = GBAInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value);
265 if (key != GBA_KEY_NONE) {
266 activeButtons |= 1 << key;
267 }
268 }
269 return activeButtons;
270}
271
272QSet<int> InputController::activeGamepadButtons() {
273 SDL_Joystick* joystick = m_sdlPlayer.joystick;
274 SDL_JoystickUpdate();
275 int numButtons = SDL_JoystickNumButtons(joystick);
276 QSet<int> activeButtons;
277 int i;
278 for (i = 0; i < numButtons; ++i) {
279 if (SDL_JoystickGetButton(joystick, i)) {
280 activeButtons.insert(i);
281 }
282 }
283 return activeButtons;
284}
285
286void InputController::recalibrateAxes() {
287 SDL_Joystick* joystick = m_sdlPlayer.joystick;
288 SDL_JoystickUpdate();
289 int numAxes = SDL_JoystickNumAxes(joystick);
290 if (numAxes < 1) {
291 return;
292 }
293 m_deadzones.resize(numAxes);
294 int i;
295 for (i = 0; i < numAxes; ++i) {
296 m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
297 }
298}
299
300QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes() {
301 SDL_Joystick* joystick = m_sdlPlayer.joystick;
302 SDL_JoystickUpdate();
303 int numAxes = SDL_JoystickNumAxes(joystick);
304 QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
305 if (numAxes < 1) {
306 return activeAxes;
307 }
308 m_deadzones.resize(numAxes);
309 int i;
310 for (i = 0; i < numAxes; ++i) {
311 int32_t axis = SDL_JoystickGetAxis(joystick, i);
312 axis -= m_deadzones[i];
313 if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
314 activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
315 }
316 }
317 return activeAxes;
318}
319
320void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
321 const GBAAxis* old = GBAInputQueryAxis(&m_inputMap, SDL_BINDING_BUTTON, axis);
322 GBAAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
323 if (old) {
324 description = *old;
325 }
326 switch (direction) {
327 case GamepadAxisEvent::NEGATIVE:
328 description.lowDirection = key;
329 description.deadLow = m_deadzones[axis] - AXIS_THRESHOLD;
330 break;
331 case GamepadAxisEvent::POSITIVE:
332 description.highDirection = key;
333 description.deadHigh = m_deadzones[axis] + AXIS_THRESHOLD;
334 break;
335 default:
336 return;
337 }
338 GBAInputBindAxis(&m_inputMap, SDL_BINDING_BUTTON, axis, &description);
339}
340#endif
341
342void InputController::testGamepad() {
343#ifdef BUILD_SDL
344 auto activeAxes = activeGamepadAxes();
345 auto oldAxes = m_activeAxes;
346 m_activeAxes = activeAxes;
347
348 auto activeButtons = activeGamepadButtons();
349 auto oldButtons = m_activeButtons;
350 m_activeButtons = activeButtons;
351
352 if (!QApplication::focusWidget()) {
353 return;
354 }
355
356 activeAxes.subtract(oldAxes);
357 oldAxes.subtract(m_activeAxes);
358
359 for (auto& axis : m_activeAxes) {
360 bool newlyAboveThreshold = activeAxes.contains(axis);
361 if (newlyAboveThreshold) {
362 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, this);
363 postPendingEvent(event->gbaKey());
364 QApplication::sendEvent(QApplication::focusWidget(), event);
365 if (!event->isAccepted()) {
366 clearPendingEvent(event->gbaKey());
367 }
368 }
369 }
370 for (auto axis : oldAxes) {
371 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, this);
372 clearPendingEvent(event->gbaKey());
373 QApplication::sendEvent(QApplication::focusWidget(), event);
374 }
375
376 if (!QApplication::focusWidget()) {
377 return;
378 }
379
380 activeButtons.subtract(oldButtons);
381 oldButtons.subtract(m_activeButtons);
382
383 for (int button : activeButtons) {
384 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, this);
385 postPendingEvent(event->gbaKey());
386 QApplication::sendEvent(QApplication::focusWidget(), event);
387 if (!event->isAccepted()) {
388 clearPendingEvent(event->gbaKey());
389 }
390 }
391 for (int button : oldButtons) {
392 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, this);
393 clearPendingEvent(event->gbaKey());
394 QApplication::sendEvent(QApplication::focusWidget(), event);
395 }
396#endif
397}
398
399void InputController::postPendingEvent(GBAKey key) {
400 m_pendingEvents.insert(key);
401}
402
403void InputController::clearPendingEvent(GBAKey key) {
404 m_pendingEvents.remove(key);
405}
406
407bool InputController::hasPendingEvent(GBAKey key) const {
408 return m_pendingEvents.contains(key);
409}