all repos — mgba @ 13dfb144e88290b0e747ac51391f0882e57ce4b2

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