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