all repos — mgba @ 1a0e44c014ad34bea30d237d27015d82d02cda4b

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