all repos — mgba @ 049e3639d1d692c74980892e0c3e07a35ecd9d68

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
 12#include <QApplication>
 13#include <QTimer>
 14#include <QWidget>
 15
 16extern "C" {
 17#include "util/configuration.h"
 18}
 19
 20using namespace QGBA;
 21
 22#ifdef BUILD_SDL
 23int InputController::s_sdlInited = 0;
 24GBASDLEvents InputController::s_sdlEvents;
 25#endif
 26
 27InputController::InputController(int playerId, 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{
 37	GBAInputMapInit(&m_inputMap);
 38
 39#ifdef BUILD_SDL
 40	if (s_sdlInited == 0) {
 41		GBASDLInitEvents(&s_sdlEvents);
 42	}
 43	++s_sdlInited;
 44	m_sdlPlayer.bindings = &m_inputMap;
 45	GBASDLInitBindings(&m_inputMap);
 46
 47	m_gamepadTimer = new QTimer(this);
 48	connect(m_gamepadTimer, SIGNAL(timeout()), this, SLOT(testGamepad()));
 49	m_gamepadTimer->setInterval(50);
 50	m_gamepadTimer->start();
 51#endif
 52
 53	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
 54	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
 55	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
 56	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
 57	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
 58	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
 59	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
 60	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
 61	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
 62	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
 63}
 64
 65InputController::~InputController() {
 66	GBAInputMapDeinit(&m_inputMap);
 67
 68#ifdef BUILD_SDL
 69	--s_sdlInited;
 70	if (s_sdlInited == 0) {
 71		GBASDLDeinitEvents(&s_sdlEvents);
 72	}
 73#endif
 74}
 75
 76void InputController::setConfiguration(ConfigController* config) {
 77	m_config = config;
 78	setAllowOpposing(config->getOption("allowOpposingDirections").toInt());
 79	loadConfiguration(KEYBOARD);
 80#ifdef BUILD_SDL
 81	GBASDLEventsLoadConfig(&s_sdlEvents, config->input());
 82	if (!m_playerAttached) {
 83		GBASDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
 84		m_playerAttached = true;
 85	}
 86	loadConfiguration(SDL_BINDING_BUTTON);
 87	loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
 88#endif
 89}
 90
 91void InputController::loadConfiguration(uint32_t type) {
 92	GBAInputMapLoad(&m_inputMap, type, m_config->input());
 93}
 94
 95void InputController::loadProfile(uint32_t type, const QString& profile) {
 96	GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toLocal8Bit().constData());
 97#ifdef BUILD_SDL
 98	recalibrateAxes();
 99#endif
100}
101
102void InputController::saveConfiguration(uint32_t type) {
103	GBAInputMapSave(&m_inputMap, type, m_config->input());
104	m_config->write();
105}
106
107void InputController::saveProfile(uint32_t type, const QString& profile) {
108	GBAInputProfileSave(&m_inputMap, type, m_config->input(), profile.toLocal8Bit().constData());
109	m_config->write();
110}
111
112const char* InputController::profileForType(uint32_t type) {
113	UNUSED(type);
114#ifdef BUILD_SDL
115	if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
116#if SDL_VERSION_ATLEAST(2, 0, 0)
117		return SDL_JoystickName(m_sdlPlayer.joystick);
118#else
119		return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick));
120#endif
121	}
122#endif
123	return 0;
124}
125
126#ifdef BUILD_SDL
127QStringList InputController::connectedGamepads(uint32_t type) const {
128	UNUSED(type);
129	if (type != SDL_BINDING_BUTTON) {
130		return QStringList();
131	}
132
133	QStringList pads;
134	for (size_t i = 0; i < s_sdlEvents.nJoysticks; ++i) {
135		const char* name;
136#if SDL_VERSION_ATLEAST(2, 0, 0)
137		name = SDL_JoystickName(s_sdlEvents.joysticks[i]);
138#else
139		name = SDL_JoystickName(SDL_JoystickIndex(s_sdlEvents.joysticks[i]));
140#endif
141		if (name) {
142			pads.append(QString(name));
143		} else {
144			pads.append(QString());
145		}
146	}
147	return pads;
148}
149
150void InputController::setPreferredGamepad(uint32_t type, const QString& device) {
151	if (!m_config) {
152		return;
153	}
154	GBAInputSetPreferredDevice(m_config->input(), type, m_sdlPlayer.playerId, device.toLocal8Bit().constData());
155}
156
157GBARumble* InputController::rumble() {
158	return &m_sdlPlayer.rumble.d;
159}
160#endif
161
162GBAKey InputController::mapKeyboard(int key) const {
163	return GBAInputMapKey(&m_inputMap, KEYBOARD, key);
164}
165
166void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
167	return GBAInputBindKey(&m_inputMap, type, key, gbaKey);
168}
169
170#ifdef BUILD_SDL
171int InputController::testSDLEvents() {
172	SDL_Joystick* joystick = m_sdlPlayer.joystick;
173	SDL_JoystickUpdate();
174	int numButtons = SDL_JoystickNumButtons(joystick);
175	int activeButtons = 0;
176	int i;
177	for (i = 0; i < numButtons; ++i) {
178		GBAKey key = GBAInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i);
179		if (key == GBA_KEY_NONE) {
180			continue;
181		}
182		if (hasPendingEvent(key)) {
183			continue;
184		}
185		if (SDL_JoystickGetButton(joystick, i)) {
186			activeButtons |= 1 << key;
187		}
188	}
189	int numHats = SDL_JoystickNumHats(joystick);
190	for (i = 0; i < numHats; ++i) {
191		int hat = SDL_JoystickGetHat(joystick, i);
192		if (hat & SDL_HAT_UP) {
193			activeButtons |= 1 << GBA_KEY_UP;
194		}
195		if (hat & SDL_HAT_LEFT) {
196			activeButtons |= 1 << GBA_KEY_LEFT;
197		}
198		if (hat & SDL_HAT_DOWN) {
199			activeButtons |= 1 << GBA_KEY_DOWN;
200		}
201		if (hat & SDL_HAT_RIGHT) {
202			activeButtons |= 1 << GBA_KEY_RIGHT;
203		}
204	}
205
206	int numAxes = SDL_JoystickNumAxes(joystick);
207	for (i = 0; i < numAxes; ++i) {
208		int value = SDL_JoystickGetAxis(joystick, i);
209
210		enum GBAKey key = GBAInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value);
211		if (key != GBA_KEY_NONE) {
212			activeButtons |= 1 << key;
213		}
214	}
215	return activeButtons;
216}
217
218QSet<int> InputController::activeGamepadButtons() {
219	SDL_Joystick* joystick = m_sdlPlayer.joystick;
220	SDL_JoystickUpdate();
221	int numButtons = SDL_JoystickNumButtons(joystick);
222	QSet<int> activeButtons;
223	int i;
224	for (i = 0; i < numButtons; ++i) {
225		if (SDL_JoystickGetButton(joystick, i)) {
226			activeButtons.insert(i);
227		}
228	}
229	return activeButtons;
230}
231
232void InputController::recalibrateAxes() {
233	SDL_Joystick* joystick = m_sdlPlayer.joystick;
234	SDL_JoystickUpdate();
235	int numAxes = SDL_JoystickNumAxes(joystick);
236	if (numAxes < 1) {
237		return;
238	}
239	m_deadzones.resize(numAxes);
240	int i;
241	for (i = 0; i < numAxes; ++i) {
242		m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
243	}
244}
245
246QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes() {
247	SDL_Joystick* joystick = m_sdlPlayer.joystick;
248	SDL_JoystickUpdate();
249	int numAxes = SDL_JoystickNumAxes(joystick);
250	QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
251	if (numAxes < 1) {
252		return activeAxes;
253	}
254	m_deadzones.resize(numAxes);
255	int i;
256	for (i = 0; i < numAxes; ++i) {
257		int32_t axis = SDL_JoystickGetAxis(joystick, i);
258		axis -= m_deadzones[i];
259		if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
260			activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
261		}
262	}
263	return activeAxes;
264}
265
266void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
267	const GBAAxis* old = GBAInputQueryAxis(&m_inputMap, SDL_BINDING_BUTTON, axis);
268	GBAAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
269	if (old) {
270		description = *old;
271	}
272	switch (direction) {
273	case GamepadAxisEvent::NEGATIVE:
274		description.lowDirection = key;
275		description.deadLow = m_deadzones[axis] - AXIS_THRESHOLD;
276		break;
277	case GamepadAxisEvent::POSITIVE:
278		description.highDirection = key;
279		description.deadHigh = m_deadzones[axis] + AXIS_THRESHOLD;
280		break;
281	default:
282		return;
283	}
284	GBAInputBindAxis(&m_inputMap, SDL_BINDING_BUTTON, axis, &description);
285}
286#endif
287
288void InputController::testGamepad() {
289#ifdef BUILD_SDL
290	auto activeAxes = activeGamepadAxes();
291	auto oldAxes = m_activeAxes;
292	m_activeAxes = activeAxes;
293
294	auto activeButtons = activeGamepadButtons();
295	auto oldButtons = m_activeButtons;
296	m_activeButtons = activeButtons;
297
298	if (!QApplication::focusWidget()) {
299		return;
300	}
301
302	activeAxes.subtract(oldAxes);
303	oldAxes.subtract(m_activeAxes);
304
305	for (auto& axis : m_activeAxes) {
306		bool newlyAboveThreshold = activeAxes.contains(axis);
307		if (newlyAboveThreshold) {
308			GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, this);
309			postPendingEvent(event->gbaKey());
310			QApplication::sendEvent(QApplication::focusWidget(), event);
311			if (!event->isAccepted()) {
312				clearPendingEvent(event->gbaKey());
313			}
314		}
315	}
316	for (auto axis : oldAxes) {
317		GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, this);
318		clearPendingEvent(event->gbaKey());
319		QApplication::sendEvent(QApplication::focusWidget(), event);
320	}
321
322	if (!QApplication::focusWidget()) {
323		return;
324	}
325
326	activeButtons.subtract(oldButtons);
327	oldButtons.subtract(m_activeButtons);
328
329	for (int button : activeButtons) {
330		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, this);
331		postPendingEvent(event->gbaKey());
332		QApplication::sendEvent(QApplication::focusWidget(), event);
333		if (!event->isAccepted()) {
334			clearPendingEvent(event->gbaKey());
335		}
336	}
337	for (int button : oldButtons) {
338		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, this);
339		clearPendingEvent(event->gbaKey());
340		QApplication::sendEvent(QApplication::focusWidget(), event);
341	}
342#endif
343}
344
345void InputController::postPendingEvent(GBAKey key) {
346	m_pendingEvents.insert(key);
347}
348
349void InputController::clearPendingEvent(GBAKey key) {
350	m_pendingEvents.remove(key);
351}
352
353bool InputController::hasPendingEvent(GBAKey key) const {
354	return m_pendingEvents.contains(key);
355}