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