all repos — mgba @ ec626d723fbb4716a697ad3cac0b0b29943dba00

mGBA Game Boy Advance Emulator

src/platform/qt/input/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 "CoreController.h"
  10#include "GamepadAxisEvent.h"
  11#include "GamepadButtonEvent.h"
  12#include "InputItem.h"
  13#include "InputModel.h"
  14#include "InputProfile.h"
  15#include "LogController.h"
  16
  17#include <QApplication>
  18#include <QKeyEvent>
  19#include <QMenu>
  20#include <QTimer>
  21#include <QWidget>
  22#ifdef BUILD_QT_MULTIMEDIA
  23#include <QCameraInfo>
  24#include <QVideoSurfaceFormat>
  25#endif
  26
  27#include <mgba/core/interface.h>
  28#include <mgba-util/configuration.h>
  29
  30#ifdef M_CORE_GBA
  31#include <mgba/internal/gba/input.h>
  32#endif
  33#ifdef M_CORE_GB
  34#include <mgba/internal/gb/input.h>
  35#endif
  36#ifdef M_CORE_DS
  37#include <mgba/internal/ds/input.h>
  38#endif
  39#include <initializer_list>
  40
  41using namespace QGBA;
  42
  43#ifdef BUILD_SDL
  44int InputController::s_sdlInited = 0;
  45mSDLEvents InputController::s_sdlEvents;
  46#endif
  47
  48InputController::InputController(int playerId, QWidget* topLevel, QObject* parent)
  49	: QObject(parent)
  50	, m_playerId(playerId)
  51	, m_topLevel(topLevel)
  52	, m_focusParent(topLevel)
  53	, m_bindings(new QMenu(tr("Controls")))
  54	, m_autofire(new QMenu(tr("Autofire")))
  55{
  56#ifdef BUILD_SDL
  57	if (s_sdlInited == 0) {
  58		mSDLInitEvents(&s_sdlEvents);
  59	}
  60	++s_sdlInited;
  61	updateJoysticks();
  62#endif
  63
  64#ifdef BUILD_SDL
  65	connect(&m_gamepadTimer, &QTimer::timeout, [this]() {
  66		testGamepad(SDL_BINDING_BUTTON);
  67		if (m_playerId == 0) {
  68			updateJoysticks();
  69		}
  70	});
  71#endif
  72	m_gamepadTimer.setInterval(50);
  73	m_gamepadTimer.start();
  74
  75#ifdef BUILD_QT_MULTIMEDIA
  76	connect(&m_videoDumper, &VideoDumper::imageAvailable, this, &InputController::setCamImage);
  77#endif
  78
  79	static QList<QPair<QString, int>> defaultBindings({
  80		qMakePair(QLatin1String("A"), Qt::Key_Z),
  81		qMakePair(QLatin1String("B"), Qt::Key_X),
  82		qMakePair(QLatin1String("L"), Qt::Key_A),
  83		qMakePair(QLatin1String("R"), Qt::Key_S),
  84		qMakePair(QLatin1String("Start"), Qt::Key_Return),
  85		qMakePair(QLatin1String("Select"), Qt::Key_Backspace),
  86		qMakePair(QLatin1String("Up"), Qt::Key_Up),
  87		qMakePair(QLatin1String("Down"), Qt::Key_Down),
  88		qMakePair(QLatin1String("Left"), Qt::Key_Left),
  89		qMakePair(QLatin1String("Right"), Qt::Key_Right)
  90	});
  91
  92	for (auto k : defaultBindings) {
  93		addKey(k.first);
  94	}
  95	m_keyIndex.rebuild();
  96	for (auto k : defaultBindings) {
  97		bindKey(KEYBOARD, k.second, k.first);
  98	}
  99}
 100
 101void InputController::addKey(const QString& name) {
 102	if (itemForKey(name)) {
 103		return;
 104	}
 105	m_keyIndex.addItem(qMakePair([this, name]() {
 106		m_activeKeys |= 1 << keyId(name);
 107	}, [this, name]() {
 108		m_activeKeys &= ~(1 << keyId(name));
 109	}), name, QString("key%0").arg(name), m_bindings.get());
 110
 111	m_keyIndex.addItem(qMakePair([this, name]() {
 112		setAutofire(keyId(name), true);
 113	}, [this, name]() {
 114		setAutofire(keyId(name), false);
 115	}), name, QString("autofire%1").arg(name), m_autofire.get());
 116}
 117
 118void InputController::setAutofire(int key, bool enable) {
 119	if (key >= 32 || key < 0) {
 120		return;
 121	}
 122
 123	m_autofireEnabled[key] = enable;
 124	m_autofireStatus[key] = 0;
 125}
 126
 127int InputController::updateAutofire() {
 128	int active = 0;
 129	for (int k = 0; k < 32; ++k) {
 130		if (!m_autofireEnabled[k]) {
 131			continue;
 132		}
 133		++m_autofireStatus[k];
 134		if (m_autofireStatus[k]) {
 135			m_autofireStatus[k] = 0;
 136			active |= 1 << k;
 137		}
 138	}
 139	return active;
 140}
 141
 142void InputController::addPlatform(mPlatform platform, const mInputPlatformInfo* info) {
 143	m_keyInfo[platform] = info;
 144	for (size_t i = 0; i < info->nKeys; ++i) {
 145		addKey(info->keyId[i]);
 146	}
 147}
 148
 149void InputController::setPlatform(mPlatform platform) {
 150	if (m_activeKeyInfo) {
 151		mInputMapDeinit(&m_inputMap);
 152	}
 153
 154	m_sdlPlayer.bindings = &m_inputMap;
 155	m_activeKeyInfo = m_keyInfo[platform];
 156	mInputMapInit(&m_inputMap, m_activeKeyInfo);
 157
 158	loadConfiguration(KEYBOARD);
 159#ifdef BUILD_SDL
 160	mSDLInitBindingsGBA(&m_inputMap);
 161	loadConfiguration(SDL_BINDING_BUTTON);
 162#endif
 163
 164	rebuildKeyIndex();
 165	restoreModel();
 166
 167#ifdef M_CORE_GBA
 168	m_lux.p = this;
 169	m_lux.sample = [](GBALuminanceSource* context) {
 170		InputControllerLux* lux = static_cast<InputControllerLux*>(context);
 171		lux->value = 0xFF - lux->p->m_luxValue;
 172	};
 173
 174	m_lux.readLuminance = [](GBALuminanceSource* context) {
 175		InputControllerLux* lux = static_cast<InputControllerLux*>(context);
 176		return lux->value;
 177	};
 178	setLuminanceLevel(0);
 179#endif
 180
 181	m_image.p = this;
 182	m_image.startRequestImage = [](mImageSource* context, unsigned w, unsigned h, int) {
 183		InputControllerImage* image = static_cast<InputControllerImage*>(context);
 184		image->w = w;
 185		image->h = h;
 186		if (image->image.isNull()) {
 187			image->image.load(":/res/no-cam.png");
 188		}
 189#ifdef BUILD_QT_MULTIMEDIA
 190		if (image->p->m_config->getQtOption("cameraDriver").toInt() == static_cast<int>(CameraDriver::QT_MULTIMEDIA)) {
 191			QByteArray camera = image->p->m_config->getQtOption("camera").toByteArray();
 192			if (!camera.isNull()) {
 193				QMetaObject::invokeMethod(image->p, "setCamera", Q_ARG(QByteArray, camera));
 194			}
 195			QMetaObject::invokeMethod(image->p, "setupCam");
 196		}
 197#endif
 198	};
 199
 200	m_image.stopRequestImage = [](mImageSource* context) {
 201		InputControllerImage* image = static_cast<InputControllerImage*>(context);
 202#ifdef BUILD_QT_MULTIMEDIA
 203		QMetaObject::invokeMethod(image->p, "teardownCam");
 204#endif
 205	};
 206
 207	m_image.requestImage = [](mImageSource* context, const void** buffer, size_t* stride, mColorFormat* format) {
 208		InputControllerImage* image = static_cast<InputControllerImage*>(context);
 209		QSize size;
 210		{
 211			QMutexLocker locker(&image->mutex);
 212			if (image->outOfDate) {
 213				image->resizedImage = image->image.scaled(image->w, image->h, Qt::KeepAspectRatioByExpanding);
 214				image->resizedImage = image->resizedImage.convertToFormat(QImage::Format_RGB16);
 215				image->outOfDate = false;
 216			}
 217		}
 218		size = image->resizedImage.size();
 219		const uint16_t* bits = reinterpret_cast<const uint16_t*>(image->resizedImage.constBits());
 220		if (size.width() > image->w) {
 221			bits += (size.width() - image->w) / 2;
 222		}
 223		if (size.height() > image->h) {
 224			bits += ((size.height() - image->h) / 2) * size.width();
 225		}
 226		*buffer = bits;
 227		*stride = image->resizedImage.bytesPerLine() / sizeof(*bits);
 228		*format = mCOLOR_RGB565;
 229	};
 230}
 231
 232InputController::~InputController() {
 233	if (m_activeKeyInfo) {
 234		mInputMapDeinit(&m_inputMap);
 235	}
 236
 237#ifdef BUILD_SDL
 238	if (m_playerAttached) {
 239		mSDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
 240	}
 241
 242	--s_sdlInited;
 243	if (s_sdlInited == 0) {
 244		mSDLDeinitEvents(&s_sdlEvents);
 245	}
 246#endif
 247}
 248
 249void InputController::rebuildIndex(const InputIndex* index) {
 250	m_inputIndex.rebuild(index);
 251}
 252
 253void InputController::rebuildKeyIndex(const InputIndex* index) {
 254	m_keyIndex.rebuild(index);
 255
 256	for (const InputItem* item : m_keyIndex.items()) {
 257		if (!item->name().startsWith(QLatin1String("key"))) {
 258			rebindKey(item->visibleName());
 259		}
 260	}
 261}
 262
 263void InputController::setConfiguration(ConfigController* config) {
 264	m_config = config;
 265	m_inputIndex.setConfigController(config);
 266	m_keyIndex.setConfigController(config);
 267	loadConfiguration(KEYBOARD);
 268	loadProfile(KEYBOARD, profileForType(KEYBOARD));
 269#ifdef BUILD_SDL
 270	mSDLEventsLoadConfig(&s_sdlEvents, config->input());
 271	if (!m_playerAttached) {
 272		m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
 273	}
 274	loadConfiguration(SDL_BINDING_BUTTON);
 275	loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
 276#endif
 277	restoreModel();
 278}
 279
 280void InputController::loadConfiguration(uint32_t type) {
 281	if (!m_activeKeyInfo) {
 282		return;
 283	}
 284	mInputMapLoad(&m_inputMap, type, m_config->input());
 285#ifdef BUILD_SDL
 286	if (m_playerAttached) {
 287		mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input());
 288	}
 289#endif
 290}
 291
 292void InputController::loadProfile(uint32_t type, const QString& profile) {
 293	const InputProfile* ip = InputProfile::findProfile(profile);
 294	if (ip) {
 295		ip->apply(this);
 296	}
 297	recalibrateAxes();
 298	emit profileLoaded(profile);
 299}
 300
 301void InputController::saveConfiguration() {
 302	saveConfiguration(KEYBOARD);
 303#ifdef BUILD_SDL
 304	saveConfiguration(SDL_BINDING_BUTTON);
 305	saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
 306	if (m_playerAttached) {
 307		mSDLPlayerSaveConfig(&m_sdlPlayer, m_config->input());
 308	}
 309#endif
 310	m_inputIndex.saveConfig();
 311	m_keyIndex.saveConfig();
 312	m_config->write();
 313}
 314
 315void InputController::saveConfiguration(uint32_t type) {
 316	if (m_activeKeyInfo) {
 317		mInputMapSave(&m_inputMap, type, m_config->input());
 318	}
 319	m_config->write();
 320}
 321
 322void InputController::saveProfile(uint32_t type, const QString& profile) {
 323	if (m_activeKeyInfo) {
 324		mInputProfileSave(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
 325	}
 326	m_config->write();
 327}
 328
 329const char* InputController::profileForType(uint32_t type) {
 330	UNUSED(type);
 331#ifdef BUILD_SDL
 332	if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
 333#if SDL_VERSION_ATLEAST(2, 0, 0)
 334		return SDL_JoystickName(m_sdlPlayer.joystick->joystick);
 335#else
 336		return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick));
 337#endif
 338	}
 339#endif
 340	return 0;
 341}
 342
 343QStringList InputController::connectedGamepads(uint32_t type) const {
 344	UNUSED(type);
 345
 346#ifdef BUILD_SDL
 347	if (type == SDL_BINDING_BUTTON) {
 348		QStringList pads;
 349		for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
 350			const char* name;
 351#if SDL_VERSION_ATLEAST(2, 0, 0)
 352			name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick);
 353#else
 354			name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick));
 355#endif
 356			if (name) {
 357				pads.append(QString(name));
 358			} else {
 359				pads.append(QString());
 360			}
 361		}
 362		return pads;
 363	}
 364#endif
 365
 366	return QStringList();
 367}
 368
 369int InputController::gamepad(uint32_t type) const {
 370#ifdef BUILD_SDL
 371	if (type == SDL_BINDING_BUTTON) {
 372		return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0;
 373	}
 374#endif
 375	return 0;
 376}
 377
 378void InputController::setGamepad(uint32_t type, int index) {
 379#ifdef BUILD_SDL
 380	if (type == SDL_BINDING_BUTTON) {
 381		mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index);
 382	}
 383#endif
 384}
 385
 386void InputController::setPreferredGamepad(uint32_t type, int index) {
 387	if (!m_config) {
 388		return;
 389	}
 390#ifdef BUILD_SDL
 391#if SDL_VERSION_ATLEAST(2, 0, 0)
 392	char name[34] = {0};
 393	SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick), name, sizeof(name));
 394#else
 395	const char* name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick));
 396	if (!name) {
 397		return;
 398	}
 399#endif
 400	mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, name);
 401#else
 402	UNUSED(type);
 403	UNUSED(index);
 404#endif
 405}
 406
 407mRumble* InputController::rumble() {
 408#ifdef BUILD_SDL
 409#if SDL_VERSION_ATLEAST(2, 0, 0)
 410	if (m_playerAttached) {
 411		return &m_sdlPlayer.rumble.d;
 412	}
 413#endif
 414#endif
 415	return nullptr;
 416}
 417
 418mRotationSource* InputController::rotationSource() {
 419#ifdef BUILD_SDL
 420	if (m_playerAttached) {
 421		return &m_sdlPlayer.rotation.d;
 422	}
 423#endif
 424	return nullptr;
 425}
 426
 427void InputController::registerTiltAxisX(int axis) {
 428#ifdef BUILD_SDL
 429	if (m_playerAttached) {
 430		m_sdlPlayer.rotation.axisX = axis;
 431	}
 432#endif
 433}
 434
 435void InputController::registerTiltAxisY(int axis) {
 436#ifdef BUILD_SDL
 437	if (m_playerAttached) {
 438		m_sdlPlayer.rotation.axisY = axis;
 439	}
 440#endif
 441}
 442
 443void InputController::registerGyroAxisX(int axis) {
 444#ifdef BUILD_SDL
 445	if (m_playerAttached) {
 446		m_sdlPlayer.rotation.gyroX = axis;
 447	}
 448#endif
 449}
 450
 451void InputController::registerGyroAxisY(int axis) {
 452#ifdef BUILD_SDL
 453	if (m_playerAttached) {
 454		m_sdlPlayer.rotation.gyroY = axis;
 455	}
 456#endif
 457}
 458
 459float InputController::gyroSensitivity() const {
 460#ifdef BUILD_SDL
 461	if (m_playerAttached) {
 462		return m_sdlPlayer.rotation.gyroSensitivity;
 463	}
 464#endif
 465	return 0;
 466}
 467
 468void InputController::setGyroSensitivity(float sensitivity) {
 469#ifdef BUILD_SDL
 470	if (m_playerAttached) {
 471		m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
 472	}
 473#endif
 474}
 475
 476void InputController::updateJoysticks() {
 477#ifdef BUILD_SDL
 478	QString profile = profileForType(SDL_BINDING_BUTTON);
 479	mSDLUpdateJoysticks(&s_sdlEvents, m_config->input());
 480	QString newProfile = profileForType(SDL_BINDING_BUTTON);
 481	if (profile != newProfile) {
 482		loadProfile(SDL_BINDING_BUTTON, newProfile);
 483	}
 484#endif
 485}
 486
 487const mInputMap* InputController::map() {
 488	if (!m_activeKeyInfo) {
 489		return nullptr;
 490	}
 491	return &m_inputMap;
 492}
 493
 494int InputController::pollEvents() {
 495	int activeButtons = m_activeKeys;
 496#ifdef BUILD_SDL
 497	if (m_playerAttached && m_sdlPlayer.joystick) {
 498		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
 499		SDL_JoystickUpdate();
 500		int numButtons = SDL_JoystickNumButtons(joystick);
 501		int i;
 502		for (i = 0; i < numButtons; ++i) {
 503			GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i));
 504			if (key == GBA_KEY_NONE) {
 505				continue;
 506			}
 507			if (hasPendingEvent(key)) {
 508				continue;
 509			}
 510			if (SDL_JoystickGetButton(joystick, i)) {
 511				activeButtons |= 1 << key;
 512			}
 513		}
 514		int numHats = SDL_JoystickNumHats(joystick);
 515		for (i = 0; i < numHats; ++i) {
 516			int hat = SDL_JoystickGetHat(joystick, i);
 517			activeButtons |= mInputMapHat(&m_inputMap, SDL_BINDING_BUTTON, i, hat);
 518		}
 519
 520		int numAxes = SDL_JoystickNumAxes(joystick);
 521		for (i = 0; i < numAxes; ++i) {
 522			int value = SDL_JoystickGetAxis(joystick, i);
 523
 524			enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value));
 525			if (key != GBA_KEY_NONE) {
 526				activeButtons |= 1 << key;
 527			}
 528		}
 529	}
 530#endif
 531	return activeButtons;
 532}
 533
 534QSet<int> InputController::activeGamepadButtons(int type) {
 535	QSet<int> activeButtons;
 536#ifdef BUILD_SDL
 537	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
 538		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
 539		SDL_JoystickUpdate();
 540		int numButtons = SDL_JoystickNumButtons(joystick);
 541		int i;
 542		for (i = 0; i < numButtons; ++i) {
 543			if (SDL_JoystickGetButton(joystick, i)) {
 544				activeButtons.insert(i);
 545			}
 546		}
 547	}
 548#endif
 549	return activeButtons;
 550}
 551
 552void InputController::recalibrateAxes() {
 553#ifdef BUILD_SDL
 554	if (m_playerAttached && m_sdlPlayer.joystick) {
 555		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
 556		SDL_JoystickUpdate();
 557		int numAxes = SDL_JoystickNumAxes(joystick);
 558		if (numAxes < 1) {
 559			return;
 560		}
 561		m_deadzones.resize(numAxes);
 562		int i;
 563		for (i = 0; i < numAxes; ++i) {
 564			m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
 565		}
 566	}
 567#endif
 568}
 569
 570QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
 571	QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
 572#ifdef BUILD_SDL
 573	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
 574		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
 575		SDL_JoystickUpdate();
 576		int numAxes = SDL_JoystickNumAxes(joystick);
 577		if (numAxes < 1) {
 578			return activeAxes;
 579		}
 580		m_deadzones.resize(numAxes);
 581		int i;
 582		for (i = 0; i < numAxes; ++i) {
 583			int32_t axis = SDL_JoystickGetAxis(joystick, i);
 584			axis -= m_deadzones[i];
 585			if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
 586				activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
 587			}
 588		}
 589	}
 590#endif
 591	return activeAxes;
 592}
 593
 594void InputController::bindKey(uint32_t type, int key, const QString& keyName) {
 595	InputItem* item = itemForKey(keyName);
 596	if (type != KEYBOARD) {
 597		item->setButton(key);
 598	} else {
 599		item->setShortcut(key);
 600	}
 601	if (m_activeKeyInfo) {
 602		int coreKey = keyId(keyName);
 603		mInputBindKey(&m_inputMap, type, key, coreKey);
 604	}
 605}
 606
 607void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, const QString& key) {
 608	InputItem* item = itemForKey(key);
 609	item->setAxis(axis, direction);
 610	
 611	if (!m_activeKeyInfo) {
 612		return;
 613	}
 614
 615	const mInputAxis* old = mInputQueryAxis(&m_inputMap, type, axis);
 616	mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
 617	if (old) {
 618		description = *old;
 619	}
 620	int deadzone = 0;
 621	if (axis > 0 && m_deadzones.size() > axis) {
 622		deadzone = m_deadzones[axis];
 623	}
 624	switch (direction) {
 625	case GamepadAxisEvent::NEGATIVE:
 626		description.lowDirection = keyId(key);
 627
 628		description.deadLow = deadzone - AXIS_THRESHOLD;
 629		break;
 630	case GamepadAxisEvent::POSITIVE:
 631		description.highDirection = keyId(key);
 632		description.deadHigh = deadzone + AXIS_THRESHOLD;
 633		break;
 634	default:
 635		return;
 636	}
 637	mInputBindAxis(&m_inputMap, type, axis, &description);
 638}
 639
 640QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {
 641	QSet<QPair<int, GamepadHatEvent::Direction>> activeHats;
 642#ifdef BUILD_SDL
 643	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
 644		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
 645		SDL_JoystickUpdate();
 646		int numHats = SDL_JoystickNumHats(joystick);
 647		if (numHats < 1) {
 648			return activeHats;
 649		}
 650
 651		int i;
 652		for (i = 0; i < numHats; ++i) {
 653			int hat = SDL_JoystickGetHat(joystick, i);
 654			if (hat & GamepadHatEvent::UP) {
 655				activeHats.insert(qMakePair(i, GamepadHatEvent::UP));
 656			}
 657			if (hat & GamepadHatEvent::RIGHT) {
 658				activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT));
 659			}
 660			if (hat & GamepadHatEvent::DOWN) {
 661				activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN));
 662			}
 663			if (hat & GamepadHatEvent::LEFT) {
 664				activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT));
 665			}
 666		}
 667	}
 668#endif
 669	return activeHats;
 670}
 671
 672void InputController::bindHat(uint32_t type, int hat, GamepadHatEvent::Direction direction, const QString& key) {
 673	if (!m_activeKeyInfo) {
 674		return;
 675	}
 676
 677	mInputHatBindings bindings{ -1, -1, -1, -1 };
 678	mInputQueryHat(&m_inputMap, type, hat, &bindings);
 679	switch (direction) {
 680	case GamepadHatEvent::UP:
 681		bindings.up = keyId(key);
 682		break;
 683	case GamepadHatEvent::RIGHT:
 684		bindings.right = keyId(key);
 685		break;
 686	case GamepadHatEvent::DOWN:
 687		bindings.down = keyId(key);
 688		break;
 689	case GamepadHatEvent::LEFT:
 690		bindings.left = keyId(key);
 691		break;
 692	default:
 693		return;
 694	}
 695	mInputBindHat(&m_inputMap, type, hat, &bindings);
 696}
 697
 698void InputController::testGamepad(int type) {
 699	auto activeAxes = activeGamepadAxes(type);
 700	auto oldAxes = m_activeAxes;
 701	m_activeAxes = activeAxes;
 702
 703	auto activeButtons = activeGamepadButtons(type);
 704	auto oldButtons = m_activeButtons;
 705	m_activeButtons = activeButtons;
 706
 707	auto activeHats = activeGamepadHats(type);
 708	auto oldHats = m_activeHats;
 709	m_activeHats = activeHats;
 710
 711	if (!QApplication::focusWidget()) {
 712		return;
 713	}
 714
 715	activeAxes.subtract(oldAxes);
 716	oldAxes.subtract(m_activeAxes);
 717
 718	for (auto& axis : m_activeAxes) {
 719		bool newlyAboveThreshold = activeAxes.contains(axis);
 720		if (newlyAboveThreshold) {
 721			GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
 722			postPendingEvent(event->gbaKey());
 723			sendGamepadEvent(event);
 724			if (!event->isAccepted()) {
 725				clearPendingEvent(event->gbaKey());
 726			}
 727		}
 728	}
 729	for (auto axis : oldAxes) {
 730		GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
 731		clearPendingEvent(event->gbaKey());
 732		sendGamepadEvent(event);
 733	}
 734
 735	if (!QApplication::focusWidget()) {
 736		return;
 737	}
 738
 739	activeButtons.subtract(oldButtons);
 740	oldButtons.subtract(m_activeButtons);
 741
 742	for (int button : activeButtons) {
 743		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
 744		postPendingEvent(event->gbaKey());
 745		sendGamepadEvent(event);
 746		if (!event->isAccepted()) {
 747			clearPendingEvent(event->gbaKey());
 748		}
 749	}
 750	for (int button : oldButtons) {
 751		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
 752		clearPendingEvent(event->gbaKey());
 753		sendGamepadEvent(event);
 754	}
 755
 756	activeHats.subtract(oldHats);
 757	oldHats.subtract(m_activeHats);
 758
 759	for (auto& hat : activeHats) {
 760		GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this);
 761		postPendingEvent(event->gbaKey());
 762		sendGamepadEvent(event);
 763		if (!event->isAccepted()) {
 764			clearPendingEvent(event->gbaKey());
 765		}
 766	}
 767	for (auto& hat : oldHats) {
 768		GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this);
 769		clearPendingEvent(event->gbaKey());
 770		sendGamepadEvent(event);
 771	}
 772}
 773
 774void InputController::sendGamepadEvent(QEvent* event) {
 775	QWidget* focusWidget = nullptr;
 776	if (m_focusParent) {
 777		focusWidget = m_focusParent->focusWidget();
 778		if (!focusWidget) {
 779			focusWidget = m_focusParent;
 780		}
 781	} else {
 782		focusWidget = QApplication::focusWidget();
 783	}
 784	QApplication::sendEvent(focusWidget, event);
 785}
 786
 787void InputController::postPendingEvent(int key) {
 788	m_pendingEvents.insert(key);
 789}
 790
 791void InputController::clearPendingEvent(int key) {
 792	m_pendingEvents.remove(key);
 793}
 794
 795bool InputController::hasPendingEvent(int key) const {
 796	return m_pendingEvents.contains(key);
 797}
 798
 799void InputController::suspendScreensaver() {
 800#ifdef BUILD_SDL
 801#if SDL_VERSION_ATLEAST(2, 0, 0)
 802	mSDLSuspendScreensaver(&s_sdlEvents);
 803#endif
 804#endif
 805}
 806
 807void InputController::resumeScreensaver() {
 808#ifdef BUILD_SDL
 809#if SDL_VERSION_ATLEAST(2, 0, 0)
 810	mSDLResumeScreensaver(&s_sdlEvents);
 811#endif
 812#endif
 813}
 814
 815void InputController::setScreensaverSuspendable(bool suspendable) {
 816#ifdef BUILD_SDL
 817#if SDL_VERSION_ATLEAST(2, 0, 0)
 818	mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
 819#endif
 820#endif
 821}
 822
 823void InputController::stealFocus(QWidget* focus) {
 824	m_focusParent = focus;
 825}
 826
 827void InputController::releaseFocus(QWidget* focus) {
 828	if (focus == m_focusParent) {
 829		m_focusParent = m_topLevel;
 830	}
 831}
 832
 833bool InputController::eventFilter(QObject*, QEvent* event) {
 834	event->ignore();
 835	if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
 836		QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
 837		int key = keyEvent->key();
 838		if (!InputIndex::isModifierKey(key)) {
 839			key |= (keyEvent->modifiers() & ~Qt::KeypadModifier);
 840		} else {
 841			key = InputIndex::toModifierKey(key | (keyEvent->modifiers() & ~Qt::KeypadModifier));
 842		}
 843
 844		if (keyEvent->isAutoRepeat()) {
 845			event->accept();
 846			return true;
 847		}
 848
 849		event->ignore();
 850		InputItem* item = m_inputIndex.itemForShortcut(key);
 851		if (item) {
 852			item->trigger(event->type() == QEvent::KeyPress);
 853			event->accept();
 854		}
 855		item = m_keyIndex.itemForShortcut(key);
 856		if (item) {
 857			item->trigger(event->type() == QEvent::KeyPress);
 858			event->accept();
 859		}
 860	}
 861
 862
 863	if (event->type() == GamepadButtonEvent::Down() || event->type() == GamepadButtonEvent::Up()) {
 864		GamepadButtonEvent* gbe = static_cast<GamepadButtonEvent*>(event);
 865		InputItem* item = m_inputIndex.itemForButton(gbe->value());
 866		if (item) {
 867			item->trigger(event->type() == GamepadButtonEvent::Down());
 868			event->accept();
 869		}
 870		item = m_keyIndex.itemForButton(gbe->value());
 871		if (item) {
 872			item->trigger(event->type() == GamepadButtonEvent::Down());
 873			event->accept();
 874		}
 875	}
 876	if (event->type() == GamepadAxisEvent::Type()) {
 877		GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event);
 878		InputItem* item = m_inputIndex.itemForAxis(gae->axis(), gae->direction());
 879		if (item) {
 880			item->trigger(event->type() == gae->isNew());
 881			event->accept();
 882		}
 883		item = m_keyIndex.itemForAxis(gae->axis(), gae->direction());
 884		if (item) {
 885			item->trigger(event->type() == gae->isNew());
 886			event->accept();
 887		}
 888	}
 889	return event->isAccepted();
 890}
 891
 892InputItem* InputController::itemForKey(const QString& key) {
 893	return m_keyIndex.itemAt(QString("key%0").arg(key));
 894}
 895
 896int InputController::keyId(const QString& key) {
 897	for (int i = 0; i < m_activeKeyInfo->nKeys; ++i) {
 898		if (m_activeKeyInfo->keyId[i] == key) {
 899			return i;
 900		}
 901	}
 902	return -1;
 903}
 904
 905void InputController::restoreModel() {
 906	if (!m_activeKeyInfo) {
 907		return;
 908	}
 909	int nKeys = m_inputMap.info->nKeys;
 910	for (int i = 0; i < nKeys; ++i) {
 911		const QString& keyName = m_inputMap.info->keyId[i];
 912		InputItem* item = itemForKey(keyName);
 913		if (item) {
 914			int key = mInputQueryBinding(&m_inputMap, KEYBOARD, i);
 915			if (key >= 0) {
 916				item->setShortcut(key);
 917			} else {
 918				item->clearShortcut();
 919			}
 920#ifdef BUILD_SDL
 921			key = mInputQueryBinding(&m_inputMap, SDL_BINDING_BUTTON, i);
 922			if (key >= 0) {
 923				item->setButton(key);
 924			} else {
 925				item->clearButton();
 926			}
 927#endif
 928		}
 929	}
 930#ifdef BUILD_SDL
 931	mInputEnumerateAxes(&m_inputMap, SDL_BINDING_BUTTON, [](int axis, const struct mInputAxis* description, void* user) {
 932		InputController* controller = static_cast<InputController*>(user);
 933		InputItem* item;
 934		const mInputPlatformInfo* inputMap = controller->m_inputMap.info;
 935		if (description->highDirection >= 0 && description->highDirection < controller->m_inputMap.info->nKeys) {
 936			int id = description->lowDirection;
 937			if (id >= 0 && id < inputMap->nKeys) {
 938				item = controller->itemForKey(inputMap->keyId[id]);
 939				if (item) {
 940					item->setAxis(axis, GamepadAxisEvent::POSITIVE);
 941				}
 942			}
 943		}
 944		if (description->lowDirection >= 0 && description->lowDirection < controller->m_inputMap.info->nKeys) {
 945			int id = description->highDirection;
 946			if (id >= 0 && id < inputMap->nKeys) {
 947				item = controller->itemForKey(inputMap->keyId[id]);
 948				if (item) {
 949					item->setAxis(axis, GamepadAxisEvent::NEGATIVE);
 950				}
 951			}
 952		}
 953	}, this);
 954#endif
 955	rebuildKeyIndex();
 956}
 957
 958void InputController::rebindKey(const QString& key) {
 959	InputItem* item = itemForKey(key);
 960	bindKey(KEYBOARD, item->shortcut(), key);
 961#ifdef BUILD_SDL
 962	bindKey(SDL_BINDING_BUTTON, item->button(), key);
 963	bindAxis(SDL_BINDING_BUTTON, item->axis(), item->direction(), key);
 964#endif
 965}
 966
 967void InputController::loadCamImage(const QString& path) {
 968	setCamImage(QImage(path));
 969}
 970
 971void InputController::setCamImage(const QImage& image) {
 972	if (image.isNull()) {
 973		return;
 974	}
 975	QMutexLocker locker(&m_image.mutex);
 976	m_image.image = image;
 977	m_image.resizedImage = QImage();
 978	m_image.outOfDate = true;
 979}
 980
 981QList<QPair<QByteArray, QString>> InputController::listCameras() const {
 982	QList<QPair<QByteArray, QString>> out;
 983#ifdef BUILD_QT_MULTIMEDIA
 984	QList<QCameraInfo> cams = QCameraInfo::availableCameras();
 985	for (const auto& cam : cams) {
 986		out.append(qMakePair(cam.deviceName().toLatin1(), cam.description()));
 987	}
 988#endif
 989	return out;
 990}
 991
 992void InputController::increaseLuminanceLevel() {
 993	setLuminanceLevel(m_luxLevel + 1);
 994}
 995
 996void InputController::decreaseLuminanceLevel() {
 997	setLuminanceLevel(m_luxLevel - 1);
 998}
 999
