all repos — mgba @ 9dfcef3f45efe580e252ec8bc3852257e9de9fd6

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