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 
25 using namespace QGBA;
26 
27 #ifdef BUILD_SDL
28 int InputController::s_sdlInited = 0;
29 mSDLEvents InputController::s_sdlEvents;
30 #endif
31 
InputController(int playerId,QWidget * topLevel,QObject * parent)32 InputController::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 
~InputController()142 InputController::~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 
setConfiguration(ConfigController * config)157 void 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 
loadConfiguration(uint32_t type)170 void 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 
loadProfile(uint32_t type,const QString & profile)179 void 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 
saveConfiguration()191 void 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 
saveConfiguration(uint32_t type)203 void InputController::saveConfiguration(uint32_t type) {
204 	mInputMapSave(&m_inputMap, type, m_config->input());
205 	m_config->write();
206 }
207 
saveProfile(uint32_t type,const QString & profile)208 void 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 
profileForType(uint32_t type)213 const 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 
connectedGamepads(uint32_t type) const227 QStringList 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 
gamepad(uint32_t type) const253 int 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 
setGamepad(uint32_t type,int index)262 void 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 
setPreferredGamepad(uint32_t type,int index)270 void InputController::setPreferredGamepad(uint32_t type, int index) {
271 	if (!m_config) {
272 		return;
273 	}
274 #ifdef BUILD_SDL
275 	char name[34] = {0};
276 #if SDL_VERSION_ATLEAST(2, 0, 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 
rumble()291 mRumble* 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 
rotationSource()302 mRotationSource* InputController::rotationSource() {
303 #ifdef BUILD_SDL
304 	if (m_playerAttached) {
305 		return &m_sdlPlayer.rotation.d;
306 	}
307 #endif
308 	return nullptr;
309 }
310 
registerTiltAxisX(int axis)311 void InputController::registerTiltAxisX(int axis) {
312 #ifdef BUILD_SDL
313 	if (m_playerAttached) {
314 		m_sdlPlayer.rotation.axisX = axis;
315 	}
316 #endif
317 }
318 
registerTiltAxisY(int axis)319 void InputController::registerTiltAxisY(int axis) {
320 #ifdef BUILD_SDL
321 	if (m_playerAttached) {
322 		m_sdlPlayer.rotation.axisY = axis;
323 	}
324 #endif
325 }
326 
registerGyroAxisX(int axis)327 void InputController::registerGyroAxisX(int axis) {
328 #ifdef BUILD_SDL
329 	if (m_playerAttached) {
330 		m_sdlPlayer.rotation.gyroX = axis;
331 	}
332 #endif
333 }
334 
registerGyroAxisY(int axis)335 void InputController::registerGyroAxisY(int axis) {
336 #ifdef BUILD_SDL
337 	if (m_playerAttached) {
338 		m_sdlPlayer.rotation.gyroY = axis;
339 	}
340 #endif
341 }
342 
gyroSensitivity() const343 float 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 
setGyroSensitivity(float sensitivity)352 void InputController::setGyroSensitivity(float sensitivity) {
353 #ifdef BUILD_SDL
354 	if (m_playerAttached) {
355 		m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
356 	}
357 #endif
358 }
359 
mapKeyboard(int key) const360 GBAKey InputController::mapKeyboard(int key) const {
361 	return static_cast<GBAKey>(mInputMapKey(&m_inputMap, KEYBOARD, key));
362 }
363 
bindKey(uint32_t type,int key,GBAKey gbaKey)364 void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
365 	return mInputBindKey(&m_inputMap, type, key, gbaKey);
366 }
367 
updateJoysticks()368 void 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 
pollEvents()379 int 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 
activeGamepadButtons(int type)421 QSet<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 
recalibrateAxes()439 void 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 
activeGamepadAxes(int type)457 QSet<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 
bindAxis(uint32_t type,int axis,GamepadAxisEvent::Direction direction,GBAKey key)481 void 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 
unbindAllAxes(uint32_t type)507 void InputController::unbindAllAxes(uint32_t type) {
508 	mInputUnbindAllAxes(&m_inputMap, type);
509 }
510 
activeGamepadHats(int type)511 QSet<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 
bindHat(uint32_t type,int hat,GamepadHatEvent::Direction direction,GBAKey gbaKey)543 void 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 
testGamepad(int type)565 void 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 
sendGamepadEvent(QEvent * event)642 void 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 
postPendingEvent(GBAKey key)655 void InputController::postPendingEvent(GBAKey key) {
656 	m_pendingEvents.insert(key);
657 }
658 
clearPendingEvent(GBAKey key)659 void InputController::clearPendingEvent(GBAKey key) {
660 	m_pendingEvents.remove(key);
661 }
662 
hasPendingEvent(GBAKey key) const663 bool InputController::hasPendingEvent(GBAKey key) const {
664 	return m_pendingEvents.contains(key);
665 }
666 
suspendScreensaver()667 void InputController::suspendScreensaver() {
668 #ifdef BUILD_SDL
669 #if SDL_VERSION_ATLEAST(2, 0, 0)
670 	mSDLSuspendScreensaver(&s_sdlEvents);
671 #endif
672 #endif
673 }
674 
resumeScreensaver()675 void InputController::resumeScreensaver() {
676 #ifdef BUILD_SDL
677 #if SDL_VERSION_ATLEAST(2, 0, 0)
678 	mSDLResumeScreensaver(&s_sdlEvents);
679 #endif
680 #endif
681 }
682 
setScreensaverSuspendable(bool suspendable)683 void 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 
stealFocus(QWidget * focus)691 void InputController::stealFocus(QWidget* focus) {
692 	m_focusParent = focus;
693 }
694 
releaseFocus(QWidget * focus)695 void InputController::releaseFocus(QWidget* focus) {
696 	if (focus == m_focusParent) {
697 		m_focusParent = m_topLevel;
698 	}
699 }
700 
loadCamImage(const QString & path)701 void InputController::loadCamImage(const QString& path) {
702 	setCamImage(QImage(path));
703 }
704 
setCamImage(const QImage & image)705 void 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 
listCameras() const715 QList<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 
increaseLuminanceLevel()726 void InputController::increaseLuminanceLevel() {
727 	setLuminanceLevel(m_luxLevel + 1);
728 }
729 
decreaseLuminanceLevel()730 void InputController::decreaseLuminanceLevel() {
731 	setLuminanceLevel(m_luxLevel - 1);
732 }
733 
setLuminanceLevel(int level)734 void 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 
setLuminanceValue(uint8_t value)743 void 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 
setupCam()756 void 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
prepareCamSettings(QCamera::Status status)769 void 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 
teardownCam()811 void InputController::teardownCam() {
812 #ifdef BUILD_QT_MULTIMEDIA
813 	if (m_camera) {
814 		m_camera->stop();
815 	}
816 #endif
817 }
818 
setCamera(const QByteArray & name)819 void 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 }
832