all repos — mgba @ 686380b6c4d8acf219a704a8932668b11efc47c2

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