all repos — mgba @ 56c6e3b497b45e59982f2cae19740d5fbe18bedc

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
 17#include <mgba/core/interface.h>
 18#include <mgba-util/configuration.h>
 19
 20using namespace QGBA;
 21
 22#ifdef BUILD_SDL
 23int InputController::s_sdlInited = 0;
 24mSDLEvents InputController::s_sdlEvents;
 25#endif
 26
 27InputController::InputController(int playerId, QWidget* topLevel, QObject* parent)
 28	: QObject(parent)
 29	, m_playerId(playerId)
 30	, m_topLevel(topLevel)
 31	, m_focusParent(topLevel)
 32{
 33	mInputMapInit(&m_inputMap, &GBAInputInfo);
 34
 35#ifdef BUILD_SDL
 36	if (s_sdlInited == 0) {
 37		mSDLInitEvents(&s_sdlEvents);
 38	}
 39	++s_sdlInited;
 40	m_sdlPlayer.bindings = &m_inputMap;
 41	mSDLInitBindingsGBA(&m_inputMap);
 42	updateJoysticks();
 43#endif
 44
 45#ifdef BUILD_SDL
 46	connect(&m_gamepadTimer, &QTimer::timeout, [this]() {
 47		testGamepad(SDL_BINDING_BUTTON);
 48		if (m_playerId == 0) {
 49			updateJoysticks();
 50		}
 51	});
 52#endif
 53	m_gamepadTimer.setInterval(50);
 54	m_gamepadTimer.start();
 55
 56	mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
 57	mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
 58	mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
 59	mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
 60	mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
 61	mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
 62	mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
 63	mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
 64	mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
 65	mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
 66
 67
 68#ifdef M_CORE_GBA
 69	m_lux.p = this;
 70	m_lux.sample = [](GBALuminanceSource* context) {
 71		InputControllerLux* lux = static_cast<InputControllerLux*>(context);
 72		lux->value = 0xFF - lux->p->m_luxValue;
 73	};
 74
 75	m_lux.readLuminance = [](GBALuminanceSource* context) {
 76		InputControllerLux* lux = static_cast<InputControllerLux*>(context);
 77		return lux->value;
 78	};
 79	setLuminanceLevel(0);
 80#endif
 81}
 82
 83InputController::~InputController() {
 84	mInputMapDeinit(&m_inputMap);
 85
 86#ifdef BUILD_SDL
 87	if (m_playerAttached) {
 88		mSDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
 89	}
 90
 91	--s_sdlInited;
 92	if (s_sdlInited == 0) {
 93		mSDLDeinitEvents(&s_sdlEvents);
 94	}
 95#endif
 96}
 97
 98void InputController::setConfiguration(ConfigController* config) {
 99	m_config = config;
100	loadConfiguration(KEYBOARD);
101#ifdef BUILD_SDL
102	mSDLEventsLoadConfig(&s_sdlEvents, config->input());
103	if (!m_playerAttached) {
104		m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
105	}
106	loadConfiguration(SDL_BINDING_BUTTON);
107	loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
108#endif
109}
110
111void InputController::loadConfiguration(uint32_t type) {
112	mInputMapLoad(&m_inputMap, type, m_config->input());
113#ifdef BUILD_SDL
114	if (m_playerAttached) {
115		mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input());
116	}
117#endif
118}
119
120void InputController::loadProfile(uint32_t type, const QString& profile) {
121	bool loaded = mInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
122	recalibrateAxes();
123	if (!loaded) {
124		const InputProfile* ip = InputProfile::findProfile(profile);
125		if (ip) {
126			ip->apply(this);
127		}
128	}
129	emit profileLoaded(profile);
130}
131
132void InputController::saveConfiguration() {
133	saveConfiguration(KEYBOARD);
134#ifdef BUILD_SDL
135	saveConfiguration(SDL_BINDING_BUTTON);
136	saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
137	if (m_playerAttached) {
138		mSDLPlayerSaveConfig(&m_sdlPlayer, m_config->input());
139	}
140#endif
141	m_config->write();
142}
143
144void InputController::saveConfiguration(uint32_t type) {
145	mInputMapSave(&m_inputMap, type, m_config->input());
146	m_config->write();
147}
148
149void InputController::saveProfile(uint32_t type, const QString& profile) {
150	mInputProfileSave(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
151	m_config->write();
152}
153
154const char* InputController::profileForType(uint32_t type) {
155	UNUSED(type);
156#ifdef BUILD_SDL
157	if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
158#if SDL_VERSION_ATLEAST(2, 0, 0)
159		return SDL_JoystickName(m_sdlPlayer.joystick->joystick);
160#else
161		return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick));
162#endif
163	}
164#endif
165	return 0;
166}
167
168QStringList InputController::connectedGamepads(uint32_t type) const {
169	UNUSED(type);
170
171#ifdef BUILD_SDL
172	if (type == SDL_BINDING_BUTTON) {
173		QStringList pads;
174		for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
175			const char* name;
176#if SDL_VERSION_ATLEAST(2, 0, 0)
177			name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick);
178#else
179			name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick));
180#endif
181			if (name) {
182				pads.append(QString(name));
183			} else {
184				pads.append(QString());
185			}
186		}
187		return pads;
188	}
189#endif
190
191	return QStringList();
192}
193
194int InputController::gamepad(uint32_t type) const {
195#ifdef BUILD_SDL
196	if (type == SDL_BINDING_BUTTON) {
197		return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0;
198	}
199#endif
200	return 0;
201}
202
203void InputController::setGamepad(uint32_t type, int index) {
204#ifdef BUILD_SDL
205	if (type == SDL_BINDING_BUTTON) {
206		mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index);
207	}
208#endif
209}
210
211void InputController::setPreferredGamepad(uint32_t type, const QString& device) {
212	if (!m_config) {
213		return;
214	}
215	mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, device.toUtf8().constData());
216}
217
218mRumble* InputController::rumble() {
219#ifdef BUILD_SDL
220#if SDL_VERSION_ATLEAST(2, 0, 0)
221	if (m_playerAttached) {
222		return &m_sdlPlayer.rumble.d;
223	}
224#endif
225#endif
226	return nullptr;
227}
228
229mRotationSource* InputController::rotationSource() {
230#ifdef BUILD_SDL
231	if (m_playerAttached) {
232		return &m_sdlPlayer.rotation.d;
233	}
234#endif
235	return nullptr;
236}
237
238void InputController::registerTiltAxisX(int axis) {
239#ifdef BUILD_SDL
240	if (m_playerAttached) {
241		m_sdlPlayer.rotation.axisX = axis;
242	}
243#endif
244}
245
246void InputController::registerTiltAxisY(int axis) {
247#ifdef BUILD_SDL
248	if (m_playerAttached) {
249		m_sdlPlayer.rotation.axisY = axis;
250	}
251#endif
252}
253
254void InputController::registerGyroAxisX(int axis) {
255#ifdef BUILD_SDL
256	if (m_playerAttached) {
257		m_sdlPlayer.rotation.gyroX = axis;
258	}
259#endif
260}
261
262void InputController::registerGyroAxisY(int axis) {
263#ifdef BUILD_SDL
264	if (m_playerAttached) {
265		m_sdlPlayer.rotation.gyroY = axis;
266	}
267#endif
268}
269
270float InputController::gyroSensitivity() const {
271#ifdef BUILD_SDL
272	if (m_playerAttached) {
273		return m_sdlPlayer.rotation.gyroSensitivity;
274	}
275#endif
276	return 0;
277}
278
279void InputController::setGyroSensitivity(float sensitivity) {
280#ifdef BUILD_SDL
281	if (m_playerAttached) {
282		m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
283	}
284#endif
285}
286
287GBAKey InputController::mapKeyboard(int key) const {
288	return static_cast<GBAKey>(mInputMapKey(&m_inputMap, KEYBOARD, key));
289}
290
291void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
292	return mInputBindKey(&m_inputMap, type, key, gbaKey);
293}
294
295void InputController::updateJoysticks() {
296#ifdef BUILD_SDL
297	QString profile = profileForType(SDL_BINDING_BUTTON);
298	mSDLUpdateJoysticks(&s_sdlEvents, m_config->input());
299	QString newProfile = profileForType(SDL_BINDING_BUTTON);
300	if (profile != newProfile) {
301		loadProfile(SDL_BINDING_BUTTON, newProfile);
302	}
303#endif
304}
305
306int InputController::pollEvents() {
307	int activeButtons = 0;
308#ifdef BUILD_SDL
309	if (m_playerAttached && m_sdlPlayer.joystick) {
310		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
311		SDL_JoystickUpdate();
312		int numButtons = SDL_JoystickNumButtons(joystick);
313		int i;
314		for (i = 0; i < numButtons; ++i) {
315			GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i));
316			if (key == GBA_KEY_NONE) {
317				continue;
318			}
319			if (hasPendingEvent(key)) {
320				continue;
321			}
322			if (SDL_JoystickGetButton(joystick, i)) {
323				activeButtons |= 1 << key;
324			}
325		}
326		int numHats = SDL_JoystickNumHats(joystick);
327		for (i = 0; i < numHats; ++i) {
328			int hat = SDL_JoystickGetHat(joystick, i);
329			activeButtons |= mInputMapHat(&m_inputMap, SDL_BINDING_BUTTON, i, hat);
330		}
331
332		int numAxes = SDL_JoystickNumAxes(joystick);
333		for (i = 0; i < numAxes; ++i) {
334			int value = SDL_JoystickGetAxis(joystick, i);
335
336			enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value));
337			if (key != GBA_KEY_NONE) {
338				activeButtons |= 1 << key;
339			}
340		}
341	}
342#endif
343	return activeButtons;
344}
345
346QSet<int> InputController::activeGamepadButtons(int type) {
347	QSet<int> activeButtons;
348#ifdef BUILD_SDL
349	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
350		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
351		SDL_JoystickUpdate();
352		int numButtons = SDL_JoystickNumButtons(joystick);
353		int i;
354		for (i = 0; i < numButtons; ++i) {
355			if (SDL_JoystickGetButton(joystick, i)) {
356				activeButtons.insert(i);
357			}
358		}
359	}
360#endif
361	return activeButtons;
362}
363
364void InputController::recalibrateAxes() {
365#ifdef BUILD_SDL
366	if (m_playerAttached && m_sdlPlayer.joystick) {
367		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
368		SDL_JoystickUpdate();
369		int numAxes = SDL_JoystickNumAxes(joystick);
370		if (numAxes < 1) {
371			return;
372		}
373		m_deadzones.resize(numAxes);
374		int i;
375		for (i = 0; i < numAxes; ++i) {
376			m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
377		}
378	}
379#endif
380}
381
382QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
383	QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
384#ifdef BUILD_SDL
385	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
386		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
387		SDL_JoystickUpdate();
388		int numAxes = SDL_JoystickNumAxes(joystick);
389		if (numAxes < 1) {
390			return activeAxes;
391		}
392		m_deadzones.resize(numAxes);
393		int i;
394		for (i = 0; i < numAxes; ++i) {
395			int32_t axis = SDL_JoystickGetAxis(joystick, i);
396			axis -= m_deadzones[i];
397			if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
398				activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
399			}
400		}
401	}
402#endif
403	return activeAxes;
404}
405
406void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
407	const mInputAxis* old = mInputQueryAxis(&m_inputMap, type, axis);
408	mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
409	if (old) {
410		description = *old;
411	}
412	int deadzone = 0;
413	if (axis > 0 && m_deadzones.size() > axis) {
414		deadzone = m_deadzones[axis];
415	}
416	switch (direction) {
417	case GamepadAxisEvent::NEGATIVE:
418		description.lowDirection = key;
419
420		description.deadLow = deadzone - AXIS_THRESHOLD;
421		break;
422	case GamepadAxisEvent::POSITIVE:
423		description.highDirection = key;
424		description.deadHigh = deadzone + AXIS_THRESHOLD;
425		break;
426	default:
427		return;
428	}
429	mInputBindAxis(&m_inputMap, type, axis, &description);
430}
431
432void InputController::unbindAllAxes(uint32_t type) {
433	mInputUnbindAllAxes(&m_inputMap, type);
434}
435
436QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {
437	QSet<QPair<int, GamepadHatEvent::Direction>> activeHats;
438#ifdef BUILD_SDL
439	if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
440		SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
441		SDL_JoystickUpdate();
442		int numHats = SDL_JoystickNumHats(joystick);
443		if (numHats < 1) {
444			return activeHats;
445		}
446
447		int i;
448		for (i = 0; i < numHats; ++i) {
449			int hat = SDL_JoystickGetHat(joystick, i);
450			if (hat & GamepadHatEvent::UP) {
451				activeHats.insert(qMakePair(i, GamepadHatEvent::UP));
452			}
453			if (hat & GamepadHatEvent::RIGHT) {
454				activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT));
455			}
456			if (hat & GamepadHatEvent::DOWN) {
457				activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN));
458			}
459			if (hat & GamepadHatEvent::LEFT) {
460				activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT));
461			}
462		}
463	}
464#endif
465	return activeHats;
466}
467
468void InputController::bindHat(uint32_t type, int hat, GamepadHatEvent::Direction direction, GBAKey gbaKey) {
469	mInputHatBindings bindings{ -1, -1, -1, -1 };
470	mInputQueryHat(&m_inputMap, type, hat, &bindings);
471	switch (direction) {
472	case GamepadHatEvent::UP:
473		bindings.up = gbaKey;
474		break;
475	case GamepadHatEvent::RIGHT:
476		bindings.right = gbaKey;
477		break;
478	case GamepadHatEvent::DOWN:
479		bindings.down = gbaKey;
480		break;
481	case GamepadHatEvent::LEFT:
482		bindings.left = gbaKey;
483		break;
484	default:
485		return;
486	}
487	mInputBindHat(&m_inputMap, type, hat, &bindings);
488}
489
490void InputController::testGamepad(int type) {
491	auto activeAxes = activeGamepadAxes(type);
492	auto oldAxes = m_activeAxes;
493	m_activeAxes = activeAxes;
494
495	auto activeButtons = activeGamepadButtons(type);
496	auto oldButtons = m_activeButtons;
497	m_activeButtons = activeButtons;
498
499	auto activeHats = activeGamepadHats(type);
500	auto oldHats = m_activeHats;
501	m_activeHats = activeHats;
502
503	if (!QApplication::focusWidget()) {
504		return;
505	}
506
507	activeAxes.subtract(oldAxes);
508	oldAxes.subtract(m_activeAxes);
509
510	for (auto& axis : m_activeAxes) {
511		bool newlyAboveThreshold = activeAxes.contains(axis);
512		if (newlyAboveThreshold) {
513			GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
514			postPendingEvent(event->gbaKey());
515			sendGamepadEvent(event);
516			if (!event->isAccepted()) {
517				clearPendingEvent(event->gbaKey());
518			}
519		}
520	}
521	for (auto axis : oldAxes) {
522		GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
523		clearPendingEvent(event->gbaKey());
524		sendGamepadEvent(event);
525	}
526
527	if (!QApplication::focusWidget()) {
528		return;
529	}
530
531	activeButtons.subtract(oldButtons);
532	oldButtons.subtract(m_activeButtons);
533
534	for (int button : activeButtons) {
535		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
536		postPendingEvent(event->gbaKey());
537		sendGamepadEvent(event);
538		if (!event->isAccepted()) {
539			clearPendingEvent(event->gbaKey());
540		}
541	}
542	for (int button : oldButtons) {
543		GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
544		clearPendingEvent(event->gbaKey());
545		sendGamepadEvent(event);
546	}
547
548	activeHats.subtract(oldHats);
549	oldHats.subtract(m_activeHats);
550
551	for (auto& hat : activeHats) {
552		GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this);
553		postPendingEvent(event->gbaKey());
554		sendGamepadEvent(event);
555		if (!event->isAccepted()) {
556			clearPendingEvent(event->gbaKey());
557		}
558	}
559	for (auto& hat : oldHats) {
560		GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this);
561		clearPendingEvent(event->gbaKey());
562		sendGamepadEvent(event);
563	}
564}
565
566void InputController::sendGamepadEvent(QEvent* event) {
567	QWidget* focusWidget = nullptr;
568	if (m_focusParent) {
569		focusWidget = m_focusParent->focusWidget();
570		if (!focusWidget) {
571			focusWidget = m_focusParent;
572		}
573	} else {
574		focusWidget = QApplication::focusWidget();
575	}
576	QApplication::sendEvent(focusWidget, event);
577}
578
579void InputController::postPendingEvent(GBAKey key) {
580	m_pendingEvents.insert(key);
581}
582
583void InputController::clearPendingEvent(GBAKey key) {
584	m_pendingEvents.remove(key);
585}
586
587bool InputController::hasPendingEvent(GBAKey key) const {
588	return m_pendingEvents.contains(key);
589}
590
591void InputController::suspendScreensaver() {
592#ifdef BUILD_SDL
593#if SDL_VERSION_ATLEAST(2, 0, 0)
594	mSDLSuspendScreensaver(&s_sdlEvents);
595#endif
596#endif
597}
598
599void InputController::resumeScreensaver() {
600#ifdef BUILD_SDL
601#if SDL_VERSION_ATLEAST(2, 0, 0)
602	mSDLResumeScreensaver(&s_sdlEvents);
603#endif
604#endif
605}
606
607void InputController::setScreensaverSuspendable(bool suspendable) {
608#ifdef BUILD_SDL
609#if SDL_VERSION_ATLEAST(2, 0, 0)
610	mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
611#endif
612#endif
613}
614
615void InputController::stealFocus(QWidget* focus) {
616	m_focusParent = focus;
617}
618
619void InputController::releaseFocus(QWidget* focus) {
620	if (focus == m_focusParent) {
621		m_focusParent = m_topLevel;
622	}
623}
624
625void InputController::increaseLuminanceLevel() {
626	setLuminanceLevel(m_luxLevel + 1);
627}
628
629void InputController::decreaseLuminanceLevel() {
630	setLuminanceLevel(m_luxLevel - 1);
631}
632
633void InputController::setLuminanceLevel(int level) {
634	int value = 0x16;
635	level = std::max(0, std::min(10, level));
636	if (level > 0) {
637		value += GBA_LUX_LEVELS[level - 1];
638	}
639	setLuminanceValue(value);
640}
641
642void InputController::setLuminanceValue(uint8_t value) {
643	m_luxValue = value;
644	value = std::max<int>(value - 0x16, 0);
645	m_luxLevel = 10;
646	for (int i = 0; i < 10; ++i) {
647		if (value < GBA_LUX_LEVELS[i]) {
648			m_luxLevel = i;
649			break;
650		}
651	}
652	emit luminanceValueChanged(m_luxValue);
653}
654