all repos — mgba @ fa73d25acb0b20636bd0adbe3611b528cc1f954f

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