all repos — mgba @ dd1387732defa3ec801d194341a578aea919a264

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