all repos — mgba @ 08fee36c208ecfb8c8298b7ea76d036e4550a769

mGBA Game Boy Advance Emulator

src/platform/qt/GameController.cpp (view raw)

  1#include "GameController.h"
  2
  3#include "AudioProcessor.h"
  4
  5#include <QThread>
  6
  7extern "C" {
  8#include "gba.h"
  9#include "gba-audio.h"
 10#include "renderers/video-software.h"
 11#include "util/vfs.h"
 12}
 13
 14using namespace QGBA;
 15
 16GameController::GameController(QObject* parent)
 17	: QObject(parent)
 18	, m_drawContext(new uint32_t[256 * 256])
 19	, m_activeKeys(0)
 20	, m_rom(nullptr)
 21	, m_audioThread(new QThread(this))
 22	, m_audioProcessor(new AudioProcessor)
 23{
 24#ifdef BUILD_SDL
 25	SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);
 26	GBASDLInitEvents(&m_sdlEvents);
 27	SDL_JoystickEventState(SDL_QUERY);
 28#endif
 29	m_renderer = new GBAVideoSoftwareRenderer;
 30	GBAVideoSoftwareRendererCreate(m_renderer);
 31	m_renderer->outputBuffer = (color_t*) m_drawContext;
 32	m_renderer->outputBufferStride = 256;
 33	m_threadContext = {
 34		.state = THREAD_INITIALIZED,
 35		.debugger = 0,
 36		.frameskip = 0,
 37		.bios = 0,
 38		.renderer = &m_renderer->d,
 39		.userData = this,
 40		.rewindBufferCapacity = 0
 41	};
 42	m_threadContext.startCallback = [] (GBAThread* context) {
 43		GameController* controller = static_cast<GameController*>(context->userData);
 44		controller->m_audioProcessor->setInput(context);
 45		controller->gameStarted(context);
 46	};
 47
 48	m_threadContext.cleanCallback = [] (GBAThread* context) {
 49		GameController* controller = static_cast<GameController*>(context->userData);
 50		controller->gameStopped(context);
 51	};
 52
 53	m_threadContext.frameCallback = [] (GBAThread* context) {
 54		GameController* controller = static_cast<GameController*>(context->userData);
 55		controller->m_pauseMutex.lock();
 56		if (controller->m_pauseAfterFrame) {
 57			GBAThreadPauseFromThread(context);
 58			controller->m_pauseAfterFrame = false;
 59			controller->gamePaused(&controller->m_threadContext);
 60		}
 61		controller->m_pauseMutex.unlock();
 62		controller->frameAvailable(controller->m_drawContext);
 63	};
 64
 65	m_audioThread->start();
 66	m_audioProcessor->moveToThread(m_audioThread);
 67	connect(this, SIGNAL(gameStarted(GBAThread*)), m_audioProcessor, SLOT(start()));
 68	connect(this, SIGNAL(gameStopped(GBAThread*)), m_audioProcessor, SLOT(pause()));
 69	connect(this, SIGNAL(gamePaused(GBAThread*)), m_audioProcessor, SLOT(pause()));
 70	connect(this, SIGNAL(gameUnpaused(GBAThread*)), m_audioProcessor, SLOT(start()));
 71
 72#ifdef BUILD_SDL
 73	connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(testSDLEvents()));
 74#endif
 75}
 76
 77GameController::~GameController() {
 78	m_audioThread->quit();
 79	if (GBAThreadIsPaused(&m_threadContext)) {
 80		GBAThreadUnpause(&m_threadContext);
 81	}
 82	GBAThreadEnd(&m_threadContext);
 83	GBAThreadJoin(&m_threadContext);
 84	delete m_renderer;
 85}
 86
 87ARMDebugger* GameController::debugger() {
 88	return m_threadContext.debugger;
 89}
 90
 91void GameController::setDebugger(ARMDebugger* debugger) {
 92	bool wasPaused = isPaused();
 93	setPaused(true);
 94	if (m_threadContext.debugger && GBAThreadHasStarted(&m_threadContext)) {
 95		GBADetachDebugger(m_threadContext.gba);
 96	}
 97	m_threadContext.debugger = debugger;
 98	if (m_threadContext.debugger && GBAThreadHasStarted(&m_threadContext)) {
 99		GBAAttachDebugger(m_threadContext.gba, m_threadContext.debugger);
100	}
101	setPaused(wasPaused);
102}
103
104void GameController::loadGame(const QString& path) {
105	closeGame();
106	m_threadContext.sync.videoFrameWait = 0;
107	m_threadContext.sync.audioWait = 1;
108	m_rom = new QFile(path);
109	if (!m_rom->open(QIODevice::ReadOnly)) {
110		delete m_rom;
111		m_rom = nullptr;
112	}
113
114	m_pauseAfterFrame = false;
115
116	m_threadContext.rom = VFileFromFD(m_rom->handle());
117	m_threadContext.fname = path.toLocal8Bit().constData();
118
119	GBAThreadStart(&m_threadContext);
120}
121
122void GameController::closeGame() {
123	if (!m_rom) {
124		return;
125	}
126	GBAThreadEnd(&m_threadContext);
127	GBAThreadJoin(&m_threadContext);
128	if (m_rom) {
129		m_rom->close();
130		delete m_rom;
131		m_rom = nullptr;
132	}
133	emit gameStopped(&m_threadContext);
134}
135
136bool GameController::isPaused() {
137	return GBAThreadIsPaused(&m_threadContext);
138}
139
140void GameController::setPaused(bool paused) {
141	if (paused == GBAThreadIsPaused(&m_threadContext)) {
142		return;
143	}
144	if (paused) {
145		GBAThreadPause(&m_threadContext);
146		emit gamePaused(&m_threadContext);
147	} else {
148		GBAThreadUnpause(&m_threadContext);
149		emit gameUnpaused(&m_threadContext);
150	}
151}
152
153void GameController::reset() {
154	GBAThreadReset(&m_threadContext);
155}
156
157void GameController::frameAdvance() {
158	m_pauseMutex.lock();
159	m_pauseAfterFrame = true;
160	setPaused(false);
161	m_pauseMutex.unlock();
162}
163
164void GameController::keyPressed(int key) {
165	int mappedKey = 1 << key;
166	m_activeKeys |= mappedKey;
167	updateKeys();
168}
169
170void GameController::keyReleased(int key) {
171	int mappedKey = 1 << key;
172	m_activeKeys &= ~mappedKey;
173	updateKeys();
174}
175
176void GameController::setAudioBufferSamples(int samples) {
177	GBAThreadInterrupt(&m_threadContext);
178	m_threadContext.audioBuffers = samples;
179	GBAAudioResizeBuffer(&m_threadContext.gba->audio, samples);
180	GBAThreadContinue(&m_threadContext);
181	QMetaObject::invokeMethod(m_audioProcessor, "setBufferSamples", Q_ARG(int, samples));
182}
183
184void GameController::setFPSTarget(float fps) {
185	GBAThreadInterrupt(&m_threadContext);
186	m_threadContext.fpsTarget = fps;
187	GBAThreadContinue(&m_threadContext);
188	QMetaObject::invokeMethod(m_audioProcessor, "inputParametersChanged");
189}
190
191void GameController::updateKeys() {
192	int activeKeys = m_activeKeys;
193#ifdef BUILD_SDL
194	activeKeys |= m_activeButtons;
195#endif
196	m_threadContext.activeKeys = activeKeys;
197}
198
199#ifdef BUILD_SDL
200void GameController::testSDLEvents() {
201	SDL_Joystick* joystick = m_sdlEvents.joystick;
202	SDL_JoystickUpdate();
203	int numButtons = SDL_JoystickNumButtons(joystick);
204	m_activeButtons = 0;
205	int i;
206	for (i = 0; i < numButtons; ++i) {
207		GBAKey key = GBASDLMapButtonToKey(i);
208		if (key == GBA_KEY_NONE) {
209			continue;
210		}
211		if (SDL_JoystickGetButton(joystick, i)) {
212			m_activeButtons |= 1 << key;
213		}
214	}
215	int numHats = SDL_JoystickNumHats(joystick);
216	for (i = 0; i < numHats; ++i) {
217		int hat = SDL_JoystickGetHat(joystick, i);
218		if (hat & SDL_HAT_UP) {
219			m_activeButtons |= 1 << GBA_KEY_UP;
220		}
221		if (hat & SDL_HAT_LEFT) {
222			m_activeButtons |= 1 << GBA_KEY_LEFT;
223		}
224		if (hat & SDL_HAT_DOWN) {
225			m_activeButtons |= 1 << GBA_KEY_DOWN;
226		}
227		if (hat & SDL_HAT_RIGHT) {
228			m_activeButtons |= 1 << GBA_KEY_RIGHT;
229		}
230	}
231	updateKeys();
232}
233#endif