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 
26 using namespace QGBA;
27 
28 #ifdef BUILD_SDL
29 int InputController::s_sdlInited = 0;
30 mSDLEvents InputController::s_sdlEvents;
31 #endif
32 
InputController(int playerId,QWidget * topLevel,QObject * parent)33 InputController::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 		image->p->m_cameraActive = true;
102 		QByteArray camera = image->p->m_config->getQtOption("camera").toByteArray();
103 		if (!camera.isNull()) {
104 			image->p->m_cameraDevice = camera;
105 		}
106 		QMetaObject::invokeMethod(image->p, "setupCam");
107 #endif
108 	};
109 
110 	m_image.stopRequestImage = [](mImageSource* context) {
111 		InputControllerImage* image = static_cast<InputControllerImage*>(context);
112 #ifdef BUILD_QT_MULTIMEDIA
113 		image->p->m_cameraActive = false;
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 
~InputController()143 InputController::~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 
setConfiguration(ConfigController * config)158 void 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 
loadConfiguration(uint32_t type)171 void 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 
loadProfile(uint32_t type,const QString & profile)180 void InputController::loadProfile(uint32_t type, const QString& profile) {
181 	if (profile.isEmpty()) {
182 		return;
183 	}
184 	bool loaded = mInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
185 	recalibrateAxes();
186 	if (!loaded) {
187 		const InputProfile* ip = InputProfile::findProfile(profile);
188 		if (ip) {
189 			ip->apply(this);
190 		}
191 	}
192 	emit profileLoaded(profile);
193 }
194 
saveConfiguration()195 void InputController::saveConfiguration() {
196 	saveConfiguration(KEYBOARD);
197 #ifdef BUILD_SDL
198 	saveConfiguration(SDL_BINDING_BUTTON);
199 	saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
200 	if (m_playerAttached) {
201 		mSDLPlayerSaveConfig(&m_sdlPlayer, m_config->input());
202 	}
203 #endif
204 	m_config->write();
205 }
206 
saveConfiguration(uint32_t type)207 void InputController::saveConfiguration(uint32_t type) {
208 	mInputMapSave(&m_inputMap, type, m_config->input());
209 	m_config->write();
210 }
211 
saveProfile(uint32_t type,const QString & profile)212 void InputController::saveProfile(uint32_t type, const QString& profile) {
213 	if (profile.isEmpty()) {
214 		return;
215 	}
216 	mInputProfileSave(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
217 	m_config->write();
218 }
219 
profileForType(uint32_t type)220 const char* InputController::profileForType(uint32_t type) {
221 	UNUSED(type);
222 #ifdef BUILD_SDL
223 	if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
224 #if SDL_VERSION_ATLEAST(2, 0, 0)
225 		return SDL_JoystickName(m_sdlPlayer.joystick->joystick);
226 #else
227 		return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick));
228 #endif
229 	}
230 #endif
231 	return 0;
232 }
233 
connectedGamepads(uint32_t type) const234 QStringList InputController::connectedGamepads(uint32_t type) const {
235 	UNUSED(type);
236 
237 #ifdef BUILD_SDL
238 	if (type == SDL_BINDING_BUTTON) {
239 		QStringList pads;
240 		for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
241 			const char* name;
242 #if SDL_VERSION_ATLEAST(2, 0, 0)
243 			name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick);
244 #else
245 			name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick));
246 #endif
247 			if (name) {
248 				pads.append(QString(name));
249 			} else {
250 				pads.append(QString());
251 			}
252 		}
253 		return pads;
254 	}
255 #endif
256 
257 	return QStringList();
258 }
259 
gamepad(uint32_t type) const260 int InputController::gamepad(uint32_t type) const {
261 #ifdef BUILD_SDL
262 	if (type == SDL_BINDING_BUTTON) {
263 		return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0;
264 	}
265 #endif
266 	return 0;
267 }
268 
setGamepad(uint32_t type,int index)269 void InputController::setGamepad(uint32_t type, int index) {
270 #ifdef BUILD_SDL
271 	if (type == SDL_BINDING_BUTTON) {
272 		mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index);
273 	}
274 #endif
275 }
276 
setPreferredGamepad(uint32_t type,int index)277 void InputController::setPreferredGamepad(uint32_t type, int index) {
278 	if (!m_config) {
279 		return;
280 	}
281 #ifdef BUILD_SDL
282 #if SDL_VERSION_ATLEAST(2, 0, 0)
283 	char name[34] = {0};
284 	SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick), name, sizeof(name));
285 #else
286 	const char* name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick));
287 	if (!name) {
288 		return;
289 	}
290 #endif
291 	mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, name);
292 #else
293 	UNUSED(type);
294 	UNUSED(index);
295 #endif
296 }
297 
rumble()298 mRumble* InputController::rumble() {
299 #ifdef BUILD_SDL
300 #if SDL_VERSION_ATLEAST(2, 0, 0)
301 	if (m_playerAttached) {
302 		return &m_sdlPlayer.rumble.d;
303 	}
304 #endif
305 #endif
306 	return nullptr;
307 }
308 
rotationSource()309 mRotationSource* InputController::rotationSource() {
310 #ifdef BUILD_SDL
311 	if (m_playerAttached) {
312 		return &m_sdlPlayer.rotation.d;
313 	}
314 #endif
315 	return nullptr;
316 }
317 
registerTiltAxisX(int axis)318 void InputController::registerTiltAxisX(int axis) {
319 #ifdef BUILD_SDL
320 	if (m_playerAttached) {
321 		m_sdlPlayer.rotation.axisX = axis;
322 	}
323 #endif
324 }
325 
registerTiltAxisY(int axis)326 void InputController::registerTiltAxisY(int axis) {
327 #ifdef BUILD_SDL
328 	if (m_playerAttached) {
329 		m_sdlPlayer.rotation.axisY = axis;
330 	}
331 #endif
332 }
333 
registerGyroAxisX(int axis)334 void InputController::registerGyroAxisX(int axis) {
335 #ifdef BUILD_SDL
336 	if (m_playerAttached) {
337 		m_sdlPlayer.rotation.gyroX = axis;
338 	}
339 #endif
340 }
341 
registerGyroAxisY(int axis)342 void InputController::registerGyroAxisY(int axis) {
343 #ifdef BUILD_SDL
344 	if (m_playerAttached) {
345 		m_sdlPlayer.rotation.gyroY = axis;
346 	}
347 #endif
348 }
349 
gyroSensitivity() const350 float InputController::gyroSensitivity() const {
351 #ifdef BUILD_SDL
352 	if (m_playerAttached) {
353 		return m_sdlPlayer.rotation.gyroSensitivity;
354 	}
355 #endif
356 	return 0;
357 }
358 
setGyroSensitivity(float sensitivity)359 void InputController::setGyroSensitivity(float sensitivity) {
360 #ifdef BUILD_SDL
361 	if (m_playerAttached) {
362 		m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
363 	}
364 #endif
365 }
366 
mapKeyboard(int key) const367 GBAKey InputController::mapKeyboard(int key) const {
368 	return static_cast<GBAKey>(mInputMapKey(&m_inputMap, KEYBOARD, key));
369 }
370 
bindKey(uint32_t type,int key,GBAKey gbaKey)371 void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
372 	return mInputBindKey(&m_inputMap, type, key, gbaKey);
373 }
374 
updateJoysticks()375 void InputController::updateJoysticks() {
376 #ifdef BUILD_SDL
377 	QString profile = profileForType(SDL_BINDING_BUTTON);
378 	mSDLUpdateJoysticks(&s_sdlEvents, m_config->input());
379 	QString newProfile = profileForType(SDL_BINDING_BUTTON);
380 	if (profile != newProfile) {
381 		loadProfile(SDL_BINDING_BUTTON, newProfile);
382 	}
383 #endif
384 }
385 
pollEvents()386 int InputController::pollEvents() {
387 	int activeButtons = 0;
388 #ifdef BUILD_SDL
389 	if (m_playerAttached && m_sdlPlayer.joystick) {
390 		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
391 		SDL_JoystickUpdate();
392 		int numButtons = SDL_JoystickNumButtons(joystick);
393 		int i;
394 		QReadLocker l(&m_eventsLock);
395 		for (i = 0; i < numButtons; ++i) {
396 			GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i));
397 			if (key == GBA_KEY_NONE) {
398 				continue;
399 			}
400 			if (hasPendingEvent(key)) {
401 				continue;
402 			}
403 			if (SDL_JoystickGetButton(joystick, i)) {
404 				activeButtons |= 1 << key;
405 			}
406 		}
407 		l.unlock();
408 		int numHats = SDL_JoystickNumHats(joystick);
409 		for (i = 0; i < numHats; ++i) {
410 			int hat = SDL_JoystickGetHat(joystick, i);
411 			activeButtons |= mInputMapHat(&m_inputMap, SDL_BINDING_BUTTON, i, hat);
412 		}
413 
414 		int numAxes = SDL_JoystickNumAxes(joystick);
415 		for (i = 0; i < numAxes; ++i) {
416 			int value = SDL_JoystickGetAxis(joystick, i);
417 
418 			enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value));
419 			if (key != GBA_KEY_NONE) {
420 				activeButtons |= 1 << key;
421 			}
422 		}
423 	}
424 #endif
425 	return activeButtons;
426 }
427 
activeGamepadButtons(int type)428 QSet<int> InputController::activeGamepadButtons(int type) {
429 	QSet<int> activeButtons;
430 #ifdef BUILD_SDL
431 	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
432 		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
433 		SDL_JoystickUpdate();
434 		int numButtons = SDL_JoystickNumButtons(joystick);
435 		int i;
436 		for (i = 0; i < numButtons; ++i) {
437 			if (SDL_JoystickGetButton(joystick, i)) {
438 				activeButtons.insert(i);
439 			}
440 		}
441 	}
442 #endif
443 	return activeButtons;
444 }
445 
recalibrateAxes()446 void InputController::recalibrateAxes() {
447 #ifdef BUILD_SDL
448 	if (m_playerAttached && m_sdlPlayer.joystick) {
449 		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
450 		SDL_JoystickUpdate();
451 		int numAxes = SDL_JoystickNumAxes(joystick);
452 		if (numAxes < 1) {
453 			return;
454 		}
455 		m_deadzones.resize(numAxes);
456 		int i;
457 		for (i = 0; i < numAxes; ++i) {
458 			m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
459 		}
460 	}
461 #endif
462 }
463 
activeGamepadAxes(int type)464 QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
465 	QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
466 #ifdef BUILD_SDL
467 	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
468 		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
469 		SDL_JoystickUpdate();
470 		int numAxes = SDL_JoystickNumAxes(joystick);
471 		if (numAxes < 1) {
472 			return activeAxes;
473 		}
474 		m_deadzones.resize(numAxes);
475 		int i;
476 		for (i = 0; i < numAxes; ++i) {
477 			int32_t axis = SDL_JoystickGetAxis(joystick, i);
478 			axis -= m_deadzones[i];
479 			if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
480 				activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
481 			}
482 		}
483 	}
484 #endif
485 	return activeAxes;
486 }
487 
bindAxis(uint32_t type,int axis,GamepadAxisEvent::Direction direction,GBAKey key)488 void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
489 	const mInputAxis* old = mInputQueryAxis(&m_inputMap, type, axis);
490 	mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
491 	if (old) {
492 		description = *old;
493 	}
494 	int deadzone = 0;
495 	if (axis > 0 && m_deadzones.size() > axis) {
496 		deadzone = m_deadzones[axis];
497 	}
498 	switch (direction) {
499 	case GamepadAxisEvent::NEGATIVE:
500 		description.lowDirection = key;
501 
502 		description.deadLow = deadzone - AXIS_THRESHOLD;
503 		break;
504 	case GamepadAxisEvent::POSITIVE:
505 		description.highDirection = key;
506 		description.deadHigh = deadzone + AXIS_THRESHOLD;
507 		break;
508 	default:
509 		return;
510 	}
511 	mInputBindAxis(&m_inputMap, type, axis, &description);
512 }
513 
unbindAllAxes(uint32_t type)514 void InputController::unbindAllAxes(uint32_t type) {
515 	mInputUnbindAllAxes(&m_inputMap, type);
516 }
517 
activeGamepadHats(int type)518 QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {
519 	QSet<QPair<int, GamepadHatEvent::Direction>> activeHats;
520 #ifdef BUILD_SDL
521 	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
522 		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
523 		SDL_JoystickUpdate();
524 		int numHats = SDL_JoystickNumHats(joystick);
525 		if (numHats < 1) {
526 			return activeHats;
527 		}
528 
529 		int i;
530 		for (i = 0; i < numHats; ++i) {
531 			int hat = SDL_JoystickGetHat(joystick, i);
532 			if (hat & GamepadHatEvent::UP) {
533 				activeHats.insert(qMakePair(i, GamepadHatEvent::UP));
534 			}
535 			if (hat & GamepadHatEvent::RIGHT) {
536 				activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT));
537 			}
538 			if (hat & GamepadHatEvent::DOWN) {
539 				activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN));
540 			}
541 			if (hat & GamepadHatEvent::LEFT) {
542 				activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT));
543 			}
544 		}
545 	}
546 #endif
547 	return activeHats;
548 }
549 
bindHat(uint32_t type,int hat,GamepadHatEvent::Direction direction,GBAKey gbaKey)550 void InputController::bindHat(uint32_t type, int hat, GamepadHatEvent::Direction direction, GBAKey gbaKey) {
551 	mInputHatBindings bindings{ -1, -1, -1, -1 };
552 	mInputQueryHat(&m_inputMap, type, hat, &bindings);
553 	switch (direction) {
554 	case GamepadHatEvent::UP:
555 		bindings.up = gbaKey;
556 		break;
557 	case GamepadHatEvent::RIGHT:
558 		bindings.right = gbaKey;
559 		break;
560 	case GamepadHatEvent::DOWN:
561 		bindings.down = gbaKey;
562 		break;
563 	case GamepadHatEvent::LEFT:
564 		bindings.left = gbaKey;
565 		break;
566 	default:
567 		return;
568 	}
569 	mInputBindHat(&m_inputMap, type, hat, &bindings);
570 }
571 
unbindAllHats(uint32_t type)572 void InputController::unbindAllHats(uint32_t type) {
573 	mInputUnbindAllHats(&m_inputMap, type);
574 }
575 
testGamepad(int type)576 void InputController::testGamepad(int type) {
577 	QWriteLocker l(&m_eventsLock);
578 	auto activeAxes = activeGamepadAxes(type);
579 	auto oldAxes = m_activeAxes;
580 	m_activeAxes = activeAxes;
581 
582 	auto activeButtons = activeGamepadButtons(type);
583 	auto oldButtons = m_activeButtons;
584 	m_activeButtons = activeButtons;
585 
586 	auto activeHats = activeGamepadHats(type);
587 	auto oldHats = m_activeHats;
588 	m_activeHats = activeHats;
589 
590 	if (!QApplication::focusWidget()) {
591 		return;
592 	}
593 
594 	activeAxes.subtract(oldAxes);
595 	oldAxes.subtract(m_activeAxes);
596 
597 	for (auto& axis : m_activeAxes) {
598 		bool newlyAboveThreshold = activeAxes.contains(axis);
599 		if (newlyAboveThreshold) {
600 			GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
601 			postPendingEvent(event->gbaKey());
602 			sendGamepadEvent(event);
603 			if (!event->isAccepted()) {
604 				clearPendingEvent(event->gbaKey());
605 			}
606 		}
607 	}
608 	for (auto axis : oldAxes) {
609 		GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
610 		clearPendingEvent(event->gbaKey());
611 		sendGamepadEvent(event);
612 	}
613 
614 	if (!QApplication::focusWidget()) {
615 		return;
616 	}
617 
618 	activeButtons.subtract(oldButtons);
619 	oldButtons.subtract(m_activeButtons);
620 
621 	for (int button : activeButtons) {
622 		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
623 		postPendingEvent(event->gbaKey());
624 		sendGamepadEvent(event);
625 		if (!event->isAccepted()) {
626 			clearPendingEvent(event->gbaKey());
627 		}
628 	}
629 	for (int button : oldButtons) {
630 		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
631 		clearPendingEvent(event->gbaKey());
632 		sendGamepadEvent(event);
633 	}
634 
635 	activeHats.subtract(oldHats);
636 	oldHats.subtract(m_activeHats);
637 
638 	for (auto& hat : activeHats) {
639 		GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this);
640 		postPendingEvent(event->gbaKey());
641 		sendGamepadEvent(event);
642 		if (!event->isAccepted()) {
643 			clearPendingEvent(event->gbaKey());
644 		}
645 	}
646 	for (auto& hat : oldHats) {
647 		GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this);
648 		clearPendingEvent(event->gbaKey());
649 		sendGamepadEvent(event);
650 	}
651 }
652 
sendGamepadEvent(QEvent * event)653 void InputController::sendGamepadEvent(QEvent* event) {
654 	QWidget* focusWidget = nullptr;
655 	if (m_focusParent) {
656 		focusWidget = m_focusParent->focusWidget();
657 		if (!focusWidget) {
658 			focusWidget = m_focusParent;
659 		}
660 	} else {
661 		focusWidget = QApplication::focusWidget();
662 	}
663 	QApplication::postEvent(focusWidget, event, Qt::HighEventPriority);
664 }
665 
postPendingEvent(GBAKey key)666 void InputController::postPendingEvent(GBAKey key) {
667 	m_pendingEvents.insert(key);
668 }
669 
clearPendingEvent(GBAKey key)670 void InputController::clearPendingEvent(GBAKey key) {
671 	m_pendingEvents.remove(key);
672 }
673 
hasPendingEvent(GBAKey key) const674 bool InputController::hasPendingEvent(GBAKey key) const {
675 	return m_pendingEvents.contains(key);
676 }
677 
suspendScreensaver()678 void InputController::suspendScreensaver() {
679 #ifdef BUILD_SDL
680 #if SDL_VERSION_ATLEAST(2, 0, 0)
681 	mSDLSuspendScreensaver(&s_sdlEvents);
682 #endif
683 #endif
684 }
685 
resumeScreensaver()686 void InputController::resumeScreensaver() {
687 #ifdef BUILD_SDL
688 #if SDL_VERSION_ATLEAST(2, 0, 0)
689 	mSDLResumeScreensaver(&s_sdlEvents);
690 #endif
691 #endif
692 }
693 
setScreensaverSuspendable(bool suspendable)694 void InputController::setScreensaverSuspendable(bool suspendable) {
695 #ifdef BUILD_SDL
696 #if SDL_VERSION_ATLEAST(2, 0, 0)
697 	mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
698 #endif
699 #endif
700 }
701 
stealFocus(QWidget * focus)702 void InputController::stealFocus(QWidget* focus) {
703 	m_focusParent = focus;
704 }
705 
releaseFocus(QWidget * focus)706 void InputController::releaseFocus(QWidget* focus) {
707 	if (focus == m_focusParent) {
708 		m_focusParent = m_topLevel;
709 	}
710 }
711 
loadCamImage(const QString & path)712 void InputController::loadCamImage(const QString& path) {
713 	setCamImage(QImage(path));
714 }
715 
setCamImage(const QImage & image)716 void InputController::setCamImage(const QImage& image) {
717 	if (image.isNull()) {
718 		return;
719 	}
720 	QMutexLocker locker(&m_image.mutex);
721 	m_image.image = image;
722 	m_image.resizedImage = QImage();
723 	m_image.outOfDate = true;
724 }
725 
listCameras() const726 QList<QPair<QByteArray, QString>> InputController::listCameras() const {
727 	QList<QPair<QByteArray, QString>> out;
728 #ifdef BUILD_QT_MULTIMEDIA
729 	QList<QCameraInfo> cams = QCameraInfo::availableCameras();
730 	for (const auto& cam : cams) {
731 		out.append(qMakePair(cam.deviceName().toLatin1(), cam.description()));
732 	}
733 #endif
734 	return out;
735 }
736 
increaseLuminanceLevel()737 void InputController::increaseLuminanceLevel() {
738 	setLuminanceLevel(m_luxLevel + 1);
739 }
740 
decreaseLuminanceLevel()741 void InputController::decreaseLuminanceLevel() {
742 	setLuminanceLevel(m_luxLevel - 1);
743 }
744 
setLuminanceLevel(int level)745 void InputController::setLuminanceLevel(int level) {
746 	int value = 0x16;
747 	level = clamp(level, 0, 10);
748 	if (level > 0) {
749 		value += GBA_LUX_LEVELS[level - 1];
750 	}
751 	setLuminanceValue(value);
752 }
753 
setLuminanceValue(uint8_t value)754 void InputController::setLuminanceValue(uint8_t value) {
755 	m_luxValue = value;
756 	value = std::max<int>(value - 0x16, 0);
757 	m_luxLevel = 10;
758 	for (int i = 0; i < 10; ++i) {
759 		if (value < GBA_LUX_LEVELS[i]) {
760 			m_luxLevel = i;
761 			break;
762 		}
763 	}
764 	emit luminanceValueChanged(m_luxValue);
765 }
766 
setupCam()767 void InputController::setupCam() {
768 #ifdef BUILD_QT_MULTIMEDIA
769 	if (m_config->getQtOption("cameraDriver").toInt() != static_cast<int>(CameraDriver::QT_MULTIMEDIA)) {
770 		return;
771 	}
772 
773 	if (!m_camera) {
774 		m_camera = std::make_unique<QCamera>(m_cameraDevice);
775 		connect(m_camera.get(), &QCamera::statusChanged, this, &InputController::prepareCamSettings, Qt::QueuedConnection);
776 	}
777 	if (m_camera->status() == QCamera::UnavailableStatus) {
778 		m_camera.reset();
779 		return;
780 	}
781 	m_camera->setCaptureMode(QCamera::CaptureVideo);
782 	m_camera->setViewfinder(&m_videoDumper);
783 	m_camera->load();
784 #endif
785 }
786 
787 #ifdef BUILD_QT_MULTIMEDIA
prepareCamSettings(QCamera::Status status)788 void InputController::prepareCamSettings(QCamera::Status status) {
789 	if (status != QCamera::LoadedStatus || m_camera->state() == QCamera::ActiveState) {
790 		return;
791 	}
792 #if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
793 	QCameraViewfinderSettings settings;
794 	QSize size(1280, 720);
795 	auto cameraRes = m_camera->supportedViewfinderResolutions(settings);
796 	for (auto& cameraSize : cameraRes) {
797 		if (cameraSize.width() < m_image.w || cameraSize.height() < m_image.h) {
798 			continue;
799 		}
800 		if (cameraSize.width() <= size.width() && cameraSize.height() <= size.height()) {
801 			size = cameraSize;
802 		}
803 	}
804 	settings.setResolution(size);
805 
806 	auto cameraFormats = m_camera->supportedViewfinderPixelFormats(settings);
807 	auto goodFormats = m_videoDumper.supportedPixelFormats();
808 	bool goodFormatFound = false;
809 	for (const auto& goodFormat : goodFormats) {
810 		if (cameraFormats.contains(goodFormat)) {
811 			settings.setPixelFormat(goodFormat);
812 			goodFormatFound = true;
813 			break;
814 		}
815 	}
816 	if (!goodFormatFound) {
817 		LOG(QT, WARN) << "Could not find a valid camera format!";
818 		for (const auto& format : cameraFormats) {
819 			LOG(QT, WARN) << "Camera supported format: " << QString::number(format);
820 		}
821 	}
822 	m_camera->setViewfinderSettings(settings);
823 #endif
824 	m_camera->start();
825 }
826 #endif
827 
teardownCam()828 void InputController::teardownCam() {
829 #ifdef BUILD_QT_MULTIMEDIA
830 	if (m_camera) {
831 		m_camera->unload();
832 		m_camera.reset();
833 	}
834 #endif
835 }
836 
setCamera(const QByteArray & name)837 void InputController::setCamera(const QByteArray& name) {
838 #ifdef BUILD_QT_MULTIMEDIA
839 	if (m_cameraDevice == name) {
840 		return;
841 	}
842 	m_cameraDevice = name;
843 	if (m_camera && m_camera->state() == QCamera::ActiveState) {
844 		teardownCam();
845 	}
846 	if (m_cameraActive) {
847 		setupCam();
848 	}
849 #endif
850 }
851