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