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