all repos — mgba @ 51b8c862b969390582cb39b3ac98239cdea9a4b1

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