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