all repos — mgba @ b04bf153048f3452da372e22b6abb8f875d0c915

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