all repos — mgba @ ffed2ecd30404ee3cabdf30f3a40e9769a2e347e

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