all repos — mgba @ c95d0b7b07da602d9894a6e01c667dc7e4c80343

mGBA Game Boy Advance Emulator

src/platform/qt/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 "GameController.h"
 10#include "GamepadAxisEvent.h"
 11#include "GamepadButtonEvent.h"
 12#include "InputModel.h"
 13#include "InputProfile.h"
 14
 15#include <QApplication>
 16#include <QKeyEvent>
 17#include <QMenu>
 18#include <QTimer>
 19#include <QWidget>
 20
 21#include <mgba/core/interface.h>
 22#include <mgba-util/configuration.h>
 23
 24using namespace QGBA;
 25
 26#ifdef BUILD_SDL
 27int InputController::s_sdlInited = 0;
 28mSDLEvents InputController::s_sdlEvents;
 29#endif
 30
 31InputController::InputController(InputModel* model, int playerId, QWidget* topLevel, QObject* parent)
 32	: QObject(parent)
 33	, m_inputModel(model)
 34	, m_platform(PLATFORM_NONE)
 35	, m_playerId(playerId)
 36	, m_config(nullptr)
 37	, m_gamepadTimer(nullptr)
 38#ifdef BUILD_SDL
 39	, m_sdlPlayer{}
 40	, m_playerAttached(false)
 41#endif
 42	, m_allowOpposing(false)
 43	, m_topLevel(topLevel)
 44	, m_focusParent(topLevel)
 45{
 46#ifdef BUILD_SDL
 47	if (s_sdlInited == 0) {
 48		mSDLInitEvents(&s_sdlEvents);
 49	}
 50	++s_sdlInited;
 51	updateJoysticks();
 52#endif
 53
 54#ifdef BUILD_SDL
 55	connect(&m_gamepadTimer, &QTimer::timeout, [this]() {
 56		testGamepad(SDL_BINDING_BUTTON);
 57		if (m_playerId == 0) {
 58			updateJoysticks();
 59		}
 60	});
 61#endif
 62	m_gamepadTimer.setInterval(50);
 63	m_gamepadTimer.start();
 64
 65	m_autofireMenu = std::unique_ptr<QMenu>(new QMenu(tr("Autofire")));
 66	m_inputModel->addMenu(m_autofireMenu.get());
 67
 68	m_inputMenu = std::unique_ptr<QMenu>(new QMenu(tr("Bindings")));
 69	m_inputModel->addMenu(m_inputMenu.get());
 70
 71	connect(m_inputModel, SIGNAL(keyRebound(const QModelIndex&, int)), this, SLOT(bindKey(const QModelIndex&, int)));
 72	connect(m_inputModel, SIGNAL(buttonRebound(const QModelIndex&, int)), this, SLOT(bindButton(const QModelIndex&, int)));
 73	connect(m_inputModel, SIGNAL(axisRebound(const QModelIndex&, int,  GamepadAxisEvent::Direction)), this, SLOT(bindAxis(const QModelIndex&, int, GamepadAxisEvent::Direction)));
 74}
 75
 76InputController::~InputController() {
 77	for (auto& inputMap : m_inputMaps) {
 78		mInputMapDeinit(&inputMap);
 79	}
 80
 81#ifdef BUILD_SDL
 82	if (m_playerAttached) {
 83		mSDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
 84	}
 85
 86	--s_sdlInited;
 87	if (s_sdlInited == 0) {
 88		mSDLDeinitEvents(&s_sdlEvents);
 89	}
 90#endif
 91}
 92
 93void InputController::addPlatform(mPlatform platform, const QString& visibleName, const mInputPlatformInfo* info) {
 94	mInputMap* inputMap = &m_inputMaps[platform];
 95	mInputMapInit(inputMap, info);
 96
 97	QMenu* input = m_inputMenu->addMenu(visibleName);
 98	QMenu* autofire = m_autofireMenu->addMenu(visibleName);
 99	m_inputMenuIndices[platform] = m_inputModel->addMenu(input, m_inputMenu.get());
100	m_inputModel->addMenu(autofire, m_autofireMenu.get());
101
102	for (size_t i = 0; i < info->nKeys; ++i) {
103		m_inputModel->addKey(input, platform, i, 0, info->keyId[i], info->keyId[i]);
104		m_inputModel->addKey(autofire, platform, i, 0, info->keyId[i], info->keyId[i]);
105	}
106
107#ifdef BUILD_SDL
108	mSDLInitBindingsGBA(inputMap);
109#endif
110	mInputBindKey(inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
111	mInputBindKey(inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
112	mInputBindKey(inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
113	mInputBindKey(inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
114	mInputBindKey(inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
115	mInputBindKey(inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
116	mInputBindKey(inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
117	mInputBindKey(inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
118	mInputBindKey(inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
119	mInputBindKey(inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
120}
121
122void InputController::setPlatform(mPlatform platform) {
123#ifdef BUILD_SDL
124	m_sdlPlayer.bindings = &m_inputMaps[platform];
125#endif
126	m_platform = platform;
127}
128
129void InputController::setConfiguration(ConfigController* config) {
130	m_config = config;
131	setAllowOpposing(config->getOption("allowOpposingDirections").toInt());
132	loadConfiguration(KEYBOARD);
133#ifdef BUILD_SDL
134	mSDLEventsLoadConfig(&s_sdlEvents, config->input());
135	if (!m_playerAttached) {
136		m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
137	}
138	loadConfiguration(SDL_BINDING_BUTTON);
139	loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
140#endif
141	restoreModel();
142}
143
144void InputController::loadConfiguration(uint32_t type) {
145	for (auto& inputMap : m_inputMaps) {
146		mInputMapLoad(&inputMap, type, m_config->input());
147#ifdef BUILD_SDL
148		if (m_playerAttached) {
149			mInputMap* bindings = m_sdlPlayer.bindings;
150			m_sdlPlayer.bindings = &inputMap;
151			mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input());
152			m_sdlPlayer.bindings = bindings;
153		}
154#endif
155	}
156}
157
158void InputController::loadProfile(uint32_t type, const QString& profile) {
159	for (auto iter = m_inputMaps.begin(); iter != m_inputMaps.end(); ++iter) {
160		bool loaded = mInputProfileLoad(&iter.value(), type, m_config->input(), profile.toUtf8().constData());
161		if (!loaded) {
162			const InputProfile* ip = InputProfile::findProfile(iter.key(), profile);
163			if (ip) {
164				ip->apply(iter.key(), this);
165			}
166		}
167	}
168	recalibrateAxes();
169	m_inputModel->loadProfile(PLATFORM_NONE, profile); // TODO
170	emit profileLoaded(profile);
171}
172
173void InputController::saveConfiguration() {
174	saveConfiguration(KEYBOARD);
175#ifdef BUILD_SDL
176	saveConfiguration(SDL_BINDING_BUTTON);
177	saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
178	if (m_playerAttached) {
179		mSDLPlayerSaveConfig(&m_sdlPlayer, m_config->input());
180	}
181	m_config->write();
182#endif
183}
184
185void InputController::saveConfiguration(uint32_t type) {
186	for (auto& inputMap : m_inputMaps) {
187		if (!inputMap.info) {
188			continue;
189		}
190		mInputMapSave(&inputMap, type, m_config->input());
191	}
192	m_config->write();
193}
194
195void InputController::saveProfile(uint32_t type, const QString& profile) {
196	for (auto& inputMap : m_inputMaps) {
197		mInputProfileSave(&inputMap, type, m_config->input(), profile.toUtf8().constData());
198	}
199	m_config->write();
200}
201
202const char* InputController::profileForType(uint32_t type) {
203	UNUSED(type);
204#ifdef BUILD_SDL
205	if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
206#if SDL_VERSION_ATLEAST(2, 0, 0)
207		return SDL_JoystickName(m_sdlPlayer.joystick->joystick);
208#else
209		return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick));
210#endif
211	}
212#endif
213	return 0;
214}
215
216QStringList InputController::connectedGamepads(uint32_t type) const {
217	UNUSED(type);
218
219#ifdef BUILD_SDL
220	if (type == SDL_BINDING_BUTTON) {
221		QStringList pads;
222		for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
223			const char* name;
224#if SDL_VERSION_ATLEAST(2, 0, 0)
225			name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick);
226#else
227			name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick));
228#endif
229			if (name) {
230				pads.append(QString(name));
231			} else {
232				pads.append(QString());
233			}
234		}
235		return pads;
236	}
237#endif
238
239	return QStringList();
240}
241
242int InputController::gamepad(uint32_t type) const {
243#ifdef BUILD_SDL
244	if (type == SDL_BINDING_BUTTON) {
245		return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0;
246	}
247#endif
248	return 0;
249}
250
251void InputController::setGamepad(uint32_t type, int index) {
252#ifdef BUILD_SDL
253	if (type == SDL_BINDING_BUTTON) {
254		mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index);
255	}
256#endif
257}
258
259void InputController::setPreferredGamepad(uint32_t type, const QString& device) {
260	if (!m_config) {
261		return;
262	}
263	mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, device.toUtf8().constData());
264}
265
266mRumble* InputController::rumble() {
267#ifdef BUILD_SDL
268#if SDL_VERSION_ATLEAST(2, 0, 0)
269	if (m_playerAttached) {
270		return &m_sdlPlayer.rumble.d;
271	}
272#endif
273#endif
274	return nullptr;
275}
276
277mRotationSource* InputController::rotationSource() {
278#ifdef BUILD_SDL
279	if (m_playerAttached) {
280		return &m_sdlPlayer.rotation.d;
281	}
282#endif
283	return nullptr;
284}
285
286void InputController::registerTiltAxisX(int axis) {
287#ifdef BUILD_SDL
288	if (m_playerAttached) {
289		m_sdlPlayer.rotation.axisX = axis;
290	}
291#endif
292}
293
294void InputController::registerTiltAxisY(int axis) {
295#ifdef BUILD_SDL
296	if (m_playerAttached) {
297		m_sdlPlayer.rotation.axisY = axis;
298	}
299#endif
300}
301
302void InputController::registerGyroAxisX(int axis) {
303#ifdef BUILD_SDL
304	if (m_playerAttached) {
305		m_sdlPlayer.rotation.gyroX = axis;
306	}
307#endif
308}
309
310void InputController::registerGyroAxisY(int axis) {
311#ifdef BUILD_SDL
312	if (m_playerAttached) {
313		m_sdlPlayer.rotation.gyroY = axis;
314	}
315#endif
316}
317
318float InputController::gyroSensitivity() const {
319#ifdef BUILD_SDL
320	if (m_playerAttached) {
321		return m_sdlPlayer.rotation.gyroSensitivity;
322	}
323#endif
324	return 0;
325}
326
327void InputController::setGyroSensitivity(float sensitivity) {
328#ifdef BUILD_SDL
329	if (m_playerAttached) {
330		m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
331	}
332#endif
333}
334
335void InputController::updateJoysticks() {
336#ifdef BUILD_SDL
337	QString profile = profileForType(SDL_BINDING_BUTTON);
338	mSDLUpdateJoysticks(&s_sdlEvents, m_config->input());
339	QString newProfile = profileForType(SDL_BINDING_BUTTON);
340	if (profile != newProfile) {
341		loadProfile(SDL_BINDING_BUTTON, newProfile);
342	}
343#endif
344}
345
346const mInputMap* InputController::map() {
347	return &m_inputMaps[m_platform];
348}
349
350int InputController::pollEvents() {
351	int activeButtons = 0;
352#ifdef BUILD_SDL
353	if (m_playerAttached && m_sdlPlayer.joystick) {
354		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
355		SDL_JoystickUpdate();
356		int numButtons = SDL_JoystickNumButtons(joystick);
357		int i;
358		for (i = 0; i < numButtons; ++i) {
359			GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i));
360			if (key == GBA_KEY_NONE) {
361				continue;
362			}
363			if (hasPendingEvent(key)) {
364				continue;
365			}
366			if (SDL_JoystickGetButton(joystick, i)) {
367				activeButtons |= 1 << key;
368			}
369		}
370		int numHats = SDL_JoystickNumHats(joystick);
371		for (i = 0; i < numHats; ++i) {
372			int hat = SDL_JoystickGetHat(joystick, i);
373			activeButtons |= mInputMapHat(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i, hat);
374		}
375
376		int numAxes = SDL_JoystickNumAxes(joystick);
377		for (i = 0; i < numAxes; ++i) {
378			int value = SDL_JoystickGetAxis(joystick, i);
379
380			enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i, value));
381			if (key != GBA_KEY_NONE) {
382				activeButtons |= 1 << key;
383			}
384		}
385	}
386#endif
387	return activeButtons;
388}
389
390QSet<int> InputController::activeGamepadButtons(int type) {
391	QSet<int> activeButtons;
392#ifdef BUILD_SDL
393	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
394		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
395		SDL_JoystickUpdate();
396		int numButtons = SDL_JoystickNumButtons(joystick);
397		int i;
398		for (i = 0; i < numButtons; ++i) {
399			if (SDL_JoystickGetButton(joystick, i)) {
400				activeButtons.insert(i);
401			}
402		}
403	}
404#endif
405	return activeButtons;
406}
407
408void InputController::recalibrateAxes() {
409#ifdef BUILD_SDL
410	if (m_playerAttached && m_sdlPlayer.joystick) {
411		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
412		SDL_JoystickUpdate();
413		int numAxes = SDL_JoystickNumAxes(joystick);
414		if (numAxes < 1) {
415			return;
416		}
417		m_deadzones.resize(numAxes);
418		int i;
419		for (i = 0; i < numAxes; ++i) {
420			m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
421		}
422	}
423#endif
424}
425
426QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
427	QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
428#ifdef BUILD_SDL
429	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
430		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
431		SDL_JoystickUpdate();
432		int numAxes = SDL_JoystickNumAxes(joystick);
433		if (numAxes < 1) {
434			return activeAxes;
435		}
436		m_deadzones.resize(numAxes);
437		int i;
438		for (i = 0; i < numAxes; ++i) {
439			int32_t axis = SDL_JoystickGetAxis(joystick, i);
440			axis -= m_deadzones[i];
441			if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
442				activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
443			}
444		}
445	}
446#endif
447	return activeAxes;
448}
449
450void InputController::bindKey(mPlatform platform, uint32_t type, int key, int coreKey) {
451	if (m_inputMaps.find(platform) == m_inputMaps.end() || coreKey >= m_inputMaps[platform].info->nKeys) {
452		return;
453	}
454	QModelIndex index = m_inputModel->index(coreKey, 0, m_inputMenuIndices[platform]);
455	bool signalsBlocked = m_inputModel->blockSignals(true);
456	if (type != KEYBOARD) {
457		m_inputModel->updateButton(index, key);
458	} else {
459		m_inputModel->updateKey(index, key);		
460	}
461	m_inputModel->blockSignals(signalsBlocked);
462	mInputBindKey(&m_inputMaps[platform], type, key, coreKey);
463}
464
465void InputController::bindAxis(mPlatform platform, uint32_t type, int axis, GamepadAxisEvent::Direction direction, int key) {
466	if (m_inputMaps.find(platform) == m_inputMaps.end() || key >= m_inputMaps[platform].info->nKeys) {
467		return;
468	}
469	QModelIndex index = m_inputModel->index(key, 0, m_inputMenuIndices[platform]);
470	bool signalsBlocked = m_inputModel->blockSignals(true);
471	m_inputModel->updateAxis(index, axis, direction);
472	m_inputModel->blockSignals(signalsBlocked);
473
474	const mInputAxis* old = mInputQueryAxis(&m_inputMaps[platform], type, axis);
475	mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
476	if (old) {
477		description = *old;
478	}
479	int deadzone = 0;
480	if (axis > 0 && m_deadzones.size() > axis) {
481		deadzone = m_deadzones[axis];
482	}
483	switch (direction) {
484	case GamepadAxisEvent::NEGATIVE:
485		description.lowDirection = key;
486
487		description.deadLow = deadzone - AXIS_THRESHOLD;
488		break;
489	case GamepadAxisEvent::POSITIVE:
490		description.highDirection = key;
491		description.deadHigh = deadzone + AXIS_THRESHOLD;
492		break;
493	default:
494		return;
495	}
496	mInputBindAxis(&m_inputMaps[platform], type, axis, &description);
497}
498
499QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {
500	QSet<QPair<int, GamepadHatEvent::Direction>> activeHats;
501#ifdef BUILD_SDL
502	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
503		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
504		SDL_JoystickUpdate();
505		int numHats = SDL_JoystickNumHats(joystick);
506		if (numHats < 1) {
507			return activeHats;
508		}
509
510		int i;
511		for (i = 0; i < numHats; ++i) {
512			int hat = SDL_JoystickGetHat(joystick, i);
513			if (hat & GamepadHatEvent::UP) {
514				activeHats.insert(qMakePair(i, GamepadHatEvent::UP));
515			}
516			if (hat & GamepadHatEvent::RIGHT) {
517				activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT));
518			}
519			if (hat & GamepadHatEvent::DOWN) {
520				activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN));
521			}
522			if (hat & GamepadHatEvent::LEFT) {
523				activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT));
524			}
525		}
526	}
527#endif
528	return activeHats;
529}
530
531void InputController::bindHat(mPlatform platform, uint32_t type, int hat, GamepadHatEvent::Direction direction, int coreKey) {
532	if (m_inputMaps.find(platform) == m_inputMaps.end() || coreKey >= m_inputMaps[platform].info->nKeys) {
533		return;
534	}
535	QModelIndex index = m_inputModel->index(coreKey, 0, m_inputMenuIndices[platform]);
536	//m_inputModel->updateHat(index, hat, direction);
537
538	mInputHatBindings bindings{ -1, -1, -1, -1 };
539	mInputQueryHat(&m_inputMaps[platform], type, hat, &bindings);
540	switch (direction) {
541	case GamepadHatEvent::UP:
542		bindings.up = coreKey;
543		break;
544	case GamepadHatEvent::RIGHT:
545		bindings.right = coreKey;
546		break;
547	case GamepadHatEvent::DOWN:
548		bindings.down = coreKey;
549		break;
550	case GamepadHatEvent::LEFT:
551		bindings.left = coreKey;
552		break;
553	default:
554		return;
555	}
556	mInputBindHat(&m_inputMaps[platform], type, hat, &bindings);
557}
558
559void InputController::testGamepad(int type) {
560	auto activeAxes = activeGamepadAxes(type);
561	auto oldAxes = m_activeAxes;
562	m_activeAxes = activeAxes;
563
564	auto activeButtons = activeGamepadButtons(type);
565	auto oldButtons = m_activeButtons;
566	m_activeButtons = activeButtons;
567
568	auto activeHats = activeGamepadHats(type);
569	auto oldHats = m_activeHats;
570	m_activeHats = activeHats;
571
572	if (!QApplication::focusWidget()) {
573		return;
574	}
575
576	activeAxes.subtract(oldAxes);
577	oldAxes.subtract(m_activeAxes);
578
579	for (auto& axis : m_activeAxes) {
580		bool newlyAboveThreshold = activeAxes.contains(axis);
581		if (newlyAboveThreshold) {
582			GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
583			postPendingEvent(event->gbaKey());
584			sendGamepadEvent(event);
585			if (!event->isAccepted()) {
586				clearPendingEvent(event->gbaKey());
587			}
588		}
589	}
590	for (auto axis : oldAxes) {
591		GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
592		clearPendingEvent(event->gbaKey());
593		sendGamepadEvent(event);
594	}
595
596	if (!QApplication::focusWidget()) {
597		return;
598	}
599
600	activeButtons.subtract(oldButtons);
601	oldButtons.subtract(m_activeButtons);
602
603	for (int button : activeButtons) {
604		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
605		postPendingEvent(event->gbaKey());
606		sendGamepadEvent(event);
607		if (!event->isAccepted()) {
608			clearPendingEvent(event->gbaKey());
609		}
610	}
611	for (int button : oldButtons) {
612		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
613		clearPendingEvent(event->gbaKey());
614		sendGamepadEvent(event);
615	}
616
617	activeHats.subtract(oldHats);
618	oldHats.subtract(m_activeHats);
619
620	for (auto& hat : activeHats) {
621		GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this);
622		postPendingEvent(event->gbaKey());
623		sendGamepadEvent(event);
624		if (!event->isAccepted()) {
625			clearPendingEvent(event->gbaKey());
626		}
627	}
628	for (auto& hat : oldHats) {
629		GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this);
630		clearPendingEvent(event->gbaKey());
631		sendGamepadEvent(event);
632	}
633}
634
635void InputController::sendGamepadEvent(QEvent* event) {
636	QWidget* focusWidget = nullptr;
637	if (m_focusParent) {
638		focusWidget = m_focusParent->focusWidget();
639		if (!focusWidget) {
640			focusWidget = m_focusParent;
641		}
642	} else {
643		focusWidget = QApplication::focusWidget();
644	}
645	QApplication::sendEvent(focusWidget, event);
646}
647
648void InputController::postPendingEvent(int key) {
649	m_pendingEvents.insert(key);
650}
651
652void InputController::clearPendingEvent(int key) {
653	m_pendingEvents.remove(key);
654}
655
656bool InputController::hasPendingEvent(int key) const {
657	return m_pendingEvents.contains(key);
658}
659
660void InputController::suspendScreensaver() {
661#ifdef BUILD_SDL
662#if SDL_VERSION_ATLEAST(2, 0, 0)
663	mSDLSuspendScreensaver(&s_sdlEvents);
664#endif
665#endif
666}
667
668void InputController::resumeScreensaver() {
669#ifdef BUILD_SDL
670#if SDL_VERSION_ATLEAST(2, 0, 0)
671	mSDLResumeScreensaver(&s_sdlEvents);
672#endif
673#endif
674}
675
676void InputController::setScreensaverSuspendable(bool suspendable) {
677#ifdef BUILD_SDL
678#if SDL_VERSION_ATLEAST(2, 0, 0)
679	mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
680#endif
681#endif
682}
683
684void InputController::stealFocus(QWidget* focus) {
685	m_focusParent = focus;
686}
687
688void InputController::releaseFocus(QWidget* focus) {
689	if (focus == m_focusParent) {
690		m_focusParent = m_topLevel;
691	}
692}
693
694void InputController::setupCallback(GameController* controller) {
695	m_inputModel->setKeyCallback([this, controller](QMenu* menu, int key, bool down) {
696		if (menu == m_autofireMenu.get()) {
697			controller->setAutofire(key, down);
698		} else {
699			if (down) {
700				controller->keyPressed(key);
701			} else {
702				controller->keyReleased(key);
703			}
704		}
705	});
706}
707
708void InputController::bindKey(const QModelIndex& index, int key) {
709	int coreKey = m_inputModel->keyAt(index);
710	if (coreKey < 0) {
711		return;
712	}
713	mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE);
714	bindKey(platform, KEYBOARD, key, coreKey);
715}
716
717#ifdef BUILD_SDL
718void InputController::bindButton(const QModelIndex& index, int key) {
719	int coreKey = m_inputModel->keyAt(index);
720	if (coreKey < 0) {
721		return;
722	}
723	mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE);
724	bindKey(platform, SDL_BINDING_BUTTON, key, coreKey);
725}
726
727void InputController::bindAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction direction) {
728	int coreKey = m_inputModel->keyAt(index);
729	if (coreKey < 0) {
730		return;
731	}
732	mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE);
733	bindAxis(platform, SDL_BINDING_BUTTON, axis, direction, coreKey);
734}
735
736void InputController::bindHat(const QModelIndex& index, int hat, GamepadHatEvent::Direction direction) {
737	int coreKey = m_inputModel->keyAt(index);
738	if (coreKey < 0) {
739		return;
740	}
741	mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE);
742	bindHat(platform, SDL_BINDING_BUTTON, hat, direction, coreKey);
743}
744#else
745void InputController::bindButton(const QModelIndex& index, int key) {}
746void InputController::bindAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction) {}
747void InputController::bindHat(const QModelIndex& index, int hat, GamepadHatEvent::Direction) {}
748#endif
749
750bool InputController::eventFilter(QObject*, QEvent* event) {
751	if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
752		QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
753		int key = keyEvent->key();
754		if (!InputModel::isModifierKey(key)) {
755			key |= (keyEvent->modifiers() & ~Qt::KeypadModifier);
756		} else {
757			key = InputModel::toModifierKey(key | (keyEvent->modifiers() & ~Qt::KeypadModifier));
758		}
759
760		if (keyEvent->isAutoRepeat()) {
761			event->accept();
762			return true;
763		}
764
765		if (m_inputModel->triggerKey(key, event->type() == QEvent::KeyPress, m_platform)) {
766			event->accept();
767			return true;			
768		}
769	}
770
771
772	if (event->type() == GamepadButtonEvent::Down() || event->type() == GamepadButtonEvent::Up()) {
773		GamepadButtonEvent* gbe = static_cast<GamepadButtonEvent*>(event);
774		if (m_inputModel->triggerButton(gbe->value(), event->type() == GamepadButtonEvent::Down())) {
775			event->accept();
776			return true;
777		}
778	}
779	if (event->type() == GamepadAxisEvent::Type()) {
780		GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event);
781		if (m_inputModel->triggerAxis(gae->axis(), gae->direction(), gae->isNew())) {
782			event->accept();
783			return true;
784		}
785	}
786	return false;
787}
788
789void InputController::restoreModel() {
790	bool signalsBlocked = m_inputModel->blockSignals(true);
791	for (auto iter = m_inputMaps.begin(); iter != m_inputMaps.end(); ++iter) {
792		mPlatform platform = iter.key();
793		QModelIndex parent = m_inputMenuIndices[platform];
794		int nKeys = iter->info->nKeys;
795		for (int i = 0; i < nKeys; ++i) {
796			int key = mInputQueryBinding(&iter.value(), KEYBOARD, i);
797			if (key >= 0) {
798				m_inputModel->updateKey(m_inputModel->index(i, 0, parent), key);
799			} else {
800				m_inputModel->clearKey(m_inputModel->index(i, 0, parent));
801			}
802#ifdef BUILD_SDL
803			key = mInputQueryBinding(&iter.value(), SDL_BINDING_BUTTON, i);
804			if (key >= 0) {
805				m_inputModel->updateButton(m_inputModel->index(i, 0, parent), key);
806			} else {
807				m_inputModel->clearButton(m_inputModel->index(i, 0, parent));
808			}
809#endif
810		}
811#ifdef BUILD_SDL
812		struct Context {
813			InputModel* model;
814			QModelIndex parent;
815		} context{ m_inputModel, parent };
816		mInputEnumerateAxes(&iter.value(), SDL_BINDING_BUTTON, [](int axis, const struct mInputAxis* description, void* user) {
817			Context* context = static_cast<Context*>(user);
818			if (description->highDirection >= 0) {
819				context->model->updateAxis(context->model->index(description->highDirection, 0, context->parent), axis, GamepadAxisEvent::POSITIVE);
820			}
821			if (description->lowDirection >= 0) {
822				context->model->updateAxis(context->model->index(description->lowDirection, 0, context->parent), axis, GamepadAxisEvent::NEGATIVE);
823			}
824		}, &context);
825#endif
826	}
827	m_inputModel->blockSignals(signalsBlocked);
828}