diff --git a/include/emulator.hpp b/include/emulator.hpp index 8782fb1f..b508338e 100644 --- a/include/emulator.hpp +++ b/include/emulator.hpp @@ -56,7 +56,8 @@ class Emulator { static constexpr u32 width = 400; static constexpr u32 height = 240 * 2; // * 2 because 2 screens ROMType romType = ROMType::None; - bool running = true; + bool running = false; // Is the emulator running a game? + bool programRunning = false; // Is the emulator program itself running? #ifdef PANDA3DS_ENABLE_HTTP_SERVER HttpServer httpServer; @@ -86,6 +87,10 @@ class Emulator { void run(); void runFrame(); + void resume(); // Resume the emulator + void pause(); // Pause the emulator + void togglePause(); + bool loadROM(const std::filesystem::path& path); bool loadNCSD(const std::filesystem::path& path, ROMType type); bool loadELF(const std::filesystem::path& path); diff --git a/include/httpserver.hpp b/include/httpserver.hpp index 25bc9c7c..82aee2a4 100644 --- a/include/httpserver.hpp +++ b/include/httpserver.hpp @@ -13,7 +13,7 @@ #include "helpers.hpp" -enum class HttpActionType { None, Screenshot, Key, LoadRom }; +enum class HttpActionType { None, Screenshot, Key, TogglePause, Reset, LoadRom }; class Emulator; namespace httplib { @@ -44,6 +44,8 @@ class HttpAction { static std::unique_ptr createScreenshotAction(DeferredResponseWrapper& response); static std::unique_ptr createKeyAction(uint32_t key, bool state); static std::unique_ptr createLoadRomAction(std::filesystem::path path, bool paused); + static std::unique_ptr createTogglePauseAction(); + static std::unique_ptr createResetAction(); }; struct HttpServer { diff --git a/src/emulator.cpp b/src/emulator.cpp index 5e42c086..fd5efe6b 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -75,6 +75,8 @@ Emulator::Emulator() } } + running = false; + programRunning = false; reset(ReloadOption::NoReload); } @@ -113,7 +115,9 @@ void Emulator::step() {} void Emulator::render() {} void Emulator::run() { - while (running) { + programRunning = true; + + while (programRunning) { #ifdef PANDA3DS_ENABLE_HTTP_SERVER httpServer.processActions(); #endif @@ -127,7 +131,7 @@ void Emulator::run() { switch (event.type) { case SDL_QUIT: printf("Bye :(\n"); - running = false; + programRunning = false; return; case SDL_KEYDOWN: @@ -169,6 +173,19 @@ void Emulator::run() { case SDLK_RETURN: hid.pressKey(Keys::Start); break; case SDLK_BACKSPACE: hid.pressKey(Keys::Select); break; + + // Use the F4 button as a hot-key to pause or resume the emulator + // We can't use the audio play/pause buttons because it's annoying + case SDLK_F4: { + togglePause(); + break; + } + + // Use F5 as a reset button + case SDLK_F5: { + reset(ReloadOption::Reload); + break; + } } break; @@ -346,8 +363,13 @@ void Emulator::run() { } } +// Only resume if a ROM is properly loaded +void Emulator::resume() { running = (romType != ROMType::None); } +void Emulator::pause() { running = false; } +void Emulator::togglePause() { running ? pause() : resume(); } + void Emulator::runFrame() { - if (romType != ROMType::None) { + if (running) { cpu.runFrame(); // Run 1 frame of instructions gpu.display(); // Display graphics @@ -360,6 +382,10 @@ void Emulator::runFrame() { if (cheats.haveCheats()) [[unlikely]] { cheats.run(); } + } else if (romType != ROMType::None) { + // If the emulator is not running and a game is loaded, we still want to display the framebuffer otherwise we will get weird + // double-buffering issues + gpu.display(); } } @@ -408,6 +434,7 @@ bool Emulator::loadROM(const std::filesystem::path& path) { romType = ROMType::None; } + resume(); // Start the emulator return success; } diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 2def1887..4f9e0fe2 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -20,6 +20,16 @@ class HttpActionScreenshot : public HttpAction { DeferredResponseWrapper& getResponse() { return response; } }; +class HttpActionTogglePause : public HttpAction { + public: + HttpActionTogglePause() : HttpAction(HttpActionType::TogglePause) {} +}; + +class HttpActionReset : public HttpAction { + public: + HttpActionReset() : HttpAction(HttpActionType::Reset) {} +}; + class HttpActionKey : public HttpAction { u32 key; bool state; @@ -47,6 +57,8 @@ std::unique_ptr HttpAction::createScreenshotAction(DeferredResponseW } std::unique_ptr HttpAction::createKeyAction(u32 key, bool state) { return std::make_unique(key, state); } +std::unique_ptr HttpAction::createTogglePauseAction() { return std::make_unique(); } +std::unique_ptr HttpAction::createResetAction() { return std::make_unique(); } std::unique_ptr HttpAction::createLoadRomAction(std::filesystem::path path, bool paused) { return std::make_unique(path, paused); @@ -154,6 +166,16 @@ void HttpServer::startHttpServer() { } pushAction(HttpAction::createLoadRomAction(romPath, paused)); + response.set_content("ok", "text/plain"); + }); + + server->Get("/togglepause", [this](const httplib::Request&, httplib::Response& response) { + pushAction(HttpAction::createTogglePauseAction()); + response.set_content("ok", "text/plain"); + }); + + server->Get("/reset", [this](const httplib::Request&, httplib::Response& response) { + pushAction(HttpAction::createResetAction()); response.set_content("ok", "text/plain"); }); @@ -219,6 +241,9 @@ void HttpServer::processActions() { break; } + case HttpActionType::TogglePause: emulator->togglePause(); break; + case HttpActionType::Reset: emulator->reset(Emulator::ReloadOption::Reload); break; + default: break; } }