all repos — mgba @ cb0f95b07053e63e817bd05df0a36bf917667d09

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