all repos — mgba @ 4da22679b92f59acc67abaeadd7c37c3c459cdfc

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