all repos — mgba @ 8ff8876e3724981739d1343223eb2347a0b28810

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