all repos — mgba @ 8fad1d136c878e263fcf21772f74d66235d17a23

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