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 <QCameraInfo>
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 QByteArray camera = image->p->m_config->getQtOption("camera").toByteArray();
192 if (!camera.isNull()) {
193 QMetaObject::invokeMethod(image->p, "setCamera", Q_ARG(QByteArray, camera));
194 }
195 QMetaObject::invokeMethod(image->p, "setupCam");
196 }
197#endif
198 };
199
200 m_image.stopRequestImage = [](mImageSource* context) {
201 InputControllerImage* image = static_cast<InputControllerImage*>(context);
202#ifdef BUILD_QT_MULTIMEDIA
203 QMetaObject::invokeMethod(image->p, "teardownCam");
204#endif
205 };
206
207 m_image.requestImage = [](mImageSource* context, const void** buffer, size_t* stride, mColorFormat* format) {
208 InputControllerImage* image = static_cast<InputControllerImage*>(context);
209 QSize size;
210 {
211 QMutexLocker locker(&image->mutex);
212 if (image->outOfDate) {
213 image->resizedImage = image->image.scaled(image->w, image->h, Qt::KeepAspectRatioByExpanding);
214 image->resizedImage = image->resizedImage.convertToFormat(QImage::Format_RGB16);
215 image->outOfDate = false;
216 }
217 }
218 size = image->resizedImage.size();
219 const uint16_t* bits = reinterpret_cast<const uint16_t*>(image->resizedImage.constBits());
220 if (size.width() > image->w) {
221 bits += (size.width() - image->w) / 2;
222 }
223 if (size.height() > image->h) {
224 bits += ((size.height() - image->h) / 2) * size.width();
225 }
226 *buffer = bits;
227 *stride = image->resizedImage.bytesPerLine() / sizeof(*bits);
228 *format = mCOLOR_RGB565;
229 };
230}
231
232InputController::~InputController() {
233 if (m_activeKeyInfo) {
234 mInputMapDeinit(&m_inputMap);
235 }
236
237#ifdef BUILD_SDL
238 if (m_playerAttached) {
239 mSDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
240 }
241
242 --s_sdlInited;
243 if (s_sdlInited == 0) {
244 mSDLDeinitEvents(&s_sdlEvents);
245 }
246#endif
247}
248
249void InputController::rebuildIndex(const InputIndex* index) {
250 m_inputIndex.rebuild(index);
251}
252
253void InputController::rebuildKeyIndex(const InputIndex* index) {
254 m_keyIndex.rebuild(index);
255
256 for (const InputItem* item : m_keyIndex.items()) {
257 if (!item->name().startsWith(QLatin1String("key"))) {
258 rebindKey(item->visibleName());
259 }
260 }
261}
262
263void InputController::setConfiguration(ConfigController* config) {
264 m_config = config;
265 m_inputIndex.setConfigController(config);
266 m_keyIndex.setConfigController(config);
267 loadConfiguration(KEYBOARD);
268 loadProfile(KEYBOARD, profileForType(KEYBOARD));
269#ifdef BUILD_SDL
270 mSDLEventsLoadConfig(&s_sdlEvents, config->input());
271 if (!m_playerAttached) {
272 m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
273 }
274 loadConfiguration(SDL_BINDING_BUTTON);
275 loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
276#endif
277 restoreModel();
278}
279
280void InputController::loadConfiguration(uint32_t type) {
281 if (!m_activeKeyInfo) {
282 return;
283 }
284 mInputMapLoad(&m_inputMap, type, m_config->input());
285#ifdef BUILD_SDL
286 if (m_playerAttached) {
287 mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input());
288 }
289#endif
290}
291
292void InputController::loadProfile(uint32_t type, const QString& profile) {
293 const InputProfile* ip = InputProfile::findProfile(profile);
294 if (ip) {
295 ip->apply(this);
296 }
297 recalibrateAxes();
298 emit profileLoaded(profile);
299}
300
301void InputController::saveConfiguration() {
302 saveConfiguration(KEYBOARD);
303#ifdef BUILD_SDL
304 saveConfiguration(SDL_BINDING_BUTTON);
305 saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
306 if (m_playerAttached) {
307 mSDLPlayerSaveConfig(&m_sdlPlayer, m_config->input());
308 }
309#endif
310 m_inputIndex.saveConfig();
311 m_keyIndex.saveConfig();
312 m_config->write();
313}
314
315void InputController::saveConfiguration(uint32_t type) {
316 if (m_activeKeyInfo) {
317 mInputMapSave(&m_inputMap, type, m_config->input());
318 }
319 m_config->write();
320}
321
322void InputController::saveProfile(uint32_t type, const QString& profile) {
323 if (m_activeKeyInfo) {
324 mInputProfileSave(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
325 }
326 m_config->write();
327}
328
329const char* InputController::profileForType(uint32_t type) {
330 UNUSED(type);
331#ifdef BUILD_SDL
332 if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
333#if SDL_VERSION_ATLEAST(2, 0, 0)
334 return SDL_JoystickName(m_sdlPlayer.joystick->joystick);
335#else
336 return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick));
337#endif
338 }
339#endif
340 return 0;
341}
342
343QStringList InputController::connectedGamepads(uint32_t type) const {
344 UNUSED(type);
345
346#ifdef BUILD_SDL
347 if (type == SDL_BINDING_BUTTON) {
348 QStringList pads;
349 for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
350 const char* name;
351#if SDL_VERSION_ATLEAST(2, 0, 0)
352 name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick);
353#else
354 name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick));
355#endif
356 if (name) {
357 pads.append(QString(name));
358 } else {
359 pads.append(QString());
360 }
361 }
362 return pads;
363 }
364#endif
365
366 return QStringList();
367}
368
369int InputController::gamepad(uint32_t type) const {
370#ifdef BUILD_SDL
371 if (type == SDL_BINDING_BUTTON) {
372 return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0;
373 }
374#endif
375 return 0;
376}
377
378void InputController::setGamepad(uint32_t type, int index) {
379#ifdef BUILD_SDL
380 if (type == SDL_BINDING_BUTTON) {
381 mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index);
382 }
383#endif
384}
385
386void InputController::setPreferredGamepad(uint32_t type, int index) {
387 if (!m_config) {
388 return;
389 }
390#ifdef BUILD_SDL
391#if SDL_VERSION_ATLEAST(2, 0, 0)
392 char name[34] = {0};
393 SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick), name, sizeof(name));
394#else
395 const char* name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick));
396 if (!name) {
397 return;
398 }
399#endif
400 mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, name);
401#else
402 UNUSED(type);
403 UNUSED(index);
404#endif
405}
406
407mRumble* InputController::rumble() {
408#ifdef BUILD_SDL
409#if SDL_VERSION_ATLEAST(2, 0, 0)
410 if (m_playerAttached) {
411 return &m_sdlPlayer.rumble.d;
412 }
413#endif
414#endif
415 return nullptr;
416}
417
418mRotationSource* InputController::rotationSource() {
419#ifdef BUILD_SDL
420 if (m_playerAttached) {
421 return &m_sdlPlayer.rotation.d;
422 }
423#endif
424 return nullptr;
425}
426
427void InputController::registerTiltAxisX(int axis) {
428#ifdef BUILD_SDL
429 if (m_playerAttached) {
430 m_sdlPlayer.rotation.axisX = axis;
431 }
432#endif
433}
434
435void InputController::registerTiltAxisY(int axis) {
436#ifdef BUILD_SDL
437 if (m_playerAttached) {
438 m_sdlPlayer.rotation.axisY = axis;
439 }
440#endif
441}
442
443void InputController::registerGyroAxisX(int axis) {
444#ifdef BUILD_SDL
445 if (m_playerAttached) {
446 m_sdlPlayer.rotation.gyroX = axis;
447 }
448#endif
449}
450
451void InputController::registerGyroAxisY(int axis) {
452#ifdef BUILD_SDL
453 if (m_playerAttached) {
454 m_sdlPlayer.rotation.gyroY = axis;
455 }
456#endif
457}
458
459float InputController::gyroSensitivity() const {
460#ifdef BUILD_SDL
461 if (m_playerAttached) {
462 return m_sdlPlayer.rotation.gyroSensitivity;
463 }
464#endif
465 return 0;
466}
467
468void InputController::setGyroSensitivity(float sensitivity) {
469#ifdef BUILD_SDL
470 if (m_playerAttached) {
471 m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
472 }
473#endif
474}
475
476void InputController::updateJoysticks() {
477#ifdef BUILD_SDL
478 QString profile = profileForType(SDL_BINDING_BUTTON);
479 mSDLUpdateJoysticks(&s_sdlEvents, m_config->input());
480 QString newProfile = profileForType(SDL_BINDING_BUTTON);
481 if (profile != newProfile) {
482 loadProfile(SDL_BINDING_BUTTON, newProfile);
483 }
484#endif
485}
486
487const mInputMap* InputController::map() {
488 if (!m_activeKeyInfo) {
489 return nullptr;
490 }
491 return &m_inputMap;
492}
493
494int InputController::pollEvents() {
495 int activeButtons = m_activeKeys;
496#ifdef BUILD_SDL
497 if (m_playerAttached && m_sdlPlayer.joystick) {
498 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
499 SDL_JoystickUpdate();
500 int numButtons = SDL_JoystickNumButtons(joystick);
501 int i;
502 for (i = 0; i < numButtons; ++i) {
503 GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i));
504 if (key == GBA_KEY_NONE) {
505 continue;
506 }
507 if (hasPendingEvent(key)) {
508 continue;
509 }
510 if (SDL_JoystickGetButton(joystick, i)) {
511 activeButtons |= 1 << key;
512 }
513 }
514 int numHats = SDL_JoystickNumHats(joystick);
515 for (i = 0; i < numHats; ++i) {
516 int hat = SDL_JoystickGetHat(joystick, i);
517 activeButtons |= mInputMapHat(&m_inputMap, SDL_BINDING_BUTTON, i, hat);
518 }
519
520 int numAxes = SDL_JoystickNumAxes(joystick);
521 for (i = 0; i < numAxes; ++i) {
522 int value = SDL_JoystickGetAxis(joystick, i);
523
524 enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value));
525 if (key != GBA_KEY_NONE) {
526 activeButtons |= 1 << key;
527 }
528 }
529 }
530#endif
531 return activeButtons;
532}
533
534QSet<int> InputController::activeGamepadButtons(int type) {
535 QSet<int> activeButtons;
536#ifdef BUILD_SDL
537 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
538 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
539 SDL_JoystickUpdate();
540 int numButtons = SDL_JoystickNumButtons(joystick);
541 int i;
542 for (i = 0; i < numButtons; ++i) {
543 if (SDL_JoystickGetButton(joystick, i)) {
544 activeButtons.insert(i);
545 }
546 }
547 }
548#endif
549 return activeButtons;
550}
551
552void InputController::recalibrateAxes() {
553#ifdef BUILD_SDL
554 if (m_playerAttached && m_sdlPlayer.joystick) {
555 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
556 SDL_JoystickUpdate();
557 int numAxes = SDL_JoystickNumAxes(joystick);
558 if (numAxes < 1) {
559 return;
560 }
561 m_deadzones.resize(numAxes);
562 int i;
563 for (i = 0; i < numAxes; ++i) {
564 m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
565 }
566 }
567#endif
568}
569
570QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
571 QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
572#ifdef BUILD_SDL
573 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
574 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
575 SDL_JoystickUpdate();
576 int numAxes = SDL_JoystickNumAxes(joystick);
577 if (numAxes < 1) {
578 return activeAxes;
579 }
580 m_deadzones.resize(numAxes);
581 int i;
582 for (i = 0; i < numAxes; ++i) {
583 int32_t axis = SDL_JoystickGetAxis(joystick, i);
584 axis -= m_deadzones[i];
585 if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
586 activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
587 }
588 }
589 }
590#endif
591 return activeAxes;
592}
593
594void InputController::bindKey(uint32_t type, int key, const QString& keyName) {
595 InputItem* item = itemForKey(keyName);
596 if (type != KEYBOARD) {
597 item->setButton(key);
598 } else {
599 item->setShortcut(key);
600 }
601 if (m_activeKeyInfo) {
602 int coreKey = keyId(keyName);
603 mInputBindKey(&m_inputMap, type, key, coreKey);
604 }
605}
606
607void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, const QString& key) {
608 InputItem* item = itemForKey(key);
609 item->setAxis(axis, direction);
610
611 if (!m_activeKeyInfo) {
612 return;
613 }
614
615 const mInputAxis* old = mInputQueryAxis(&m_inputMap, type, axis);
616 mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
617 if (old) {
618 description = *old;
619 }
620 int deadzone = 0;
621 if (axis > 0 && m_deadzones.size() > axis) {
622 deadzone = m_deadzones[axis];
623 }
624 switch (direction) {
625 case GamepadAxisEvent::NEGATIVE:
626 description.lowDirection = keyId(key);
627
628 description.deadLow = deadzone - AXIS_THRESHOLD;
629 break;
630 case GamepadAxisEvent::POSITIVE:
631 description.highDirection = keyId(key);
632 description.deadHigh = deadzone + AXIS_THRESHOLD;
633 break;
634 default:
635 return;
636 }
637 mInputBindAxis(&m_inputMap, type, axis, &description);
638}
639
640QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {
641 QSet<QPair<int, GamepadHatEvent::Direction>> activeHats;
642#ifdef BUILD_SDL
643 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
644 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
645 SDL_JoystickUpdate();
646 int numHats = SDL_JoystickNumHats(joystick);
647 if (numHats < 1) {
648 return activeHats;
649 }
650
651 int i;
652 for (i = 0; i < numHats; ++i) {
653 int hat = SDL_JoystickGetHat(joystick, i);
654 if (hat & GamepadHatEvent::UP) {
655 activeHats.insert(qMakePair(i, GamepadHatEvent::UP));
656 }
657 if (hat & GamepadHatEvent::RIGHT) {
658 activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT));
659 }
660 if (hat & GamepadHatEvent::DOWN) {
661 activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN));
662 }
663 if (hat & GamepadHatEvent::LEFT) {
664 activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT));
665 }
666 }
667 }
668#endif
669 return activeHats;
670}
671
672void InputController::bindHat(uint32_t type, int hat, GamepadHatEvent::Direction direction, const QString& key) {
673 if (!m_activeKeyInfo) {
674 return;
675 }
676
677 mInputHatBindings bindings{ -1, -1, -1, -1 };
678 mInputQueryHat(&m_inputMap, type, hat, &bindings);
679 switch (direction) {
680 case GamepadHatEvent::UP:
681 bindings.up = keyId(key);
682 break;
683 case GamepadHatEvent::RIGHT:
684 bindings.right = keyId(key);
685 break;
686 case GamepadHatEvent::DOWN:
687 bindings.down = keyId(key);
688 break;
689 case GamepadHatEvent::LEFT:
690 bindings.left = keyId(key);
691 break;
692 default:
693 return;
694 }
695 mInputBindHat(&m_inputMap, type, hat, &bindings);
696}
697
698void InputController::testGamepad(int type) {
699 auto activeAxes = activeGamepadAxes(type);
700 auto oldAxes = m_activeAxes;
701 m_activeAxes = activeAxes;
702
703 auto activeButtons = activeGamepadButtons(type);
704 auto oldButtons = m_activeButtons;
705 m_activeButtons = activeButtons;
706
707 auto activeHats = activeGamepadHats(type);
708 auto oldHats = m_activeHats;
709 m_activeHats = activeHats;
710
711 if (!QApplication::focusWidget()) {
712 return;
713 }
714
715 activeAxes.subtract(oldAxes);
716 oldAxes.subtract(m_activeAxes);
717
718 for (auto& axis : m_activeAxes) {
719 bool newlyAboveThreshold = activeAxes.contains(axis);
720 if (newlyAboveThreshold) {
721 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
722 postPendingEvent(event->gbaKey());
723 sendGamepadEvent(event);
724 if (!event->isAccepted()) {
725 clearPendingEvent(event->gbaKey());
726 }
727 }
728 }
729 for (auto axis : oldAxes) {
730 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
731 clearPendingEvent(event->gbaKey());
732 sendGamepadEvent(event);
733 }
734
735 if (!QApplication::focusWidget()) {
736 return;
737 }
738
739 activeButtons.subtract(oldButtons);
740 oldButtons.subtract(m_activeButtons);
741
742 for (int button : activeButtons) {
743 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
744 postPendingEvent(event->gbaKey());
745 sendGamepadEvent(event);
746 if (!event->isAccepted()) {
747 clearPendingEvent(event->gbaKey());
748 }
749 }
750 for (int button : oldButtons) {
751 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
752 clearPendingEvent(event->gbaKey());
753 sendGamepadEvent(event);
754 }
755
756 activeHats.subtract(oldHats);
757 oldHats.subtract(m_activeHats);
758
759 for (auto& hat : activeHats) {
760 GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this);
761 postPendingEvent(event->gbaKey());
762 sendGamepadEvent(event);
763 if (!event->isAccepted()) {
764 clearPendingEvent(event->gbaKey());
765 }
766 }
767 for (auto& hat : oldHats) {
768 GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this);
769 clearPendingEvent(event->gbaKey());
770 sendGamepadEvent(event);
771 }
772}
773
774void InputController::sendGamepadEvent(QEvent* event) {
775 QWidget* focusWidget = nullptr;
776 if (m_focusParent) {
777 focusWidget = m_focusParent->focusWidget();
778 if (!focusWidget) {
779 focusWidget = m_focusParent;
780 }
781 } else {
782 focusWidget = QApplication::focusWidget();
783 }
784 QApplication::sendEvent(focusWidget, event);
785}
786
787void InputController::postPendingEvent(int key) {
788 m_pendingEvents.insert(key);
789}
790
791void InputController::clearPendingEvent(int key) {
792 m_pendingEvents.remove(key);
793}
794
795bool InputController::hasPendingEvent(int key) const {
796 return m_pendingEvents.contains(key);
797}
798
799void InputController::suspendScreensaver() {
800#ifdef BUILD_SDL
801#if SDL_VERSION_ATLEAST(2, 0, 0)
802 mSDLSuspendScreensaver(&s_sdlEvents);
803#endif
804#endif
805}
806
807void InputController::resumeScreensaver() {
808#ifdef BUILD_SDL
809#if SDL_VERSION_ATLEAST(2, 0, 0)
810 mSDLResumeScreensaver(&s_sdlEvents);
811#endif
812#endif
813}
814
815void InputController::setScreensaverSuspendable(bool suspendable) {
816#ifdef BUILD_SDL
817#if SDL_VERSION_ATLEAST(2, 0, 0)
818 mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
819#endif
820#endif
821}
822
823void InputController::stealFocus(QWidget* focus) {
824 m_focusParent = focus;
825}
826
827void InputController::releaseFocus(QWidget* focus) {
828 if (focus == m_focusParent) {
829 m_focusParent = m_topLevel;
830 }
831}
832
833bool InputController::eventFilter(QObject*, QEvent* event) {
834 event->ignore();
835 if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
836 QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
837 int key = keyEvent->key();
838 if (!InputIndex::isModifierKey(key)) {
839 key |= (keyEvent->modifiers() & ~Qt::KeypadModifier);
840 } else {
841 key = InputIndex::toModifierKey(key | (keyEvent->modifiers() & ~Qt::KeypadModifier));
842 }
843
844 if (keyEvent->isAutoRepeat()) {
845 event->accept();
846 return true;
847 }
848
849 event->ignore();
850 InputItem* item = m_inputIndex.itemForShortcut(key);
851 if (item) {
852 item->trigger(event->type() == QEvent::KeyPress);
853 event->accept();
854 }
855 item = m_keyIndex.itemForShortcut(key);
856 if (item) {
857 item->trigger(event->type() == QEvent::KeyPress);
858 event->accept();
859 }
860 }
861
862
863 if (event->type() == GamepadButtonEvent::Down() || event->type() == GamepadButtonEvent::Up()) {
864 GamepadButtonEvent* gbe = static_cast<GamepadButtonEvent*>(event);
865 InputItem* item = m_inputIndex.itemForButton(gbe->value());
866 if (item) {
867 item->trigger(event->type() == GamepadButtonEvent::Down());
868 event->accept();
869 }
870 item = m_keyIndex.itemForButton(gbe->value());
871 if (item) {
872 item->trigger(event->type() == GamepadButtonEvent::Down());
873 event->accept();
874 }
875 }
876 if (event->type() == GamepadAxisEvent::Type()) {
877 GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event);
878 InputItem* item = m_inputIndex.itemForAxis(gae->axis(), gae->direction());
879 if (item) {
880 item->trigger(event->type() == gae->isNew());
881 event->accept();
882 }
883 item = m_keyIndex.itemForAxis(gae->axis(), gae->direction());
884 if (item) {
885 item->trigger(event->type() == gae->isNew());
886 event->accept();
887 }
888 }
889 return event->isAccepted();
890}
891
892InputItem* InputController::itemForKey(const QString& key) {
893 return m_keyIndex.itemAt(QString("key%0").arg(key));
894}
895
896int InputController::keyId(const QString& key) {
897 for (int i = 0; i < m_activeKeyInfo->nKeys; ++i) {
898 if (m_activeKeyInfo->keyId[i] == key) {
899 return i;
900 }
901 }
902 return -1;
903}
904
905void InputController::restoreModel() {
906 if (!m_activeKeyInfo) {
907 return;
908 }
909 int nKeys = m_inputMap.info->nKeys;
910 for (int i = 0; i < nKeys; ++i) {
911 const QString& keyName = m_inputMap.info->keyId[i];
912 InputItem* item = itemForKey(keyName);
913 if (item) {
914 int key = mInputQueryBinding(&m_inputMap, KEYBOARD, i);
915 if (key >= 0) {
916 item->setShortcut(key);
917 } else {
918 item->clearShortcut();
919 }
920#ifdef BUILD_SDL
921 key = mInputQueryBinding(&m_inputMap, SDL_BINDING_BUTTON, i);
922 if (key >= 0) {
923 item->setButton(key);
924 } else {
925 item->clearButton();
926 }
927#endif
928 }
929 }
930#ifdef BUILD_SDL
931 mInputEnumerateAxes(&m_inputMap, SDL_BINDING_BUTTON, [](int axis, const struct mInputAxis* description, void* user) {
932 InputController* controller = static_cast<InputController*>(user);
933 InputItem* item;
934 const mInputPlatformInfo* inputMap = controller->m_inputMap.info;
935 if (description->highDirection >= 0 && description->highDirection < controller->m_inputMap.info->nKeys) {
936 int id = description->lowDirection;
937 if (id >= 0 && id < inputMap->nKeys) {
938 item = controller->itemForKey(inputMap->keyId[id]);
939 if (item) {
940 item->setAxis(axis, GamepadAxisEvent::POSITIVE);
941 }
942 }
943 }
944 if (description->lowDirection >= 0 && description->lowDirection < controller->m_inputMap.info->nKeys) {
945 int id = description->highDirection;
946 if (id >= 0 && id < inputMap->nKeys) {
947 item = controller->itemForKey(inputMap->keyId[id]);
948 if (item) {
949 item->setAxis(axis, GamepadAxisEvent::NEGATIVE);
950 }
951 }
952 }
953 }, this);
954#endif
955 rebuildKeyIndex();
956}
957
958void InputController::rebindKey(const QString& key) {
959 InputItem* item = itemForKey(key);
960 bindKey(KEYBOARD, item->shortcut(), key);
961#ifdef BUILD_SDL
962 bindKey(SDL_BINDING_BUTTON, item->button(), key);
963 bindAxis(SDL_BINDING_BUTTON, item->axis(), item->direction(), key);
964#endif
965}
966
967void InputController::loadCamImage(const QString& path) {
968 setCamImage(QImage(path));
969}
970
971void InputController::setCamImage(const QImage& image) {
972 if (image.isNull()) {
973 return;
974 }
975 QMutexLocker locker(&m_image.mutex);
976 m_image.image = image;
977 m_image.resizedImage = QImage();
978 m_image.outOfDate = true;
979}
980
981QList<QPair<QByteArray, QString>> InputController::listCameras() const {
982 QList<QPair<QByteArray, QString>> out;
983#ifdef BUILD_QT_MULTIMEDIA
984 QList<QCameraInfo> cams = QCameraInfo::availableCameras();
985 for (const auto& cam : cams) {
986 out.append(qMakePair(cam.deviceName().toLatin1(), cam.description()));
987 }
988#endif
989 return out;
990}
991
992void InputController::increaseLuminanceLevel() {
993 setLuminanceLevel(m_luxLevel + 1);
994}
995
996void InputController::decreaseLuminanceLevel() {
997 setLuminanceLevel(m_luxLevel - 1);
998}
999
1000void InputController::setLuminanceLevel(int level) {
1001 int value = 0x16;
1002 level = std::max(0, std::min(10, level));
1003 if (level > 0) {
1004 value += GBA_LUX_LEVELS[level - 1];
1005 }
1006 setLuminanceValue(value);
1007}
1008
1009void InputController::setLuminanceValue(uint8_t value) {
1010 m_luxValue = value;
1011 value = std::max<int>(value - 0x16, 0);
1012 m_luxLevel = 10;
1013 for (int i = 0; i < 10; ++i) {
1014 if (value < GBA_LUX_LEVELS[i]) {
1015 m_luxLevel = i;
1016 break;
1017 }
1018 }
1019 emit luminanceValueChanged(m_luxValue);
1020}
1021
1022void InputController::setupCam() {
1023#ifdef BUILD_QT_MULTIMEDIA
1024 if (!m_camera) {
1025 m_camera = std::make_unique<QCamera>();
1026 connect(m_camera.get(), &QCamera::statusChanged, this, &InputController::prepareCamSettings, Qt::QueuedConnection);
1027 }
1028 m_camera->setCaptureMode(QCamera::CaptureVideo);
1029 m_camera->setViewfinder(&m_videoDumper);
1030 m_camera->load();
1031#endif
1032}
1033
1034#ifdef BUILD_QT_MULTIMEDIA
1035void InputController::prepareCamSettings(QCamera::Status status) {
1036 if (status != QCamera::LoadedStatus || m_camera->state() == QCamera::ActiveState) {
1037 return;
1038 }
1039#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
1040 QVideoFrame::PixelFormat format(QVideoFrame::Format_RGB32);
1041 QCameraViewfinderSettings settings;
1042 QSize size(1280, 720);
1043 auto cameraRes = m_camera->supportedViewfinderResolutions(settings);
1044 for (auto& cameraSize : cameraRes) {
1045 if (cameraSize.width() < m_image.w || cameraSize.height() < m_image.h) {
1046 continue;
1047 }
1048 if (cameraSize.width() <= size.width() && cameraSize.height() <= size.height()) {
1049 size = cameraSize;
1050 }
1051 }
1052 settings.setResolution(size);
1053
1054 auto cameraFormats = m_camera->supportedViewfinderPixelFormats(settings);
1055 auto goodFormats = m_videoDumper.supportedPixelFormats();
1056 bool goodFormatFound = false;
1057 for (const auto& goodFormat : goodFormats) {
1058 if (cameraFormats.contains(goodFormat)) {
1059 settings.setPixelFormat(goodFormat);
1060 format = goodFormat;
1061 goodFormatFound = true;
1062 break;
1063 }
1064 }
1065 if (!goodFormatFound) {
1066 LOG(QT, WARN) << "Could not find a valid camera format!";
1067 for (const auto& format : cameraFormats) {
1068 LOG(QT, WARN) << "Camera supported format: " << QString::number(format);
1069 }
1070 }
1071 m_camera->setViewfinderSettings(settings);
1072#endif
1073 m_camera->start();
1074}
1075#endif
1076
1077void InputController::teardownCam() {
1078#ifdef BUILD_QT_MULTIMEDIA
1079 if (m_camera) {
1080 m_camera->stop();
1081 }
1082#endif
1083}
1084
1085void InputController::setCamera(const QByteArray& name) {
1086#ifdef BUILD_QT_MULTIMEDIA
1087 bool needsRestart = false;
1088 if (m_camera) {
1089 needsRestart = m_camera->state() == QCamera::ActiveState;
1090 }
1091 m_camera = std::make_unique<QCamera>(name);
1092 connect(m_camera.get(), &QCamera::statusChanged, this, &InputController::prepareCamSettings, Qt::QueuedConnection);
1093 if (needsRestart) {
1094 setupCam();
1095 }
1096#endif
1097}