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#include "InputProfile.h"
12
13#include <QApplication>
14#include <QTimer>
15#include <QWidget>
16
17extern "C" {
18#include "util/configuration.h"
19}
20
21using namespace QGBA;
22
23#ifdef BUILD_SDL
24int InputController::s_sdlInited = 0;
25GBASDLEvents InputController::s_sdlEvents;
26#endif
27
28InputController::InputController(int playerId, QObject* parent)
29 : QObject(parent)
30 , m_playerId(playerId)
31 , m_config(nullptr)
32 , m_gamepadTimer(nullptr)
33#ifdef BUILD_SDL
34 , m_playerAttached(false)
35#endif
36 , m_allowOpposing(false)
37{
38 GBAInputMapInit(&m_inputMap);
39
40#ifdef BUILD_SDL
41 if (s_sdlInited == 0) {
42 GBASDLInitEvents(&s_sdlEvents);
43 }
44 ++s_sdlInited;
45 m_sdlPlayer.bindings = &m_inputMap;
46 GBASDLInitBindings(&m_inputMap);
47#endif
48
49 m_gamepadTimer = new QTimer(this);
50#ifdef BUILD_SDL
51 connect(m_gamepadTimer, &QTimer::timeout, [this]() {
52 testGamepad(SDL_BINDING_BUTTON);
53 });
54#endif
55 m_gamepadTimer->setInterval(50);
56 m_gamepadTimer->start();
57
58 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
59 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
60 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
61 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
62 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
63 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
64 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
65 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
66 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
67 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
68}
69
70InputController::~InputController() {
71 GBAInputMapDeinit(&m_inputMap);
72
73#ifdef BUILD_SDL
74 if (m_playerAttached) {
75 GBASDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
76 }
77
78 --s_sdlInited;
79 if (s_sdlInited == 0) {
80 GBASDLDeinitEvents(&s_sdlEvents);
81 }
82#endif
83}
84
85void InputController::setConfiguration(ConfigController* config) {
86 m_config = config;
87 setAllowOpposing(config->getOption("allowOpposingDirections").toInt());
88 loadConfiguration(KEYBOARD);
89#ifdef BUILD_SDL
90 GBASDLEventsLoadConfig(&s_sdlEvents, config->input());
91 if (!m_playerAttached) {
92 GBASDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
93 m_playerAttached = true;
94 }
95 loadConfiguration(SDL_BINDING_BUTTON);
96 loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
97#endif
98}
99
100void InputController::loadConfiguration(uint32_t type) {
101 GBAInputMapLoad(&m_inputMap, type, m_config->input());
102#ifdef BUILD_SDL
103 if (m_playerAttached) {
104 GBASDLPlayerLoadConfig(&m_sdlPlayer, m_config->input());
105 }
106#endif
107}
108
109void InputController::loadProfile(uint32_t type, const QString& profile) {
110 bool loaded = GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
111 recalibrateAxes();
112 if (!loaded) {
113 const InputProfile* ip = InputProfile::findProfile(profile);
114 if (ip) {
115 ip->apply(this);
116 }
117 }
118 emit profileLoaded(profile);
119}
120
121void InputController::saveConfiguration() {
122 saveConfiguration(KEYBOARD);
123#ifdef BUILD_SDL
124 saveConfiguration(SDL_BINDING_BUTTON);
125 saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
126 if (m_playerAttached) {
127 GBASDLPlayerSaveConfig(&m_sdlPlayer, m_config->input());
128 }
129 m_config->write();
130#endif
131}
132
133void InputController::saveConfiguration(uint32_t type) {
134 GBAInputMapSave(&m_inputMap, type, m_config->input());
135 m_config->write();
136}
137
138void InputController::saveProfile(uint32_t type, const QString& profile) {
139 GBAInputProfileSave(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
140 m_config->write();
141}
142
143const char* InputController::profileForType(uint32_t type) {
144 UNUSED(type);
145#ifdef BUILD_SDL
146 if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
147#if SDL_VERSION_ATLEAST(2, 0, 0)
148 return SDL_JoystickName(m_sdlPlayer.joystick);
149#else
150 return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick));
151#endif
152 }
153#endif
154 return 0;
155}
156
157QStringList InputController::connectedGamepads(uint32_t type) const {
158 UNUSED(type);
159
160#ifdef BUILD_SDL
161 if (type == SDL_BINDING_BUTTON) {
162 QStringList pads;
163 for (size_t i = 0; i < s_sdlEvents.nJoysticks; ++i) {
164 const char* name;
165#if SDL_VERSION_ATLEAST(2, 0, 0)
166 name = SDL_JoystickName(s_sdlEvents.joysticks[i]);
167#else
168 name = SDL_JoystickName(SDL_JoystickIndex(s_sdlEvents.joysticks[i]));
169#endif
170 if (name) {
171 pads.append(QString(name));
172 } else {
173 pads.append(QString());
174 }
175 }
176 return pads;
177 }
178#endif
179
180 return QStringList();
181}
182
183int InputController::gamepad(uint32_t type) const {
184#ifdef BUILD_SDL
185 if (type == SDL_BINDING_BUTTON) {
186 return m_sdlPlayer.joystickIndex;
187 }
188#endif
189 return 0;
190}
191
192void InputController::setGamepad(uint32_t type, int index) {
193#ifdef BUILD_SDL
194 if (type == SDL_BINDING_BUTTON) {
195 GBASDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index);
196 }
197#endif
198}
199
200void InputController::setPreferredGamepad(uint32_t type, const QString& device) {
201 if (!m_config) {
202 return;
203 }
204 GBAInputSetPreferredDevice(m_config->input(), type, m_playerId, device.toUtf8().constData());
205}
206
207GBARumble* InputController::rumble() {
208#ifdef BUILD_SDL
209 if (m_playerAttached) {
210 return &m_sdlPlayer.rumble.d;
211 }
212#endif
213 return nullptr;
214}
215
216GBARotationSource* InputController::rotationSource() {
217#ifdef BUILD_SDL
218 if (m_playerAttached) {
219 return &m_sdlPlayer.rotation.d;
220 }
221#endif
222 return nullptr;
223}
224
225void InputController::registerTiltAxisX(int axis) {
226#ifdef BUILD_SDL
227 if (m_playerAttached) {
228 m_sdlPlayer.rotation.axisX = axis;
229 }
230#endif
231}
232
233void InputController::registerTiltAxisY(int axis) {
234#ifdef BUILD_SDL
235 if (m_playerAttached) {
236 m_sdlPlayer.rotation.axisY = axis;
237 }
238#endif
239}
240
241void InputController::registerGyroAxisX(int axis) {
242#ifdef BUILD_SDL
243 if (m_playerAttached) {
244 m_sdlPlayer.rotation.gyroX = axis;
245 }
246#endif
247}
248
249void InputController::registerGyroAxisY(int axis) {
250#ifdef BUILD_SDL
251 if (m_playerAttached) {
252 m_sdlPlayer.rotation.gyroY = axis;
253 }
254#endif
255}
256
257float InputController::gyroSensitivity() const {
258#ifdef BUILD_SDL
259 if (m_playerAttached) {
260 return m_sdlPlayer.rotation.gyroSensitivity;
261 }
262#endif
263 return 0;
264}
265
266void InputController::setGyroSensitivity(float sensitivity) {
267#ifdef BUILD_SDL
268 if (m_playerAttached) {
269 m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
270 }
271#endif
272}
273
274GBAKey InputController::mapKeyboard(int key) const {
275 return GBAInputMapKey(&m_inputMap, KEYBOARD, key);
276}
277
278void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
279 return GBAInputBindKey(&m_inputMap, type, key, gbaKey);
280}
281
282int InputController::pollEvents() {
283 int activeButtons = 0;
284#ifdef BUILD_SDL
285 if (m_playerAttached) {
286 SDL_Joystick* joystick = m_sdlPlayer.joystick;
287 SDL_JoystickUpdate();
288 int numButtons = SDL_JoystickNumButtons(joystick);
289 int i;
290 for (i = 0; i < numButtons; ++i) {
291 GBAKey key = GBAInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i);
292 if (key == GBA_KEY_NONE) {
293 continue;
294 }
295 if (hasPendingEvent(key)) {
296 continue;
297 }
298 if (SDL_JoystickGetButton(joystick, i)) {
299 activeButtons |= 1 << key;
300 }
301 }
302 int numHats = SDL_JoystickNumHats(joystick);
303 for (i = 0; i < numHats; ++i) {
304 int hat = SDL_JoystickGetHat(joystick, i);
305 if (hat & SDL_HAT_UP) {
306 activeButtons |= 1 << GBA_KEY_UP;
307 }
308 if (hat & SDL_HAT_LEFT) {
309 activeButtons |= 1 << GBA_KEY_LEFT;
310 }
311 if (hat & SDL_HAT_DOWN) {
312 activeButtons |= 1 << GBA_KEY_DOWN;
313 }
314 if (hat & SDL_HAT_RIGHT) {
315 activeButtons |= 1 << GBA_KEY_RIGHT;
316 }
317 }
318
319 int numAxes = SDL_JoystickNumAxes(joystick);
320 for (i = 0; i < numAxes; ++i) {
321 int value = SDL_JoystickGetAxis(joystick, i);
322
323 enum GBAKey key = GBAInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value);
324 if (key != GBA_KEY_NONE) {
325 activeButtons |= 1 << key;
326 }
327 }
328 }
329#endif
330 return activeButtons;
331}
332
333QSet<int> InputController::activeGamepadButtons(int type) {
334 QSet<int> activeButtons;
335#ifdef BUILD_SDL
336 if (m_playerAttached && type == SDL_BINDING_BUTTON) {
337 SDL_Joystick* joystick = m_sdlPlayer.joystick;
338 SDL_JoystickUpdate();
339 int numButtons = SDL_JoystickNumButtons(joystick);
340 int i;
341 for (i = 0; i < numButtons; ++i) {
342 if (SDL_JoystickGetButton(joystick, i)) {
343 activeButtons.insert(i);
344 }
345 }
346 }
347#endif
348 return activeButtons;
349}
350
351void InputController::recalibrateAxes() {
352#ifdef BUILD_SDL
353 if (m_playerAttached) {
354 SDL_Joystick* joystick = m_sdlPlayer.joystick;
355 SDL_JoystickUpdate();
356 int numAxes = SDL_JoystickNumAxes(joystick);
357 if (numAxes < 1) {
358 return;
359 }
360 m_deadzones.resize(numAxes);
361 int i;
362 for (i = 0; i < numAxes; ++i) {
363 m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
364 }
365 }
366#endif
367}
368
369QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
370 QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
371#ifdef BUILD_SDL
372 if (m_playerAttached && type == SDL_BINDING_BUTTON) {
373 SDL_Joystick* joystick = m_sdlPlayer.joystick;
374 SDL_JoystickUpdate();
375 int numAxes = SDL_JoystickNumAxes(joystick);
376 if (numAxes < 1) {
377 return activeAxes;
378 }
379 m_deadzones.resize(numAxes);
380 int i;
381 for (i = 0; i < numAxes; ++i) {
382 int32_t axis = SDL_JoystickGetAxis(joystick, i);
383 axis -= m_deadzones[i];
384 if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
385 activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
386 }
387 }
388 }
389#endif
390 return activeAxes;
391}
392
393void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
394 const GBAAxis* old = GBAInputQueryAxis(&m_inputMap, type, axis);
395 GBAAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
396 if (old) {
397 description = *old;
398 }
399 switch (direction) {
400 case GamepadAxisEvent::NEGATIVE:
401 description.lowDirection = key;
402 description.deadLow = m_deadzones[axis] - AXIS_THRESHOLD;
403 break;
404 case GamepadAxisEvent::POSITIVE:
405 description.highDirection = key;
406 description.deadHigh = m_deadzones[axis] + AXIS_THRESHOLD;
407 break;
408 default:
409 return;
410 }
411 GBAInputBindAxis(&m_inputMap, type, axis, &description);
412}
413
414void InputController::testGamepad(int type) {
415 auto activeAxes = activeGamepadAxes(type);
416 auto oldAxes = m_activeAxes;
417 m_activeAxes = activeAxes;
418
419 auto activeButtons = activeGamepadButtons(type);
420 auto oldButtons = m_activeButtons;
421 m_activeButtons = activeButtons;
422
423 if (!QApplication::focusWidget()) {
424 return;
425 }
426
427 activeAxes.subtract(oldAxes);
428 oldAxes.subtract(m_activeAxes);
429
430 for (auto& axis : m_activeAxes) {
431 bool newlyAboveThreshold = activeAxes.contains(axis);
432 if (newlyAboveThreshold) {
433 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
434 postPendingEvent(event->gbaKey());
435 QApplication::sendEvent(QApplication::focusWidget(), event);
436 if (!event->isAccepted()) {
437 clearPendingEvent(event->gbaKey());
438 }
439 }
440 }
441 for (auto axis : oldAxes) {
442 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
443 clearPendingEvent(event->gbaKey());
444 QApplication::sendEvent(QApplication::focusWidget(), event);
445 }
446
447 if (!QApplication::focusWidget()) {
448 return;
449 }
450
451 activeButtons.subtract(oldButtons);
452 oldButtons.subtract(m_activeButtons);
453
454 for (int button : activeButtons) {
455 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
456 postPendingEvent(event->gbaKey());
457 QApplication::sendEvent(QApplication::focusWidget(), event);
458 if (!event->isAccepted()) {
459 clearPendingEvent(event->gbaKey());
460 }
461 }
462 for (int button : oldButtons) {
463 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
464 clearPendingEvent(event->gbaKey());
465 QApplication::sendEvent(QApplication::focusWidget(), event);
466 }
467}
468
469void InputController::postPendingEvent(GBAKey key) {
470 m_pendingEvents.insert(key);
471}
472
473void InputController::clearPendingEvent(GBAKey key) {
474 m_pendingEvents.remove(key);
475}
476
477bool InputController::hasPendingEvent(GBAKey key) const {
478 return m_pendingEvents.contains(key);
479}
480
481#if defined(BUILD_SDL) && SDL_VERSION_ATLEAST(2, 0, 0)
482void InputController::suspendScreensaver() {
483 GBASDLSuspendScreensaver(&s_sdlEvents);
484}
485
486void InputController::resumeScreensaver() {
487 GBASDLResumeScreensaver(&s_sdlEvents);
488}
489
490void InputController::setScreensaverSuspendable(bool suspendable) {
491 GBASDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
492}
493#endif