all repos — mgba @ 5a932631bee45043aabc0eda64858164b91c171e

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#endif
 50
 51	m_gamepadTimer = new QTimer(this);
 52#ifdef BUILD_SDL
 53	connect(m_gamepadTimer, &QTimer::timeout, [this]() {
 54		testGamepad(SDL_BINDING_BUTTON);
 55	});
 56#endif
 57	m_gamepadTimer->setInterval(50);
 58	m_gamepadTimer->start();
 59
 60	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
 61	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
 62	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
 63	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
 64	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
 65	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
 66	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
 67	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
 68	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
 69	GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
 70}
 71
 72InputController::~InputController() {
 73	GBAInputMapDeinit(&m_inputMap);
 74
 75#ifdef BUILD_SDL
 76	if (m_playerAttached) {
 77		GBASDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
 78	}
 79
 80	--s_sdlInited;
 81	if (s_sdlInited == 0) {
 82		GBASDLDeinitEvents(&s_sdlEvents);
 83	}
 84#endif
 85}
 86
 87void InputController::setConfiguration(ConfigController* config) {
 88	m_config = config;
 89	setAllowOpposing(config->getOption("allowOpposingDirections").toInt());
 90	loadConfiguration(KEYBOARD);
 91#ifdef BUILD_SDL
 92	GBASDLEventsLoadConfig(&s_sdlEvents, config->input());
 93	if (!m_playerAttached) {
 94		GBASDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
 95		m_playerAttached = true;
 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);
151#else
152		return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.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 < s_sdlEvents.nJoysticks; ++i) {
166			const char* name;
167#if SDL_VERSION_ATLEAST(2, 0, 0)
168			name = SDL_JoystickName(s_sdlEvents.joysticks[i]);
169#else
170			name = SDL_JoystickName(SDL_JoystickIndex(s_sdlEvents.joysticks[i]));
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.joystickIndex;
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#if defined(BUILD_SDL) && SDL_VERSION_ATLEAST(2, 0, 0)
211	if (m_playerAttached) {
212		return &m_sdlPlayer.rumble.d;
213	}
214#endif
215	return nullptr;
216}
217
218GBARotationSource* InputController::rotationSource() {
219#ifdef BUILD_SDL
220	if (m_playerAttached) {
221		return &m_sdlPlayer.rotation.d;
222	}
223#endif
224	return nullptr;
225}
226
227void InputController::registerTiltAxisX(int axis) {
228#ifdef BUILD_SDL
229	if (m_playerAttached) {
230		m_sdlPlayer.rotation.axisX = axis;
231	}
232#endif
233}
234
235void InputController::registerTiltAxisY(int axis) {
236#ifdef BUILD_SDL
237	if (m_playerAttached) {
238		m_sdlPlayer.rotation.axisY = axis;
239	}
240#endif
241}
242
243void InputController::registerGyroAxisX(int axis) {
244#ifdef BUILD_SDL
245	if (m_playerAttached) {
246		m_sdlPlayer.rotation.gyroX = axis;
247	}
248#endif
249}
250
251void InputController::registerGyroAxisY(int axis) {
252#ifdef BUILD_SDL
253	if (m_playerAttached) {
254		m_sdlPlayer.rotation.gyroY = axis;
255	}
256#endif
257}
258
259float InputController::gyroSensitivity() const {
260#ifdef BUILD_SDL
261	if (m_playerAttached) {
262		return m_sdlPlayer.rotation.gyroSensitivity;
263	}
264#endif
265	return 0;
266}
267
268void InputController::setGyroSensitivity(float sensitivity) {
269#ifdef BUILD_SDL
270	if (m_playerAttached) {
271		m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
272	}
273#endif
274}
275
276GBAKey InputController::mapKeyboard(int key) const {
277	return GBAInputMapKey(&m_inputMap, KEYBOARD, key);
278}
279
280void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
281	return GBAInputBindKey(&m_inputMap, type, key, gbaKey);
282}
283
284int InputController::pollEvents() {
285	int activeButtons = 0;
286#ifdef BUILD_SDL
287	if (m_playerAttached) {
288		SDL_Joystick* joystick = m_sdlPlayer.joystick;
289		SDL_JoystickUpdate();
290		int numButtons = SDL_JoystickNumButtons(joystick);
291		int i;
292		for (i = 0; i < numButtons; ++i) {
293			GBAKey key = GBAInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i);
294			if (key == GBA_KEY_NONE) {
295				continue;
296			}
297			if (hasPendingEvent(key)) {
298				continue;
299			}
300			if (SDL_JoystickGetButton(joystick, i)) {
301				activeButtons |= 1 << key;
302			}
303		}
304		int numHats = SDL_JoystickNumHats(joystick);
305		for (i = 0; i < numHats; ++i) {
306			int hat = SDL_JoystickGetHat(joystick, i);
307			if (hat & SDL_HAT_UP) {
308				activeButtons |= 1 << GBA_KEY_UP;
309			}
310			if (hat & SDL_HAT_LEFT) {
311				activeButtons |= 1 << GBA_KEY_LEFT;
312			}
313			if (hat & SDL_HAT_DOWN) {
314				activeButtons |= 1 << GBA_KEY_DOWN;
315			}
316			if (hat & SDL_HAT_RIGHT) {
317				activeButtons |= 1 << GBA_KEY_RIGHT;
318			}
319		}
320
321		int numAxes = SDL_JoystickNumAxes(joystick);
322		for (i = 0; i < numAxes; ++i) {
323			int value = SDL_JoystickGetAxis(joystick, i);
324
325			enum GBAKey key = GBAInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value);
326			if (key != GBA_KEY_NONE) {
327				activeButtons |= 1 << key;
328			}
329		}
330	}
331#endif
332	return activeButtons;
333}
334
335QSet<int> InputController::activeGamepadButtons(int type) {
336	QSet<int> activeButtons;
337#ifdef BUILD_SDL
338	if (m_playerAttached && type == SDL_BINDING_BUTTON) {
339		SDL_Joystick* joystick = m_sdlPlayer.joystick;
340		SDL_JoystickUpdate();
341		int numButtons = SDL_JoystickNumButtons(joystick);
342		int i;
343		for (i = 0; i < numButtons; ++i) {
344			if (SDL_JoystickGetButton(joystick, i)) {
345				activeButtons.insert(i);
346			}
347		}
348	}
349#endif
350	return activeButtons;
351}
352
353void InputController::recalibrateAxes() {
354#ifdef BUILD_SDL
355	if (m_playerAttached) {
356		SDL_Joystick* joystick = m_sdlPlayer.joystick;
357		SDL_JoystickUpdate();
358		int numAxes = SDL_JoystickNumAxes(joystick);
359		if (numAxes < 1) {
360			return;
361		}
362		m_deadzones.resize(numAxes);
363		int i;
364		for (i = 0; i < numAxes; ++i) {
365			m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
366		}
367	}
368#endif
369}
370
371QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
372	QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
373#ifdef BUILD_SDL
374	if (m_playerAttached && type == SDL_BINDING_BUTTON) {
375		SDL_Joystick* joystick = m_sdlPlayer.joystick;
376		SDL_JoystickUpdate();
377		int numAxes = SDL_JoystickNumAxes(joystick);
378		if (numAxes < 1) {
379			return activeAxes;
380		}
381		m_deadzones.resize(numAxes);
382		int i;
383		for (i = 0; i < numAxes; ++i) {
384			int32_t axis = SDL_JoystickGetAxis(joystick, i);
385			axis -= m_deadzones[i];
386			if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
387				activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
388			}
389		}
390	}
391#endif
392	return activeAxes;
393}
394
395void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
396	const GBAAxis* old = GBAInputQueryAxis(&m_inputMap, type, axis);
397	GBAAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
398	if (old) {
399		description = *old;
400	}
401	switch (direction) {
402	case GamepadAxisEvent::NEGATIVE:
403		description.lowDirection = key;
404		description.deadLow = m_deadzones[axis] - AXIS_THRESHOLD;
405		break;
406	case GamepadAxisEvent::POSITIVE:
407		description.highDirection = key;
408		description.deadHigh = m_deadzones[axis] + AXIS_THRESHOLD;
409		break;
410	default:
411		return;
412	}
413	GBAInputBindAxis(&m_inputMap, type, axis, &description);
414}
415
416void InputController::unbindAllAxes(uint32_t type) {
417	GBAInputUnbindAllAxes(&m_inputMap, type);
418}
419
420void InputController::testGamepad(int type) {
421	auto activeAxes = activeGamepadAxes(type);
422	auto oldAxes = m_activeAxes;
423	m_activeAxes = activeAxes;
424
425	auto activeButtons = activeGamepadButtons(type);
426	auto oldButtons = m_activeButtons;
427	m_activeButtons = activeButtons;
428
429	if (!QApplication::focusWidget()) {
430		return;
431	}
432
433	activeAxes.subtract(oldAxes);
434	oldAxes.subtract(m_activeAxes);
435
436	for (auto& axis : m_activeAxes) {
437		bool newlyAboveThreshold = activeAxes.contains(axis);
438		if (newlyAboveThreshold) {
439			GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
440			postPendingEvent(event->gbaKey());
441			sendGamepadEvent(event);
442			if (!event->isAccepted()) {
443				clearPendingEvent(event->gbaKey());
444			}
445		}
446	}
447	for (auto axis : oldAxes) {
448		GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
449		clearPendingEvent(event->gbaKey());
450		sendGamepadEvent(event);
451	}
452
453	if (!QApplication::focusWidget()) {
454		return;
455	}
456
457	activeButtons.subtract(oldButtons);
458	oldButtons.subtract(m_activeButtons);
459
460	for (int button : activeButtons) {
461		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
462		postPendingEvent(event->gbaKey());
463		sendGamepadEvent(event);
464		if (!event->isAccepted()) {
465			clearPendingEvent(event->gbaKey());
466		}
467	}
468	for (int button : oldButtons) {
469		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
470		clearPendingEvent(event->gbaKey());
471		sendGamepadEvent(event);
472	}
473}
474
475void InputController::sendGamepadEvent(QEvent* event) {
476	QWidget* focusWidget = nullptr;
477	if (m_focusParent) {
478		focusWidget = m_focusParent->focusWidget();
479		if (!focusWidget) {
480			focusWidget = m_focusParent;
481		}
482	} else {
483		focusWidget = QApplication::focusWidget();
484	}
485	QApplication::sendEvent(focusWidget, event);
486}
487
488void InputController::postPendingEvent(GBAKey key) {
489	m_pendingEvents.insert(key);
490}
491
492void InputController::clearPendingEvent(GBAKey key) {
493	m_pendingEvents.remove(key);
494}
495
496bool InputController::hasPendingEvent(GBAKey key) const {
497	return m_pendingEvents.contains(key);
498}
499
500#if defined(BUILD_SDL) && SDL_VERSION_ATLEAST(2, 0, 0)
501void InputController::suspendScreensaver() {
502	GBASDLSuspendScreensaver(&s_sdlEvents);
503}
504
505void InputController::resumeScreensaver() {
506	GBASDLResumeScreensaver(&s_sdlEvents);
507}
508
509void InputController::setScreensaverSuspendable(bool suspendable) {
510	GBASDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
511}
512#endif
513
514void InputController::stealFocus(QWidget* focus) {
515	m_focusParent = focus;
516}
517
518void InputController::releaseFocus(QWidget* focus) {
519	if (focus == m_focusParent) {
520		m_focusParent = m_topLevel;
521	}
522}