all repos — mgba @ f247d3b337835cf29dd6d678fb7b3afa2086cbeb

mGBA Game Boy Advance Emulator

src/platform/qt/input/InputController.cpp (view raw)

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