all repos — mgba @ 2f14f58911539fd67228c9113665c5df26061154

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 "GamepadAxisEvent.h"
 10#include "GamepadButtonEvent.h"
 11#include "InputProfile.h"
 12
 13#include <QApplication>
 14#include <QTimer>
 15#include <QWidget>
 16
 17#include <mgba/core/interface.h>
 18#include <mgba-util/configuration.h>
 19
 20using namespace QGBA;
 21
 22#ifdef BUILD_SDL
 23int InputController::s_sdlInited = 0;
 24mSDLEvents InputController::s_sdlEvents;
 25#endif
 26
 27InputController::InputController(int playerId, QWidget* topLevel, QObject* parent)
 28	: QObject(parent)
 29	, m_playerId(playerId)
 30	, m_config(nullptr)
 31	, m_gamepadTimer(nullptr)
 32#ifdef BUILD_SDL
 33	, m_playerAttached(false)
 34#endif
 35	, m_allowOpposing(false)
 36	, m_topLevel(topLevel)
 37	, m_focusParent(topLevel)
 38{
 39	mInputMapInit(&m_inputMap, &GBAInputInfo);
 40
 41#ifdef BUILD_SDL
 42	if (s_sdlInited == 0) {
 43		mSDLInitEvents(&s_sdlEvents);
 44	}
 45	++s_sdlInited;
 46	m_sdlPlayer.bindings = &m_inputMap;
 47	mSDLInitBindingsGBA(&m_inputMap);
 48	updateJoysticks();
 49#endif
 50
 51	m_gamepadTimer = new QTimer(this);
 52#ifdef BUILD_SDL
 53	connect(m_gamepadTimer, &QTimer::timeout, [this]() {
 54		testGamepad(SDL_BINDING_BUTTON);
 55	});
 56#endif
 57	m_gamepadTimer->setInterval(50);
 58	m_gamepadTimer->start();
 59
 60	mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
 61	mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
 62	mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
 63	mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
 64	mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
 65	mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
 66	mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
 67	mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
 68	mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
 69	mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
 70}
 71
 72InputController::~InputController() {
 73	mInputMapDeinit(&m_inputMap);
 74
 75#ifdef BUILD_SDL
 76	if (m_playerAttached) {
 77		mSDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
 78	}
 79
 80	--s_sdlInited;
 81	if (s_sdlInited == 0) {
 82		mSDLDeinitEvents(&s_sdlEvents);
 83	}
 84#endif
 85}
 86
 87void InputController::setConfiguration(ConfigController* config) {
 88	m_config = config;
 89	setAllowOpposing(config->getOption("allowOpposingDirections").toInt());
 90	loadConfiguration(KEYBOARD);
 91#ifdef BUILD_SDL
 92	mSDLEventsLoadConfig(&s_sdlEvents, config->input());
 93	if (!m_playerAttached) {
 94		m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
 95	}
 96	loadConfiguration(SDL_BINDING_BUTTON);
 97	loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
 98#endif
 99}
