src/platform/qt/input/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 "CoreController.h"
10#include "GamepadAxisEvent.h"
11#include "GamepadButtonEvent.h"
12#include "InputItem.h"
13#include "InputModel.h"
14#include "InputProfile.h"
15#include "LogController.h"
16
17#include <QApplication>
18#include <QKeyEvent>
19#include <QMenu>
20#include <QTimer>
21#include <QWidget>
22#ifdef BUILD_QT_MULTIMEDIA
23#include <QCamera>
24#include <QVideoSurfaceFormat>
25#endif
26
27#include <mgba/core/interface.h>
28#include <mgba-util/configuration.h>
29
30#ifdef M_CORE_GBA
31#include <mgba/internal/gba/input.h>
32#endif
33#ifdef M_CORE_GB
34#include <mgba/internal/gb/input.h>
35#endif
36#ifdef M_CORE_DS
37#include <mgba/internal/ds/input.h>
38#endif
39#include <initializer_list>
40
41using namespace QGBA;
42
43#ifdef BUILD_SDL
44int InputController::s_sdlInited = 0;
45mSDLEvents InputController::s_sdlEvents;
46#endif
47
48InputController::InputController(int playerId, QWidget* topLevel, QObject* parent)
49 : QObject(parent)
50 , m_playerId(playerId)
51 , m_topLevel(topLevel)
52 , m_focusParent(topLevel)
53 , m_bindings(new QMenu(tr("Controls")))
54 , m_autofire(new QMenu(tr("Autofire")))
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#ifdef BUILD_QT_MULTIMEDIA
76 connect(&m_videoDumper, &VideoDumper::imageAvailable, this, &InputController::setCamImage);
77#endif
78
79 static QList<QPair<QString, int>> defaultBindings({
80 qMakePair(QLatin1String("A"), Qt::Key_Z),
81 qMakePair(QLatin1String("B"), Qt::Key_X),
82 qMakePair(QLatin1String("L"), Qt::Key_A),
83 qMakePair(QLatin1String("R"), Qt::Key_S),
84 qMakePair(QLatin1String("Start"), Qt::Key_Return),
85 qMakePair(QLatin1String("Select"), Qt::Key_Backspace),
86 qMakePair(QLatin1String("Up"), Qt::Key_Up),
87 qMakePair(QLatin1String("Down"), Qt::Key_Down),
88 qMakePair(QLatin1String("Left"), Qt::Key_Left),
89 qMakePair(QLatin1String("Right"), Qt::Key_Right)
90 });
91
92 for (auto k : defaultBindings) {
93 addKey(k.first);
94 }
95 m_keyIndex.rebuild();
96 for (auto k : defaultBindings) {
97 bindKey(KEYBOARD, k.second, k.first);
98 }
99}
100
101void InputController::addKey(const QString& name) {
102 if (itemForKey(name)) {
103 return;
104 }
105 m_keyIndex.addItem(qMakePair([this, name]() {
106 m_activeKeys |= 1 << keyId(name);
107 }, [this, name]() {
108 m_activeKeys &= ~(1 << keyId(name));
109 }), name, QString("key%0").arg(name), m_bindings.get());
110
111 m_keyIndex.addItem(qMakePair([this, name]() {
112 setAutofire(keyId(name), true);
113 }, [this, name]() {
114 setAutofire(keyId(name), false);
115 }), name, QString("autofire%1").arg(name), m_autofire.get());
116}
117
118void InputController::setAutofire(int key, bool enable) {
119 if (key >= 32 || key < 0) {
120 return;
121 }
122
123 m_autofireEnabled[key] = enable;
124 m_autofireStatus[key] = 0;
125}
126
127int InputController::updateAutofire() {
128 int active = 0;
129 for (int k = 0; k < 32; ++k) {
130 if (!m_autofireEnabled[k]) {
131 continue;
132 }
133 ++m_autofireStatus[k];
134 if (m_autofireStatus[k]) {
135 m_autofireStatus[k] = 0;
136 active |= 1 << k;
137 }
138 }
139 return active;
140}
141
142void InputController::addPlatform(mPlatform platform, const mInputPlatformInfo* info) {
143 m_keyInfo[platform] = info;
144 for (size_t i = 0; i < info->nKeys; ++i) {
145 addKey(info->keyId[i]);
146 }
147}
148
149void InputController::setPlatform(mPlatform platform) {
150 if (m_activeKeyInfo) {
151 mInputMapDeinit(&m_inputMap);
152 }
153
154 m_sdlPlayer.bindings = &m_inputMap;
155 m_activeKeyInfo = m_keyInfo[platform];
156 mInputMapInit(&m_inputMap, m_activeKeyInfo);
157
158 loadConfiguration(KEYBOARD);
159#ifdef BUILD_SDL
160 mSDLInitBindingsGBA(&m_inputMap);
161 loadConfiguration(SDL_BINDING_BUTTON);
162#endif
163
164 rebuildKeyIndex();
165 restoreModel();
166
167#ifdef M_CORE_GBA
168 m_lux.p = this;
169 m_lux.sample = [](GBALuminanceSource* context) {
170 InputControllerLux* lux = static_cast<InputControllerLux*>(context);
171 lux->value = 0xFF - lux->p->m_luxValue;
172 };
173
174 m_lux.readLuminance = [](GBALuminanceSource* context) {
175 InputControllerLux* lux = static_cast<InputControllerLux*>(context);
176 return lux->value;
177 };
178 setLuminanceLevel(0);
179#endif
180
181 m_image.p = this;
182 m_image.startRequestImage = [](mImageSource* context, unsigned w, unsigned h, int) {
183 InputControllerImage* image = static_cast<InputControllerImage*>(context);
184 image->w = w;
185 image->h = h;
186 if (image->image.isNull()) {
187 image->image.load(":/res/no-cam.png");
188 }
189#ifdef BUILD_QT_MULTIMEDIA
190 if (image->p->m_config->getQtOption("cameraDriver").toInt() == static_cast<int>(CameraDriver::QT_MULTIMEDIA)) {
191 QMetaObject::invokeMethod(image->p, "setupCam");
192 }
193#endif
194 };
195
196 m_image.stopRequestImage = [](mImageSource* context) {
197 InputControllerImage* image = static_cast<InputControllerImage*>(context);
198#ifdef BUILD_QT_MULTIMEDIA
199 QMetaObject::invokeMethod(image->p, "teardownCam");
200#endif
201 };
202
203 m_image.requestImage = [](mImageSource* context, const void** buffer, size_t* stride, mColorFormat* format) {
204 InputControllerImage* image = static_cast<InputControllerImage*>(context);
205 QSize size;
206 {
207 QMutexLocker locker(&image->mutex);
208 if (image->outOfDate) {
209 image->resizedImage = image->image.scaled(image->w, image->h, Qt::KeepAspectRatioByExpanding);
210 image->resizedImage = image->resizedImage.convertToFormat(QImage::Format_RGB16);
211 image->outOfDate = false;
212 }
213 }
214 size = image->resizedImage.size();
215 const uint16_t* bits = reinterpret_cast<const uint16_t*>(image->resizedImage.constBits());
216 if (size.width() > image->w) {
217 bits += (size.width() - image->w) / 2;
218 }
219 if (size.height() > image->h) {
220 bits += ((size.height() - image->h) / 2) * size.width();
221 }
222 *buffer = bits;
223 *stride = image->resizedImage.bytesPerLine() / sizeof(*bits);
224 *format = mCOLOR_RGB565;
225 };
226}
227
228InputController::~InputController() {
229 if (m_activeKeyInfo) {
230 mInputMapDeinit(&m_inputMap);
231 }
232
233#ifdef BUILD_SDL
234 if (m_playerAttached) {
235 mSDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
236 }
237
238 --s_sdlInited;
239 if (s_sdlInited == 0) {
240 mSDLDeinitEvents(&s_sdlEvents);
241 }
242#endif
243}
244
245void InputController::rebuildIndex(const InputIndex* index) {
246 m_inputIndex.rebuild(index);
247}
248
249void InputController::rebuildKeyIndex(const InputIndex* index) {
250 m_keyIndex.rebuild(index);
251
252 for (const InputItem* item : m_keyIndex.items()) {
253 if (!item->name().startsWith(QLatin1String("key"))) {
254 rebindKey(item->visibleName());
255 }
256 }
257}
258
259void InputController::setConfiguration(ConfigController* config) {
260 m_config = config;
261 m_inputIndex.setConfigController(config);
262 m_keyIndex.setConfigController(config);
263 loadConfiguration(KEYBOARD);
264 loadProfile(KEYBOARD, profileForType(KEYBOARD));
265#ifdef BUILD_SDL
266 mSDLEventsLoadConfig(&s_sdlEvents, config->input());
267 if (!m_playerAttached) {
268 m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
269 }
270 loadConfiguration(SDL_BINDING_BUTTON);
271 loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
272#endif
273 restoreModel();
274}
275
276void InputController::loadConfiguration(uint32_t type) {
277 if (!m_activeKeyInfo) {
278 return;
279 }
280 mInputMapLoad(&m_inputMap, type, m_config->input());
281#ifdef BUILD_SDL
282 if (m_playerAttached) {
283 mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input());
284 }
285#endif
286}
287
288void InputController::loadProfile(uint32_t type, const QString& profile) {
289 const InputProfile* ip = InputProfile::findProfile(profile);
290 if (ip) {
291 ip->apply(this);
292 }
293 recalibrateAxes();
294 emit profileLoaded(profile);
295}
296
297void InputController::saveConfiguration() {
298 saveConfiguration(KEYBOARD);
299#ifdef BUILD_SDL
300 saveConfiguration(SDL_BINDING_BUTTON);
301 saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
302 if (m_playerAttached) {
303 mSDLPlayerSaveConfig(&m_sdlPlayer, m_config->input());
304 }
305#endif
306 m_inputIndex.saveConfig();
307 m_keyIndex.saveConfig();
308 m_config->write();
309}
310
311void InputController::saveConfiguration(uint32_t type) {
312 if (m_activeKeyInfo) {
313 mInputMapSave(&m_inputMap, type, m_config->input());
314 }
315 m_config->write();
316}
317
318void InputController::saveProfile(uint32_t type, const QString& profile) {
319 if (m_activeKeyInfo) {
320 mInputProfileSave(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
321 }
322 m_config->write();
323}
324
325const char* InputController::profileForType(uint32_t type) {
326 UNUSED(type);
327#ifdef BUILD_SDL
328 if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
329#if SDL_VERSION_ATLEAST(2, 0, 0)
330 return SDL_JoystickName(m_sdlPlayer.joystick->joystick);
331#else
332 return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick));
333#endif
334 }
335#endif
336 return 0;
337}
338
339QStringList InputController::connectedGamepads(uint32_t type) const {
340 UNUSED(type);
341
342#ifdef BUILD_SDL
343 if (type == SDL_BINDING_BUTTON) {
344 QStringList pads;
345 for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
346 const char* name;
347#if SDL_VERSION_ATLEAST(2, 0, 0)
348 name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick);
349#else
350 name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick));
351#endif
352 if (name) {
353 pads.append(QString(name));
354 } else {
355 pads.append(QString());
356 }
357 }
358 return pads;
359 }
360#endif
361
362 return QStringList();
363}
364
365int InputController::gamepad(uint32_t type) const {
366#ifdef BUILD_SDL
367 if (type == SDL_BINDING_BUTTON) {
368 return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0;
369 }
370#endif
371 return 0;
372}
373
374void InputController::setGamepad(uint32_t type, int index) {
375#ifdef BUILD_SDL
376 if (type == SDL_BINDING_BUTTON) {
377 mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index);
378 }
379#endif
380}
381
382void InputController::setPreferredGamepad(uint32_t type, const QString& device) {
383 if (!m_config) {
384 return;
385 }
386 mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, device.toUtf8().constData());
387}
388
389mRumble* InputController::rumble() {
390#ifdef BUILD_SDL
391#if SDL_VERSION_ATLEAST(2, 0, 0)
392 if (m_playerAttached) {
393 return &m_sdlPlayer.rumble.d;
394 }
395#endif
396#endif
397 return nullptr;
398}
399
400mRotationSource* InputController::rotationSource() {
401#ifdef BUILD_SDL
402 if (m_playerAttached) {
403 return &m_sdlPlayer.rotation.d;
404 }
405#endif
406 return nullptr;
407}
408
409void InputController::registerTiltAxisX(int axis) {
410#ifdef BUILD_SDL
411 if (m_playerAttached) {
412 m_sdlPlayer.rotation.axisX = axis;
413 }
414#endif
415}
416
417void InputController::registerTiltAxisY(int axis) {
418#ifdef BUILD_SDL
419 if (m_playerAttached) {
420 m_sdlPlayer.rotation.axisY = axis;
421 }
422#endif
423}
424
425void InputController::registerGyroAxisX(int axis) {
426#ifdef BUILD_SDL
427 if (m_playerAttached) {
428 m_sdlPlayer.rotation.gyroX = axis;
429 }
430#endif
431}
432
433void InputController::registerGyroAxisY(int axis) {
434#ifdef BUILD_SDL
435 if (m_playerAttached) {
436 m_sdlPlayer.rotation.gyroY = axis;
437 }
438#endif
439}
440
441float InputController::gyroSensitivity() const {
442#ifdef BUILD_SDL
443 if (m_playerAttached) {
444 return m_sdlPlayer.rotation.gyroSensitivity;
445 }
446#endif
447 return 0;
448}
449
450void InputController::setGyroSensitivity(float sensitivity) {
451#ifdef BUILD_SDL
452 if (m_playerAttached) {
453 m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
454 }
455#endif
456}
457
458void InputController::updateJoysticks() {
459#ifdef BUILD_SDL
460 QString profile = profileForType(SDL_BINDING_BUTTON);
461 mSDLUpdateJoysticks(&s_sdlEvents, m_config->input());
462 QString newProfile = profileForType(SDL_BINDING_BUTTON);
463 if (profile != newProfile) {
464 loadProfile(SDL_BINDING_BUTTON, newProfile);
465 }
466#endif
467}
468
469const mInputMap* InputController::map() {
470 if (!m_activeKeyInfo) {
471 return nullptr;
472 }
473 return &m_inputMap;
474}
475
476int InputController::pollEvents() {
477 int activeButtons = m_activeKeys;
478#ifdef BUILD_SDL
479 if (m_playerAttached && m_sdlPlayer.joystick) {
480 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
481 SDL_JoystickUpdate();
482 int numButtons = SDL_JoystickNumButtons(joystick);
483 int i;
484 for (i = 0; i < numButtons; ++i) {
485 GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i));
486 if (key == GBA_KEY_NONE) {
487 continue;
488 }
489 if (hasPendingEvent(key)) {
490 continue;
491 }
492 if (SDL_JoystickGetButton(joystick, i)) {
493 activeButtons |= 1 << key;
494 }
495 }
496 int numHats = SDL_JoystickNumHats(joystick);
497 for (i = 0; i < numHats; ++i) {
498 int hat = SDL_JoystickGetHat(joystick, i);
499 activeButtons |= mInputMapHat(&m_inputMap, SDL_BINDING_BUTTON, i, hat);
500 }
501
502 int numAxes = SDL_JoystickNumAxes(joystick);
503 for (i = 0; i < numAxes; ++i) {
504 int value = SDL_JoystickGetAxis(joystick, i);
505
506 enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value));
507 if (key != GBA_KEY_NONE) {
508 activeButtons |= 1 << key;
509 }
510 }
511 }
512#endif
513 return activeButtons;
514}
515
516QSet<int> InputController::activeGamepadButtons(int type) {
517 QSet<int> activeButtons;
518#ifdef BUILD_SDL
519 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
520 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
521 SDL_JoystickUpdate();
522 int numButtons = SDL_JoystickNumButtons(joystick);
523 int i;
524 for (i = 0; i < numButtons; ++i) {
525 if (SDL_JoystickGetButton(joystick, i)) {
526 activeButtons.insert(i);
527 }
528 }
529 }
530#endif
531 return activeButtons;
532}
533
534void InputController::recalibrateAxes() {
535#ifdef BUILD_SDL
536 if (m_playerAttached && m_sdlPlayer.joystick) {
537 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
538 SDL_JoystickUpdate();
539 int numAxes = SDL_JoystickNumAxes(joystick);
540 if (numAxes < 1) {
541 return;
542 }
543 m_deadzones.resize(numAxes);
544 int i;
545 for (i = 0; i < numAxes; ++i) {
546 m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
547 }
548 }
549#endif
550}
551
552QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
553 QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
554#ifdef BUILD_SDL
555 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
556 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
557 SDL_JoystickUpdate();
558 int numAxes = SDL_JoystickNumAxes(joystick);
559 if (numAxes < 1) {
560 return activeAxes;
561 }
562 m_deadzones.resize(numAxes);
563 int i;
564 for (i = 0; i < numAxes; ++i) {
565 int32_t axis = SDL_JoystickGetAxis(joystick, i);
566 axis -= m_deadzones[i];
567 if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
568 activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
569 }
570 }
571 }
572#endif
573 return activeAxes;
574}
575
576void InputController::bindKey(uint32_t type, int key, const QString& keyName) {
577 InputItem* item = itemForKey(keyName);
578 if (type != KEYBOARD) {
579 item->setButton(key);
580 } else {
581 item->setShortcut(key);
582 }
583 if (m_activeKeyInfo) {
584 int coreKey = keyId(keyName);
585 mInputBindKey(&m_inputMap, type, key, coreKey);
586 }
587}
588
589void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, const QString& key) {
590 InputItem* item = itemForKey(key);
591 item->setAxis(axis, direction);
592
593 if (!m_activeKeyInfo) {
594 return;
595 }
596
597 const mInputAxis* old = mInputQueryAxis(&m_inputMap, type, axis);
598 mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
599 if (old) {
600 description = *old;
601 }
602 int deadzone = 0;
603 if (axis > 0 && m_deadzones.size() > axis) {
604 deadzone = m_deadzones[axis];
605 }
606 switch (direction) {
607 case GamepadAxisEvent::NEGATIVE:
608 description.lowDirection = keyId(key);
609
610 description.deadLow = deadzone - AXIS_THRESHOLD;
611 break;
612 case GamepadAxisEvent::POSITIVE:
613 description.highDirection = keyId(key);
614 description.deadHigh = deadzone + AXIS_THRESHOLD;
615 break;
616 default:
617 return;
618 }
619 mInputBindAxis(&m_inputMap, type, axis, &description);
620}
621
622QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {
623 QSet<QPair<int, GamepadHatEvent::Direction>> activeHats;
624#ifdef BUILD_SDL
625 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
626 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
627 SDL_JoystickUpdate();
628 int numHats = SDL_JoystickNumHats(joystick);
629 if (numHats < 1) {
630 return activeHats;
631 }
632
633 int i;
634 for (i = 0; i < numHats; ++i) {
635 int hat = SDL_JoystickGetHat(joystick, i);
636 if (hat & GamepadHatEvent::UP) {
637 activeHats.insert(qMakePair(i, GamepadHatEvent::UP));
638 }
639 if (hat & GamepadHatEvent::RIGHT) {
640 activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT));
641 }
642 if (hat & GamepadHatEvent::DOWN) {
643 activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN));
644 }
645 if (hat & GamepadHatEvent::LEFT) {
646 activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT));
647 }
648 }
649 }
650#endif
651 return activeHats;
652}
653
654void InputController::bindHat(uint32_t type, int hat, GamepadHatEvent::Direction direction, const QString& key) {
655 if (!m_activeKeyInfo) {
656 return;
657 }
658
659 mInputHatBindings bindings{ -1, -1, -1, -1 };
660 mInputQueryHat(&m_inputMap, type, hat, &bindings);
661 switch (direction) {
662 case GamepadHatEvent::UP:
663 bindings.up = keyId(key);
664 break;
665 case GamepadHatEvent::RIGHT:
666 bindings.right = keyId(key);
667 break;
668 case GamepadHatEvent::DOWN:
669 bindings.down = keyId(key);
670 break;
671 case GamepadHatEvent::LEFT:
672 bindings.left = keyId(key);
673 break;
674 default:
675 return;
676 }
677 mInputBindHat(&m_inputMap, type, hat, &bindings);
678}
679
680void InputController::testGamepad(int type) {
681 auto activeAxes = activeGamepadAxes(type);
682 auto oldAxes = m_activeAxes;
683 m_activeAxes = activeAxes;
684
685 auto activeButtons = activeGamepadButtons(type);
686 auto oldButtons = m_activeButtons;
687 m_activeButtons = activeButtons;
688
689 auto activeHats = activeGamepadHats(type);
690 auto oldHats = m_activeHats;
691 m_activeHats = activeHats;
692
693 if (!QApplication::focusWidget()) {
694 return;
695 }
696
697 activeAxes.subtract(oldAxes);
698 oldAxes.subtract(m_activeAxes);
699
700 for (auto& axis : m_activeAxes) {
701 bool newlyAboveThreshold = activeAxes.contains(axis);
702 if (newlyAboveThreshold) {
703 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
704 postPendingEvent(event->gbaKey());
705 sendGamepadEvent(event);
706 if (!event->isAccepted()) {
707 clearPendingEvent(event->gbaKey());
708 }
709 }
710 }
711 for (auto axis : oldAxes) {
712 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
713 clearPendingEvent(event->gbaKey());
714 sendGamepadEvent(event);
715 }
716
717 if (!QApplication::focusWidget()) {
718 return;
719 }
720
721 activeButtons.subtract(oldButtons);
722 oldButtons.subtract(m_activeButtons);
723
724 for (int button : activeButtons) {
725 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
726 postPendingEvent(event->gbaKey());
727 sendGamepadEvent(event);
728 if (!event->isAccepted()) {
729 clearPendingEvent(event->gbaKey());
730 }
731 }
732 for (int button : oldButtons) {
733 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
734 clearPendingEvent(event->gbaKey());
735 sendGamepadEvent(event);
736 }
737
738 activeHats.subtract(oldHats);
739 oldHats.subtract(m_activeHats);
740
741 for (auto& hat : activeHats) {
742 GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this);
743 postPendingEvent(event->gbaKey());
744 sendGamepadEvent(event);
745 if (!event->isAccepted()) {
746 clearPendingEvent(event->gbaKey());
747 }
748 }
749 for (auto& hat : oldHats) {
750 GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this);
751 clearPendingEvent(event->gbaKey());
752 sendGamepadEvent(event);
753 }
754}
755
756void InputController::sendGamepadEvent(QEvent* event) {
757 QWidget* focusWidget = nullptr;
758 if (m_focusParent) {
759 focusWidget = m_focusParent->focusWidget();
760 if (!focusWidget) {
761 focusWidget = m_focusParent;
762 }
763 } else {
764 focusWidget = QApplication::focusWidget();
765 }
766 QApplication::sendEvent(focusWidget, event);
767}
768
769void InputController::postPendingEvent(int key) {
770 m_pendingEvents.insert(key);
771}
772
773void InputController::clearPendingEvent(int key) {
774 m_pendingEvents.remove(key);
775}
776
777bool InputController::hasPendingEvent(int key) const {
778 return m_pendingEvents.contains(key);
779}
780
781void InputController::suspendScreensaver() {
782#ifdef BUILD_SDL
783#if SDL_VERSION_ATLEAST(2, 0, 0)
784 mSDLSuspendScreensaver(&s_sdlEvents);
785#endif
786#endif
787}
788
789void InputController::resumeScreensaver() {
790#ifdef BUILD_SDL
791#if SDL_VERSION_ATLEAST(2, 0, 0)
792 mSDLResumeScreensaver(&s_sdlEvents);
793#endif
794#endif
795}
796
797void InputController::setScreensaverSuspendable(bool suspendable) {
798#ifdef BUILD_SDL
799#if SDL_VERSION_ATLEAST(2, 0, 0)
800 mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
801#endif
802#endif
803}
804
805void InputController::stealFocus(QWidget* focus) {
806 m_focusParent = focus;
807}
808
809void InputController::releaseFocus(QWidget* focus) {
810 if (focus == m_focusParent) {
811 m_focusParent = m_topLevel;
812 }
813}
814
815bool InputController::eventFilter(QObject*, QEvent* event) {
816 event->ignore();
817 if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
818 QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
819 int key = keyEvent->key();
820 if (!InputIndex::isModifierKey(key)) {
821 key |= (keyEvent->modifiers() & ~Qt::KeypadModifier);
822 } else {
823 key = InputIndex::toModifierKey(key | (keyEvent->modifiers() & ~Qt::KeypadModifier));
824 }
825
826 if (keyEvent->isAutoRepeat()) {
827 event->accept();
828 return true;
829 }
830
831 event->ignore();
832 InputItem* item = m_inputIndex.itemForShortcut(key);
833 if (item) {
834 item->trigger(event->type() == QEvent::KeyPress);
835 event->accept();
836 }
837 item = m_keyIndex.itemForShortcut(key);
838 if (item) {
839 item->trigger(event->type() == QEvent::KeyPress);
840 event->accept();
841 }
842 }
843
844
845 if (event->type() == GamepadButtonEvent::Down() || event->type() == GamepadButtonEvent::Up()) {
846 GamepadButtonEvent* gbe = static_cast<GamepadButtonEvent*>(event);
847 InputItem* item = m_inputIndex.itemForButton(gbe->value());
848 if (item) {
849 item->trigger(event->type() == GamepadButtonEvent::Down());
850 event->accept();
851 }
852 item = m_keyIndex.itemForButton(gbe->value());
853 if (item) {
854 item->trigger(event->type() == GamepadButtonEvent::Down());
855 event->accept();
856 }
857 }
858 if (event->type() == GamepadAxisEvent::Type()) {
859 GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event);
860 InputItem* item = m_inputIndex.itemForAxis(gae->axis(), gae->direction());
861 if (item) {
862 item->trigger(event->type() == gae->isNew());
863 event->accept();
864 }
865 item = m_keyIndex.itemForAxis(gae->axis(), gae->direction());
866 if (item) {
867 item->trigger(event->type() == gae->isNew());
868 event->accept();
869 }
870 }
871 return event->isAccepted();
872}
873
874InputItem* InputController::itemForKey(const QString& key) {
875 return m_keyIndex.itemAt(QString("key%0").arg(key));
876}
877
878int InputController::keyId(const QString& key) {
879 for (int i = 0; i < m_activeKeyInfo->nKeys; ++i) {
880 if (m_activeKeyInfo->keyId[i] == key) {
881 return i;
882 }
883 }
884 return -1;
885}
886
887void InputController::restoreModel() {
888 if (!m_activeKeyInfo) {
889 return;
890 }
891 int nKeys = m_inputMap.info->nKeys;
892 for (int i = 0; i < nKeys; ++i) {
893 const QString& keyName = m_inputMap.info->keyId[i];
894 InputItem* item = itemForKey(keyName);
895 if (item) {
896 int key = mInputQueryBinding(&m_inputMap, KEYBOARD, i);
897 if (key >= 0) {
898 item->setShortcut(key);
899 } else {
900 item->clearShortcut();
901 }
902#ifdef BUILD_SDL
903 key = mInputQueryBinding(&m_inputMap, SDL_BINDING_BUTTON, i);
904 if (key >= 0) {
905 item->setButton(key);
906 } else {
907 item->clearButton();
908 }
909#endif
910 }
911 }
912#ifdef BUILD_SDL
913 mInputEnumerateAxes(&m_inputMap, SDL_BINDING_BUTTON, [](int axis, const struct mInputAxis* description, void* user) {
914 InputController* controller = static_cast<InputController*>(user);
915 InputItem* item;
916 const mInputPlatformInfo* inputMap = controller->m_inputMap.info;
917 if (description->highDirection >= 0 && description->highDirection < controller->m_inputMap.info->nKeys) {
918 int id = description->lowDirection;
919 if (id >= 0 && id < inputMap->nKeys) {
920 item = controller->itemForKey(inputMap->keyId[id]);
921 if (item) {
922 item->setAxis(axis, GamepadAxisEvent::POSITIVE);
923 }
924 }
925 }
926 if (description->lowDirection >= 0 && description->lowDirection < controller->m_inputMap.info->nKeys) {
927 int id = description->highDirection;
928 if (id >= 0 && id < inputMap->nKeys) {
929 item = controller->itemForKey(inputMap->keyId[id]);
930 if (item) {
931 item->setAxis(axis, GamepadAxisEvent::NEGATIVE);
932 }
933 }
934 }
935 }, this);
936#endif
937 rebuildKeyIndex();
938}
939
940void InputController::rebindKey(const QString& key) {
941 InputItem* item = itemForKey(key);
942 bindKey(KEYBOARD, item->shortcut(), key);
943#ifdef BUILD_SDL
944 bindKey(SDL_BINDING_BUTTON, item->button(), key);
945 bindAxis(SDL_BINDING_BUTTON, item->axis(), item->direction(), key);
946#endif
947}
948
949void InputController::loadCamImage(const QString& path) {
950 QMutexLocker locker(&m_image.mutex);
951 m_image.image.load(path);
952 m_image.resizedImage = QImage();
953 m_image.outOfDate = true;
954}
955
956void InputController::setCamImage(const QImage& image) {
957 QMutexLocker locker(&m_image.mutex);
958 m_image.image = image;
959 m_image.resizedImage = QImage();
960 m_image.outOfDate = true;
961}
962
963void InputController::increaseLuminanceLevel() {
964 setLuminanceLevel(m_luxLevel + 1);
965}
966
967void InputController::decreaseLuminanceLevel() {
968 setLuminanceLevel(m_luxLevel - 1);
969}
970
971void InputController::setLuminanceLevel(int level) {
972 int value = 0x16;
973 level = std::max(0, std::min(10, level));
974 if (level > 0) {
975 value += GBA_LUX_LEVELS[level - 1];
976 }
977 setLuminanceValue(value);
978}
979
980void InputController::setLuminanceValue(uint8_t value) {
981 m_luxValue = value;
982 value = std::max<int>(value - 0x16, 0);
983 m_luxLevel = 10;
984 for (int i = 0; i < 10; ++i) {
985 if (value < GBA_LUX_LEVELS[i]) {
986 m_luxLevel = i;
987 break;
988 }
989 }
990 emit luminanceValueChanged(m_luxValue);
991}
992
993void InputController::setupCam() {
994#ifdef BUILD_QT_MULTIMEDIA
995 if (!m_camera) {
996 m_camera = std::make_unique<QCamera>();
997 }
998 QVideoFrame::PixelFormat format(QVideoFrame::Format_RGB32);
999#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
1000 m_camera->load();
1001 QCameraViewfinderSettings settings;
1002 QSize size(1920, 1080);
1003 auto cameraRes = m_camera->supportedViewfinderResolutions(settings);
1004 for (auto& cameraSize : cameraRes) {
1005 if (cameraSize.width() < m_image.w || cameraSize.height() < m_image.h) {
1006 continue;
1007 }
1008 if (cameraSize.width() <= size.width() && cameraSize.height() <= size.height()) {
1009 size = cameraSize;
1010 }
1011 }
1012 settings.setResolution(size);
1013 auto cameraFormats = m_camera->supportedViewfinderPixelFormats(settings);
1014 auto goodFormats = m_videoDumper.supportedPixelFormats();
1015 bool goodFormatFound = false;
1016 for (auto& goodFormat : goodFormats) {
1017 if (cameraFormats.contains(goodFormat)) {
1018 settings.setPixelFormat(goodFormat);
1019 format = goodFormat;
1020 goodFormatFound = true;
1021 break;
1022 }
1023 }
1024 if (!goodFormatFound) {
1025 LOG(QT, WARN) << "Could not find a valid camera format!";
1026 }
1027 m_camera->setViewfinderSettings(settings);
1028#endif
1029 m_camera->setCaptureMode(QCamera::CaptureVideo);
1030 m_camera->setViewfinder(&m_videoDumper);
1031 m_camera->start();
1032#endif
1033}
1034
1035void InputController::teardownCam() {
1036#ifdef BUILD_QT_MULTIMEDIA
1037 if (m_camera) {
1038 m_camera->stop();
1039 m_camera.reset();
1040 }
1041#endif
1042}