all repos — mgba @ 291d5c500b9f6e6fc3a4e1b936b6cdb3457406a5

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