all repos — mgba @ 2da3d3e6ba282d437c69a8d80b0a2e0f46214a39

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	mSDLUpdateJoysticks(&s_sdlEvents);
290#endif
291}
292
293int InputController::pollEvents() {
294	int activeButtons = 0;
295#ifdef BUILD_SDL
296	if (m_playerAttached && m_sdlPlayer.joystick) {
297		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
298		SDL_JoystickUpdate();
299		int numButtons = SDL_JoystickNumButtons(joystick);
300		int i;
301		for (i = 0; i < numButtons; ++i) {
302			GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i));
303			if (key == GBA_KEY_NONE) {
304				continue;
305			}
306			if (hasPendingEvent(key)) {
307				continue;
308			}
309			if (SDL_JoystickGetButton(joystick, i)) {
310				activeButtons |= 1 << key;
311			}
312		}
313		int numHats = SDL_JoystickNumHats(joystick);
314		for (i = 0; i < numHats; ++i) {
315			int hat = SDL_JoystickGetHat(joystick, i);
316			activeButtons |= mInputMapHat(&m_inputMap, SDL_BINDING_BUTTON, i, hat);
317		}
318
319		int numAxes = SDL_JoystickNumAxes(joystick);
320		for (i = 0; i < numAxes; ++i) {
321			int value = SDL_JoystickGetAxis(joystick, i);
322
323			enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value));
324			if (key != GBA_KEY_NONE) {
325				activeButtons |= 1 << key;
326			}
327		}
328	}
329#endif
330	return activeButtons;
331}
332
333QSet<int> InputController::activeGamepadButtons(int type) {
334	QSet<int> activeButtons;
335#ifdef BUILD_SDL
336	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
337		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
338		SDL_JoystickUpdate();
339		int numButtons = SDL_JoystickNumButtons(joystick);
340		int i;
341		for (i = 0; i < numButtons; ++i) {
342			if (SDL_JoystickGetButton(joystick, i)) {
343				activeButtons.insert(i);
344			}
345		}
346	}
347#endif
348	return activeButtons;
349}
350
351void InputController::recalibrateAxes() {
352#ifdef BUILD_SDL
353	if (m_playerAttached && m_sdlPlayer.joystick) {
354		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
355		SDL_JoystickUpdate();
356		int numAxes = SDL_JoystickNumAxes(joystick);
357		if (numAxes < 1) {
358			return;
359		}
360		m_deadzones.resize(numAxes);
361		int i;
362		for (i = 0; i < numAxes; ++i) {
363			m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
364		}
365	}
366#endif
367}
368
369QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
370	QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
371#ifdef BUILD_SDL
372	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
373		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
374		SDL_JoystickUpdate();
375		int numAxes = SDL_JoystickNumAxes(joystick);
376		if (numAxes < 1) {
377			return activeAxes;
378		}
379		m_deadzones.resize(numAxes);
380		int i;
381		for (i = 0; i < numAxes; ++i) {
382			int32_t axis = SDL_JoystickGetAxis(joystick, i);
383			axis -= m_deadzones[i];
384			if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
385				activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
386			}
387		}
388	}
389#endif
390	return activeAxes;
391}
392
393void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
394	const mInputAxis* old = mInputQueryAxis(&m_inputMap, type, axis);
395	mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
396	if (old) {
397		description = *old;
398	}
399	int deadzone = 0;
400	if (axis > 0 && m_deadzones.size() > axis) {
401		deadzone = m_deadzones[axis];
402	}
403	switch (direction) {
404	case GamepadAxisEvent::NEGATIVE:
405		description.lowDirection = key;
406
407		description.deadLow = deadzone - AXIS_THRESHOLD;
408		break;
409	case GamepadAxisEvent::POSITIVE:
410		description.highDirection = key;
411		description.deadHigh = deadzone + AXIS_THRESHOLD;
412		break;
413	default:
414		return;
415	}
416	mInputBindAxis(&m_inputMap, type, axis, &description);
417}
418
419void InputController::unbindAllAxes(uint32_t type) {
420	mInputUnbindAllAxes(&m_inputMap, type);
421}
422
423QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {
424	QSet<QPair<int, GamepadHatEvent::Direction>> activeHats;
425#ifdef BUILD_SDL
426	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
427		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
428		SDL_JoystickUpdate();
429		int numHats = SDL_JoystickNumHats(joystick);
430		if (numHats < 1) {
431			return activeHats;
432		}
433
434		int i;
435		for (i = 0; i < numHats; ++i) {
436			int hat = SDL_JoystickGetHat(joystick, i);
437			if (hat & GamepadHatEvent::UP) {
438				activeHats.insert(qMakePair(i, GamepadHatEvent::UP));
439			}
440			if (hat & GamepadHatEvent::RIGHT) {
441				activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT));
442			}
443			if (hat & GamepadHatEvent::DOWN) {
444				activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN));
445			}
446			if (hat & GamepadHatEvent::LEFT) {
447				activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT));
448			}
449		}
450	}
451#endif
452	return activeHats;
453}
454
455void InputController::bindHat(uint32_t type, int hat, GamepadHatEvent::Direction direction, GBAKey gbaKey) {
456	mInputHatBindings bindings{ -1, -1, -1, -1 };
457	mInputQueryHat(&m_inputMap, type, hat, &bindings);
458	switch (direction) {
459	case GamepadHatEvent::UP:
460		bindings.up = gbaKey;
461		break;
462	case GamepadHatEvent::RIGHT:
463		bindings.right = gbaKey;
464		break;
465	case GamepadHatEvent::DOWN:
466		bindings.down = gbaKey;
467		break;
468	case GamepadHatEvent::LEFT:
469		bindings.left = gbaKey;
470		break;
471	default:
472		return;
473	}
474	mInputBindHat(&m_inputMap, type, hat, &bindings);
475}
476
477void InputController::testGamepad(int type) {
478	auto activeAxes = activeGamepadAxes(type);
479	auto oldAxes = m_activeAxes;
480	m_activeAxes = activeAxes;
481
482	auto activeButtons = activeGamepadButtons(type);
483	auto oldButtons = m_activeButtons;
484	m_activeButtons = activeButtons;
485
486	auto activeHats = activeGamepadHats(type);
487	auto oldHats = m_activeHats;
488	m_activeHats = activeHats;
489
490	if (!QApplication::focusWidget()) {
491		return;
492	}
493
494	activeAxes.subtract(oldAxes);
495	oldAxes.subtract(m_activeAxes);
496
497	for (auto& axis : m_activeAxes) {
498		bool newlyAboveThreshold = activeAxes.contains(axis);
499		if (newlyAboveThreshold) {
500			GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
501			postPendingEvent(event->gbaKey());
502			sendGamepadEvent(event);
503			if (!event->isAccepted()) {
504				clearPendingEvent(event->gbaKey());
505			}
506		}
507	}
508	for (auto axis : oldAxes) {
509		GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
510		clearPendingEvent(event->gbaKey());
511		sendGamepadEvent(event);
512	}
513
514	if (!QApplication::focusWidget()) {
515		return;
516	}
517
518	activeButtons.subtract(oldButtons);
519	oldButtons.subtract(m_activeButtons);
520
521	for (int button : activeButtons) {
522		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
523		postPendingEvent(event->gbaKey());
524		sendGamepadEvent(event);
525		if (!event->isAccepted()) {
526			clearPendingEvent(event->gbaKey());
527		}
528	}
529	for (int button : oldButtons) {
530		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
531		clearPendingEvent(event->gbaKey());
532		sendGamepadEvent(event);
533	}
534
535	activeHats.subtract(oldHats);
536	oldHats.subtract(m_activeHats);
537
538	for (auto& hat : activeHats) {
539		GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this);
540		postPendingEvent(event->gbaKey());
541		sendGamepadEvent(event);
542		if (!event->isAccepted()) {
543			clearPendingEvent(event->gbaKey());
544		}
545	}
546	for (auto& hat : oldHats) {
547		GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this);
548		clearPendingEvent(event->gbaKey());
549		sendGamepadEvent(event);
550	}
551}
552
553void InputController::sendGamepadEvent(QEvent* event) {
554	QWidget* focusWidget = nullptr;
555	if (m_focusParent) {
556		focusWidget = m_focusParent->focusWidget();
557		if (!focusWidget) {
558			focusWidget = m_focusParent;
559		}
560	} else {
561		focusWidget = QApplication::focusWidget();
562	}
563	QApplication::sendEvent(focusWidget, event);
564}
565
566void InputController::postPendingEvent(GBAKey key) {
567	m_pendingEvents.insert(key);
568}
569
570void InputController::clearPendingEvent(GBAKey key) {
571	m_pendingEvents.remove(key);
572}
573
574bool InputController::hasPendingEvent(GBAKey key) const {
575	return m_pendingEvents.contains(key);
576}
577
578void InputController::suspendScreensaver() {
579#ifdef BUILD_SDL
580#if SDL_VERSION_ATLEAST(2, 0, 0)
581	mSDLSuspendScreensaver(&s_sdlEvents);
582#endif
583#endif
584}
585
586void InputController::resumeScreensaver() {
587#ifdef BUILD_SDL
588#if SDL_VERSION_ATLEAST(2, 0, 0)
589	mSDLResumeScreensaver(&s_sdlEvents);
590#endif
591#endif
592}
593
594void InputController::setScreensaverSuspendable(bool suspendable) {
595#ifdef BUILD_SDL
596#if SDL_VERSION_ATLEAST(2, 0, 0)
597	mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
598#endif
599#endif
600}
601
602void InputController::stealFocus(QWidget* focus) {
603	m_focusParent = focus;
604}
605
606void InputController::releaseFocus(QWidget* focus) {
607	if (focus == m_focusParent) {
608		m_focusParent = m_topLevel;
609	}
610}