1000void InputController::setLuminanceLevel(int level) {
1001	int value = 0x16;
1002	level = std::max(0, std::min(10, level));
1003	if (level > 0) {
1004		value += GBA_LUX_LEVELS[level - 1];
1005	}
1006	setLuminanceValue(value);
1007}
1008
1009void InputController::setLuminanceValue(uint8_t value) {
1010	m_luxValue = value;
1011	value = std::max<int>(value - 0x16, 0);
1012	m_luxLevel = 10;
1013	for (int i = 0; i < 10; ++i) {
1014		if (value < GBA_LUX_LEVELS[i]) {
1015			m_luxLevel = i;
1016			break;
1017		}
1018	}
1019	emit luminanceValueChanged(m_luxValue);
1020}
1021
1022void InputController::setupCam() {
1023#ifdef BUILD_QT_MULTIMEDIA
1024	if (!m_camera) {
1025		m_camera = std::make_unique<QCamera>();
1026		connect(m_camera.get(), &QCamera::statusChanged, this, &InputController::prepareCamSettings, Qt::QueuedConnection);
1027	}
1028	m_camera->setCaptureMode(QCamera::CaptureVideo);
1029	m_camera->setViewfinder(&m_videoDumper);
1030	m_camera->load();
1031#endif
1032}
1033
1034#ifdef BUILD_QT_MULTIMEDIA
1035void InputController::prepareCamSettings(QCamera::Status status) {
1036	if (status != QCamera::LoadedStatus || m_camera->state() == QCamera::ActiveState) {
1037		return;
1038	}
1039#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
1040	QVideoFrame::PixelFormat format(QVideoFrame::Format_RGB32);
1041	QCameraViewfinderSettings settings;
1042	QSize size(1280, 720);
1043	auto cameraRes = m_camera->supportedViewfinderResolutions(settings);
1044	for (auto& cameraSize : cameraRes) {
1045		if (cameraSize.width() < m_image.w || cameraSize.height() < m_image.h) {
1046			continue;
1047		}
1048		if (cameraSize.width() <= size.width() && cameraSize.height() <= size.height()) {
1049			size = cameraSize;
1050		}
1051	}
1052	settings.setResolution(size);
1053
1054	auto cameraFormats = m_camera->supportedViewfinderPixelFormats(settings);
1055	auto goodFormats = m_videoDumper.supportedPixelFormats();
1056	bool goodFormatFound = false;
1057	for (const auto& goodFormat : goodFormats) {
1058		if (cameraFormats.contains(goodFormat)) {
1059			settings.setPixelFormat(goodFormat);
1060			format = goodFormat;
1061			goodFormatFound = true;
1062			break;
1063		}
1064	}
1065	if (!goodFormatFound) {
1066		LOG(QT, WARN) << "Could not find a valid camera format!";
1067		for (const auto& format : cameraFormats) {
1068			LOG(QT, WARN) << "Camera supported format: " << QString::number(format);
1069		}
1070	}
1071	m_camera->setViewfinderSettings(settings);
1072#endif
1073	m_camera->start();
1074}
1075#endif
1076
1077void InputController::teardownCam() {
1078#ifdef BUILD_QT_MULTIMEDIA
1079	if (m_camera) {
1080		m_camera->stop();
1081	}
1082#endif
1083}
1084
1085void InputController::setCamera(const QByteArray& name) {
1086#ifdef BUILD_QT_MULTIMEDIA
1087	bool needsRestart = false;
1088	if (m_camera) {
1089		needsRestart = m_camera->state() == QCamera::ActiveState;
1090	}
1091	m_camera = std::make_unique<QCamera>(name);
1092	connect(m_camera.get(), &QCamera::statusChanged, this, &InputController::prepareCamSettings, Qt::QueuedConnection);
1093	if (needsRestart) {
1094		setupCam();
1095	}
1096#endif
1097}