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 "GameController.h"
10#include "GamepadAxisEvent.h"
11#include "GamepadButtonEvent.h"
12#include "InputModel.h"
13#include "InputProfile.h"
14
15#include <QApplication>
16#include <QKeyEvent>
17#include <QMenu>
18#include <QTimer>
19#include <QWidget>
20
21#include <mgba/core/interface.h>
22#include <mgba-util/configuration.h>
23
24using namespace QGBA;
25
26#ifdef BUILD_SDL
27int InputController::s_sdlInited = 0;
28mSDLEvents InputController::s_sdlEvents;
29#endif
30
31InputController::InputController(InputModel* model, int playerId, QWidget* topLevel, QObject* parent)
32 : QObject(parent)
33 , m_inputModel(model)
34 , m_platform(PLATFORM_NONE)
35 , m_playerId(playerId)
36 , m_config(nullptr)
37 , m_gamepadTimer(nullptr)
38#ifdef BUILD_SDL
39 , m_sdlPlayer{}
40 , m_playerAttached(false)
41#endif
42 , m_allowOpposing(false)
43 , m_topLevel(topLevel)
44 , m_focusParent(topLevel)
45{
46#ifdef BUILD_SDL
47 if (s_sdlInited == 0) {
48 mSDLInitEvents(&s_sdlEvents);
49 }
50 ++s_sdlInited;
51 updateJoysticks();
52#endif
53
54#ifdef BUILD_SDL
55 connect(&m_gamepadTimer, &QTimer::timeout, [this]() {
56 testGamepad(SDL_BINDING_BUTTON);
57 if (m_playerId == 0) {
58 updateJoysticks();
59 }
60 });
61#endif
62 m_gamepadTimer.setInterval(50);
63 m_gamepadTimer.start();
64
65 m_autofireMenu = std::unique_ptr<QMenu>(new QMenu(tr("Autofire")));
66 m_inputModel->addMenu(m_autofireMenu.get());
67
68 m_inputMenu = std::unique_ptr<QMenu>(new QMenu(tr("Bindings")));
69 m_inputModel->addMenu(m_inputMenu.get());
70
71 connect(m_inputModel, SIGNAL(keyRebound(const QModelIndex&, int)), this, SLOT(bindKey(const QModelIndex&, int)));
72 connect(m_inputModel, SIGNAL(buttonRebound(const QModelIndex&, int)), this, SLOT(bindButton(const QModelIndex&, int)));
73 connect(m_inputModel, SIGNAL(axisRebound(const QModelIndex&, int, GamepadAxisEvent::Direction)), this, SLOT(bindAxis(const QModelIndex&, int, GamepadAxisEvent::Direction)));
74}
75
76InputController::~InputController() {
77 for (auto& inputMap : m_inputMaps) {
78 mInputMapDeinit(&inputMap);
79 }
80
81#ifdef BUILD_SDL
82 if (m_playerAttached) {
83 mSDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
84 }
85
86 --s_sdlInited;
87 if (s_sdlInited == 0) {
88 mSDLDeinitEvents(&s_sdlEvents);
89 }
90#endif
91}
92
93void InputController::addPlatform(mPlatform platform, const QString& visibleName, const mInputPlatformInfo* info) {
94 mInputMap* inputMap = &m_inputMaps[platform];
95 mInputMapInit(inputMap, info);
96
97 QMenu* input = m_inputMenu->addMenu(visibleName);
98 QMenu* autofire = m_autofireMenu->addMenu(visibleName);
99 m_inputMenuIndices[platform] = m_inputModel->addMenu(input, m_inputMenu.get());
100 m_inputModel->addMenu(autofire, m_autofireMenu.get());
101
102 for (size_t i = 0; i < info->nKeys; ++i) {
103 m_inputModel->addKey(input, platform, i, 0, info->keyId[i], info->keyId[i]);
104 m_inputModel->addKey(autofire, platform, i, 0, info->keyId[i], info->keyId[i]);
105 }
106
107#ifdef BUILD_SDL
108 mSDLInitBindingsGBA(inputMap);
109#endif
110 mInputBindKey(inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
111 mInputBindKey(inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
112 mInputBindKey(inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
113 mInputBindKey(inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
114 mInputBindKey(inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
115 mInputBindKey(inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
116 mInputBindKey(inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
117 mInputBindKey(inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
118 mInputBindKey(inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
119 mInputBindKey(inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
120}
121
122void InputController::setPlatform(mPlatform platform) {
123#ifdef BUILD_SDL
124 m_sdlPlayer.bindings = &m_inputMaps[platform];
125#endif
126 m_platform = platform;
127}
128
129void InputController::setConfiguration(ConfigController* config) {
130 m_config = config;
131 setAllowOpposing(config->getOption("allowOpposingDirections").toInt());
132 loadConfiguration(KEYBOARD);
133#ifdef BUILD_SDL
134 mSDLEventsLoadConfig(&s_sdlEvents, config->input());
135 if (!m_playerAttached) {
136 m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
137 }
138 loadConfiguration(SDL_BINDING_BUTTON);
139 loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
140#endif
141 restoreModel();
142}
143
144void InputController::loadConfiguration(uint32_t type) {
145 for (auto& inputMap : m_inputMaps) {
146 mInputMapLoad(&inputMap, type, m_config->input());
147#ifdef BUILD_SDL
148 if (m_playerAttached) {
149 mInputMap* bindings = m_sdlPlayer.bindings;
150 m_sdlPlayer.bindings = &inputMap;
151 mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input());
152 m_sdlPlayer.bindings = bindings;
153 }
154#endif
155 }
156}
157
158void InputController::loadProfile(uint32_t type, const QString& profile) {
159 for (auto iter = m_inputMaps.begin(); iter != m_inputMaps.end(); ++iter) {
160 bool loaded = mInputProfileLoad(&iter.value(), type, m_config->input(), profile.toUtf8().constData());
161 if (!loaded) {
162 const InputProfile* ip = InputProfile::findProfile(iter.key(), profile);
163 if (ip) {
164 ip->apply(iter.key(), this);
165 }
166 }
167 }
168 recalibrateAxes();
169 m_inputModel->loadProfile(PLATFORM_NONE, profile); // TODO
170 emit profileLoaded(profile);
171}
172
173void InputController::saveConfiguration() {
174 saveConfiguration(KEYBOARD);
175#ifdef BUILD_SDL
176 saveConfiguration(SDL_BINDING_BUTTON);
177 saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
178 if (m_playerAttached) {
179 mSDLPlayerSaveConfig(&m_sdlPlayer, m_config->input());
180 }
181 m_config->write();
182#endif
183}
184
185void InputController::saveConfiguration(uint32_t type) {
186 for (auto& inputMap : m_inputMaps) {
187 if (!inputMap.info) {
188 continue;
189 }
190 mInputMapSave(&inputMap, type, m_config->input());
191 }
192 m_config->write();
193}
194
195void InputController::saveProfile(uint32_t type, const QString& profile) {
196 for (auto& inputMap : m_inputMaps) {
197 mInputProfileSave(&inputMap, type, m_config->input(), profile.toUtf8().constData());
198 }
199 m_config->write();
200}
201
202const char* InputController::profileForType(uint32_t type) {
203 UNUSED(type);
204#ifdef BUILD_SDL
205 if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
206#if SDL_VERSION_ATLEAST(2, 0, 0)
207 return SDL_JoystickName(m_sdlPlayer.joystick->joystick);
208#else
209 return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick));
210#endif
211 }
212#endif
213 return 0;
214}
215
216QStringList InputController::connectedGamepads(uint32_t type) const {
217 UNUSED(type);
218
219#ifdef BUILD_SDL
220 if (type == SDL_BINDING_BUTTON) {
221 QStringList pads;
222 for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
223 const char* name;
224#if SDL_VERSION_ATLEAST(2, 0, 0)
225 name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick);
226#else
227 name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick));
228#endif
229 if (name) {
230 pads.append(QString(name));
231 } else {
232 pads.append(QString());
233 }
234 }
235 return pads;
236 }
237#endif
238
239 return QStringList();
240}
241
242int InputController::gamepad(uint32_t type) const {
243#ifdef BUILD_SDL
244 if (type == SDL_BINDING_BUTTON) {
245 return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0;
246 }
247#endif
248 return 0;
249}
250
251void InputController::setGamepad(uint32_t type, int index) {
252#ifdef BUILD_SDL
253 if (type == SDL_BINDING_BUTTON) {
254 mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index);
255 }
256#endif
257}
258
259void InputController::setPreferredGamepad(uint32_t type, const QString& device) {
260 if (!m_config) {
261 return;
262 }
263 mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, device.toUtf8().constData());
264}
265
266mRumble* InputController::rumble() {
267#ifdef BUILD_SDL
268#if SDL_VERSION_ATLEAST(2, 0, 0)
269 if (m_playerAttached) {
270 return &m_sdlPlayer.rumble.d;
271 }
272#endif
273#endif
274 return nullptr;
275}
276
277mRotationSource* InputController::rotationSource() {
278#ifdef BUILD_SDL
279 if (m_playerAttached) {
280 return &m_sdlPlayer.rotation.d;
281 }
282#endif
283 return nullptr;
284}
285
286void InputController::registerTiltAxisX(int axis) {
287#ifdef BUILD_SDL
288 if (m_playerAttached) {
289 m_sdlPlayer.rotation.axisX = axis;
290 }
291#endif
292}
293
294void InputController::registerTiltAxisY(int axis) {
295#ifdef BUILD_SDL
296 if (m_playerAttached) {
297 m_sdlPlayer.rotation.axisY = axis;
298 }
299#endif
300}
301
302void InputController::registerGyroAxisX(int axis) {
303#ifdef BUILD_SDL
304 if (m_playerAttached) {
305 m_sdlPlayer.rotation.gyroX = axis;
306 }
307#endif
308}
309
310void InputController::registerGyroAxisY(int axis) {
311#ifdef BUILD_SDL
312 if (m_playerAttached) {
313 m_sdlPlayer.rotation.gyroY = axis;
314 }
315#endif
316}
317
318float InputController::gyroSensitivity() const {
319#ifdef BUILD_SDL
320 if (m_playerAttached) {
321 return m_sdlPlayer.rotation.gyroSensitivity;
322 }
323#endif
324 return 0;
325}
326
327void InputController::setGyroSensitivity(float sensitivity) {
328#ifdef BUILD_SDL
329 if (m_playerAttached) {
330 m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
331 }
332#endif
333}
334
335void InputController::updateJoysticks() {
336#ifdef BUILD_SDL
337 QString profile = profileForType(SDL_BINDING_BUTTON);
338 mSDLUpdateJoysticks(&s_sdlEvents, m_config->input());
339 QString newProfile = profileForType(SDL_BINDING_BUTTON);
340 if (profile != newProfile) {
341 loadProfile(SDL_BINDING_BUTTON, newProfile);
342 }
343#endif
344}
345
346const mInputMap* InputController::map() {
347 return &m_inputMaps[m_platform];
348}
349
350int InputController::pollEvents() {
351 int activeButtons = 0;
352#ifdef BUILD_SDL
353 if (m_playerAttached && m_sdlPlayer.joystick) {
354 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
355 SDL_JoystickUpdate();
356 int numButtons = SDL_JoystickNumButtons(joystick);
357 int i;
358 for (i = 0; i < numButtons; ++i) {
359 GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i));
360 if (key == GBA_KEY_NONE) {
361 continue;
362 }
363 if (hasPendingEvent(key)) {
364 continue;
365 }
366 if (SDL_JoystickGetButton(joystick, i)) {
367 activeButtons |= 1 << key;
368 }
369 }
370 int numHats = SDL_JoystickNumHats(joystick);
371 for (i = 0; i < numHats; ++i) {
372 int hat = SDL_JoystickGetHat(joystick, i);
373 activeButtons |= mInputMapHat(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i, hat);
374 }
375
376 int numAxes = SDL_JoystickNumAxes(joystick);
377 for (i = 0; i < numAxes; ++i) {
378 int value = SDL_JoystickGetAxis(joystick, i);
379
380 enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i, value));
381 if (key != GBA_KEY_NONE) {
382 activeButtons |= 1 << key;
383 }
384 }
385 }
386#endif
387 return activeButtons;
388}
389
390QSet<int> InputController::activeGamepadButtons(int type) {
391 QSet<int> activeButtons;
392#ifdef BUILD_SDL
393 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
394 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
395 SDL_JoystickUpdate();
396 int numButtons = SDL_JoystickNumButtons(joystick);
397 int i;
398 for (i = 0; i < numButtons; ++i) {
399 if (SDL_JoystickGetButton(joystick, i)) {
400 activeButtons.insert(i);
401 }
402 }
403 }
404#endif
405 return activeButtons;
406}
407
408void InputController::recalibrateAxes() {
409#ifdef BUILD_SDL
410 if (m_playerAttached && m_sdlPlayer.joystick) {
411 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
412 SDL_JoystickUpdate();
413 int numAxes = SDL_JoystickNumAxes(joystick);
414 if (numAxes < 1) {
415 return;
416 }
417 m_deadzones.resize(numAxes);
418 int i;
419 for (i = 0; i < numAxes; ++i) {
420 m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
421 }
422 }
423#endif
424}
425
426QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
427 QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
428#ifdef BUILD_SDL
429 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
430 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
431 SDL_JoystickUpdate();
432 int numAxes = SDL_JoystickNumAxes(joystick);
433 if (numAxes < 1) {
434 return activeAxes;
435 }
436 m_deadzones.resize(numAxes);
437 int i;
438 for (i = 0; i < numAxes; ++i) {
439 int32_t axis = SDL_JoystickGetAxis(joystick, i);
440 axis -= m_deadzones[i];
441 if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
442 activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
443 }
444 }
445 }
446#endif
447 return activeAxes;
448}
449
450void InputController::bindKey(mPlatform platform, uint32_t type, int key, int coreKey) {
451 if (m_inputMaps.find(platform) == m_inputMaps.end() || coreKey >= m_inputMaps[platform].info->nKeys) {
452 return;
453 }
454 QModelIndex index = m_inputModel->index(coreKey, 0, m_inputMenuIndices[platform]);
455 bool signalsBlocked = m_inputModel->blockSignals(true);
456 if (type != KEYBOARD) {
457 m_inputModel->updateButton(index, key);
458 } else {
459 m_inputModel->updateKey(index, key);
460 }
461 m_inputModel->blockSignals(signalsBlocked);
462 mInputBindKey(&m_inputMaps[platform], type, key, coreKey);
463}
464
465void InputController::bindAxis(mPlatform platform, uint32_t type, int axis, GamepadAxisEvent::Direction direction, int key) {
466 if (m_inputMaps.find(platform) == m_inputMaps.end() || key >= m_inputMaps[platform].info->nKeys) {
467 return;
468 }
469 QModelIndex index = m_inputModel->index(key, 0, m_inputMenuIndices[platform]);
470 bool signalsBlocked = m_inputModel->blockSignals(true);
471 m_inputModel->updateAxis(index, axis, direction);
472 m_inputModel->blockSignals(signalsBlocked);
473
474 const mInputAxis* old = mInputQueryAxis(&m_inputMaps[platform], type, axis);
475 mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
476 if (old) {
477 description = *old;
478 }
479 int deadzone = 0;
480 if (axis > 0 && m_deadzones.size() > axis) {
481 deadzone = m_deadzones[axis];
482 }
483 switch (direction) {
484 case GamepadAxisEvent::NEGATIVE:
485 description.lowDirection = key;
486
487 description.deadLow = deadzone - AXIS_THRESHOLD;
488 break;
489 case GamepadAxisEvent::POSITIVE:
490 description.highDirection = key;
491 description.deadHigh = deadzone + AXIS_THRESHOLD;
492 break;
493 default:
494 return;
495 }
496 mInputBindAxis(&m_inputMaps[platform], type, axis, &description);
497}
498
499QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {
500 QSet<QPair<int, GamepadHatEvent::Direction>> activeHats;
501#ifdef BUILD_SDL
502 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
503 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
504 SDL_JoystickUpdate();
505 int numHats = SDL_JoystickNumHats(joystick);
506 if (numHats < 1) {
507 return activeHats;
508 }
509
510 int i;
511 for (i = 0; i < numHats; ++i) {
512 int hat = SDL_JoystickGetHat(joystick, i);
513 if (hat & GamepadHatEvent::UP) {
514 activeHats.insert(qMakePair(i, GamepadHatEvent::UP));
515 }
516 if (hat & GamepadHatEvent::RIGHT) {
517 activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT));
518 }
519 if (hat & GamepadHatEvent::DOWN) {
520 activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN));
521 }
522 if (hat & GamepadHatEvent::LEFT) {
523 activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT));
524 }
525 }
526 }
527#endif
528 return activeHats;
529}
530
531void InputController::bindHat(mPlatform platform, uint32_t type, int hat, GamepadHatEvent::Direction direction, int coreKey) {
532 if (m_inputMaps.find(platform) == m_inputMaps.end() || coreKey >= m_inputMaps[platform].info->nKeys) {
533 return;
534 }
535 QModelIndex index = m_inputModel->index(coreKey, 0, m_inputMenuIndices[platform]);
536 //m_inputModel->updateHat(index, hat, direction);
537
538 mInputHatBindings bindings{ -1, -1, -1, -1 };
539 mInputQueryHat(&m_inputMaps[platform], type, hat, &bindings);
540 switch (direction) {
541 case GamepadHatEvent::UP:
542 bindings.up = coreKey;
543 break;
544 case GamepadHatEvent::RIGHT:
545 bindings.right = coreKey;
546 break;
547 case GamepadHatEvent::DOWN:
548 bindings.down = coreKey;
549 break;
550 case GamepadHatEvent::LEFT:
551 bindings.left = coreKey;
552 break;
553 default:
554 return;
555 }
556 mInputBindHat(&m_inputMaps[platform], type, hat, &bindings);
557}
558
559void InputController::testGamepad(int type) {
560 auto activeAxes = activeGamepadAxes(type);
561 auto oldAxes = m_activeAxes;
562 m_activeAxes = activeAxes;
563
564 auto activeButtons = activeGamepadButtons(type);
565 auto oldButtons = m_activeButtons;
566 m_activeButtons = activeButtons;
567
568 auto activeHats = activeGamepadHats(type);
569 auto oldHats = m_activeHats;
570 m_activeHats = activeHats;
571
572 if (!QApplication::focusWidget()) {
573 return;
574 }
575
576 activeAxes.subtract(oldAxes);
577 oldAxes.subtract(m_activeAxes);
578
579 for (auto& axis : m_activeAxes) {
580 bool newlyAboveThreshold = activeAxes.contains(axis);
581 if (newlyAboveThreshold) {
582 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
583 postPendingEvent(event->gbaKey());
584 sendGamepadEvent(event);
585 if (!event->isAccepted()) {
586 clearPendingEvent(event->gbaKey());
587 }
588 }
589 }
590 for (auto axis : oldAxes) {
591 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
592 clearPendingEvent(event->gbaKey());
593 sendGamepadEvent(event);
594 }
595
596 if (!QApplication::focusWidget()) {
597 return;
598 }
599
600 activeButtons.subtract(oldButtons);
601 oldButtons.subtract(m_activeButtons);
602
603 for (int button : activeButtons) {
604 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
605 postPendingEvent(event->gbaKey());
606 sendGamepadEvent(event);
607 if (!event->isAccepted()) {
608 clearPendingEvent(event->gbaKey());
609 }
610 }
611 for (int button : oldButtons) {
612 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
613 clearPendingEvent(event->gbaKey());
614 sendGamepadEvent(event);
615 }
616
617 activeHats.subtract(oldHats);
618 oldHats.subtract(m_activeHats);
619
620 for (auto& hat : activeHats) {
621 GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this);
622 postPendingEvent(event->gbaKey());
623 sendGamepadEvent(event);
624 if (!event->isAccepted()) {
625 clearPendingEvent(event->gbaKey());
626 }
627 }
628 for (auto& hat : oldHats) {
629 GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this);
630 clearPendingEvent(event->gbaKey());
631 sendGamepadEvent(event);
632 }
633}
634
635void InputController::sendGamepadEvent(QEvent* event) {
636 QWidget* focusWidget = nullptr;
637 if (m_focusParent) {
638 focusWidget = m_focusParent->focusWidget();
639 if (!focusWidget) {
640 focusWidget = m_focusParent;
641 }
642 } else {
643 focusWidget = QApplication::focusWidget();
644 }
645 QApplication::sendEvent(focusWidget, event);
646}
647
648void InputController::postPendingEvent(int key) {
649 m_pendingEvents.insert(key);
650}
651
652void InputController::clearPendingEvent(int key) {
653 m_pendingEvents.remove(key);
654}
655
656bool InputController::hasPendingEvent(int key) const {
657 return m_pendingEvents.contains(key);
658}
659
660void InputController::suspendScreensaver() {
661#ifdef BUILD_SDL
662#if SDL_VERSION_ATLEAST(2, 0, 0)
663 mSDLSuspendScreensaver(&s_sdlEvents);
664#endif
665#endif
666}
667
668void InputController::resumeScreensaver() {
669#ifdef BUILD_SDL
670#if SDL_VERSION_ATLEAST(2, 0, 0)
671 mSDLResumeScreensaver(&s_sdlEvents);
672#endif
673#endif
674}
675
676void InputController::setScreensaverSuspendable(bool suspendable) {
677#ifdef BUILD_SDL
678#if SDL_VERSION_ATLEAST(2, 0, 0)
679 mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
680#endif
681#endif
682}
683
684void InputController::stealFocus(QWidget* focus) {
685 m_focusParent = focus;
686}
687
688void InputController::releaseFocus(QWidget* focus) {
689 if (focus == m_focusParent) {
690 m_focusParent = m_topLevel;
691 }
692}
693
694void InputController::setupCallback(GameController* controller) {
695 m_inputModel->setKeyCallback([this, controller](QMenu* menu, int key, bool down) {
696 if (menu == m_autofireMenu.get()) {
697 controller->setAutofire(key, down);
698 } else {
699 if (down) {
700 controller->keyPressed(key);
701 } else {
702 controller->keyReleased(key);
703 }
704 }
705 });
706}
707
708void InputController::bindKey(const QModelIndex& index, int key) {
709 int coreKey = m_inputModel->keyAt(index);
710 if (coreKey < 0) {
711 return;
712 }
713 mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE);
714 bindKey(platform, KEYBOARD, key, coreKey);
715}
716
717#ifdef BUILD_SDL
718void InputController::bindButton(const QModelIndex& index, int key) {
719 int coreKey = m_inputModel->keyAt(index);
720 if (coreKey < 0) {
721 return;
722 }
723 mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE);
724 bindKey(platform, SDL_BINDING_BUTTON, key, coreKey);
725}
726
727void InputController::bindAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction direction) {
728 int coreKey = m_inputModel->keyAt(index);
729 if (coreKey < 0) {
730 return;
731 }
732 mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE);
733 bindAxis(platform, SDL_BINDING_BUTTON, axis, direction, coreKey);
734}
735
736void InputController::bindHat(const QModelIndex& index, int hat, GamepadHatEvent::Direction direction) {
737 int coreKey = m_inputModel->keyAt(index);
738 if (coreKey < 0) {
739 return;
740 }
741 mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE);
742 bindHat(platform, SDL_BINDING_BUTTON, hat, direction, coreKey);
743}
744#else
745void InputController::bindButton(const QModelIndex& index, int key) {}
746void InputController::bindAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction) {}
747void InputController::bindHat(const QModelIndex& index, int hat, GamepadHatEvent::Direction) {}
748#endif
749
750bool InputController::eventFilter(QObject*, QEvent* event) {
751 if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
752 QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
753 int key = keyEvent->key();
754 if (!InputModel::isModifierKey(key)) {
755 key |= (keyEvent->modifiers() & ~Qt::KeypadModifier);
756 } else {
757 key = InputModel::toModifierKey(key | (keyEvent->modifiers() & ~Qt::KeypadModifier));
758 }
759
760 if (keyEvent->isAutoRepeat()) {
761 event->accept();
762 return true;
763 }
764
765 if (m_inputModel->triggerKey(key, event->type() == QEvent::KeyPress, m_platform)) {
766 event->accept();
767 return true;
768 }
769 }
770
771
772 if (event->type() == GamepadButtonEvent::Down() || event->type() == GamepadButtonEvent::Up()) {
773 GamepadButtonEvent* gbe = static_cast<GamepadButtonEvent*>(event);
774 if (m_inputModel->triggerButton(gbe->value(), event->type() == GamepadButtonEvent::Down())) {
775 event->accept();
776 return true;
777 }
778 }
779 if (event->type() == GamepadAxisEvent::Type()) {
780 GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event);
781 if (m_inputModel->triggerAxis(gae->axis(), gae->direction(), gae->isNew())) {
782 event->accept();
783 return true;
784 }
785 }
786 return false;
787}
788
789void InputController::restoreModel() {
790 bool signalsBlocked = m_inputModel->blockSignals(true);
791 for (auto iter = m_inputMaps.begin(); iter != m_inputMaps.end(); ++iter) {
792 mPlatform platform = iter.key();
793 QModelIndex parent = m_inputMenuIndices[platform];
794 int nKeys = iter->info->nKeys;
795 for (int i = 0; i < nKeys; ++i) {
796 int key = mInputQueryBinding(&iter.value(), KEYBOARD, i);
797 if (key >= 0) {
798 m_inputModel->updateKey(m_inputModel->index(i, 0, parent), key);
799 } else {
800 m_inputModel->clearKey(m_inputModel->index(i, 0, parent));
801 }
802#ifdef BUILD_SDL
803 key = mInputQueryBinding(&iter.value(), SDL_BINDING_BUTTON, i);
804 if (key >= 0) {
805 m_inputModel->updateButton(m_inputModel->index(i, 0, parent), key);
806 } else {
807 m_inputModel->clearButton(m_inputModel->index(i, 0, parent));
808 }
809#endif
810 }
811#ifdef BUILD_SDL
812 struct Context {
813 InputModel* model;
814 QModelIndex parent;
815 } context{ m_inputModel, parent };
816 mInputEnumerateAxes(&iter.value(), SDL_BINDING_BUTTON, [](int axis, const struct mInputAxis* description, void* user) {
817 Context* context = static_cast<Context*>(user);
818 if (description->highDirection >= 0) {
819 context->model->updateAxis(context->model->index(description->highDirection, 0, context->parent), axis, GamepadAxisEvent::POSITIVE);
820 }
821 if (description->lowDirection >= 0) {
822 context->model->updateAxis(context->model->index(description->lowDirection, 0, context->parent), axis, GamepadAxisEvent::NEGATIVE);
823 }
824 }, &context);
825#endif
826 }
827 m_inputModel->blockSignals(signalsBlocked);
828}