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