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
17#include <mgba/core/interface.h>
18#include <mgba-util/configuration.h>
19
20using namespace QGBA;
21
22#ifdef BUILD_SDL
23int InputController::s_sdlInited = 0;
24mSDLEvents InputController::s_sdlEvents;
25#endif
26
27InputController::InputController(int playerId, QWidget* topLevel, 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 , m_topLevel(topLevel)
37 , m_focusParent(topLevel)
38{
39 mInputMapInit(&m_inputMap, &GBAInputInfo);
40
41#ifdef BUILD_SDL
42 if (s_sdlInited == 0) {
43 mSDLInitEvents(&s_sdlEvents);
44 }
45 ++s_sdlInited;
46 m_sdlPlayer.bindings = &m_inputMap;
47 mSDLInitBindingsGBA(&m_inputMap);
48 updateJoysticks();
49#endif
50
51 m_gamepadTimer = new QTimer(this);
52#ifdef BUILD_SDL
53 connect(m_gamepadTimer, &QTimer::timeout, [this]() {
54 testGamepad(SDL_BINDING_BUTTON);
55 });
56#endif
57 m_gamepadTimer->setInterval(50);
58 m_gamepadTimer->start();
59
60 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
61 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
62 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
63 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
64 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
65 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
66 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
67 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
68 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
69 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
70}
71
72InputController::~InputController() {
73 mInputMapDeinit(&m_inputMap);
74
75#ifdef BUILD_SDL
76 if (m_playerAttached) {
77 mSDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
78 }
79
80 --s_sdlInited;
81 if (s_sdlInited == 0) {
82 mSDLDeinitEvents(&s_sdlEvents);
83 }
84#endif
85}
86
87void InputController::setConfiguration(ConfigController* config) {
88 m_config = config;
89 setAllowOpposing(config->getOption("allowOpposingDirections").toInt());
90 loadConfiguration(KEYBOARD);
91#ifdef BUILD_SDL
92 mSDLEventsLoadConfig(&s_sdlEvents, config->input());
93 if (!m_playerAttached) {
94 m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
95 }
96 loadConfiguration(SDL_BINDING_BUTTON);
97 loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
98#endif
99}
100
101void InputController::loadConfiguration(uint32_t type) {
102 mInputMapLoad(&m_inputMap, type, m_config->input());
103#ifdef BUILD_SDL
104 if (m_playerAttached) {
105 mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input());
106 }
107#endif
108}
109
110void InputController::loadProfile(uint32_t type, const QString& profile) {
111 bool loaded = mInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
112 recalibrateAxes();
113 if (!loaded) {
114 const InputProfile* ip = InputProfile::findProfile(profile);
115 if (ip) {
116 ip->apply(this);
117 }
118 }
119 emit profileLoaded(profile);
120}
121
122void InputController::saveConfiguration() {
123 saveConfiguration(KEYBOARD);
124#ifdef BUILD_SDL
125 saveConfiguration(SDL_BINDING_BUTTON);
126 saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
127 if (m_playerAttached) {
128 mSDLPlayerSaveConfig(&m_sdlPlayer, m_config->input());
129 }
130 m_config->write();
131#endif
132}
133
134void InputController::saveConfiguration(uint32_t type) {
135 mInputMapSave(&m_inputMap, type, m_config->input());
136 m_config->write();
137}
138
139void InputController::saveProfile(uint32_t type, const QString& profile) {
140 mInputProfileSave(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
141 m_config->write();
142}
143
144const char* InputController::profileForType(uint32_t type) {
145 UNUSED(type);
146#ifdef BUILD_SDL
147 if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
148#if SDL_VERSION_ATLEAST(2, 0, 0)
149 return SDL_JoystickName(m_sdlPlayer.joystick->joystick);
150#else
151 return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick));
152#endif
153 }
154#endif
155 return 0;
156}
157
158QStringList InputController::connectedGamepads(uint32_t type) const {
159 UNUSED(type);
160
161#ifdef BUILD_SDL
162 if (type == SDL_BINDING_BUTTON) {
163 QStringList pads;
164 for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
165 const char* name;
166#if SDL_VERSION_ATLEAST(2, 0, 0)
167 name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick);
168#else
169 name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick));
170#endif
171 if (name) {
172 pads.append(QString(name));
173 } else {
174 pads.append(QString());
175 }
176 }
177 return pads;
178 }
179#endif
180
181 return QStringList();
182}
183
184int InputController::gamepad(uint32_t type) const {
185#ifdef BUILD_SDL
186 if (type == SDL_BINDING_BUTTON) {
187 return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0;
188 }
189#endif
190 return 0;
191}
192
193void InputController::setGamepad(uint32_t type, int index) {
194#ifdef BUILD_SDL
195 if (type == SDL_BINDING_BUTTON) {
196 mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index);
197 }
198#endif
199}
200
201void InputController::setPreferredGamepad(uint32_t type, const QString& device) {
202 if (!m_config) {
203 return;
204 }
205 mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, device.toUtf8().constData());
206}
207
208mRumble* InputController::rumble() {
209#ifdef BUILD_SDL
210#if SDL_VERSION_ATLEAST(2, 0, 0)
211 if (m_playerAttached) {
212 return &m_sdlPlayer.rumble.d;
213 }
214#endif
215#endif
216 return nullptr;
217}
218
219mRotationSource* InputController::rotationSource() {
220#ifdef BUILD_SDL
221 if (m_playerAttached) {
222 return &m_sdlPlayer.rotation.d;
223 }
224#endif
225 return nullptr;
226}
227
228void InputController::registerTiltAxisX(int axis) {
229#ifdef BUILD_SDL
230 if (m_playerAttached) {
231 m_sdlPlayer.rotation.axisX = axis;
232 }
233#endif
234}
235
236void InputController::registerTiltAxisY(int axis) {
237#ifdef BUILD_SDL
238 if (m_playerAttached) {
239 m_sdlPlayer.rotation.axisY = axis;
240 }
241#endif
242}
243
244void InputController::registerGyroAxisX(int axis) {
245#ifdef BUILD_SDL
246 if (m_playerAttached) {
247 m_sdlPlayer.rotation.gyroX = axis;
248 }
249#endif
250}
251
252void InputController::registerGyroAxisY(int axis) {
253#ifdef BUILD_SDL
254 if (m_playerAttached) {
255 m_sdlPlayer.rotation.gyroY = axis;
256 }
257#endif
258}
259
260float InputController::gyroSensitivity() const {
261#ifdef BUILD_SDL
262 if (m_playerAttached) {
263 return m_sdlPlayer.rotation.gyroSensitivity;
264 }
265#endif
266 return 0;
267}
268
269void InputController::setGyroSensitivity(float sensitivity) {
270#ifdef BUILD_SDL
271 if (m_playerAttached) {
272 m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
273 }
274#endif
275}
276
277GBAKey InputController::mapKeyboard(int key) const {
278 return static_cast<GBAKey>(mInputMapKey(&m_inputMap, KEYBOARD, key));
279}
280
281void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
282 return mInputBindKey(&m_inputMap, type, key, gbaKey);
283}
284
285void InputController::updateJoysticks() {
286#ifdef BUILD_SDL
287 mSDLUpdateJoysticks(&s_sdlEvents);
288#endif
289}
290
291int InputController::pollEvents() {
292 int activeButtons = 0;
293#ifdef BUILD_SDL
294 if (m_playerAttached && m_sdlPlayer.joystick) {
295 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
296 SDL_JoystickUpdate();
297 int numButtons = SDL_JoystickNumButtons(joystick);
298 int i;
299 for (i = 0; i < numButtons; ++i) {
300 GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i));
301 if (key == GBA_KEY_NONE) {
302 continue;
303 }
304 if (hasPendingEvent(key)) {
305 continue;
306 }
307 if (SDL_JoystickGetButton(joystick, i)) {
308 activeButtons |= 1 << key;
309 }
310 }
311 int numHats = SDL_JoystickNumHats(joystick);
312 for (i = 0; i < numHats; ++i) {
313 int hat = SDL_JoystickGetHat(joystick, i);
314 activeButtons |= mInputMapHat(&m_inputMap, SDL_BINDING_BUTTON, i, hat);
315 }
316
317 int numAxes = SDL_JoystickNumAxes(joystick);
318 for (i = 0; i < numAxes; ++i) {
319 int value = SDL_JoystickGetAxis(joystick, i);
320
321 enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value));
322 if (key != GBA_KEY_NONE) {
323 activeButtons |= 1 << key;
324 }
325 }
326 }
327#endif
328 return activeButtons;
329}
330
331QSet<int> InputController::activeGamepadButtons(int type) {
332 QSet<int> activeButtons;
333#ifdef BUILD_SDL
334 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
335 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
336 SDL_JoystickUpdate();
337 int numButtons = SDL_JoystickNumButtons(joystick);
338 int i;
339 for (i = 0; i < numButtons; ++i) {
340 if (SDL_JoystickGetButton(joystick, i)) {
341 activeButtons.insert(i);
342 }
343 }
344 }
345#endif
346 return activeButtons;
347}
348
349void InputController::recalibrateAxes() {
350#ifdef BUILD_SDL
351 if (m_playerAttached && m_sdlPlayer.joystick) {
352 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
353 SDL_JoystickUpdate();
354 int numAxes = SDL_JoystickNumAxes(joystick);
355 if (numAxes < 1) {
356 return;
357 }
358 m_deadzones.resize(numAxes);
359 int i;
360 for (i = 0; i < numAxes; ++i) {
361 m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
362 }
363 }
364#endif
365}
366
367QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
368 QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
369#ifdef BUILD_SDL
370 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
371 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
372 SDL_JoystickUpdate();
373 int numAxes = SDL_JoystickNumAxes(joystick);
374 if (numAxes < 1) {
375 return activeAxes;
376 }
377 m_deadzones.resize(numAxes);
378 int i;
379 for (i = 0; i < numAxes; ++i) {
380 int32_t axis = SDL_JoystickGetAxis(joystick, i);
381 axis -= m_deadzones[i];
382 if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
383 activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
384 }
385 }
386 }
387#endif
388 return activeAxes;
389}
390
391void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
392 const mInputAxis* old = mInputQueryAxis(&m_inputMap, type, axis);
393 mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
394 if (old) {
395 description = *old;
396 }
397 int deadzone = 0;
398 if (axis > 0 && m_deadzones.size() > axis) {
399 deadzone = m_deadzones[axis];
400 }
401 switch (direction) {
402 case GamepadAxisEvent::NEGATIVE:
403 description.lowDirection = key;
404
405 description.deadLow = deadzone - AXIS_THRESHOLD;
406 break;
407 case GamepadAxisEvent::POSITIVE:
408 description.highDirection = key;
409 description.deadHigh = deadzone + AXIS_THRESHOLD;
410 break;
411 default:
412 return;
413 }
414 mInputBindAxis(&m_inputMap, type, axis, &description);
415}
416
417void InputController::unbindAllAxes(uint32_t type) {
418 mInputUnbindAllAxes(&m_inputMap, type);
419}
420
421QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {
422 QSet<QPair<int, GamepadHatEvent::Direction>> activeHats;
423#ifdef BUILD_SDL
424 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
425 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
426 SDL_JoystickUpdate();
427 int numHats = SDL_JoystickNumHats(joystick);
428 if (numHats < 1) {
429 return activeHats;
430 }
431
432 int i;
433 for (i = 0; i < numHats; ++i) {
434 int hat = SDL_JoystickGetHat(joystick, i);
435 if (hat & GamepadHatEvent::UP) {
436 activeHats.insert(qMakePair(i, GamepadHatEvent::UP));
437 }
438 if (hat & GamepadHatEvent::RIGHT) {
439 activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT));
440 }
441 if (hat & GamepadHatEvent::DOWN) {
442 activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN));
443 }
444 if (hat & GamepadHatEvent::LEFT) {
445 activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT));
446 }
447 }
448 }
449#endif
450 return activeHats;
451}
452
453void InputController::bindHat(uint32_t type, int hat, GamepadHatEvent::Direction direction, GBAKey gbaKey) {
454 mInputHatBindings bindings{ -1, -1, -1, -1 };
455 mInputQueryHat(&m_inputMap, type, hat, &bindings);
456 switch (direction) {
457 case GamepadHatEvent::UP:
458 bindings.up = gbaKey;
459 break;
460 case GamepadHatEvent::RIGHT:
461 bindings.right = gbaKey;
462 break;
463 case GamepadHatEvent::DOWN:
464 bindings.down = gbaKey;
465 break;
466 case GamepadHatEvent::LEFT:
467 bindings.left = gbaKey;
468 break;
469 default:
470 return;
471 }
472 mInputBindHat(&m_inputMap, type, hat, &bindings);
473}
474
475void InputController::testGamepad(int type) {
476 auto activeAxes = activeGamepadAxes(type);
477 auto oldAxes = m_activeAxes;
478 m_activeAxes = activeAxes;
479
480 auto activeButtons = activeGamepadButtons(type);
481 auto oldButtons = m_activeButtons;
482 m_activeButtons = activeButtons;
483
484 auto activeHats = activeGamepadHats(type);
485 auto oldHats = m_activeHats;
486 m_activeHats = activeHats;
487
488 if (!QApplication::focusWidget()) {
489 return;
490 }
491
492 activeAxes.subtract(oldAxes);
493 oldAxes.subtract(m_activeAxes);
494
495 for (auto& axis : m_activeAxes) {
496 bool newlyAboveThreshold = activeAxes.contains(axis);
497 if (newlyAboveThreshold) {
498 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
499 postPendingEvent(event->gbaKey());
500 sendGamepadEvent(event);
501 if (!event->isAccepted()) {
502 clearPendingEvent(event->gbaKey());
503 }
504 }
505 }
506 for (auto axis : oldAxes) {
507 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
508 clearPendingEvent(event->gbaKey());
509 sendGamepadEvent(event);
510 }
511
512 if (!QApplication::focusWidget()) {
513 return;
514 }
515
516 activeButtons.subtract(oldButtons);
517 oldButtons.subtract(m_activeButtons);
518
519 for (int button : activeButtons) {
520 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
521 postPendingEvent(event->gbaKey());
522 sendGamepadEvent(event);
523 if (!event->isAccepted()) {
524 clearPendingEvent(event->gbaKey());
525 }
526 }
527 for (int button : oldButtons) {
528 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
529 clearPendingEvent(event->gbaKey());
530 sendGamepadEvent(event);
531 }
532
533 activeHats.subtract(oldHats);
534 oldHats.subtract(m_activeHats);
535
536 for (auto& hat : activeHats) {
537 GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this);
538 postPendingEvent(event->gbaKey());
539 sendGamepadEvent(event);
540 if (!event->isAccepted()) {
541 clearPendingEvent(event->gbaKey());
542 }
543 }
544 for (auto& hat : oldHats) {
545 GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this);
546 clearPendingEvent(event->gbaKey());
547 sendGamepadEvent(event);
548 }
549}
550
551void InputController::sendGamepadEvent(QEvent* event) {
552 QWidget* focusWidget = nullptr;
553 if (m_focusParent) {
554 focusWidget = m_focusParent->focusWidget();
555 if (!focusWidget) {
556 focusWidget = m_focusParent;
557 }
558 } else {
559 focusWidget = QApplication::focusWidget();
560 }
561 QApplication::sendEvent(focusWidget, event);
562}
563
564void InputController::postPendingEvent(GBAKey key) {
565 m_pendingEvents.insert(key);
566}
567
568void InputController::clearPendingEvent(GBAKey key) {
569 m_pendingEvents.remove(key);
570}
571
572bool InputController::hasPendingEvent(GBAKey key) const {
573 return m_pendingEvents.contains(key);
574}
575
576void InputController::suspendScreensaver() {
577#ifdef BUILD_SDL
578#if SDL_VERSION_ATLEAST(2, 0, 0)
579 mSDLSuspendScreensaver(&s_sdlEvents);
580#endif
581#endif
582}
583
584void InputController::resumeScreensaver() {
585#ifdef BUILD_SDL
586#if SDL_VERSION_ATLEAST(2, 0, 0)
587 mSDLResumeScreensaver(&s_sdlEvents);
588#endif
589#endif
590}
591
592void InputController::setScreensaverSuspendable(bool suspendable) {
593#ifdef BUILD_SDL
594#if SDL_VERSION_ATLEAST(2, 0, 0)
595 mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
596#endif
597#endif
598}
599
600void InputController::stealFocus(QWidget* focus) {
601 m_focusParent = focus;
602}
603
604void InputController::releaseFocus(QWidget* focus) {
605 if (focus == m_focusParent) {
606 m_focusParent = m_topLevel;
607 }
608}