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], QString("%1.%2").arg(info->platformName).arg(info->keyId[i]));
114 m_inputModel->addKey(autofire, platform, i, 0, info->keyId[i], QString("%1.autofire.%2").arg(info->platformName).arg(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 if (!inputMap.info) {
234 continue;
235 }
236 mInputMapSave(&inputMap, type, m_config->input());
237 }
238 m_config->write();
239}
240
241void InputController::saveProfile(uint32_t type, const QString& profile) {
242 for (auto& inputMap : m_inputMaps) {
243 mInputProfileSave(&inputMap, type, m_config->input(), profile.toUtf8().constData());
244 }
245 m_config->write();
246}
247
248const char* InputController::profileForType(uint32_t type) {
249 UNUSED(type);
250#ifdef BUILD_SDL
251 if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
252#if SDL_VERSION_ATLEAST(2, 0, 0)
253 return SDL_JoystickName(m_sdlPlayer.joystick->joystick);
254#else
255 return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick));
256#endif
257 }
258#endif
259 return 0;
260}
261
262QStringList InputController::connectedGamepads(uint32_t type) const {
263 UNUSED(type);
264
265#ifdef BUILD_SDL
266 if (type == SDL_BINDING_BUTTON) {
267 QStringList pads;
268 for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
269 const char* name;
270#if SDL_VERSION_ATLEAST(2, 0, 0)
271 name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick);
272#else
273 name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick));
274#endif
275 if (name) {
276 pads.append(QString(name));
277 } else {
278 pads.append(QString());
279 }
280 }
281 return pads;
282 }
283#endif
284
285 return QStringList();
286}
287
288int InputController::gamepad(uint32_t type) const {
289#ifdef BUILD_SDL
290 if (type == SDL_BINDING_BUTTON) {
291 return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0;
292 }
293#endif
294 return 0;
295}
296
297void InputController::setGamepad(uint32_t type, int index) {
298#ifdef BUILD_SDL
299 if (type == SDL_BINDING_BUTTON) {
300 mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index);
301 }
302#endif
303}
304
305void InputController::setPreferredGamepad(uint32_t type, const QString& device) {
306 if (!m_config) {
307 return;
308 }
309 mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, device.toUtf8().constData());
310}
311
312mRumble* InputController::rumble() {
313#ifdef BUILD_SDL
314#if SDL_VERSION_ATLEAST(2, 0, 0)
315 if (m_playerAttached) {
316 return &m_sdlPlayer.rumble.d;
317 }
318#endif
319#endif
320 return nullptr;
321}
322
323mRotationSource* InputController::rotationSource() {
324#ifdef BUILD_SDL
325 if (m_playerAttached) {
326 return &m_sdlPlayer.rotation.d;
327 }
328#endif
329 return nullptr;
330}
331
332void InputController::registerTiltAxisX(int axis) {
333#ifdef BUILD_SDL
334 if (m_playerAttached) {
335 m_sdlPlayer.rotation.axisX = axis;
336 }
337#endif
338}
339
340void InputController::registerTiltAxisY(int axis) {
341#ifdef BUILD_SDL
342 if (m_playerAttached) {
343 m_sdlPlayer.rotation.axisY = axis;
344 }
345#endif
346}
347
348void InputController::registerGyroAxisX(int axis) {
349#ifdef BUILD_SDL
350 if (m_playerAttached) {
351 m_sdlPlayer.rotation.gyroX = axis;
352 }
353#endif
354}
355
356void InputController::registerGyroAxisY(int axis) {
357#ifdef BUILD_SDL
358 if (m_playerAttached) {
359 m_sdlPlayer.rotation.gyroY = axis;
360 }
361#endif
362}
363
364float InputController::gyroSensitivity() const {
365#ifdef BUILD_SDL
366 if (m_playerAttached) {
367 return m_sdlPlayer.rotation.gyroSensitivity;
368 }
369#endif
370 return 0;
371}
372
373void InputController::setGyroSensitivity(float sensitivity) {
374#ifdef BUILD_SDL
375 if (m_playerAttached) {
376 m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
377 }
378#endif
379}
380
381void InputController::updateJoysticks() {
382#ifdef BUILD_SDL
383 QString profile = profileForType(SDL_BINDING_BUTTON);
384 mSDLUpdateJoysticks(&s_sdlEvents, m_config->input());
385 QString newProfile = profileForType(SDL_BINDING_BUTTON);
386 if (profile != newProfile) {
387 loadProfile(SDL_BINDING_BUTTON, newProfile);
388 }
389#endif
390}
391
392const mInputMap* InputController::map() {
393 return &m_inputMaps[m_platform];
394}
395
396int InputController::pollEvents() {
397 int activeButtons = 0;
398#ifdef BUILD_SDL
399 if (m_playerAttached && m_sdlPlayer.joystick) {
400 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
401 SDL_JoystickUpdate();
402 int numButtons = SDL_JoystickNumButtons(joystick);
403 int i;
404 for (i = 0; i < numButtons; ++i) {
405 GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i));
406 if (key == GBA_KEY_NONE) {
407 continue;
408 }
409 if (hasPendingEvent(key)) {
410 continue;
411 }
412 if (SDL_JoystickGetButton(joystick, i)) {
413 activeButtons |= 1 << key;
414 }
415 }
416 int numHats = SDL_JoystickNumHats(joystick);
417 for (i = 0; i < numHats; ++i) {
418 int hat = SDL_JoystickGetHat(joystick, i);
419 activeButtons |= mInputMapHat(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i, hat);
420 }
421
422 int numAxes = SDL_JoystickNumAxes(joystick);
423 for (i = 0; i < numAxes; ++i) {
424 int value = SDL_JoystickGetAxis(joystick, i);
425
426 enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i, value));
427 if (key != GBA_KEY_NONE) {
428 activeButtons |= 1 << key;
429 }
430 }
431 }
432#endif
433 return activeButtons;
434}
435
436QSet<int> InputController::activeGamepadButtons(int type) {
437 QSet<int> activeButtons;
438#ifdef BUILD_SDL
439 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
440 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
441 SDL_JoystickUpdate();
442 int numButtons = SDL_JoystickNumButtons(joystick);
443 int i;
444 for (i = 0; i < numButtons; ++i) {
445 if (SDL_JoystickGetButton(joystick, i)) {
446 activeButtons.insert(i);
447 }
448 }
449 }
450#endif
451 return activeButtons;
452}
453
454void InputController::recalibrateAxes() {
455#ifdef BUILD_SDL
456 if (m_playerAttached && m_sdlPlayer.joystick) {
457 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
458 SDL_JoystickUpdate();
459 int numAxes = SDL_JoystickNumAxes(joystick);
460 if (numAxes < 1) {
461 return;
462 }
463 m_deadzones.resize(numAxes);
464 int i;
465 for (i = 0; i < numAxes; ++i) {
466 m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
467 }
468 }
469#endif
470}
471
472QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
473 QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
474#ifdef BUILD_SDL
475 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
476 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
477 SDL_JoystickUpdate();
478 int numAxes = SDL_JoystickNumAxes(joystick);
479 if (numAxes < 1) {
480 return activeAxes;
481 }
482 m_deadzones.resize(numAxes);
483 int i;
484 for (i = 0; i < numAxes; ++i) {
485 int32_t axis = SDL_JoystickGetAxis(joystick, i);
486 axis -= m_deadzones[i];
487 if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
488 activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
489 }
490 }
491 }
492#endif
493 return activeAxes;
494}
495
496void InputController::bindKey(mPlatform platform, uint32_t type, int key, int coreKey) {
497 if (m_inputMaps.find(platform) == m_inputMaps.end() || coreKey >= m_inputMaps[platform].info->nKeys) {
498 return;
499 }
500 QModelIndex index = m_inputModel->index(coreKey, 0, m_inputMenuIndices[platform]);
501 bool signalsBlocked = m_inputModel->blockSignals(true);
502 if (type != KEYBOARD) {
503 m_inputModel->updateButton(index, key);
504 } else {
505 m_inputModel->updateKey(index, key);
506 }
507 m_inputModel->blockSignals(signalsBlocked);
508 mInputBindKey(&m_inputMaps[platform], type, key, coreKey);
509}
510
511void InputController::bindAxis(mPlatform platform, uint32_t type, int axis, GamepadAxisEvent::Direction direction, int key) {
512 if (m_inputMaps.find(platform) == m_inputMaps.end() || key >= m_inputMaps[platform].info->nKeys) {
513 return;
514 }
515 QModelIndex index = m_inputModel->index(key, 0, m_inputMenuIndices[platform]);
516 bool signalsBlocked = m_inputModel->blockSignals(true);
517 m_inputModel->updateAxis(index, axis, direction);
518 m_inputModel->blockSignals(signalsBlocked);
519
520 const mInputAxis* old = mInputQueryAxis(&m_inputMaps[platform], type, axis);
521 mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
522 if (old) {
523 description = *old;
524 }
525 int deadzone = 0;
526 if (axis > 0 && m_deadzones.size() > axis) {
527 deadzone = m_deadzones[axis];
528 }
529 switch (direction) {
530 case GamepadAxisEvent::NEGATIVE:
531 description.lowDirection = key;
532
533 description.deadLow = deadzone - AXIS_THRESHOLD;
534 break;
535 case GamepadAxisEvent::POSITIVE:
536 description.highDirection = key;
537 description.deadHigh = deadzone + AXIS_THRESHOLD;
538 break;
539 default:
540 return;
541 }
542 mInputBindAxis(&m_inputMaps[platform], type, axis, &description);
543}
544
545QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {
546 QSet<QPair<int, GamepadHatEvent::Direction>> activeHats;
547#ifdef BUILD_SDL
548 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
549 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
550 SDL_JoystickUpdate();
551 int numHats = SDL_JoystickNumHats(joystick);
552 if (numHats < 1) {
553 return activeHats;
554 }
555
556 int i;
557 for (i = 0; i < numHats; ++i) {
558 int hat = SDL_JoystickGetHat(joystick, i);
559 if (hat & GamepadHatEvent::UP) {
560 activeHats.insert(qMakePair(i, GamepadHatEvent::UP));
561 }
562 if (hat & GamepadHatEvent::RIGHT) {
563 activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT));
564 }
565 if (hat & GamepadHatEvent::DOWN) {
566 activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN));
567 }
568 if (hat & GamepadHatEvent::LEFT) {
569 activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT));
570 }
571 }
572 }
573#endif
574 return activeHats;
575}
576
577void InputController::bindHat(mPlatform platform, uint32_t type, int hat, GamepadHatEvent::Direction direction, int coreKey) {
578 if (m_inputMaps.find(platform) == m_inputMaps.end() || coreKey >= m_inputMaps[platform].info->nKeys) {
579 return;
580 }
581 QModelIndex index = m_inputModel->index(coreKey, 0, m_inputMenuIndices[platform]);
582 //m_inputModel->updateHat(index, hat, direction);
583
584 mInputHatBindings bindings{ -1, -1, -1, -1 };
585 mInputQueryHat(&m_inputMaps[platform], type, hat, &bindings);
586 switch (direction) {
587 case GamepadHatEvent::UP:
588 bindings.up = coreKey;
589 break;
590 case GamepadHatEvent::RIGHT:
591 bindings.right = coreKey;
592 break;
593 case GamepadHatEvent::DOWN:
594 bindings.down = coreKey;
595 break;
596 case GamepadHatEvent::LEFT:
597 bindings.left = coreKey;
598 break;
599 default:
600 return;
601 }
602 mInputBindHat(&m_inputMaps[platform], type, hat, &bindings);
603}
604
605void InputController::testGamepad(int type) {
606 auto activeAxes = activeGamepadAxes(type);
607 auto oldAxes = m_activeAxes;
608 m_activeAxes = activeAxes;
609
610 auto activeButtons = activeGamepadButtons(type);
611 auto oldButtons = m_activeButtons;
612 m_activeButtons = activeButtons;
613
614 auto activeHats = activeGamepadHats(type);
615 auto oldHats = m_activeHats;
616 m_activeHats = activeHats;
617
618 if (!QApplication::focusWidget()) {
619 return;
620 }
621
622 activeAxes.subtract(oldAxes);
623 oldAxes.subtract(m_activeAxes);
624
625 for (auto& axis : m_activeAxes) {
626 bool newlyAboveThreshold = activeAxes.contains(axis);
627 if (newlyAboveThreshold) {
628 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
629 postPendingEvent(event->gbaKey());
630 sendGamepadEvent(event);
631 if (!event->isAccepted()) {
632 clearPendingEvent(event->gbaKey());
633 }
634 }
635 }
636 for (auto axis : oldAxes) {
637 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
638 clearPendingEvent(event->gbaKey());
639 sendGamepadEvent(event);
640 }
641
642 if (!QApplication::focusWidget()) {
643 return;
644 }
645
646 activeButtons.subtract(oldButtons);
647 oldButtons.subtract(m_activeButtons);
648
649 for (int button : activeButtons) {
650 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
651 postPendingEvent(event->gbaKey());
652 sendGamepadEvent(event);
653 if (!event->isAccepted()) {
654 clearPendingEvent(event->gbaKey());
655 }
656 }
657 for (int button : oldButtons) {
658 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
659 clearPendingEvent(event->gbaKey());
660 sendGamepadEvent(event);
661 }
662
663 activeHats.subtract(oldHats);
664 oldHats.subtract(m_activeHats);
665
666 for (auto& hat : activeHats) {
667 GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this);
668 postPendingEvent(event->gbaKey());
669 sendGamepadEvent(event);
670 if (!event->isAccepted()) {
671 clearPendingEvent(event->gbaKey());
672 }
673 }
674 for (auto& hat : oldHats) {
675 GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this);
676 clearPendingEvent(event->gbaKey());
677 sendGamepadEvent(event);
678 }
679}
680
681void InputController::sendGamepadEvent(QEvent* event) {
682 QWidget* focusWidget = nullptr;
683 if (m_focusParent) {
684 focusWidget = m_focusParent->focusWidget();
685 if (!focusWidget) {
686 focusWidget = m_focusParent;
687 }
688 } else {
689 focusWidget = QApplication::focusWidget();
690 }
691 QApplication::sendEvent(focusWidget, event);
692}
693
694void InputController::postPendingEvent(int key) {
695 m_pendingEvents.insert(key);
696}
697
698void InputController::clearPendingEvent(int key) {
699 m_pendingEvents.remove(key);
700}
701
702bool InputController::hasPendingEvent(int key) const {
703 return m_pendingEvents.contains(key);
704}
705
706void InputController::suspendScreensaver() {
707#ifdef BUILD_SDL
708#if SDL_VERSION_ATLEAST(2, 0, 0)
709 mSDLSuspendScreensaver(&s_sdlEvents);
710#endif
711#endif
712}
713
714void InputController::resumeScreensaver() {
715#ifdef BUILD_SDL
716#if SDL_VERSION_ATLEAST(2, 0, 0)
717 mSDLResumeScreensaver(&s_sdlEvents);
718#endif
719#endif
720}
721
722void InputController::setScreensaverSuspendable(bool suspendable) {
723#ifdef BUILD_SDL
724#if SDL_VERSION_ATLEAST(2, 0, 0)
725 mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
726#endif
727#endif
728}
729
730void InputController::stealFocus(QWidget* focus) {
731 m_focusParent = focus;
732}
733
734void InputController::releaseFocus(QWidget* focus) {
735 if (focus == m_focusParent) {
736 m_focusParent = m_topLevel;
737 }
738}
739
740void InputController::setupCallback(GameController* controller) {
741 m_inputModel->setKeyCallback([this, controller](QMenu* menu, int key, bool down) {
742 if (menu->parent() == m_autofireMenu.get()) {
743 controller->setAutofire(key, down);
744 } else {
745 if (down) {
746 controller->keyPressed(key);
747 } else {
748 controller->keyReleased(key);
749 }
750 }
751 });
752}
753
754void InputController::bindKey(const QModelIndex& index, int key) {
755 int coreKey = m_inputModel->keyAt(index);
756 if (coreKey < 0) {
757 return;
758 }
759 mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE);
760 bindKey(platform, KEYBOARD, key, coreKey);
761}
762
763#ifdef BUILD_SDL
764void InputController::bindButton(const QModelIndex& index, int key) {
765 int coreKey = m_inputModel->keyAt(index);
766 if (coreKey < 0) {
767 return;
768 }
769 mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE);
770 bindKey(platform, SDL_BINDING_BUTTON, key, coreKey);
771}
772
773void InputController::bindAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction direction) {
774 int coreKey = m_inputModel->keyAt(index);
775 if (coreKey < 0) {
776 return;
777 }
778 mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE);
779 bindAxis(platform, SDL_BINDING_BUTTON, axis, direction, coreKey);
780}
781
782void InputController::bindHat(const QModelIndex& index, int hat, GamepadHatEvent::Direction direction) {
783 int coreKey = m_inputModel->keyAt(index);
784 if (coreKey < 0) {
785 return;
786 }
787 mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE);
788 bindHat(platform, SDL_BINDING_BUTTON, hat, direction, coreKey);
789}
790#else
791void InputController::bindButton(const QModelIndex& index, int key) {}
792void InputController::bindAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction) {}
793void InputController::bindHat(const QModelIndex& index, int hat, GamepadHatEvent::Direction) {}
794#endif
795
796bool InputController::eventFilter(QObject*, QEvent* event) {
797 if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
798 QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
799 int key = keyEvent->key();
800 if (!InputModel::isModifierKey(key)) {
801 key |= (keyEvent->modifiers() & ~Qt::KeypadModifier);
802 } else {
803 key = InputModel::toModifierKey(key | (keyEvent->modifiers() & ~Qt::KeypadModifier));
804 }
805
806 if (keyEvent->isAutoRepeat()) {
807 event->accept();
808 return true;
809 }
810
811 if (m_inputModel->triggerKey(key, event->type() == QEvent::KeyPress, m_platform)) {
812 event->accept();
813 return true;
814 }
815 }
816
817
818 if (event->type() == GamepadButtonEvent::Down() || event->type() == GamepadButtonEvent::Up()) {
819 GamepadButtonEvent* gbe = static_cast<GamepadButtonEvent*>(event);
820 if (m_inputModel->triggerButton(gbe->value(), event->type() == GamepadButtonEvent::Down())) {
821 event->accept();
822 return true;
823 }
824 }
825 if (event->type() == GamepadAxisEvent::Type()) {
826 GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event);
827 if (m_inputModel->triggerAxis(gae->axis(), gae->direction(), gae->isNew())) {
828 event->accept();
829 return true;
830 }
831 }
832 return false;
833}
834
835void InputController::restoreModel() {
836 bool signalsBlocked = m_inputModel->blockSignals(true);
837 for (auto iter = m_inputMaps.begin(); iter != m_inputMaps.end(); ++iter) {
838 mPlatform platform = iter.key();
839 QModelIndex parent = m_inputMenuIndices[platform];
840 int nKeys = iter->info->nKeys;
841 for (int i = 0; i < nKeys; ++i) {
842 int key = mInputQueryBinding(&iter.value(), KEYBOARD, i);
843 if (key >= 0) {
844 m_inputModel->updateKey(m_inputModel->index(i, 0, parent), key);
845 } else {
846 m_inputModel->clearKey(m_inputModel->index(i, 0, parent));
847 }
848#ifdef BUILD_SDL
849 key = mInputQueryBinding(&iter.value(), SDL_BINDING_BUTTON, i);
850 if (key >= 0) {
851 m_inputModel->updateButton(m_inputModel->index(i, 0, parent), key);
852 } else {
853 m_inputModel->clearButton(m_inputModel->index(i, 0, parent));
854 }
855#endif
856 }
857#ifdef BUILD_SDL
858 struct Context {
859 InputModel* model;
860 QModelIndex parent;
861 } context{ m_inputModel, parent };
862 mInputEnumerateAxes(&iter.value(), SDL_BINDING_BUTTON, [](int axis, const struct mInputAxis* description, void* user) {
863 Context* context = static_cast<Context*>(user);
864 if (description->highDirection >= 0) {
865 context->model->updateAxis(context->model->index(description->highDirection, 0, context->parent), axis, GamepadAxisEvent::POSITIVE);
866 }
867 if (description->lowDirection >= 0) {
868 context->model->updateAxis(context->model->index(description->lowDirection, 0, context->parent), axis, GamepadAxisEvent::NEGATIVE);
869 }
870 }, &context);
871#endif
872 }
873 m_inputModel->blockSignals(signalsBlocked);
874}