all repos — mgba @ ad37ae3d61326865a190f4b866800474c30ebadd

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