all repos — mgba @ d957736ed95751328f6913b1954cdeac0bf7e80d

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#endif
 47
 48	m_gamepadTimer = new QTimer(this);
 49#ifdef BUILD_SDL
 50	connect(m_gamepadTimer, &QTimer::timeout, [this]() {
 51		testGamepad(SDL_BINDING_BUTTON);
 52	});
 53#endif
 54	m_gamepadTimer->setInterval(50);
 55	m_gamepadTimer->start();
 56
 57	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
 58	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
 59	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
 60	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
 61	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
 62	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
 63	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
 64	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
 65	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
 66	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
 67}
 68
 69InputController::~InputController() {
 70	GBAInputMapDeinit(&m_inputMap);
 71
 72#ifdef BUILD_SDL
 73	if (m_playerAttached) {
 74		GBASDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
 75	}
 76
 77	--s_sdlInited;
 78	if (s_sdlInited == 0) {
 79		GBASDLDeinitEvents(&s_sdlEvents);
 80	}
 81#endif
 82}
 83
 84void InputController::setConfiguration(ConfigController* config) {
 85	m_config = config;
 86	setAllowOpposing(config->getOption("allowOpposingDirections").toInt());
 87	loadConfiguration(KEYBOARD);
 88#ifdef BUILD_SDL
 89	GBASDLEventsLoadConfig(&s_sdlEvents, config->input());
 90	if (!m_playerAttached) {
 91		GBASDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
 92		m_playerAttached = true;
 93	}
 94	loadConfiguration(SDL_BINDING_BUTTON);
 95	loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
 96#endif
 97}
 98
 99void InputController::loadConfiguration(uint32_t type) {
100	GBAInputMapLoad(&m_inputMap, type, m_config->input());
101#ifdef BUILD_SDL
102	if (m_playerAttached) {
103		GBASDLPlayerLoadConfig(&m_sdlPlayer, m_config->input());
104	}
105#endif
106}
107
108void InputController::loadProfile(uint32_t type, const QString& profile) {
109	GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
110	recalibrateAxes();
111}
112
113void InputController::saveConfiguration() {
114	saveConfiguration(KEYBOARD);
115#ifdef BUILD_SDL
116	saveConfiguration(SDL_BINDING_BUTTON);
117	saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
118	if (m_playerAttached) {
119		GBASDLPlayerSaveConfig(&m_sdlPlayer, m_config->input());
120	}
121	m_config->write();
122#endif
123}
124
125void InputController::saveConfiguration(uint32_t type) {
126	GBAInputMapSave(&m_inputMap, type, m_config->input());
127	m_config->write();
128}
129
130void InputController::saveProfile(uint32_t type, const QString& profile) {
131	GBAInputProfileSave(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
132	m_config->write();
133}
134
135const char* InputController::profileForType(uint32_t type) {
136	UNUSED(type);
137#ifdef BUILD_SDL
138	if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
139#if SDL_VERSION_ATLEAST(2, 0, 0)
140		return SDL_JoystickName(m_sdlPlayer.joystick);
141#else
142		return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick));
143#endif
144	}
145#endif
146	return 0;
147}
148
149QStringList InputController::connectedGamepads(uint32_t type) const {
150	UNUSED(type);
151
152#ifdef BUILD_SDL
153	if (type == SDL_BINDING_BUTTON) {
154		QStringList pads;
155		for (size_t i = 0; i < s_sdlEvents.nJoysticks; ++i) {
156			const char* name;
157#if SDL_VERSION_ATLEAST(2, 0, 0)
158			name = SDL_JoystickName(s_sdlEvents.joysticks[i]);
159#else
160			name = SDL_JoystickName(SDL_JoystickIndex(s_sdlEvents.joysticks[i]));
161#endif
162			if (name) {
163				pads.append(QString(name));
164			} else {
165				pads.append(QString());
166			}
167		}
168		return pads;
169	}
170#endif
171
172	return QStringList();
173}
174
175int InputController::gamepad(uint32_t type) const {
176#ifdef BUILD_SDL
177	if (type == SDL_BINDING_BUTTON) {
178		return m_sdlPlayer.joystickIndex;
179	}
180#endif
181	return 0;
182}
183
184void InputController::setGamepad(uint32_t type, int index) {
185#ifdef BUILD_SDL
186	if (type == SDL_BINDING_BUTTON) {
187		GBASDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index);
188	}
189#endif
190}
191
192void InputController::setPreferredGamepad(uint32_t type, const QString& device) {
193	if (!m_config) {
194		return;
195	}
196	GBAInputSetPreferredDevice(m_config->input(), type, m_playerId, device.toUtf8().constData());
197}
198
199GBARumble* InputController::rumble() {
200#ifdef BUILD_SDL
201	if (m_playerAttached) {
202		return &m_sdlPlayer.rumble.d;
203	}
204#endif
205	return nullptr;
206}
207
208GBARotationSource* InputController::rotationSource() {
209#ifdef BUILD_SDL
210	if (m_playerAttached) {
211		return &m_sdlPlayer.rotation.d;
212	}
213#endif
214	return nullptr;
215}
216
217void InputController::registerTiltAxisX(int axis) {
218#ifdef BUILD_SDL
219	if (m_playerAttached) {
220		m_sdlPlayer.rotation.axisX = axis;
221	}
222#endif
223}
224
225void InputController::registerTiltAxisY(int axis) {
226#ifdef BUILD_SDL
227	if (m_playerAttached) {
228		m_sdlPlayer.rotation.axisY = axis;
229	}
230#endif
231}
232
233void InputController::registerGyroAxisX(int axis) {
234#ifdef BUILD_SDL
235	if (m_playerAttached) {
236		m_sdlPlayer.rotation.gyroX = axis;
237	}
238#endif
239}
240
241void InputController::registerGyroAxisY(int axis) {
242#ifdef BUILD_SDL
243	if (m_playerAttached) {
244		m_sdlPlayer.rotation.gyroY = axis;
245	}
246#endif
247}
248
249float InputController::gyroSensitivity() const {
250#ifdef BUILD_SDL
251	if (m_playerAttached) {
252		return m_sdlPlayer.rotation.gyroSensitivity;
253	}
254#endif
255	return 0;
256}
257
258void InputController::setGyroSensitivity(float sensitivity) {
259#ifdef BUILD_SDL
260	if (m_playerAttached) {
261		m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
262	}
263#endif
264}
265
266GBAKey InputController::mapKeyboard(int key) const {
267	return GBAInputMapKey(&m_inputMap, KEYBOARD, key);
268}
269
270void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
271	return GBAInputBindKey(&m_inputMap, type, key, gbaKey);
272}
273
274int InputController::pollEvents() {
275	int activeButtons = 0;
276#ifdef BUILD_SDL
277	if (m_playerAttached) {
278		SDL_Joystick* joystick = m_sdlPlayer.joystick;
279		SDL_JoystickUpdate();
280		int numButtons = SDL_JoystickNumButtons(joystick);
281		int i;
282		for (i = 0; i < numButtons; ++i) {
283			GBAKey key = GBAInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i);
284			if (key == GBA_KEY_NONE) {
285				continue;
286			}
287			if (hasPendingEvent(key)) {
288				continue;
289			}
290			if (SDL_JoystickGetButton(joystick, i)) {
291				activeButtons |= 1 << key;
292			}
293		}
294		int numHats = SDL_JoystickNumHats(joystick);
295		for (i = 0; i < numHats; ++i) {
296			int hat = SDL_JoystickGetHat(joystick, i);
297			if (hat & SDL_HAT_UP) {
298				activeButtons |= 1 << GBA_KEY_UP;
299			}
300			if (hat & SDL_HAT_LEFT) {
301				activeButtons |= 1 << GBA_KEY_LEFT;
302			}
303			if (hat & SDL_HAT_DOWN) {
304				activeButtons |= 1 << GBA_KEY_DOWN;
305			}
306			if (hat & SDL_HAT_RIGHT) {
307				activeButtons |= 1 << GBA_KEY_RIGHT;
308			}
309		}
310
311		int numAxes = SDL_JoystickNumAxes(joystick);
312		for (i = 0; i < numAxes; ++i) {
313			int value = SDL_JoystickGetAxis(joystick, i);
314
315			enum GBAKey key = GBAInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value);
316			if (key != GBA_KEY_NONE) {
317				activeButtons |= 1 << key;
318			}
319		}
320	}
321#endif
322	return activeButtons;
323}
324
325QSet<int> InputController::activeGamepadButtons(int type) {
326	QSet<int> activeButtons;
327#ifdef BUILD_SDL
328	if (m_playerAttached && type == SDL_BINDING_BUTTON) {
329		SDL_Joystick* joystick = m_sdlPlayer.joystick;
330		SDL_JoystickUpdate();
331		int numButtons = SDL_JoystickNumButtons(joystick);
332		int i;
333		for (i = 0; i < numButtons; ++i) {
334			if (SDL_JoystickGetButton(joystick, i)) {
335				activeButtons.insert(i);
336			}
337		}
338	}
339#endif
340	return activeButtons;
341}
342
343void InputController::recalibrateAxes() {
344#ifdef BUILD_SDL
345	if (m_playerAttached) {
346		SDL_Joystick* joystick = m_sdlPlayer.joystick;
347		SDL_JoystickUpdate();
348		int numAxes = SDL_JoystickNumAxes(joystick);
349		if (numAxes < 1) {
350			return;
351		}
352		m_deadzones.resize(numAxes);
353		int i;
354		for (i = 0; i < numAxes; ++i) {
355			m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
356		}
357	}
358#endif
359}
360
361QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
362	QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
363#ifdef BUILD_SDL
364	if (m_playerAttached && type == SDL_BINDING_BUTTON) {
365		SDL_Joystick* joystick = m_sdlPlayer.joystick;
366		SDL_JoystickUpdate();
367		int numAxes = SDL_JoystickNumAxes(joystick);
368		if (numAxes < 1) {
369			return activeAxes;
370		}
371		m_deadzones.resize(numAxes);
372		int i;
373		for (i = 0; i < numAxes; ++i) {
374			int32_t axis = SDL_JoystickGetAxis(joystick, i);
375			axis -= m_deadzones[i];
376			if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
377				activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
378			}
379		}
380	}
381#endif
382	return activeAxes;
383}
384
385void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
386	const GBAAxis* old = GBAInputQueryAxis(&m_inputMap, type, axis);
387	GBAAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
388	if (old) {
389		description = *old;
390	}
391	switch (direction) {
392	case GamepadAxisEvent::NEGATIVE:
393		description.lowDirection = key;
394		description.deadLow = m_deadzones[axis] - AXIS_THRESHOLD;
395		break;
396	case GamepadAxisEvent::POSITIVE:
397		description.highDirection = key;
398		description.deadHigh = m_deadzones[axis] + AXIS_THRESHOLD;
399		break;
400	default:
401		return;
402	}
403	GBAInputBindAxis(&m_inputMap, type, axis, &description);
404}
405
406void InputController::testGamepad(int type) {
407	auto activeAxes = activeGamepadAxes(type);
408	auto oldAxes = m_activeAxes;
409	m_activeAxes = activeAxes;
410
411	auto activeButtons = activeGamepadButtons(type);
412	auto oldButtons = m_activeButtons;
413	m_activeButtons = activeButtons;
414
415	if (!QApplication::focusWidget()) {
416		return;
417	}
418
419	activeAxes.subtract(oldAxes);
420	oldAxes.subtract(m_activeAxes);
421
422	for (auto& axis : m_activeAxes) {
423		bool newlyAboveThreshold = activeAxes.contains(axis);
424		if (newlyAboveThreshold) {
425			GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
426			postPendingEvent(event->gbaKey());
427			QApplication::sendEvent(QApplication::focusWidget(), event);
428			if (!event->isAccepted()) {
429				clearPendingEvent(event->gbaKey());
430			}
431		}
432	}
433	for (auto axis : oldAxes) {
434		GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
435		clearPendingEvent(event->gbaKey());
436		QApplication::sendEvent(QApplication::focusWidget(), event);
437	}
438
439	if (!QApplication::focusWidget()) {
440		return;
441	}
442
443	activeButtons.subtract(oldButtons);
444	oldButtons.subtract(m_activeButtons);
445
446	for (int button : activeButtons) {
447		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
448		postPendingEvent(event->gbaKey());
449		QApplication::sendEvent(QApplication::focusWidget(), event);
450		if (!event->isAccepted()) {
451			clearPendingEvent(event->gbaKey());
452		}
453	}
454	for (int button : oldButtons) {
455		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
456		clearPendingEvent(event->gbaKey());
457		QApplication::sendEvent(QApplication::focusWidget(), event);
458	}
459}
460
461void InputController::postPendingEvent(GBAKey key) {
462	m_pendingEvents.insert(key);
463}
464
465void InputController::clearPendingEvent(GBAKey key) {
466	m_pendingEvents.remove(key);
467}
468
469bool InputController::hasPendingEvent(GBAKey key) const {
470	return m_pendingEvents.contains(key);
471}
472
473#if defined(BUILD_SDL) && SDL_VERSION_ATLEAST(2, 0, 0)
474void InputController::suspendScreensaver() {
475	GBASDLSuspendScreensaver(&s_sdlEvents);
476}
477
478void InputController::resumeScreensaver() {
479	GBASDLResumeScreensaver(&s_sdlEvents);
480}
481
482void InputController::setScreensaverSuspendable(bool suspendable) {
483	GBASDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
484}
485#endif