all repos — mgba @ f6f3cb5d3d8b91dd603772ea0eebb2513562a0cf

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