all repos — mgba @ 657bcec87942b5583c983a1d375bc3c5dd3f8a4e

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