all repos — mgba @ 4da65d0f2f041e2db4874087e1e0d8da5a2d96be

mGBA Game Boy Advance Emulator

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

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