all repos — mgba @ 75fb2548bbb6dea49e6d1040e3b59d76f5548fa6

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
 17extern "C" {
 18#include "util/configuration.h"
 19}
 20
 21using namespace QGBA;
 22
 23#ifdef BUILD_SDL
 24int InputController::s_sdlInited = 0;
 25GBASDLEvents InputController::s_sdlEvents;
 26#endif
 27
 28InputController::InputController(int playerId, QWidget* topLevel, QObject* parent)
 29	: QObject(parent)
 30	, m_playerId(playerId)
 31	, m_config(nullptr)
 32	, m_gamepadTimer(nullptr)
 33#ifdef BUILD_SDL
 34	, m_playerAttached(false)
 35#endif
 36	, m_allowOpposing(false)
 37	, m_topLevel(topLevel)
 38{
 39	GBAInputMapInit(&m_inputMap);
 40
 41#ifdef BUILD_SDL
 42	if (s_sdlInited == 0) {
 43		GBASDLInitEvents(&s_sdlEvents);
 44	}
 45	++s_sdlInited;
 46	m_sdlPlayer.bindings = &m_inputMap;
 47	GBASDLInitBindings(&m_inputMap);
 48#endif
 49
 50	m_gamepadTimer = new QTimer(this);
 51#ifdef BUILD_SDL
 52	connect(m_gamepadTimer, &QTimer::timeout, [this]() {
 53		testGamepad(SDL_BINDING_BUTTON);
 54	});
 55#endif
 56	m_gamepadTimer->setInterval(50);
 57	m_gamepadTimer->start();
 58
 59	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
 60	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
 61	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
 62	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
 63	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
 64	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
 65	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
 66	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
 67	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
 68	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
 69}
 70
 71InputController::~InputController() {
 72	GBAInputMapDeinit(&m_inputMap);
 73
 74#ifdef BUILD_SDL
 75	if (m_playerAttached) {
 76		GBASDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
 77	}
 78
 79	--s_sdlInited;
 80	if (s_sdlInited == 0) {
 81		GBASDLDeinitEvents(&s_sdlEvents);
 82	}
 83#endif
 84}
 85
 86void InputController::setConfiguration(ConfigController* config) {
 87	m_config = config;
 88	setAllowOpposing(config->getOption("allowOpposingDirections").toInt());
 89	loadConfiguration(KEYBOARD);
 90#ifdef BUILD_SDL
 91	GBASDLEventsLoadConfig(&s_sdlEvents, config->input());
 92	if (!m_playerAttached) {
 93		GBASDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
 94		m_playerAttached = true;
 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	GBAInputMapLoad(&m_inputMap, type, m_config->input());
103#ifdef BUILD_SDL
104	if (m_playerAttached) {
105		GBASDLPlayerLoadConfig(&m_sdlPlayer, m_config->input());
106	}
107#endif
108}
109
110void InputController::loadProfile(uint32_t type, const QString& profile) {
111	bool loaded = GBAInputProfileLoad(&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		GBASDLPlayerSaveConfig(&m_sdlPlayer, m_config->input());
129	}
130	m_config->write();
131#endif
132}
133
134void InputController::saveConfiguration(uint32_t type) {
135	GBAInputMapSave(&m_inputMap, type, m_config->input());
136	m_config->write();
137}
138
139void InputController::saveProfile(uint32_t type, const QString& profile) {
140	GBAInputProfileSave(&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);
150#else
151		return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.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 < s_sdlEvents.nJoysticks; ++i) {
165			const char* name;
166#if SDL_VERSION_ATLEAST(2, 0, 0)
167			name = SDL_JoystickName(s_sdlEvents.joysticks[i]);
168#else
169			name = SDL_JoystickName(SDL_JoystickIndex(s_sdlEvents.joysticks[i]));
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.joystickIndex;
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		GBASDLPlayerChangeJoystick(&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	GBAInputSetPreferredDevice(m_config->input(), type, m_playerId, device.toUtf8().constData());
206}
207
208GBARumble* InputController::rumble() {
209#ifdef BUILD_SDL
210	if (m_playerAttached) {
211		return &m_sdlPlayer.rumble.d;
212	}
213#endif
214	return nullptr;
215}
216
217GBARotationSource* InputController::rotationSource() {
218#ifdef BUILD_SDL
219	if (m_playerAttached) {
220		return &m_sdlPlayer.rotation.d;
221	}
222#endif
223	return nullptr;
224}
225
226void InputController::registerTiltAxisX(int axis) {
227#ifdef BUILD_SDL
228	if (m_playerAttached) {
229		m_sdlPlayer.rotation.axisX = axis;
230	}
231#endif
232}
233
234void InputController::registerTiltAxisY(int axis) {
235#ifdef BUILD_SDL
236	if (m_playerAttached) {
237		m_sdlPlayer.rotation.axisY = axis;
238	}
239#endif
240}
241
242void InputController::registerGyroAxisX(int axis) {
243#ifdef BUILD_SDL
244	if (m_playerAttached) {
245		m_sdlPlayer.rotation.gyroX = axis;
246	}
247#endif
248}
249
250void InputController::registerGyroAxisY(int axis) {
251#ifdef BUILD_SDL
252	if (m_playerAttached) {
253		m_sdlPlayer.rotation.gyroY = axis;
254	}
255#endif
256}
257
258float InputController::gyroSensitivity() const {
259#ifdef BUILD_SDL
260	if (m_playerAttached) {
261		return m_sdlPlayer.rotation.gyroSensitivity;
262	}
263#endif
264	return 0;
265}
266
267void InputController::setGyroSensitivity(float sensitivity) {
268#ifdef BUILD_SDL
269	if (m_playerAttached) {
270		m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
271	}
272#endif
273}
274
275GBAKey InputController::mapKeyboard(int key) const {
276	return GBAInputMapKey(&m_inputMap, KEYBOARD, key);
277}
278
279void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
280	return GBAInputBindKey(&m_inputMap, type, key, gbaKey);
281}
282
283int InputController::pollEvents() {
284	int activeButtons = 0;
285#ifdef BUILD_SDL
286	if (m_playerAttached) {
287		SDL_Joystick* joystick = m_sdlPlayer.joystick;
288		SDL_JoystickUpdate();
289		int numButtons = SDL_JoystickNumButtons(joystick);
290		int i;
291		for (i = 0; i < numButtons; ++i) {
292			GBAKey key = GBAInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i);
293			if (key == GBA_KEY_NONE) {
294				continue;
295			}
296			if (hasPendingEvent(key)) {
297				continue;
298			}
299			if (SDL_JoystickGetButton(joystick, i)) {
300				activeButtons |= 1 << key;
301			}
302		}
303		int numHats = SDL_JoystickNumHats(joystick);
304		for (i = 0; i < numHats; ++i) {
305			int hat = SDL_JoystickGetHat(joystick, i);
306			if (hat & SDL_HAT_UP) {
307				activeButtons |= 1 << GBA_KEY_UP;
308			}
309			if (hat & SDL_HAT_LEFT) {
310				activeButtons |= 1 << GBA_KEY_LEFT;
311			}
312			if (hat & SDL_HAT_DOWN) {
313				activeButtons |= 1 << GBA_KEY_DOWN;
314			}
315			if (hat & SDL_HAT_RIGHT) {
316				activeButtons |= 1 << GBA_KEY_RIGHT;
317			}
318		}
319
320		int numAxes = SDL_JoystickNumAxes(joystick);
321		for (i = 0; i < numAxes; ++i) {
322			int value = SDL_JoystickGetAxis(joystick, i);
323
324			enum GBAKey key = GBAInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value);
325			if (key != GBA_KEY_NONE) {
326				activeButtons |= 1 << key;
327			}
328		}
329	}
330#endif
331	return activeButtons;
332}
333
334QSet<int> InputController::activeGamepadButtons(int type) {
335	QSet<int> activeButtons;
336#ifdef BUILD_SDL
337	if (m_playerAttached && type == SDL_BINDING_BUTTON) {
338		SDL_Joystick* joystick = m_sdlPlayer.joystick;
339		SDL_JoystickUpdate();
340		int numButtons = SDL_JoystickNumButtons(joystick);
341		int i;
342		for (i = 0; i < numButtons; ++i) {
343			if (SDL_JoystickGetButton(joystick, i)) {
344				activeButtons.insert(i);
345			}
346		}
347	}
348#endif
349	return activeButtons;
350}
351
352void InputController::recalibrateAxes() {
353#ifdef BUILD_SDL
354	if (m_playerAttached) {
355		SDL_Joystick* joystick = m_sdlPlayer.joystick;
356		SDL_JoystickUpdate();
357		int numAxes = SDL_JoystickNumAxes(joystick);
358		if (numAxes < 1) {
359			return;
360		}
361		m_deadzones.resize(numAxes);
362		int i;
363		for (i = 0; i < numAxes; ++i) {
364			m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
365		}
366	}
367#endif
368}
369
370QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
371	QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
372#ifdef BUILD_SDL
373	if (m_playerAttached && type == SDL_BINDING_BUTTON) {
374		SDL_Joystick* joystick = m_sdlPlayer.joystick;
375		SDL_JoystickUpdate();
376		int numAxes = SDL_JoystickNumAxes(joystick);
377		if (numAxes < 1) {
378			return activeAxes;
379		}
380		m_deadzones.resize(numAxes);
381		int i;
382		for (i = 0; i < numAxes; ++i) {
383			int32_t axis = SDL_JoystickGetAxis(joystick, i);
384			axis -= m_deadzones[i];
385			if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
386				activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
387			}
388		}
389	}
390#endif
391	return activeAxes;
392}
393
394void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
395	const GBAAxis* old = GBAInputQueryAxis(&m_inputMap, type, axis);
396	GBAAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
397	if (old) {
398		description = *old;
399	}
400	switch (direction) {
401	case GamepadAxisEvent::NEGATIVE:
402		description.lowDirection = key;
403		description.deadLow = m_deadzones[axis] - AXIS_THRESHOLD;
404		break;
405	case GamepadAxisEvent::POSITIVE:
406		description.highDirection = key;
407		description.deadHigh = m_deadzones[axis] + AXIS_THRESHOLD;
408		break;
409	default:
410		return;
411	}
412	GBAInputBindAxis(&m_inputMap, type, axis, &description);
413}
414
415void InputController::testGamepad(int type) {
416	auto activeAxes = activeGamepadAxes(type);
417	auto oldAxes = m_activeAxes;
418	m_activeAxes = activeAxes;
419
420	auto activeButtons = activeGamepadButtons(type);
421	auto oldButtons = m_activeButtons;
422	m_activeButtons = activeButtons;
423
424	if (!QApplication::focusWidget()) {
425		return;
426	}
427
428	activeAxes.subtract(oldAxes);
429	oldAxes.subtract(m_activeAxes);
430
431	for (auto& axis : m_activeAxes) {
432		bool newlyAboveThreshold = activeAxes.contains(axis);
433		if (newlyAboveThreshold) {
434			GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
435			postPendingEvent(event->gbaKey());
436			sendGamepadEvent(event);
437			if (!event->isAccepted()) {
438				clearPendingEvent(event->gbaKey());
439			}
440		}
441	}
442	for (auto axis : oldAxes) {
443		GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
444		clearPendingEvent(event->gbaKey());
445		sendGamepadEvent(event);
446	}
447
448	if (!QApplication::focusWidget()) {
449		return;
450	}
451
452	activeButtons.subtract(oldButtons);
453	oldButtons.subtract(m_activeButtons);
454
455	for (int button : activeButtons) {
456		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
457		postPendingEvent(event->gbaKey());
458		sendGamepadEvent(event);
459		if (!event->isAccepted()) {
460			clearPendingEvent(event->gbaKey());
461		}
462	}
463	for (int button : oldButtons) {
464		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
465		clearPendingEvent(event->gbaKey());
466		sendGamepadEvent(event);
467	}
468}
469
470void InputController::sendGamepadEvent(QEvent* event) {
471	QWidget* focusWidget = nullptr;
472	if (m_topLevel) {
473		focusWidget = m_topLevel->focusWidget();
474		if (!focusWidget) {
475			focusWidget = m_topLevel;
476		}
477	} else {
478		focusWidget = QApplication::focusWidget();
479	}
480	QApplication::sendEvent(focusWidget, event);
481}
482
483void InputController::postPendingEvent(GBAKey key) {
484	m_pendingEvents.insert(key);
485}
486
487void InputController::clearPendingEvent(GBAKey key) {
488	m_pendingEvents.remove(key);
489}
490
491bool InputController::hasPendingEvent(GBAKey key) const {
492	return m_pendingEvents.contains(key);
493}
494
495#if defined(BUILD_SDL) && SDL_VERSION_ATLEAST(2, 0, 0)
496void InputController::suspendScreensaver() {
497	GBASDLSuspendScreensaver(&s_sdlEvents);
498}
499
500void InputController::resumeScreensaver() {
501	GBASDLResumeScreensaver(&s_sdlEvents);
502}
503
504void InputController::setScreensaverSuspendable(bool suspendable) {
505	GBASDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
506}
507#endif