100
101void InputController::loadConfiguration(uint32_t type) {
102	mInputMapLoad(&m_inputMap, type, m_config->input());
103#ifdef BUILD_SDL
104	if (m_playerAttached) {
105		mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input());
106	}
107#endif
108}
109
110void InputController::loadProfile(uint32_t type, const QString& profile) {
111	bool loaded = mInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
112	recalibrateAxes();
113	if (!loaded) {
114		const InputProfile* ip = InputProfile::findProfile(profile);
115		if (ip) {
116			ip->apply(this);
117		}
118	}
119	emit profileLoaded(profile);
120}
121
122void InputController::saveConfiguration() {
123	saveConfiguration(KEYBOARD);
124#ifdef BUILD_SDL
125	saveConfiguration(SDL_BINDING_BUTTON);
126	saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
127	if (m_playerAttached) {
128		mSDLPlayerSaveConfig(&m_sdlPlayer, m_config->input());
129	}
130	m_config->write();
131#endif
132}
133
134void InputController::saveConfiguration(uint32_t type) {
135	mInputMapSave(&m_inputMap, type, m_config->input());
136	m_config->write();
137}
138
139void InputController::saveProfile(uint32_t type, const QString& profile) {
140	mInputProfileSave(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
141	m_config->write();
142}
143
144const char* InputController::profileForType(uint32_t type) {
145	UNUSED(type);
146#ifdef BUILD_SDL
147	if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
148#if SDL_VERSION_ATLEAST(2, 0, 0)
149		return SDL_JoystickName(m_sdlPlayer.joystick->joystick);
150#else
151		return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick));
152#endif
153	}
154#endif
155	return 0;
156}
157
158QStringList InputController::connectedGamepads(uint32_t type) const {
159	UNUSED(type);
160
161#ifdef BUILD_SDL
162	if (type == SDL_BINDING_BUTTON) {
163		QStringList pads;
164		for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
165			const char* name;
166#if SDL_VERSION_ATLEAST(2, 0, 0)
167			name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick);
168#else
169			name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick));
170#endif
171			if (name) {
172				pads.append(QString(name));
173			} else {
174				pads.append(QString());
175			}
176		}
177		return pads;
178	}
179#endif
180
181	return QStringList();
182}
183
184int InputController::gamepad(uint32_t type) const {
185#ifdef BUILD_SDL
186	if (type == SDL_BINDING_BUTTON) {
187		return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0;
188	}
189#endif
190	return 0;
191}
192
193void InputController::setGamepad(uint32_t type, int index) {
194#ifdef BUILD_SDL
195	if (type == SDL_BINDING_BUTTON) {
196		mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index);
197	}
198#endif
199}
200
201void InputController::setPreferredGamepad(uint32_t type, const QString& device) {
202	if (!m_config) {
203		return;
204	}
205	mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, device.toUtf8().constData());
206}
207
208mRumble* InputController::rumble() {
209#ifdef BUILD_SDL
210#if SDL_VERSION_ATLEAST(2, 0, 0)
211	if (m_playerAttached) {
212		return &m_sdlPlayer.rumble.d;
213	}
214#endif
215#endif
216	return nullptr;
217}
218
219mRotationSource* InputController::rotationSource() {
220#ifdef BUILD_SDL
221	if (m_playerAttached) {
222		return &m_sdlPlayer.rotation.d;
223	}
224#endif
225	return nullptr;
226}
227
228void InputController::registerTiltAxisX(int axis) {
229#ifdef BUILD_SDL
230	if (m_playerAttached) {
231		m_sdlPlayer.rotation.axisX = axis;
232	}
233#endif
234}
235
236void InputController::registerTiltAxisY(int axis) {
237#ifdef BUILD_SDL
238	if (m_playerAttached) {
239		m_sdlPlayer.rotation.axisY = axis;
240	}
241#endif
242}
243
244void InputController::registerGyroAxisX(int axis) {
245#ifdef BUILD_SDL
246	if (m_playerAttached) {
247		m_sdlPlayer.rotation.gyroX = axis;
248	}
249#endif
250}
251
252void InputController::registerGyroAxisY(int axis) {
253#ifdef BUILD_SDL
254	if (m_playerAttached) {
255		m_sdlPlayer.rotation.gyroY = axis;
256	}
257#endif
258}
259
260float InputController::gyroSensitivity() const {
261#ifdef BUILD_SDL
262	if (m_playerAttached) {
263		return m_sdlPlayer.rotation.gyroSensitivity;
264	}
265#endif
266	return 0;
267}
268
269void InputController::setGyroSensitivity(float sensitivity) {
270#ifdef BUILD_SDL
271	if (m_playerAttached) {
272		m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
273	}
274#endif
275}
276
277GBAKey InputController::mapKeyboard(int key) const {
278	return static_cast<GBAKey>(mInputMapKey(&m_inputMap, KEYBOARD, key));
279}
280
281void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
282	return mInputBindKey(&m_inputMap, type, key, gbaKey);
283}
284
285void InputController::updateJoysticks() {
286#ifdef BUILD_SDL
287	mSDLUpdateJoysticks(&s_sdlEvents);
288#endif
289}
290
291int InputController::pollEvents() {
292	int activeButtons = 0;
293#ifdef BUILD_SDL
294	if (m_playerAttached && m_sdlPlayer.joystick) {
295		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
296		SDL_JoystickUpdate();
297		int numButtons = SDL_JoystickNumButtons(joystick);
298		int i;
299		for (i = 0; i < numButtons; ++i) {
300			GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i));
301			if (key == GBA_KEY_NONE) {
302				continue;
303			}
304			if (hasPendingEvent(key)) {
305				continue;
306			}
307			if (SDL_JoystickGetButton(joystick, i)) {
308				activeButtons |= 1 << key;
309			}
310		}
311		int numHats = SDL_JoystickNumHats(joystick);
312		for (i = 0; i < numHats; ++i) {
313			int hat = SDL_JoystickGetHat(joystick, i);
314			activeButtons |= mInputMapHat(&m_inputMap, SDL_BINDING_BUTTON, i, hat);
315		}
316
317		int numAxes = SDL_JoystickNumAxes(joystick);
318		for (i = 0; i < numAxes; ++i) {
319			int value = SDL_JoystickGetAxis(joystick, i);
320
321			enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value));
322			if (key != GBA_KEY_NONE) {
323				activeButtons |= 1 << key;
324			}
325		}
326	}
327#endif
328	return activeButtons;
329}
330
331QSet<int> InputController::activeGamepadButtons(int type) {
332	QSet<int> activeButtons;
333#ifdef BUILD_SDL
334	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
335		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
336		SDL_JoystickUpdate();
337		int numButtons = SDL_JoystickNumButtons(joystick);
338		int i;
339		for (i = 0; i < numButtons; ++i) {
340			if (SDL_JoystickGetButton(joystick, i)) {
341				activeButtons.insert(i);
342			}
343		}
344	}
345#endif
346	return activeButtons;
347}
348
349void InputController::recalibrateAxes() {
350#ifdef BUILD_SDL
351	if (m_playerAttached && m_sdlPlayer.joystick) {
352		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
353		SDL_JoystickUpdate();
354		int numAxes = SDL_JoystickNumAxes(joystick);
355		if (numAxes < 1) {
356			return;
357		}
358		m_deadzones.resize(numAxes);
359		int i;
360		for (i = 0; i < numAxes; ++i) {
361			m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
362		}
363	}
364#endif
365}
366
367QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
368	QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
369#ifdef BUILD_SDL
370	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
371		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
372		SDL_JoystickUpdate();
373		int numAxes = SDL_JoystickNumAxes(joystick);
374		if (numAxes < 1) {
375			return activeAxes;
376		}
377		m_deadzones.resize(numAxes);
378		int i;
379		for (i = 0; i < numAxes; ++i) {
380			int32_t axis = SDL_JoystickGetAxis(joystick, i);
381			axis -= m_deadzones[i];
382			if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
383				activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
384			}
385		}
386	}
387#endif
388	return activeAxes;
389}
390
391void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
392	const mInputAxis* old = mInputQueryAxis(&m_inputMap, type, axis);
393	mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
394	if (old) {
395		description = *old;
396	}
397	int deadzone = 0;
398	if (axis > 0 && m_deadzones.size() > axis) {
399		deadzone = m_deadzones[axis];
400	}
401	switch (direction) {
402	case GamepadAxisEvent::NEGATIVE:
403		description.lowDirection = key;
404
405		description.deadLow = deadzone - AXIS_THRESHOLD;
406		break;
407	case GamepadAxisEvent::POSITIVE:
408		description.highDirection = key;
409		description.deadHigh = deadzone + AXIS_THRESHOLD;
410		break;
411	default:
412		return;
413	}
414	mInputBindAxis(&m_inputMap, type, axis, &description);
415}
416
417void InputController::unbindAllAxes(uint32_t type) {
418	mInputUnbindAllAxes(&m_inputMap, type);
419}
420
421QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {
422	QSet<QPair<int, GamepadHatEvent::Direction>> activeHats;
423#ifdef BUILD_SDL
424	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
425		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
426		SDL_JoystickUpdate();
427		int numHats = SDL_JoystickNumHats(joystick);
428		if (numHats < 1) {
429			return activeHats;
430		}
431
432		int i;
433		for (i = 0; i < numHats; ++i) {
434			int hat = SDL_JoystickGetHat(joystick, i);
435			if (hat & GamepadHatEvent::UP) {
436				activeHats.insert(qMakePair(i, GamepadHatEvent::UP));
437			}
438			if (hat & GamepadHatEvent::RIGHT) {
439				activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT));
440			}
441			if (hat & GamepadHatEvent::DOWN) {
442				activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN));
443			}
444			if (hat & GamepadHatEvent::LEFT) {
445				activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT));
446			}
447		}
448	}
449#endif
450	return activeHats;
451}
452
453void InputController::bindHat(uint32_t type, int hat, GamepadHatEvent::Direction direction, GBAKey gbaKey) {
454	mInputHatBindings bindings{ -1, -1, -1, -1 };
455	mInputQueryHat(&m_inputMap, type, hat, &bindings);
456	switch (direction) {
457	case GamepadHatEvent::UP:
458		bindings.up = gbaKey;
459		break;
460	case GamepadHatEvent::RIGHT:
461		bindings.right = gbaKey;
462		break;
463	case GamepadHatEvent::DOWN:
464		bindings.down = gbaKey;
465		break;
466	case GamepadHatEvent::LEFT:
467		bindings.left = gbaKey;
468		break;
469	default:
470		return;
471	}
472	mInputBindHat(&m_inputMap, type, hat, &bindings);
473}
474
475void InputController::testGamepad(int type) {
476	auto activeAxes = activeGamepadAxes(type);
477	auto oldAxes = m_activeAxes;
478	m_activeAxes = activeAxes;
479
480	auto activeButtons = activeGamepadButtons(type);
481	auto oldButtons = m_activeButtons;
482	m_activeButtons = activeButtons;
483
484	auto activeHats = activeGamepadHats(type);
485	auto oldHats = m_activeHats;
486	m_activeHats = activeHats;
487
488	if (!QApplication::focusWidget()) {
489		return;
490	}
491
492	activeAxes.subtract(oldAxes);
493	oldAxes.subtract(m_activeAxes);
494
495	for (auto& axis : m_activeAxes) {
496		bool newlyAboveThreshold = activeAxes.contains(axis);
497		if (newlyAboveThreshold) {
498			GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
499			postPendingEvent(event->gbaKey());
500			sendGamepadEvent(event);
501			if (!event->isAccepted()) {
502				clearPendingEvent(event->gbaKey());
503			}
504		}
505	}
506	for (auto axis : oldAxes) {
507		GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
508		clearPendingEvent(event->gbaKey());
509		sendGamepadEvent(event);
510	}
511
512	if (!QApplication::focusWidget()) {
513		return;
514	}
515
516	activeButtons.subtract(oldButtons);
517	oldButtons.subtract(m_activeButtons);
518
519	for (int button : activeButtons) {
520		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
521		postPendingEvent(event->gbaKey());
522		sendGamepadEvent(event);
523		if (!event->isAccepted()) {
524			clearPendingEvent(event->gbaKey());
525		}
526	}
527	for (int button : oldButtons) {
528		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
529		clearPendingEvent(event->gbaKey());
530		sendGamepadEvent(event);
531	}
532
533	activeHats.subtract(oldHats);
534	oldHats.subtract(m_activeHats);
535
536	for (auto& hat : activeHats) {
537		GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this);
538		postPendingEvent(event->gbaKey());
539		sendGamepadEvent(event);
540		if (!event->isAccepted()) {
541			clearPendingEvent(event->gbaKey());
542		}
543	}
544	for (auto& hat : oldHats) {
545		GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this);
546		clearPendingEvent(event->gbaKey());
547		sendGamepadEvent(event);
548	}
549}
550
551void InputController::sendGamepadEvent(QEvent* event) {
552	QWidget* focusWidget = nullptr;
553	if (m_focusParent) {
554		focusWidget = m_focusParent->focusWidget();
555		if (!focusWidget) {
556			focusWidget = m_focusParent;
557		}
558	} else {
559		focusWidget = QApplication::focusWidget();
560	}
561	QApplication::sendEvent(focusWidget, event);
562}
563
564void InputController::postPendingEvent(GBAKey key) {
565	m_pendingEvents.insert(key);
566}
567
568void InputController::clearPendingEvent(GBAKey key) {
569	m_pendingEvents.remove(key);
570}
571
572bool InputController::hasPendingEvent(GBAKey key) const {
573	return m_pendingEvents.contains(key);
574}
575
576void InputController::suspendScreensaver() {
577#ifdef BUILD_SDL
578#if SDL_VERSION_ATLEAST(2, 0, 0)
579	mSDLSuspendScreensaver(&s_sdlEvents);
580#endif
581#endif
582}
583
584void InputController::resumeScreensaver() {
585#ifdef BUILD_SDL
586#if SDL_VERSION_ATLEAST(2, 0, 0)
587	mSDLResumeScreensaver(&s_sdlEvents);
588#endif
589#endif
590}
591
592void InputController::setScreensaverSuspendable(bool suspendable) {
593#ifdef BUILD_SDL
594#if SDL_VERSION_ATLEAST(2, 0, 0)
595	mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
596#endif
597#endif
598}
599
600void InputController::stealFocus(QWidget* focus) {
601	m_focusParent = focus;
602}
603
604void InputController::releaseFocus(QWidget* focus) {
605	if (focus == m_focusParent) {
606		m_focusParent = m_topLevel;
607	}
608}