all repos — mgba @ f0a4c4905eb549fb24d0c882c66d07a6bae971a1

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		mInputMapSave(&inputMap, type, m_config->input());
188	}
189	m_config->write();
190}
191
192void InputController::saveProfile(uint32_t type, const QString& profile) {
193	for (auto& inputMap : m_inputMaps) {
194		mInputProfileSave(&inputMap, type, m_config->input(), profile.toUtf8().constData());
195	}
196	m_config->write();
197}
198
199const char* InputController::profileForType(uint32_t type) {
200	UNUSED(type);
201#ifdef BUILD_SDL
202	if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
203#if SDL_VERSION_ATLEAST(2, 0, 0)
204		return SDL_JoystickName(m_sdlPlayer.joystick->joystick);
205#else
206		return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick));
207#endif
208	}
209#endif
210	return 0;
211}
212
213QStringList InputController::connectedGamepads(uint32_t type) const {
214	UNUSED(type);
215
216#ifdef BUILD_SDL
217	if (type == SDL_BINDING_BUTTON) {
218		QStringList pads;
219		for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
220			const char* name;
221#if SDL_VERSION_ATLEAST(2, 0, 0)
222			name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick);
223#else
224			name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick));
225#endif
226			if (name) {
227				pads.append(QString(name));
228			} else {
229				pads.append(QString());
230			}
231		}
232		return pads;
233	}
234#endif
235
236	return QStringList();
237}
238
239int InputController::gamepad(uint32_t type) const {
240#ifdef BUILD_SDL
241	if (type == SDL_BINDING_BUTTON) {
242		return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0;
243	}
244#endif
245	return 0;
246}
247
248void InputController::setGamepad(uint32_t type, int index) {
249#ifdef BUILD_SDL
250	if (type == SDL_BINDING_BUTTON) {
251		mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index);
252	}
253#endif
254}
255
256void InputController::setPreferredGamepad(uint32_t type, const QString& device) {
257	if (!m_config) {
258		return;
259	}
260	mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, device.toUtf8().constData());
261}
262
263mRumble* InputController::rumble() {
264#ifdef BUILD_SDL
265#if SDL_VERSION_ATLEAST(2, 0, 0)
266	if (m_playerAttached) {
267		return &m_sdlPlayer.rumble.d;
268	}
269#endif
270#endif
271	return nullptr;
272}
273
274mRotationSource* InputController::rotationSource() {
275#ifdef BUILD_SDL
276	if (m_playerAttached) {
277		return &m_sdlPlayer.rotation.d;
278	}
279#endif
280	return nullptr;
281}
282
283void InputController::registerTiltAxisX(int axis) {
284#ifdef BUILD_SDL
285	if (m_playerAttached) {
286		m_sdlPlayer.rotation.axisX = axis;
287	}
288#endif
289}
290
291void InputController::registerTiltAxisY(int axis) {
292#ifdef BUILD_SDL
293	if (m_playerAttached) {
294		m_sdlPlayer.rotation.axisY = axis;
295	}
296#endif
297}
298
299void InputController::registerGyroAxisX(int axis) {
300#ifdef BUILD_SDL
301	if (m_playerAttached) {
302		m_sdlPlayer.rotation.gyroX = axis;
303	}
304#endif
305}
306
307void InputController::registerGyroAxisY(int axis) {
308#ifdef BUILD_SDL
309	if (m_playerAttached) {
310		m_sdlPlayer.rotation.gyroY = axis;
311	}
312#endif
313}
314
315float InputController::gyroSensitivity() const {
316#ifdef BUILD_SDL
317	if (m_playerAttached) {
318		return m_sdlPlayer.rotation.gyroSensitivity;
319	}
320#endif
321	return 0;
322}
323
324void InputController::setGyroSensitivity(float sensitivity) {
325#ifdef BUILD_SDL
326	if (m_playerAttached) {
327		m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
328	}
329#endif
330}
331
332void InputController::updateJoysticks() {
333#ifdef BUILD_SDL
334	QString profile = profileForType(SDL_BINDING_BUTTON);
335	mSDLUpdateJoysticks(&s_sdlEvents, m_config->input());
336	QString newProfile = profileForType(SDL_BINDING_BUTTON);
337	if (profile != newProfile) {
338		loadProfile(SDL_BINDING_BUTTON, newProfile);
339	}
340#endif
341}
342
343const mInputMap* InputController::map() {
344	return &m_inputMaps[m_platform];
345}
346
347int InputController::pollEvents() {
348	int activeButtons = 0;
349#ifdef BUILD_SDL
350	if (m_playerAttached && m_sdlPlayer.joystick) {
351		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
352		SDL_JoystickUpdate();
353		int numButtons = SDL_JoystickNumButtons(joystick);
354		int i;
355		for (i = 0; i < numButtons; ++i) {
356			GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i));
357			if (key == GBA_KEY_NONE) {
358				continue;
359			}
360			if (hasPendingEvent(key)) {
361				continue;
362			}
363			if (SDL_JoystickGetButton(joystick, i)) {
364				activeButtons |= 1 << key;
365			}
366		}
367		int numHats = SDL_JoystickNumHats(joystick);
368		for (i = 0; i < numHats; ++i) {
369			int hat = SDL_JoystickGetHat(joystick, i);
370			activeButtons |= mInputMapHat(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i, hat);
371		}
372
373		int numAxes = SDL_JoystickNumAxes(joystick);
374		for (i = 0; i < numAxes; ++i) {
375			int value = SDL_JoystickGetAxis(joystick, i);
376
377			enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i, value));
378			if (key != GBA_KEY_NONE) {
379				activeButtons |= 1 << key;
380			}
381		}
382	}
383#endif
384	return activeButtons;
385}
386
387QSet<int> InputController::activeGamepadButtons(int type) {
388	QSet<int> activeButtons;
389#ifdef BUILD_SDL
390	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
391		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
392		SDL_JoystickUpdate();
393		int numButtons = SDL_JoystickNumButtons(joystick);
394		int i;
395		for (i = 0; i < numButtons; ++i) {
396			if (SDL_JoystickGetButton(joystick, i)) {
397				activeButtons.insert(i);
398			}
399		}
400	}
401#endif
402	return activeButtons;
403}
404
405void InputController::recalibrateAxes() {
406#ifdef BUILD_SDL
407	if (m_playerAttached && m_sdlPlayer.joystick) {
408		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
409		SDL_JoystickUpdate();
410		int numAxes = SDL_JoystickNumAxes(joystick);
411		if (numAxes < 1) {
412			return;
413		}
414		m_deadzones.resize(numAxes);
415		int i;
416		for (i = 0; i < numAxes; ++i) {
417			m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
418		}
419	}
420#endif
421}
422
423QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
424	QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
425#ifdef BUILD_SDL
426	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
427		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
428		SDL_JoystickUpdate();
429		int numAxes = SDL_JoystickNumAxes(joystick);
430		if (numAxes < 1) {
431			return activeAxes;
432		}
433		m_deadzones.resize(numAxes);
434		int i;
435		for (i = 0; i < numAxes; ++i) {
436			int32_t axis = SDL_JoystickGetAxis(joystick, i);
437			axis -= m_deadzones[i];
438			if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
439				activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
440			}
441		}
442	}
443#endif
444	return activeAxes;
445}
446
447void InputController::bindKey(mPlatform platform, uint32_t type, int key, int coreKey) {
448	QModelIndex index = m_inputModel->index(coreKey, 0, m_inputMenuIndices[platform]);
449	bool signalsBlocked = m_inputModel->blockSignals(true);
450	if (type != KEYBOARD) {
451		m_inputModel->updateButton(index, key);
452	} else {
453		m_inputModel->updateKey(index, key);		
454	}
455	m_inputModel->blockSignals(signalsBlocked);
456	mInputBindKey(&m_inputMaps[platform], type, key, coreKey);
457}
458
459void InputController::bindAxis(mPlatform platform, uint32_t type, int axis, GamepadAxisEvent::Direction direction, int key) {
460	QModelIndex index = m_inputModel->index(key, 0, m_inputMenuIndices[platform]);
461	bool signalsBlocked = m_inputModel->blockSignals(true);
462	m_inputModel->updateAxis(index, axis, direction);
463	m_inputModel->blockSignals(signalsBlocked);
464
465	const mInputAxis* old = mInputQueryAxis(&m_inputMaps[platform], type, axis);
466	mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
467	if (old) {
468		description = *old;
469	}
470	int deadzone = 0;
471	if (axis > 0 && m_deadzones.size() > axis) {
472		deadzone = m_deadzones[axis];
473	}
474	switch (direction) {
475	case GamepadAxisEvent::NEGATIVE:
476		description.lowDirection = key;
477
478		description.deadLow = deadzone - AXIS_THRESHOLD;
479		break;
480	case GamepadAxisEvent::POSITIVE:
481		description.highDirection = key;
482		description.deadHigh = deadzone + AXIS_THRESHOLD;
483		break;
484	default:
485		return;
486	}
487	mInputBindAxis(&m_inputMaps[platform], type, axis, &description);
488}
489
490QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {
491	QSet<QPair<int, GamepadHatEvent::Direction>> activeHats;
492#ifdef BUILD_SDL
493	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
494		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
495		SDL_JoystickUpdate();
496		int numHats = SDL_JoystickNumHats(joystick);
497		if (numHats < 1) {
498			return activeHats;
499		}
500
501		int i;
502		for (i = 0; i < numHats; ++i) {
503			int hat = SDL_JoystickGetHat(joystick, i);
504			if (hat & GamepadHatEvent::UP) {
505				activeHats.insert(qMakePair(i, GamepadHatEvent::UP));
506			}
507			if (hat & GamepadHatEvent::RIGHT) {
508				activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT));
509			}
510			if (hat & GamepadHatEvent::DOWN) {
511				activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN));
512			}
513			if (hat & GamepadHatEvent::LEFT) {
514				activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT));
515			}
516		}
517	}
518#endif
519	return activeHats;
520}
521
522void InputController::bindHat(mPlatform platform, uint32_t type, int hat, GamepadHatEvent::Direction direction, int coreKey) {
523	QModelIndex index = m_inputModel->index(coreKey, 0, m_inputMenuIndices[platform]);
524	//m_inputModel->updateHat(index, hat, direction);
525
526	mInputHatBindings bindings{ -1, -1, -1, -1 };
527	mInputQueryHat(&m_inputMaps[platform], type, hat, &bindings);
528	switch (direction) {
529	case GamepadHatEvent::UP:
530		bindings.up = coreKey;
531		break;
532	case GamepadHatEvent::RIGHT:
533		bindings.right = coreKey;
534		break;
535	case GamepadHatEvent::DOWN:
536		bindings.down = coreKey;
537		break;
538	case GamepadHatEvent::LEFT:
539		bindings.left = coreKey;
540		break;
541	default:
542		return;
543	}
544	mInputBindHat(&m_inputMaps[platform], type, hat, &bindings);
545}
546
547void InputController::testGamepad(int type) {
548	auto activeAxes = activeGamepadAxes(type);
549	auto oldAxes = m_activeAxes;
550	m_activeAxes = activeAxes;
551
552	auto activeButtons = activeGamepadButtons(type);
553	auto oldButtons = m_activeButtons;
554	m_activeButtons = activeButtons;
555
556	auto activeHats = activeGamepadHats(type);
557	auto oldHats = m_activeHats;
558	m_activeHats = activeHats;
559
560	if (!QApplication::focusWidget()) {
561		return;
562	}
563
564	activeAxes.subtract(oldAxes);
565	oldAxes.subtract(m_activeAxes);
566
567	for (auto& axis : m_activeAxes) {
568		bool newlyAboveThreshold = activeAxes.contains(axis);
569		if (newlyAboveThreshold) {
570			GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
571			postPendingEvent(event->gbaKey());
572			sendGamepadEvent(event);
573			if (!event->isAccepted()) {
574				clearPendingEvent(event->gbaKey());
575			}
576		}
577	}
578	for (auto axis : oldAxes) {
579		GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
580		clearPendingEvent(event->gbaKey());
581		sendGamepadEvent(event);
582	}
583
584	if (!QApplication::focusWidget()) {
585		return;
586	}
587
588	activeButtons.subtract(oldButtons);
589	oldButtons.subtract(m_activeButtons);
590
591	for (int button : activeButtons) {
592		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
593		postPendingEvent(event->gbaKey());
594		sendGamepadEvent(event);
595		if (!event->isAccepted()) {
596			clearPendingEvent(event->gbaKey());
597		}
598	}
599	for (int button : oldButtons) {
600		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
601		clearPendingEvent(event->gbaKey());
602		sendGamepadEvent(event);
603	}
604
605	activeHats.subtract(oldHats);
606	oldHats.subtract(m_activeHats);
607
608	for (auto& hat : activeHats) {
609		GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this);
610		postPendingEvent(event->gbaKey());
611		sendGamepadEvent(event);
612		if (!event->isAccepted()) {
613			clearPendingEvent(event->gbaKey());
614		}
615	}
616	for (auto& hat : oldHats) {
617		GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this);
618		clearPendingEvent(event->gbaKey());
619		sendGamepadEvent(event);
620	}
621}
622
623void InputController::sendGamepadEvent(QEvent* event) {
624	QWidget* focusWidget = nullptr;
625	if (m_focusParent) {
626		focusWidget = m_focusParent->focusWidget();
627		if (!focusWidget) {
628			focusWidget = m_focusParent;
629		}
630	} else {
631		focusWidget = QApplication::focusWidget();
632	}
633	QApplication::sendEvent(focusWidget, event);
634}
635
636void InputController::postPendingEvent(int key) {
637	m_pendingEvents.insert(key);
638}
639
640void InputController::clearPendingEvent(int key) {
641	m_pendingEvents.remove(key);
642}
643
644bool InputController::hasPendingEvent(int key) const {
645	return m_pendingEvents.contains(key);
646}
647
648void InputController::suspendScreensaver() {
649#ifdef BUILD_SDL
650#if SDL_VERSION_ATLEAST(2, 0, 0)
651	mSDLSuspendScreensaver(&s_sdlEvents);
652#endif
653#endif
654}
655
656void InputController::resumeScreensaver() {
657#ifdef BUILD_SDL
658#if SDL_VERSION_ATLEAST(2, 0, 0)
659	mSDLResumeScreensaver(&s_sdlEvents);
660#endif
661#endif
662}
663
664void InputController::setScreensaverSuspendable(bool suspendable) {
665#ifdef BUILD_SDL
666#if SDL_VERSION_ATLEAST(2, 0, 0)
667	mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
668#endif
669#endif
670}
671
672void InputController::stealFocus(QWidget* focus) {
673	m_focusParent = focus;
674}
675
676void InputController::releaseFocus(QWidget* focus) {
677	if (focus == m_focusParent) {
678		m_focusParent = m_topLevel;
679	}
680}
681
682void InputController::setupCallback(GameController* controller) {
683	m_inputModel->setKeyCallback([this, controller](QMenu* menu, int key, bool down) {
684		if (menu == m_autofireMenu.get()) {
685			controller->setAutofire(key, down);
686		} else {
687			if (down) {
688				controller->keyPressed(key);
689			} else {
690				controller->keyReleased(key);
691			}
692		}
693	});
694}
695
696void InputController::bindKey(const QModelIndex& index, int key) {
697	int coreKey = m_inputModel->keyAt(index);
698	if (coreKey < 0) {
699		return;
700	}
701	mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE);
702	bindKey(platform, KEYBOARD, key, coreKey);
703}
704
705#ifdef BUILD_SDL
706void InputController::bindButton(const QModelIndex& index, int key) {
707	int coreKey = m_inputModel->keyAt(index);
708	if (coreKey < 0) {
709		return;
710	}
711	mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE);
712	bindKey(platform, SDL_BINDING_BUTTON, key, coreKey);
713}
714
715void InputController::bindAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction direction) {
716	int coreKey = m_inputModel->keyAt(index);
717	if (coreKey < 0) {
718		return;
719	}
720	mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE);
721	bindAxis(platform, SDL_BINDING_BUTTON, axis, direction, coreKey);
722}
723
724void InputController::bindHat(const QModelIndex& index, int hat, GamepadHatEvent::Direction direction) {
725	int coreKey = m_inputModel->keyAt(index);
726	if (coreKey < 0) {
727		return;
728	}
729	mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE);
730	bindHat(platform, SDL_BINDING_BUTTON, hat, direction, coreKey);
731}
732#else
733void InputController::bindButton(const QModelIndex& index, int key) {}
734void InputController::bindAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction) {}
735void InputController::bindHat(const QModelIndex& index, int hat, GamepadHatEvent::Direction) {}
736#endif
737
738bool InputController::eventFilter(QObject*, QEvent* event) {
739	if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
740		QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
741		int key = keyEvent->key();
742		if (!InputModel::isModifierKey(key)) {
743			key |= (keyEvent->modifiers() & ~Qt::KeypadModifier);
744		} else {
745			key = InputModel::toModifierKey(key | (keyEvent->modifiers() & ~Qt::KeypadModifier));
746		}
747
748		if (keyEvent->isAutoRepeat()) {
749			event->accept();
750			return true;
751		}
752
753		if (m_inputModel->triggerKey(key, event->type() == QEvent::KeyPress, m_platform)) {
754			event->accept();
755			return true;			
756		}
757	}
758
759
760	if (event->type() == GamepadButtonEvent::Down() || event->type() == GamepadButtonEvent::Up()) {
761		GamepadButtonEvent* gbe = static_cast<GamepadButtonEvent*>(event);
762		if (m_inputModel->triggerButton(gbe->value(), event->type() == GamepadButtonEvent::Down())) {
763			event->accept();
764			return true;
765		}
766	}
767	if (event->type() == GamepadAxisEvent::Type()) {
768		GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event);
769		if (m_inputModel->triggerAxis(gae->axis(), gae->direction(), gae->isNew())) {
770			event->accept();
771			return true;
772		}
773	}
774	return false;
775}
776
777void InputController::restoreModel() {
778	bool signalsBlocked = m_inputModel->blockSignals(true);
779	for (auto iter = m_inputMaps.begin(); iter != m_inputMaps.end(); ++iter) {
780		mPlatform platform = iter.key();
781		QModelIndex parent = m_inputMenuIndices[platform];
782		int nKeys = iter->info->nKeys;
783		for (int i = 0; i < nKeys; ++i) {
784			int key = mInputQueryBinding(&iter.value(), KEYBOARD, i);
785			if (key >= 0) {
786				m_inputModel->updateKey(m_inputModel->index(i, 0, parent), key);
787			} else {
788				m_inputModel->clearKey(m_inputModel->index(i, 0, parent));
789			}
790#ifdef BUILD_SDL
791			key = mInputQueryBinding(&iter.value(), SDL_BINDING_BUTTON, i);
792			if (key >= 0) {
793				m_inputModel->updateButton(m_inputModel->index(i, 0, parent), key);
794			} else {
795				m_inputModel->clearButton(m_inputModel->index(i, 0, parent));
796			}
797#endif
798		}
799#ifdef BUILD_SDL
800		struct Context {
801			InputModel* model;
802			QModelIndex parent;
803		} context{ m_inputModel, parent };
804		mInputEnumerateAxes(&iter.value(), SDL_BINDING_BUTTON, [](int axis, const struct mInputAxis* description, void* user) {
805			Context* context = static_cast<Context*>(user);
806			if (description->highDirection >= 0) {
807				context->model->updateAxis(context->model->index(description->highDirection, 0, context->parent), axis, GamepadAxisEvent::POSITIVE);
808			}
809			if (description->lowDirection >= 0) {
810				context->model->updateAxis(context->model->index(description->lowDirection, 0, context->parent), axis, GamepadAxisEvent::NEGATIVE);
811			}
812		}, &context);
813#endif
814	}
815	m_inputModel->blockSignals(signalsBlocked);
816}