all repos — mgba @ ca67e63abb787756cac5b4692ef7a0b072e03d6e

mGBA Game Boy Advance Emulator

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