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