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 "GamepadAxisEvent.h"
10#include "GamepadButtonEvent.h"
11#include "InputProfile.h"
12#include "LogController.h"
13#include "utils.h"
14
15#include <QApplication>
16#include <QTimer>
17#include <QWidget>
18#ifdef BUILD_QT_MULTIMEDIA
19#include <QCameraInfo>
20#include <QVideoSurfaceFormat>
21#endif
22
23#include <mgba/core/interface.h>
24#include <mgba-util/configuration.h>
25
26using namespace QGBA;
27
28#ifdef BUILD_SDL
29int InputController::s_sdlInited = 0;
30mSDLEvents InputController::s_sdlEvents;
31#endif
32
33InputController::InputController(int playerId, QWidget* topLevel, QObject* parent)
34 : QObject(parent)
35 , m_playerId(playerId)
36 , m_topLevel(topLevel)
37 , m_focusParent(topLevel)
38{
39 mInputMapInit(&m_inputMap, &GBAInputInfo);
40
41#ifdef BUILD_SDL
42 if (s_sdlInited == 0) {
43 mSDLInitEvents(&s_sdlEvents);
44 }
45 ++s_sdlInited;
46 m_sdlPlayer.bindings = &m_inputMap;
47 mSDLInitBindingsGBA(&m_inputMap);
48 updateJoysticks();
49#endif
50
51#ifdef BUILD_SDL
52 connect(&m_gamepadTimer, &QTimer::timeout, [this]() {
53 testGamepad(SDL_BINDING_BUTTON);
54 if (m_playerId == 0) {
55 updateJoysticks();
56 }
57 });
58#endif
59 m_gamepadTimer.setInterval(50);
60 m_gamepadTimer.start();
61
62#ifdef BUILD_QT_MULTIMEDIA
63 connect(&m_videoDumper, &VideoDumper::imageAvailable, this, &InputController::setCamImage);
64#endif
65
66 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
67 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
68 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
69 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
70 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
71 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
72 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
73 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
74 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
75 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
76
77
78#ifdef M_CORE_GBA
79 m_lux.p = this;
80 m_lux.sample = [](GBALuminanceSource* context) {
81 InputControllerLux* lux = static_cast<InputControllerLux*>(context);
82 lux->value = 0xFF - lux->p->m_luxValue;
83 };
84
85 m_lux.readLuminance = [](GBALuminanceSource* context) {
86 InputControllerLux* lux = static_cast<InputControllerLux*>(context);
87 return lux->value;
88 };
89 setLuminanceLevel(0);
90#endif
91
92 m_image.p = this;
93 m_image.startRequestImage = [](mImageSource* context, unsigned w, unsigned h, int) {
94 InputControllerImage* image = static_cast<InputControllerImage*>(context);
95 image->w = w;
96 image->h = h;
97 if (image->image.isNull()) {
98 image->image.load(":/res/no-cam.png");
99 }
100#ifdef BUILD_QT_MULTIMEDIA
101 if (image->p->m_config->getQtOption("cameraDriver").toInt() == static_cast<int>(CameraDriver::QT_MULTIMEDIA)) {
102 QByteArray camera = image->p->m_config->getQtOption("camera").toByteArray();
103 if (!camera.isNull()) {
104 QMetaObject::invokeMethod(image->p, "setCamera", Q_ARG(QByteArray, camera));
105 }
106 QMetaObject::invokeMethod(image->p, "setupCam");
107 }
108#endif
109 };
110
111 m_image.stopRequestImage = [](mImageSource* context) {
112 InputControllerImage* image = static_cast<InputControllerImage*>(context);
113#ifdef BUILD_QT_MULTIMEDIA
114 QMetaObject::invokeMethod(image->p, "teardownCam");
115#endif
116 };
117
118 m_image.requestImage = [](mImageSource* context, const void** buffer, size_t* stride, mColorFormat* format) {
119 InputControllerImage* image = static_cast<InputControllerImage*>(context);
120 QSize size;
121 {
122 QMutexLocker locker(&image->mutex);
123 if (image->outOfDate) {
124 image->resizedImage = image->image.scaled(image->w, image->h, Qt::KeepAspectRatioByExpanding);
125 image->resizedImage = image->resizedImage.convertToFormat(QImage::Format_RGB16);
126 image->outOfDate = false;
127 }
128 }
129 size = image->resizedImage.size();
130 const uint16_t* bits = reinterpret_cast<const uint16_t*>(image->resizedImage.constBits());
131 if (size.width() > image->w) {
132 bits += (size.width() - image->w) / 2;
133 }
134 if (size.height() > image->h) {
135 bits += ((size.height() - image->h) / 2) * size.width();
136 }
137 *buffer = bits;
138 *stride = image->resizedImage.bytesPerLine() / sizeof(*bits);
139 *format = mCOLOR_RGB565;
140 };
141}
142
143InputController::~InputController() {
144 mInputMapDeinit(&m_inputMap);
145
146#ifdef BUILD_SDL
147 if (m_playerAttached) {
148 mSDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
149 }
150
151 --s_sdlInited;
152 if (s_sdlInited == 0) {
153 mSDLDeinitEvents(&s_sdlEvents);
154 }
155#endif
156}
157
158void InputController::setConfiguration(ConfigController* config) {
159 m_config = config;
160 loadConfiguration(KEYBOARD);
161#ifdef BUILD_SDL
162 mSDLEventsLoadConfig(&s_sdlEvents, config->input());
163 if (!m_playerAttached) {
164 m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
165 }
166 loadConfiguration(SDL_BINDING_BUTTON);
167 loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
168#endif
169}
170
171void InputController::loadConfiguration(uint32_t type) {
172 mInputMapLoad(&m_inputMap, type, m_config->input());
173#ifdef BUILD_SDL
174 if (m_playerAttached) {
175 mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input());
176 }
177#endif
178}
179
180void InputController::loadProfile(uint32_t type, const QString& profile) {
181 bool loaded = mInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
182 recalibrateAxes();
183 if (!loaded) {
184 const InputProfile* ip = InputProfile::findProfile(profile);
185 if (ip) {
186 ip->apply(this);
187 }
188 }
189 emit profileLoaded(profile);
190}
191
192void InputController::saveConfiguration() {
193 saveConfiguration(KEYBOARD);
194#ifdef BUILD_SDL
195 saveConfiguration(SDL_BINDING_BUTTON);
196 saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
197 if (m_playerAttached) {
198 mSDLPlayerSaveConfig(&m_sdlPlayer, m_config->input());
199 }
200#endif
201 m_config->write();
202}
203
204void InputController::saveConfiguration(uint32_t type) {
205 mInputMapSave(&m_inputMap, type, m_config->input());
206 m_config->write();
207}
208
209void InputController::saveProfile(uint32_t type, const QString& profile) {
210 mInputProfileSave(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
211 m_config->write();
212}
213
214const char* InputController::profileForType(uint32_t type) {
215 UNUSED(type);
216#ifdef BUILD_SDL
217 if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
218#if SDL_VERSION_ATLEAST(2, 0, 0)
219 return SDL_JoystickName(m_sdlPlayer.joystick->joystick);
220#else
221 return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick));
222#endif
223 }
224#endif
225 return 0;
226}
227
228QStringList InputController::connectedGamepads(uint32_t type) const {
229 UNUSED(type);
230
231#ifdef BUILD_SDL
232 if (type == SDL_BINDING_BUTTON) {
233 QStringList pads;
234 for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
235 const char* name;
236#if SDL_VERSION_ATLEAST(2, 0, 0)
237 name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick);
238#else
239 name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick));
240#endif
241 if (name) {
242 pads.append(QString(name));
243 } else {
244 pads.append(QString());
245 }
246 }
247 return pads;
248 }
249#endif
250
251 return QStringList();
252}
253
254int InputController::gamepad(uint32_t type) const {
255#ifdef BUILD_SDL
256 if (type == SDL_BINDING_BUTTON) {
257 return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0;
258 }
259#endif
260 return 0;
261}
262
263void InputController::setGamepad(uint32_t type, int index) {
264#ifdef BUILD_SDL
265 if (type == SDL_BINDING_BUTTON) {
266 mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index);
267 }
268#endif
269}
270
271void InputController::setPreferredGamepad(uint32_t type, int index) {
272 if (!m_config) {
273 return;
274 }
275#ifdef BUILD_SDL
276#if SDL_VERSION_ATLEAST(2, 0, 0)
277 char name[34] = {0};
278 SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick), name, sizeof(name));
279#else
280 const char* name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick));
281 if (!name) {
282 return;
283 }
284#endif
285 mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, name);
286#else
287 UNUSED(type);
288 UNUSED(index);
289#endif
290}
291
292mRumble* InputController::rumble() {
293#ifdef BUILD_SDL
294#if SDL_VERSION_ATLEAST(2, 0, 0)
295 if (m_playerAttached) {
296 return &m_sdlPlayer.rumble.d;
297 }
298#endif
299#endif
300 return nullptr;
301}
302
303mRotationSource* InputController::rotationSource() {
304#ifdef BUILD_SDL
305 if (m_playerAttached) {
306 return &m_sdlPlayer.rotation.d;
307 }
308#endif
309 return nullptr;
310}
311
312void InputController::registerTiltAxisX(int axis) {
313#ifdef BUILD_SDL
314 if (m_playerAttached) {
315 m_sdlPlayer.rotation.axisX = axis;
316 }
317#endif
318}
319
320void InputController::registerTiltAxisY(int axis) {
321#ifdef BUILD_SDL
322 if (m_playerAttached) {
323 m_sdlPlayer.rotation.axisY = axis;
324 }
325#endif
326}
327
328void InputController::registerGyroAxisX(int axis) {
329#ifdef BUILD_SDL
330 if (m_playerAttached) {
331 m_sdlPlayer.rotation.gyroX = axis;
332 }
333#endif
334}
335
336void InputController::registerGyroAxisY(int axis) {
337#ifdef BUILD_SDL
338 if (m_playerAttached) {
339 m_sdlPlayer.rotation.gyroY = axis;
340 }
341#endif
342}
343
344float InputController::gyroSensitivity() const {
345#ifdef BUILD_SDL
346 if (m_playerAttached) {
347 return m_sdlPlayer.rotation.gyroSensitivity;
348 }
349#endif
350 return 0;
351}
352
353void InputController::setGyroSensitivity(float sensitivity) {
354#ifdef BUILD_SDL
355 if (m_playerAttached) {
356 m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
357 }
358#endif
359}
360
361GBAKey InputController::mapKeyboard(int key) const {
362 return static_cast<GBAKey>(mInputMapKey(&m_inputMap, KEYBOARD, key));
363}
364
365void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
366 return mInputBindKey(&m_inputMap, type, key, gbaKey);
367}
368
369void InputController::updateJoysticks() {
370#ifdef BUILD_SDL
371 QString profile = profileForType(SDL_BINDING_BUTTON);
372 mSDLUpdateJoysticks(&s_sdlEvents, m_config->input());
373 QString newProfile = profileForType(SDL_BINDING_BUTTON);
374 if (profile != newProfile) {
375 loadProfile(SDL_BINDING_BUTTON, newProfile);
376 }
377#endif
378}
379
380int InputController::pollEvents() {
381 int activeButtons = 0;
382#ifdef BUILD_SDL
383 if (m_playerAttached && m_sdlPlayer.joystick) {
384 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
385 SDL_JoystickUpdate();
386 int numButtons = SDL_JoystickNumButtons(joystick);
387 int i;
388 QReadLocker l(&m_eventsLock);
389 for (i = 0; i < numButtons; ++i) {
390 GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i));
391 if (key == GBA_KEY_NONE) {
392 continue;
393 }
394 if (hasPendingEvent(key)) {
395 continue;
396 }
397 if (SDL_JoystickGetButton(joystick, i)) {
398 activeButtons |= 1 << key;
399 }
400 }
401 l.unlock();
402 int numHats = SDL_JoystickNumHats(joystick);
403 for (i = 0; i < numHats; ++i) {
404 int hat = SDL_JoystickGetHat(joystick, i);
405 activeButtons |= mInputMapHat(&m_inputMap, SDL_BINDING_BUTTON, i, hat);
406 }
407
408 int numAxes = SDL_JoystickNumAxes(joystick);
409 for (i = 0; i < numAxes; ++i) {
410 int value = SDL_JoystickGetAxis(joystick, i);
411
412 enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value));
413 if (key != GBA_KEY_NONE) {
414 activeButtons |= 1 << key;
415 }
416 }
417 }
418#endif
419 return activeButtons;
420}
421
422QSet<int> InputController::activeGamepadButtons(int type) {
423 QSet<int> activeButtons;
424#ifdef BUILD_SDL
425 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
426 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
427 SDL_JoystickUpdate();
428 int numButtons = SDL_JoystickNumButtons(joystick);
429 int i;
430 for (i = 0; i < numButtons; ++i) {
431 if (SDL_JoystickGetButton(joystick, i)) {
432 activeButtons.insert(i);
433 }
434 }
435 }
436#endif
437 return activeButtons;
438}
439
440void InputController::recalibrateAxes() {
441#ifdef BUILD_SDL
442 if (m_playerAttached && m_sdlPlayer.joystick) {
443 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
444 SDL_JoystickUpdate();
445 int numAxes = SDL_JoystickNumAxes(joystick);
446 if (numAxes < 1) {
447 return;
448 }
449 m_deadzones.resize(numAxes);
450 int i;
451 for (i = 0; i < numAxes; ++i) {
452 m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
453 }
454 }
455#endif
456}
457
458QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
459 QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
460#ifdef BUILD_SDL
461 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
462 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
463 SDL_JoystickUpdate();
464 int numAxes = SDL_JoystickNumAxes(joystick);
465 if (numAxes < 1) {
466 return activeAxes;
467 }
468 m_deadzones.resize(numAxes);
469 int i;
470 for (i = 0; i < numAxes; ++i) {
471 int32_t axis = SDL_JoystickGetAxis(joystick, i);
472 axis -= m_deadzones[i];
473 if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
474 activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
475 }
476 }
477 }
478#endif
479 return activeAxes;
480}
481
482void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
483 const mInputAxis* old = mInputQueryAxis(&m_inputMap, type, axis);
484 mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
485 if (old) {
486 description = *old;
487 }
488 int deadzone = 0;
489 if (axis > 0 && m_deadzones.size() > axis) {
490 deadzone = m_deadzones[axis];
491 }
492 switch (direction) {
493 case GamepadAxisEvent::NEGATIVE:
494 description.lowDirection = key;
495
496 description.deadLow = deadzone - AXIS_THRESHOLD;
497 break;
498 case GamepadAxisEvent::POSITIVE:
499 description.highDirection = key;
500 description.deadHigh = deadzone + AXIS_THRESHOLD;
501 break;
502 default:
503 return;
504 }
505 mInputBindAxis(&m_inputMap, type, axis, &description);
506}
507
508void InputController::unbindAllAxes(uint32_t type) {
509 mInputUnbindAllAxes(&m_inputMap, type);
510}
511
512QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {
513 QSet<QPair<int, GamepadHatEvent::Direction>> activeHats;
514#ifdef BUILD_SDL
515 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
516 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
517 SDL_JoystickUpdate();
518 int numHats = SDL_JoystickNumHats(joystick);
519 if (numHats < 1) {
520 return activeHats;
521 }
522
523 int i;
524 for (i = 0; i < numHats; ++i) {
525 int hat = SDL_JoystickGetHat(joystick, i);
526 if (hat & GamepadHatEvent::UP) {
527 activeHats.insert(qMakePair(i, GamepadHatEvent::UP));
528 }
529 if (hat & GamepadHatEvent::RIGHT) {
530 activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT));
531 }
532 if (hat & GamepadHatEvent::DOWN) {
533 activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN));
534 }
535 if (hat & GamepadHatEvent::LEFT) {
536 activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT));
537 }
538 }
539 }
540#endif
541 return activeHats;
542}
543
544void InputController::bindHat(uint32_t type, int hat, GamepadHatEvent::Direction direction, GBAKey gbaKey) {
545 mInputHatBindings bindings{ -1, -1, -1, -1 };
546 mInputQueryHat(&m_inputMap, type, hat, &bindings);
547 switch (direction) {
548 case GamepadHatEvent::UP:
549 bindings.up = gbaKey;
550 break;
551 case GamepadHatEvent::RIGHT:
552 bindings.right = gbaKey;
553 break;
554 case GamepadHatEvent::DOWN:
555 bindings.down = gbaKey;
556 break;
557 case GamepadHatEvent::LEFT:
558 bindings.left = gbaKey;
559 break;
560 default:
561 return;
562 }
563 mInputBindHat(&m_inputMap, type, hat, &bindings);
564}
565
566void InputController::testGamepad(int type) {
567 QWriteLocker l(&m_eventsLock);
568 auto activeAxes = activeGamepadAxes(type);
569 auto oldAxes = m_activeAxes;
570 m_activeAxes = activeAxes;
571
572 auto activeButtons = activeGamepadButtons(type);
573 auto oldButtons = m_activeButtons;
574 m_activeButtons = activeButtons;
575
576 auto activeHats = activeGamepadHats(type);
577 auto oldHats = m_activeHats;
578 m_activeHats = activeHats;
579
580 if (!QApplication::focusWidget()) {
581 return;
582 }
583
584 activeAxes.subtract(oldAxes);
585 oldAxes.subtract(m_activeAxes);
586
587 for (auto& axis : m_activeAxes) {
588 bool newlyAboveThreshold = activeAxes.contains(axis);
589 if (newlyAboveThreshold) {
590 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
591 postPendingEvent(event->gbaKey());
592 sendGamepadEvent(event);
593 if (!event->isAccepted()) {
594 clearPendingEvent(event->gbaKey());
595 }
596 }
597 }
598 for (auto axis : oldAxes) {
599 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
600 clearPendingEvent(event->gbaKey());
601 sendGamepadEvent(event);
602 }
603
604 if (!QApplication::focusWidget()) {
605 return;
606 }
607
608 activeButtons.subtract(oldButtons);
609 oldButtons.subtract(m_activeButtons);
610
611 for (int button : activeButtons) {
612 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
613 postPendingEvent(event->gbaKey());
614 sendGamepadEvent(event);
615 if (!event->isAccepted()) {
616 clearPendingEvent(event->gbaKey());
617 }
618 }
619 for (int button : oldButtons) {
620 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
621 clearPendingEvent(event->gbaKey());
622 sendGamepadEvent(event);
623 }
624
625 activeHats.subtract(oldHats);
626 oldHats.subtract(m_activeHats);
627
628 for (auto& hat : activeHats) {
629 GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this);
630 postPendingEvent(event->gbaKey());
631 sendGamepadEvent(event);
632 if (!event->isAccepted()) {
633 clearPendingEvent(event->gbaKey());
634 }
635 }
636 for (auto& hat : oldHats) {
637 GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this);
638 clearPendingEvent(event->gbaKey());
639 sendGamepadEvent(event);
640 }
641}
642
643void InputController::sendGamepadEvent(QEvent* event) {
644 QWidget* focusWidget = nullptr;
645 if (m_focusParent) {
646 focusWidget = m_focusParent->focusWidget();
647 if (!focusWidget) {
648 focusWidget = m_focusParent;
649 }
650 } else {
651 focusWidget = QApplication::focusWidget();
652 }
653 QApplication::postEvent(focusWidget, event, Qt::HighEventPriority);
654}
655
656void InputController::postPendingEvent(GBAKey key) {
657 m_pendingEvents.insert(key);
658}
659
660void InputController::clearPendingEvent(GBAKey key) {
661 m_pendingEvents.remove(key);
662}
663
664bool InputController::hasPendingEvent(GBAKey key) const {
665 return m_pendingEvents.contains(key);
666}
667
668void InputController::suspendScreensaver() {
669#ifdef BUILD_SDL
670#if SDL_VERSION_ATLEAST(2, 0, 0)
671 mSDLSuspendScreensaver(&s_sdlEvents);
672#endif
673#endif
674}
675
676void InputController::resumeScreensaver() {
677#ifdef BUILD_SDL
678#if SDL_VERSION_ATLEAST(2, 0, 0)
679 mSDLResumeScreensaver(&s_sdlEvents);
680#endif
681#endif
682}
683
684void InputController::setScreensaverSuspendable(bool suspendable) {
685#ifdef BUILD_SDL
686#if SDL_VERSION_ATLEAST(2, 0, 0)
687 mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
688#endif
689#endif
690}
691
692void InputController::stealFocus(QWidget* focus) {
693 m_focusParent = focus;
694}
695
696void InputController::releaseFocus(QWidget* focus) {
697 if (focus == m_focusParent) {
698 m_focusParent = m_topLevel;
699 }
700}
701
702void InputController::loadCamImage(const QString& path) {
703 setCamImage(QImage(path));
704}
705
706void InputController::setCamImage(const QImage& image) {
707 if (image.isNull()) {
708 return;
709 }
710 QMutexLocker locker(&m_image.mutex);
711 m_image.image = image;
712 m_image.resizedImage = QImage();
713 m_image.outOfDate = true;
714}
715
716QList<QPair<QByteArray, QString>> InputController::listCameras() const {
717 QList<QPair<QByteArray, QString>> out;
718#ifdef BUILD_QT_MULTIMEDIA
719 QList<QCameraInfo> cams = QCameraInfo::availableCameras();
720 for (const auto& cam : cams) {
721 out.append(qMakePair(cam.deviceName().toLatin1(), cam.description()));
722 }
723#endif
724 return out;
725}
726
727void InputController::increaseLuminanceLevel() {
728 setLuminanceLevel(m_luxLevel + 1);
729}
730
731void InputController::decreaseLuminanceLevel() {
732 setLuminanceLevel(m_luxLevel - 1);
733}
734
735void InputController::setLuminanceLevel(int level) {
736 int value = 0x16;
737 level = clamp(level, 0, 10);
738 if (level > 0) {
739 value += GBA_LUX_LEVELS[level - 1];
740 }
741 setLuminanceValue(value);
742}
743
744void InputController::setLuminanceValue(uint8_t value) {
745 m_luxValue = value;
746 value = std::max<int>(value - 0x16, 0);
747 m_luxLevel = 10;
748 for (int i = 0; i < 10; ++i) {
749 if (value < GBA_LUX_LEVELS[i]) {
750 m_luxLevel = i;
751 break;
752 }
753 }
754 emit luminanceValueChanged(m_luxValue);
755}
756
757void InputController::setupCam() {
758#ifdef BUILD_QT_MULTIMEDIA
759 if (!m_camera) {
760 m_camera = std::make_unique<QCamera>();
761 connect(m_camera.get(), &QCamera::statusChanged, this, &InputController::prepareCamSettings, Qt::QueuedConnection);
762 }
763 m_camera->setCaptureMode(QCamera::CaptureVideo);
764 m_camera->setViewfinder(&m_videoDumper);
765 m_camera->load();
766#endif
767}
768
769#ifdef BUILD_QT_MULTIMEDIA
770void InputController::prepareCamSettings(QCamera::Status status) {
771 if (status != QCamera::LoadedStatus || m_camera->state() == QCamera::ActiveState) {
772 return;
773 }
774#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
775 QCameraViewfinderSettings settings;
776 QSize size(1280, 720);
777 auto cameraRes = m_camera->supportedViewfinderResolutions(settings);
778 for (auto& cameraSize : cameraRes) {
779 if (cameraSize.width() < m_image.w || cameraSize.height() < m_image.h) {
780 continue;
781 }
782 if (cameraSize.width() <= size.width() && cameraSize.height() <= size.height()) {
783 size = cameraSize;
784 }
785 }
786 settings.setResolution(size);
787
788 auto cameraFormats = m_camera->supportedViewfinderPixelFormats(settings);
789 auto goodFormats = m_videoDumper.supportedPixelFormats();
790 bool goodFormatFound = false;
791 for (const auto& goodFormat : goodFormats) {
792 if (cameraFormats.contains(goodFormat)) {
793 settings.setPixelFormat(goodFormat);
794 goodFormatFound = true;
795 break;
796 }
797 }
798 if (!goodFormatFound) {
799 LOG(QT, WARN) << "Could not find a valid camera format!";
800 for (const auto& format : cameraFormats) {
801 LOG(QT, WARN) << "Camera supported format: " << QString::number(format);
802 }
803 }
804 m_camera->setViewfinderSettings(settings);
805#endif
806 m_camera->start();
807}
808#endif
809
810void InputController::teardownCam() {
811#ifdef BUILD_QT_MULTIMEDIA
812 if (m_camera) {
813 m_camera->stop();
814 }
815#endif
816}
817
818void InputController::setCamera(const QByteArray& name) {
819#ifdef BUILD_QT_MULTIMEDIA
820 bool needsRestart = false;
821 if (m_camera) {
822 needsRestart = m_camera->state() == QCamera::ActiveState;
823 }
824 m_camera = std::make_unique<QCamera>(name);
825 connect(m_camera.get(), &QCamera::statusChanged, this, &InputController::prepareCamSettings, Qt::QueuedConnection);
826 if (needsRestart) {
827 setupCam();
828 }
829#endif
830}