all repos — mgba @ 42db8282350df4b2679c7bc4c7d631df0472af5b

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