all repos — mgba @ b9c276ee30da6edd59468f5f775abd08d1ceec3a

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