[gens-sdl] Created an EventLoop base class and rebased EmuLoop and CrazyEffectLoop.
authorDavid Korth <gerbilsoft@gerbilsoft.com>
Thu, 3 Sep 2015 02:36:13 +0000 (22:36 -0400)
committerDavid Korth <gerbilsoft@gerbilsoft.com>
Thu, 3 Sep 2015 02:36:13 +0000 (22:36 -0400)
The LibGens::Timing object has been moved to EventLoop::m_clks.

TODO:
- Move EventLoop objects into a private class in EventLoop_p.hpp.
- Make EmuLoop's and CrazyEventLoop's private classes inherit
  from EventLoopPrivate.
- More cleanup and refactoring.

src/gens-sdl/CMakeLists.txt
src/gens-sdl/CrazyEffectLoop.cpp
src/gens-sdl/CrazyEffectLoop.hpp
src/gens-sdl/EmuLoop.cpp
src/gens-sdl/EmuLoop.hpp
src/gens-sdl/EventLoop.cpp [new file with mode: 0644]
src/gens-sdl/EventLoop.hpp [new file with mode: 0644]
src/gens-sdl/gens-sdl.cpp
src/gens-sdl/gens-sdl.hpp

index 20dc947..195372a 100644 (file)
@@ -32,6 +32,7 @@ CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/config.gens-sdl.h.in" "${CMAKE_CURRE
 # Sources.
 SET(gens-sdl_SRCS
        gens-sdl.cpp
+       EventLoop.cpp
        EmuLoop.cpp
        CrazyEffectLoop.cpp
        SdlHandler.cpp
@@ -49,6 +50,7 @@ SET(gens-sdl_SRCS
 # Headers.
 SET(gens-sdl_H
        gens-sdl.hpp
+       EventLoop.hpp
        EmuLoop.hpp
        CrazyEffectLoop.hpp
        SdlHandler.hpp
index 7283747..3aa43af 100644 (file)
@@ -67,70 +67,113 @@ using LibGens::CrazyEffect;
 
 namespace GensSdl {
 
-static MdFb *crazyFb = nullptr;
-static CrazyEffect *crazyEffect = nullptr;
+class CrazyEffectLoopPrivate
+{
+       public:
+               CrazyEffectLoopPrivate();
+               ~CrazyEffectLoopPrivate();
+
+       private:
+               // Q_DISABLE_COPY() equivalent.
+               // TODO: Add GensSdl-specific version of Q_DISABLE_COPY().
+               CrazyEffectLoopPrivate(const CrazyEffectLoopPrivate &);
+               CrazyEffectLoopPrivate &operator=(const CrazyEffectLoopPrivate &);
+
+       public:
+               MdFb *crazyFb;
+               CrazyEffect *crazyEffect;
+};
+
+/** CrazyEffectLoopPrivate **/
+
+CrazyEffectLoopPrivate::CrazyEffectLoopPrivate()
+       : crazyFb(nullptr)
+       , crazyEffect(nullptr)
+{ }
+
+CrazyEffectLoopPrivate::~CrazyEffectLoopPrivate()
+{
+       if (crazyFb) {
+               crazyFb->unref();
+               crazyFb = nullptr;
+       }
+       delete crazyEffect;
+}
+
+/** CrazyEffectLoop **/
+
+CrazyEffectLoop::CrazyEffectLoop()
+       : d(new CrazyEffectLoopPrivate())
+{ }
+
+CrazyEffectLoop::~CrazyEffectLoop()
+{
+       delete d;
+}
 
 /**
- * Run the "Crazy Effect" loop.
+ * Run the event loop.
+ * @param rom_filename ROM filename. [TODO: Replace with options struct?]
  * @return Exit code.
  */
-int CrazyEffectLoop(void)
+int CrazyEffectLoop::run(const char *rom_filename)
 {
+       // rom_filename isn't used here.
+       ((void)rom_filename);
+
        // TODO: Move common code back to gens-sdl?
 
        // Initialize the SDL handlers.
-       sdlHandler = new SdlHandler();
-       if (sdlHandler->init_video() < 0)
+       m_sdlHandler = new SdlHandler();
+       if (m_sdlHandler->init_video() < 0)
                return EXIT_FAILURE;
        // No audio here.
-       //if (sdlHandler->init_audio() < 0)
+       //if (m_sdlHandler->init_audio() < 0)
        //      return EXIT_FAILURE;
-       vBackend = sdlHandler->vBackend();
+       m_vBackend = m_sdlHandler->vBackend();
 
        // Set the window title.
-       sdlHandler->set_window_title("Gens/GS II [SDL]");
+       m_sdlHandler->set_window_title("Gens/GS II [SDL]");
 
        // Check for startup messages.
        checkForStartupMessages();
 
        // Start the frame timer.
        // TODO: Region code?
-       clks.reset();
-
-       // Enable frameskip.
-       frameskip = true;
+       m_clks.reset();
 
        // Create the "Crazy" Effect framebuffer.
        // Image size defaults to the full framebuffer,
        // so we don't have to worry about "stretch modes".
-       crazyFb = new MdFb();
-       crazyFb->setBpp(MdFb::BPP_32);
+       d->crazyFb = new MdFb();
+       d->crazyFb->setBpp(MdFb::BPP_32);
        // Set the SDL video source.
-       sdlHandler->set_video_source(crazyFb);
+       m_sdlHandler->set_video_source(d->crazyFb);
 
        // Create the "Crazy" Effect object.
-       crazyEffect = new CrazyEffect();
-       crazyEffect->setColorMask(CrazyEffect::CM_WHITE);
+       d->crazyEffect = new CrazyEffect();
+       d->crazyEffect->setColorMask(CrazyEffect::CM_WHITE);
 
        // TODO: Move some more common stuff back to gens-sdl.cpp.
-       while (running) {
+       m_running = true;
+       while (m_running) {
                SDL_Event event;
                int ret;
-               if (paused.data) {
+               if (m_paused.data) {
                        // Emulation is paused.
-                       if (!vBackend->has_osd_messages()) {
+                       if (!m_vBackend->has_osd_messages()) {
                                // No OSD messages.
                                // Wait for an SDL event.
                                ret = SDL_WaitEvent(&event);
                                if (ret) {
-                                       processSdlEvent_common(&event);
+                                       processSdlEvent(&event);
                                }
                        }
 
                        // Process OSD messages.
-                       vBackend->process_osd_messages();
+                       m_vBackend->process_osd_messages();
                }
-               if (!running)
+               if (!m_running)
                        break;
 
                // Poll for SDL events, and wait for the queue
@@ -139,61 +182,64 @@ int CrazyEffectLoop(void)
                do {
                        ret = SDL_PollEvent(&event);
                        if (ret) {
-                               processSdlEvent_common(&event);
+                               processSdlEvent(&event);
                        }
-               } while (running && ret != 0);
-               if (!running)
+               } while (m_running && ret != 0);
+               if (!m_running)
                        break;
 
-               if (paused.data) {
+               if (m_paused.data) {
                        // Emulation is paused.
                        // Only update video if the VBackend is dirty
                        // or the SDL window has been exposed.
-                       sdlHandler->update_video_paused(exposed);
+                       m_sdlHandler->update_video_paused(m_exposed);
 
                        // Don't run any frames.
                        continue;
                }
 
                // Clear the 'exposed' flag.
-               exposed = false;
+               m_exposed = false;
 
                // New start time.
-               clks.new_clk = timing.getTime();
+               m_clks.new_clk = m_clks.timing.getTime();
 
                // Update the FPS counter.
-               unsigned int fps_tmp = ((clks.new_clk - clks.fps_clk) & 0x3FFFFF);
+               unsigned int fps_tmp = ((m_clks.new_clk - m_clks.fps_clk) & 0x3FFFFF);
                if (fps_tmp >= 1000000) {
                        // More than 1 second has passed.
-                       clks.fps_clk = clks.new_clk;
+                       m_clks.fps_clk = m_clks.new_clk;
                        // FIXME: Just use abs() here.
-                       if (clks.frames_old > clks.frames) {
-                               clks.fps = (clks.frames_old - clks.frames);
+                       if (m_clks.frames_old > m_clks.frames) {
+                               m_clks.fps = (m_clks.frames_old - m_clks.frames);
                        } else {
-                               clks.fps = (clks.frames - clks.frames_old);
+                               m_clks.fps = (m_clks.frames - m_clks.frames_old);
                        }
-                       clks.frames_old = clks.frames;
+                       m_clks.frames_old = m_clks.frames;
 
                        // Update the window title.
                        // TODO: Average the FPS over multiple seconds
                        // and/or quarter-seconds.
                        char win_title[256];
-                       snprintf(win_title, sizeof(win_title), "Gens/GS II [SDL] - %u fps", clks.fps);
-                       sdlHandler->set_window_title(win_title);
+                       snprintf(win_title, sizeof(win_title), "Gens/GS II [SDL] - %u fps", m_clks.fps);
+                       m_sdlHandler->set_window_title(win_title);
                }
 
                // Run the "Crazy" effect.
                // TODO: Use the frameskip code to limit frames?
-               crazyEffect->run(crazyFb);
-               sdlHandler->update_video();
-               clks.frames++;
+               d->crazyEffect->run(d->crazyFb);
+               m_sdlHandler->update_video();
+               m_clks.frames++;
                yield();
                continue;
        }
 
        // Delete/unreference the "Crazy" Effect objects.
-       crazyFb->unref();
-       delete crazyEffect;
+       // TODO: Move out of the private class and into local scope?
+       d->crazyFb->unref();
+       d->crazyFb = nullptr;
+       delete d->crazyEffect;
+       d->crazyEffect = nullptr;
 
        // Done running the "Crazy" Effect loop.
        return 0;
index 8a0c5e4..64037cd 100644 (file)
 #ifndef __GENS_SDL_CRAZYEFFECTLOOP_HPP__
 #define __GENS_SDL_CRAZYEFFECTLOOP_HPP__
 
+#include "EventLoop.hpp"
+
 namespace GensSdl {
 
-/**
- * Run the "Crazy Effect" loop.
- * @return Exit code.
- */
-int CrazyEffectLoop(void);
+class CrazyEffectLoopPrivate;
+class CrazyEffectLoop : public EventLoop
+{
+       public:
+               CrazyEffectLoop();
+               virtual ~CrazyEffectLoop();
+
+       private:
+               friend class CrazyEffectLoopPrivate;
+               CrazyEffectLoopPrivate *const d;
+       private:
+               // Q_DISABLE_COPY() equivalent.
+               // TODO: Add GensSdl-specific version of Q_DISABLE_COPY().
+               CrazyEffectLoop(const CrazyEffectLoop &);
+               CrazyEffectLoop &operator=(const CrazyEffectLoop &);
+
+       public:
+               /**
+                * Run the event loop.
+                * @param rom_filename ROM filename. [TODO: Replace with options struct?]
+                * @return Exit code.
+                */
+               virtual int run(const char *rom_filename) final;
+};
 
 }
 
index eb424c4..948bcf1 100644 (file)
@@ -93,33 +93,194 @@ using std::string;
 
 namespace GensSdl {
 
-static Rom *rom = nullptr;
-static EmuContext *context = nullptr;
-static bool isPico = false;
+class EmuLoopPrivate
+{
+       public:
+               EmuLoopPrivate();
+               ~EmuLoopPrivate();
+
+       private:
+               // Q_DISABLE_COPY() equivalent.
+               // TODO: Add GensSdl-specific version of Q_DISABLE_COPY().
+               EmuLoopPrivate(const EmuLoopPrivate &);
+               EmuLoopPrivate &operator=(const EmuLoopPrivate &);
+
+       public:
+               Rom *rom;
+               bool isPico;
+               bool frameskip; // If true, enable frameskip.
+
+               EmuContext *emuContext;
+               KeyManager *keyManager;
+
+               // Save slot.
+               int saveSlot_selected;
+
+               // Keymaps.
+               static const GensKey_t keyMap_md[];
+               static const GensKey_t keyMap_pico[];
+};
+
+/** EmuLoopPrivate **/
 
-static KeyManager *keyManager = nullptr;
 // MD 6-button keyMap.
-static const GensKey_t keyMap_md[] = {
+const GensKey_t EmuLoopPrivate::keyMap_md[] = {
        KEYV_UP, KEYV_DOWN, KEYV_LEFT, KEYV_RIGHT,      // UDLR
        KEYV_s, KEYV_d, KEYV_a, KEYV_RETURN,            // BCAS
        KEYV_e, KEYV_w, KEYV_q, KEYV_RSHIFT             // ZYXM
 };
 // Sega Pico keyMap.
-static const GensKey_t keyMap_pico[] = {
+const GensKey_t EmuLoopPrivate::keyMap_pico[] = {
        KEYV_UP, KEYV_DOWN, KEYV_LEFT, KEYV_RIGHT,              // UDLR
        KEYV_SPACE, KEYV_PAGEDOWN, KEYV_PAGEUP, KEYV_RETURN     // BCAS
        , 0, 0, 0, 0
 };
 
-// Save slot.
-static int saveSlot_selected = 0;
+EmuLoopPrivate::EmuLoopPrivate()
+       : rom(nullptr)
+       , isPico(false)
+       , frameskip(true)
+       , emuContext(nullptr)
+       , keyManager(nullptr)
+       , saveSlot_selected(0)
+{ }
+
+EmuLoopPrivate::~EmuLoopPrivate()
+{
+       delete rom;
+       delete emuContext;
+       delete keyManager;
+}
+
+/** EmuLoop **/
+
+EmuLoop::EmuLoop()
+       : d(new EmuLoopPrivate())
+{ }
+
+EmuLoop::~EmuLoop()
+{
+       delete d;
+}
+
+/**
+ * Process an SDL event.
+ * @param event SDL event.
+ * @return 0 if the event was handled; non-zero if it wasn't.
+ */
+int EmuLoop::processSdlEvent(const SDL_Event *event) {
+       int ret = 0;
+       switch (event->type) {
+               case SDL_KEYDOWN:
+                       // SDL keycodes nearly match GensKey.
+                       // TODO: Split out into a separate function?
+                       // TODO: Check for "no modifiers" for some keys?
+                       switch (event->key.keysym.sym) {
+                               case SDLK_TAB:
+                                       // Check for Shift.
+                                       if (event->key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) {
+                                               // Hard Reset.
+                                               d->emuContext->hardReset();
+                                               m_vBackend->osd_print(1500, "Hard Reset.");
+                                       } else {
+                                               // Soft Reset.
+                                               d->emuContext->softReset();
+                                               m_vBackend->osd_print(1500, "Soft Reset.");
+                                       }
+                                       break;
+
+                               case SDLK_BACKSPACE:
+                                       if (event->key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) {
+                                               // Take a screenshot.
+                                               doScreenShot();
+                                       }
+                                       break;
+
+                               case SDLK_F2:
+                                       // NOTE: This is handled here instead of in the generic
+                                       // processSdlEvent_common() because stretch functionality
+                                       // isn't used in CrazyEffectLoop.
+                                       if (event->key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) {
+                                               // Change stretch mode parameters.
+                                               doStretchMode();
+                                       } else {
+                                               // Not handling this event.
+                                               ret = 1;
+                                       }
+                                       break;
+
+                               case SDLK_0: case SDLK_1:
+                               case SDLK_2: case SDLK_3:
+                               case SDLK_4: case SDLK_5:
+                               case SDLK_6: case SDLK_7:
+                               case SDLK_8: case SDLK_9:
+                                       // Save slot selection.
+                                       doSaveSlot(event->key.keysym.sym - SDLK_0);
+                                       break;
+
+                               case SDLK_F5:
+                                       // Save state.
+                                       doSaveState();
+                                       break;
+
+                               case SDLK_F6: {
+                                       // Previous save slot.
+                                       int saveSlot  = ((d->saveSlot_selected + 9) % 10);
+                                       doSaveSlot(saveSlot);
+                                       break;
+                               }
+
+                               case SDLK_F7: {
+                                       // Next save slot.
+                                       int saveSlot  = ((d->saveSlot_selected + 1) % 10);
+                                       doSaveSlot(saveSlot);
+                                       break;
+                               }
+
+                               case SDLK_F8:
+                                       // Load state.
+                                       doLoadState();
+                                       break;
+
+                               default: {
+                                       // Check if the base class event handler will handle this.
+                                       int ret = EventLoop::processSdlEvent(event);
+                                       if (ret != 0) {
+                                               // Not handled.
+                                               // Send the key to the KeyManager.
+                                               d->keyManager->keyDown(SdlHandler::scancodeToGensKey(event->key.keysym.scancode));
+                                               break;
+                                       }
+                                       break;
+                               }
+                       }
+                       break;
+
+               case SDL_KEYUP:
+                       // SDL keycodes nearly match GensKey.
+                       d->keyManager->keyUp(SdlHandler::scancodeToGensKey(event->key.keysym.scancode));
+                       break;
+
+               default:
+                       // Event not handled.
+                       ret = 1;
+                       break;
+       }
+
+       if (ret != 0) {
+               // Event wasn't handled.
+               // Try the base class.
+               ret = EventLoop::processSdlEvent(event);
+       }
+       return ret;
+}
 
 /**
  * Get the modification time string for the specified save file.
  * @param zomg Save file.
  * @return String contianing the mtime, or an error message if invalid.
  */
-static string getSaveSlot_mtime(const ZomgBase *zomg)
+string EmuLoop::getSaveSlot_mtime(const ZomgBase *zomg)
 {
        // TODO: This function can probably be optimized more...
 
@@ -181,12 +342,12 @@ convert:
  * Save slot selection.
  * @param saveSlot Save slot. (0-9)
  */
-static void doSaveSlot(int saveSlot)
+void EmuLoop::doSaveSlot(int saveSlot)
 {
        assert(saveSlot >= 0 && saveSlot <= 9);
        if (saveSlot < 0 || saveSlot > 9)
                return;
-       saveSlot_selected = saveSlot;
+       d->saveSlot_selected = saveSlot;
 
        // Metadata variables.
        string slot_state;
@@ -195,7 +356,7 @@ static void doSaveSlot(int saveSlot)
 
        // Check if the specified savestate exists.
        // TODO: R_OK or just F_OK?
-       string filename = getSavestateFilename(rom, saveSlot);
+       string filename = getSavestateFilename(d->rom, saveSlot);
        if (!access(filename.c_str(), F_OK)) {
                // Savestate exists.
                // Load some file information.
@@ -224,36 +385,38 @@ static void doSaveSlot(int saveSlot)
        }
 
        // Show an OSD message.
-       vBackend->osd_printf(1500, "Slot %d [%s]", saveSlot, slot_state.c_str());
+       m_vBackend->osd_printf(1500, "Slot %d [%s]", saveSlot, slot_state.c_str());
        // If img_data.data is nullptr, this will hide the current image.
-       vBackend->osd_preview_image(1500, &img_data);
+       m_vBackend->osd_preview_image(1500, &img_data);
        free(img_data.data);
 }
 
 /**
  * Load the state in the selected slot.
  */
-static void doLoadState(void)
+void EmuLoop::doLoadState(void)
 {
-       assert(saveSlot_selected >= 0 && saveSlot_selected <= 9);
-       if (saveSlot_selected < 0 || saveSlot_selected > 9)
+       assert(d->saveSlot_selected >= 0 && d->saveSlot_selected <= 9);
+       if (d->saveSlot_selected < 0 || d->saveSlot_selected > 9)
                return;
 
-       string filename = getSavestateFilename(rom, saveSlot_selected);
-       int ret = context->zomgLoad(filename.c_str());
+       string filename = getSavestateFilename(d->rom, d->saveSlot_selected);
+       int ret = d->emuContext->zomgLoad(filename.c_str());
        if (ret == 0) {
                // State loaded.
-               vBackend->osd_printf(1500, "Slot %d loaded.", saveSlot_selected);
+               m_vBackend->osd_printf(1500, "Slot %d loaded.", d->saveSlot_selected);
        } else {
                // Error loading state.
                if (ret == -ENOENT) {
                        // File not found.
-                       vBackend->osd_printf(1500, "Slot %d is empty.", saveSlot_selected);
+                       m_vBackend->osd_printf(1500,
+                               "Slot %d is empty.",
+                               d->saveSlot_selected);
                } else {
                        // Other error.
-                       vBackend->osd_printf(1500,
+                       m_vBackend->osd_printf(1500,
                                "Error loading Slot %d:\n* %s",
-                               saveSlot_selected, strerror(-ret));
+                               d->saveSlot_selected, strerror(-ret));
                }
        }
 }
@@ -261,35 +424,37 @@ static void doLoadState(void)
 /**
  * Save the state in the selected slot.
  */
-static void doSaveState(void)
+void EmuLoop::doSaveState(void)
 {
-       assert(saveSlot_selected >= 0 && saveSlot_selected <= 9);
-       if (saveSlot_selected < 0 || saveSlot_selected > 9)
+       assert(d->saveSlot_selected >= 0 && d->saveSlot_selected <= 9);
+       if (d->saveSlot_selected < 0 || d->saveSlot_selected > 9)
                return;
 
-       string filename = getSavestateFilename(rom, saveSlot_selected);
-       int ret = context->zomgSave(filename.c_str());
+       string filename = getSavestateFilename(d->rom, d->saveSlot_selected);
+       int ret = d->emuContext->zomgSave(filename.c_str());
        if (ret == 0) {
                // State saved.
-               vBackend->osd_printf(1500, "Slot %d saved.", saveSlot_selected);
+               m_vBackend->osd_printf(1500,
+                               "Slot %d saved.",
+                               d->saveSlot_selected);
        } else {
                // Error saving state.
-               vBackend->osd_printf(1500,
+               m_vBackend->osd_printf(1500,
                                "Error saving Slot %d:\n* %s",
-                               saveSlot_selected, strerror(-ret));
+                               d->saveSlot_selected, strerror(-ret));
        }
 }
 
 /**
  * Change stretch mode parameters.
  */
-static void doStretchMode(void)
+void EmuLoop::doStretchMode(void)
 {
        // Change stretch mode parameters.
-       int stretchMode = (int)vBackend->stretchMode();
+       int stretchMode = (int)m_vBackend->stretchMode();
        stretchMode++;
        stretchMode &= 3;
-       vBackend->setStretchMode((VBackend::StretchMode_t)stretchMode);
+       m_vBackend->setStretchMode((VBackend::StretchMode_t)stretchMode);
 
        // Show an OSD message.
        const char *stretch;
@@ -309,187 +474,75 @@ static void doStretchMode(void)
                        break;
        }
 
-       vBackend->osd_printf(1500, "Stretch Mode set to %s.", stretch);
+       m_vBackend->osd_printf(1500, "Stretch Mode set to %s.", stretch);
 }
 
 /**
  * Take a screenshot.
  */
-static void doScreenShot(void)
+void EmuLoop::doScreenShot(void)
 {
-       int ret = GensSdl::doScreenShot(context->m_vdp->MD_Screen, rom);
+       int ret = GensSdl::doScreenShot(d->emuContext->m_vdp->MD_Screen, d->rom);
        if (ret >= 0) {
-               vBackend->osd_printf(1500, "Screenshot %d saved.", ret);
+               m_vBackend->osd_printf(1500,
+                       "Screenshot %d saved.", ret);
        } else {
-               vBackend->osd_printf(1500, "Error saving screenshot:\n* %s", strerror(-ret));
+               m_vBackend->osd_printf(1500,
+                       "Error saving screenshot:\n* %s", strerror(-ret));
        }
 }
 
 /**
- * Process an SDL event.
- * EmuLoop-specific version; delegates to gens-sdl
- * if the event isn't handled.
- * @param event SDL event.
- * @return 0 if the event was handled; non-zero if it wasn't.
- */
-static int processSdlEvent_emuLoop(const SDL_Event *event) {
-       int ret = 0;
-       switch (event->type) {
-               case SDL_KEYDOWN:
-                       // SDL keycodes nearly match GensKey.
-                       // TODO: Split out into a separate function?
-                       // TODO: Check for "no modifiers" for some keys?
-                       switch (event->key.keysym.sym) {
-                               case SDLK_TAB:
-                                       // Check for Shift.
-                                       if (event->key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) {
-                                               // Hard Reset.
-                                               context->hardReset();
-                                               vBackend->osd_print(1500, "Hard Reset.");
-                                       } else {
-                                               // Soft Reset.
-                                               context->softReset();
-                                               vBackend->osd_print(1500, "Soft Reset.");
-                                       }
-                                       break;
-
-                               case SDLK_BACKSPACE:
-                                       if (event->key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) {
-                                               // Take a screenshot.
-                                               doScreenShot();
-                                       }
-                                       break;
-
-                               case SDLK_F2:
-                                       // NOTE: This is handled here instead of in the generic
-                                       // processSdlEvent_common() because stretch functionality
-                                       // isn't used in CrazyEffectLoop.
-                                       if (event->key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) {
-                                               // Change stretch mode parameters.
-                                               doStretchMode();
-                                       } else {
-                                               // Not handling this event.
-                                               ret = 1;
-                                       }
-                                       break;
-
-                               case SDLK_0: case SDLK_1:
-                               case SDLK_2: case SDLK_3:
-                               case SDLK_4: case SDLK_5:
-                               case SDLK_6: case SDLK_7:
-                               case SDLK_8: case SDLK_9:
-                                       // Save slot selection.
-                                       doSaveSlot(event->key.keysym.sym - SDLK_0);
-                                       break;
-
-                               case SDLK_F5:
-                                       // Save state.
-                                       doSaveState();
-                                       break;
-
-                               case SDLK_F6: {
-                                       // Previous save slot.
-                                       int saveSlot  = ((saveSlot_selected + 9) % 10);
-                                       doSaveSlot(saveSlot);
-                                       break;
-                               }
-
-                               case SDLK_F7: {
-                                       // Next save slot.
-                                       int saveSlot  = ((saveSlot_selected + 1) % 10);
-                                       doSaveSlot(saveSlot);
-                                       break;
-                               }
-
-                               case SDLK_F8:
-                                       // Load state.
-                                       doLoadState();
-                                       break;
-
-                               default: {
-                                       // Check if the common event handler will handle this.
-                                       int ret = processSdlEvent_common(event);
-                                       if (ret != 0) {
-                                               // Not handled.
-                                               // Send the key to the KeyManager.
-                                               keyManager->keyDown(SdlHandler::scancodeToGensKey(event->key.keysym.scancode));
-                                               break;
-                                       }
-                                       break;
-                               }
-                       }
-                       break;
-
-               case SDL_KEYUP:
-                       // SDL keycodes nearly match GensKey.
-                       keyManager->keyUp(SdlHandler::scancodeToGensKey(event->key.keysym.scancode));
-                       break;
-
-               default:
-                       // Event not handled.
-                       ret = 1;
-                       break;
-       }
-
-       if (ret != 0) {
-               // Event wasn't handled.
-               // Try processSdlEvent_common().
-               ret = processSdlEvent_common(event);
-       }
-       return ret;
-}
-
-/**
- * Run the emulation loop.
+ * Run the event loop.
  * @param rom_filename ROM filename. [TODO: Replace with options struct?]
  * @return Exit code.
  */
-int EmuLoop(const char *rom_filename)
+int EmuLoop::run(const char *rom_filename)
 {
        // Load the ROM image.
-       rom = new Rom(rom_filename);
-       if (!rom->isOpen()) {
+       d->rom = new Rom(rom_filename);
+       if (!d->rom->isOpen()) {
                // Error opening the ROM.
                // TODO: Error code?
                fprintf(stderr, "Error opening ROM file %s: (TODO get error code)\n",
                        rom_filename);
                return EXIT_FAILURE;
        }
-       if (rom->isMultiFile()) {
+       if (d->rom->isMultiFile()) {
                // Select the first file.
-               rom->select_z_entry(rom->get_z_entry_list());
+               d->rom->select_z_entry(d->rom->get_z_entry_list());
        }
 
        // Is the ROM format supported?
-       if (!EmuContextFactory::isRomFormatSupported(rom)) {
+       if (!EmuContextFactory::isRomFormatSupported(d->rom)) {
                // ROM format is not supported.
-               const char *rom_format = romFormatToString(rom->romFormat());
+               const char *rom_format = romFormatToString(d->rom->romFormat());
                fprintf(stderr, "Error loading ROM file %s: ROM is in %s format.\nOnly plain binary and SMD-format ROMs are supported.\n",
                        rom_filename, rom_format);
                return EXIT_FAILURE;
        }
 
        // Check the ROM's system ID.
-       if (!EmuContextFactory::isRomSystemSupported(rom)) {
+       if (!EmuContextFactory::isRomSystemSupported(d->rom)) {
                // System is not supported.
-               const char *rom_sysId = sysIdToString(rom->sysId());
+               const char *rom_sysId = sysIdToString(d->rom->sysId());
                fprintf(stderr, "Error loading ROM file %s: ROM is for %s.\nOnly Mega Drive and Pico ROMs are supported.\n",
                        rom_filename, rom_sysId);
                return EXIT_FAILURE;
        }
 
        // Check for Pico controller.
-       isPico = false;
-       if (rom->sysId() == Rom::MDP_SYSTEM_PICO) {
-               isPico = true;
+       d->isPico = false;
+       if (d->rom->sysId() == Rom::MDP_SYSTEM_PICO) {
+               d->isPico = true;
        }
 
        // Set the SRAM/EEPROM path.
        EmuContext::SetPathSRam(getConfigDir("SRAM").c_str());
 
        // Create the emulation context.
-       context = EmuContextFactory::createContext(rom);
-       if (!context || !context->isRomOpened()) {
+       d->emuContext = EmuContextFactory::createContext(d->rom);
+       if (!d->emuContext || !d->emuContext->isRomOpened()) {
                // Error loading the ROM into EmuMD.
                // TODO: Error code?
                fprintf(stderr, "Error initializing EmuContext for %s: (TODO get error code)\n",
@@ -498,15 +551,15 @@ int EmuLoop(const char *rom_filename)
        }
 
        // Initialize the SDL handlers.
-       sdlHandler = new SdlHandler();
-       if (sdlHandler->init_video() < 0)
+       m_sdlHandler = new SdlHandler();
+       if (m_sdlHandler->init_video() < 0)
                return EXIT_FAILURE;
-       if (sdlHandler->init_audio() < 0)
+       if (m_sdlHandler->init_audio() < 0)
                return EXIT_FAILURE;
-       vBackend = sdlHandler->vBackend();
+       m_vBackend = m_sdlHandler->vBackend();
 
        // Set the window title.
-       sdlHandler->set_window_title("Gens/GS II [SDL]");
+       m_sdlHandler->set_window_title("Gens/GS II [SDL]");
 
        // Check for startup messages.
        checkForStartupMessages();
@@ -515,58 +568,56 @@ int EmuLoop(const char *rom_filename)
        // TODO: Region code?
        bool isPal = false;
        const unsigned int usec_per_frame = (1000000 / (isPal ? 50 : 60));
-       clks.reset();
-
-       // Enable frameskip.
-       frameskip = true;
+       m_clks.reset();
 
        // TODO: Close the ROM, or let EmuContext do it?
 
        // Set the color depth.
        // TODO: Command line option?
-       MdFb *fb = context->m_vdp->MD_Screen->ref();
+       MdFb *fb = d->emuContext->m_vdp->MD_Screen->ref();
        fb->setBpp(MdFb::BPP_32);
 
        // Set the SDL video source.
-       sdlHandler->set_video_source(fb);
+       m_sdlHandler->set_video_source(fb);
 
        // Start audio.
-       sdlHandler->pause_audio(false);
+       m_sdlHandler->pause_audio(false);
 
        // Initialize the I/O Manager with a default key layout.
-       keyManager = new KeyManager();
-       if (!isPico) {
+       d->keyManager = new KeyManager();
+       if (!d->isPico) {
                // Standard Mega Drive controllers.
-               keyManager->setIoType(IoManager::VIRTPORT_1, IoManager::IOT_6BTN);
-               keyManager->setKeyMap(IoManager::VIRTPORT_1, keyMap_md, ARRAY_SIZE(keyMap_md));
-               keyManager->setIoType(IoManager::VIRTPORT_2, IoManager::IOT_NONE);
+               d->keyManager->setIoType(IoManager::VIRTPORT_1, IoManager::IOT_6BTN);
+               d->keyManager->setKeyMap(IoManager::VIRTPORT_1, d->keyMap_md, ARRAY_SIZE(d->keyMap_md));
+               d->keyManager->setIoType(IoManager::VIRTPORT_2, IoManager::IOT_NONE);
        } else {
                // Sega Pico controller.
-               keyManager->setIoType(IoManager::VIRTPORT_1, IoManager::IOT_PICO);
-               keyManager->setKeyMap(IoManager::VIRTPORT_1, keyMap_pico, ARRAY_SIZE(keyMap_pico));
-               keyManager->setIoType(IoManager::VIRTPORT_2, IoManager::IOT_NONE);
+               d->keyManager->setIoType(IoManager::VIRTPORT_1, IoManager::IOT_PICO);
+               d->keyManager->setKeyMap(IoManager::VIRTPORT_1, d->keyMap_pico, ARRAY_SIZE(d->keyMap_pico));
+               d->keyManager->setIoType(IoManager::VIRTPORT_2, IoManager::IOT_NONE);
        }
 
        // TODO: Move some more common stuff back to gens-sdl.cpp.
        uint8_t old_paused = 0;
-       while (running) {
+       m_running = true;
+       while (m_running) {
                SDL_Event event;
                int ret;
-               if (paused.data) {
+               if (m_paused.data) {
                        // Emulation is paused.
-                       if (!vBackend->has_osd_messages()) {
+                       if (!m_vBackend->has_osd_messages()) {
                                // No OSD messages.
                                // Wait for an SDL event.
                                ret = SDL_WaitEvent(&event);
                                if (ret) {
-                                       processSdlEvent_emuLoop(&event);
+                                       processSdlEvent(&event);
                                }
                        }
 
                        // Process OSD messages.
-                       vBackend->process_osd_messages();
+                       m_vBackend->process_osd_messages();
                }
-               if (!running)
+               if (!m_running)
                        break;
 
                // Poll for SDL events, and wait for the queue
@@ -575,70 +626,70 @@ int EmuLoop(const char *rom_filename)
                do {
                        ret = SDL_PollEvent(&event);
                        if (ret) {
-                               processSdlEvent_emuLoop(&event);
+                               processSdlEvent(&event);
                        }
-               } while (running && ret != 0);
-               if (!running)
+               } while (m_running && ret != 0);
+               if (!m_running)
                        break;
 
                // Check if the 'paused' state was changed.
                // If it was, autosave SRAM/EEPROM.
-               if (old_paused != paused.data) {
+               if (old_paused != m_paused.data) {
                        // 'paused' state was changed.
                        // (TODO: Only if paused == true?)
-                       context->autoSaveData(-1);
-                       old_paused = paused.data;
+                       d->emuContext->autoSaveData(-1);
+                       old_paused = m_paused.data;
                }
 
-               if (paused.data) {
+               if (m_paused.data) {
                        // Emulation is paused.
                        // Only update video if the VBackend is dirty
                        // or the SDL window has been exposed.
-                       sdlHandler->update_video_paused(exposed);
+                       m_sdlHandler->update_video_paused(m_exposed);
 
                        // Don't run any frames.
                        continue;
                }
 
                // Clear the 'exposed' flag.
-               exposed = false;
+               m_exposed = false;
 
                // New start time.
-               clks.new_clk = timing.getTime();
+               m_clks.new_clk = m_clks.timing.getTime();
 
                // Update the FPS counter.
-               unsigned int fps_tmp = ((clks.new_clk - clks.fps_clk) & 0x3FFFFF);
+               unsigned int fps_tmp = ((m_clks.new_clk - m_clks.fps_clk) & 0x3FFFFF);
                if (fps_tmp >= 1000000) {
                        // More than 1 second has passed.
-                       clks.fps_clk = clks.new_clk;
+                       m_clks.fps_clk = m_clks.new_clk;
                        // FIXME: Just use abs() here.
-                       if (clks.frames_old > clks.frames) {
-                               clks.fps = (clks.frames_old - clks.frames);
+                       if (m_clks.frames_old > m_clks.frames) {
+                               m_clks.fps = (m_clks.frames_old - m_clks.frames);
                        } else {
-                               clks.fps = (clks.frames - clks.frames_old);
+                               m_clks.fps = (m_clks.frames - m_clks.frames_old);
                        }
-                       clks.frames_old = clks.frames;
+                       m_clks.frames_old = m_clks.frames;
 
                        // Update the window title.
                        // TODO: Average the FPS over multiple seconds
                        // and/or quarter-seconds.
                        char win_title[256];
-                       snprintf(win_title, sizeof(win_title), "Gens/GS II [SDL] - %u fps", clks.fps);
-                       sdlHandler->set_window_title(win_title);
+                       snprintf(win_title, sizeof(win_title), "Gens/GS II [SDL] - %u fps", m_clks.fps);
+                       m_sdlHandler->set_window_title(win_title);
                }
 
                // Frameskip.
-               if (frameskip) {
+               if (m_frameskip) {
                        // Determine how many frames to run.
-                       clks.usec_frameskip += ((clks.new_clk - clks.old_clk) & 0x3FFFFF); // no more than 4 secs
-                       unsigned int frames_todo = (unsigned int)(clks.usec_frameskip / usec_per_frame);
-                       clks.usec_frameskip %= usec_per_frame;
-                       clks.old_clk = clks.new_clk;
+                       m_clks.usec_frameskip += ((m_clks.new_clk - m_clks.old_clk) & 0x3FFFFF); // no more than 4 secs
+                       unsigned int frames_todo = (unsigned int)(m_clks.usec_frameskip / usec_per_frame);
+                       m_clks.usec_frameskip %= usec_per_frame;
+                       m_clks.old_clk = m_clks.new_clk;
 
                        if (frames_todo == 0) {
                                // No frames to do yet.
                                // Wait until the next frame.
-                               uint64_t usec_sleep = (usec_per_frame - clks.usec_frameskip);
+                               uint64_t usec_sleep = (usec_per_frame - m_clks.usec_frameskip);
                                if (usec_sleep > 1000) {
                                        // Never sleep for longer than the 50 Hz value
                                        // so events are checked often enough.
@@ -650,10 +701,10 @@ int EmuLoop(const char *rom_filename)
 #ifdef _WIN32
                                        // Win32: Use a yield() loop.
                                        // FIXME: Doesn't work properly on VBox/WinXP...
-                                       uint64_t yield_end = timing.getTime() + usec_sleep;
+                                       uint64_t yield_end = m_clks.timing.getTime() + usec_sleep;
                                        do {
                                                yield();
-                                       } while (yield_end > timing.getTime());
+                                       } while (yield_end > m_clks.timing.getTime());
 #else /* !_WIN32 */
                                        // Linux: Use usleep().
                                        usleep(usec_sleep);
@@ -663,37 +714,37 @@ int EmuLoop(const char *rom_filename)
                                // Draw frames.
                                for (; frames_todo != 1; frames_todo--) {
                                        // Run a frame without rendering.
-                                       context->execFrameFast();
-                                       sdlHandler->update_audio();
+                                       d->emuContext->execFrameFast();
+                                       m_sdlHandler->update_audio();
                                }
                                frames_todo = 0;
 
                                // Run a frame and render it.
-                               context->execFrame();
-                               sdlHandler->update_audio();
-                               sdlHandler->update_video();
+                               d->emuContext->execFrame();
+                               m_sdlHandler->update_audio();
+                               m_sdlHandler->update_video();
                                // Increment the frame counter.
-                               clks.frames++;
+                               m_clks.frames++;
 
                                // Autosave SRAM/EEPROM.
                                // TODO: EmuContext::execFrame() should probably do this itself...
-                               context->autoSaveData(1);
+                               d->emuContext->autoSaveData(1);
                        }
                } else {
                        // Run a frame and render it.
-                       context->execFrame();
-                       sdlHandler->update_audio();
-                       sdlHandler->update_video();
+                       d->emuContext->execFrame();
+                       m_sdlHandler->update_audio();
+                       m_sdlHandler->update_video();
                        // Increment the frame counter.
-                       clks.frames++;
+                       m_clks.frames++;
 
                        // Autosave SRAM/EEPROM.
                        // TODO: EmuContext::execFrame() should probably do this itself...
-                       context->autoSaveData(1);
+                       d->emuContext->autoSaveData(1);
                }
 
                // Update the I/O manager.
-               keyManager->updateIoManager(context->m_ioManager);
+               d->keyManager->updateIoManager(d->emuContext->m_ioManager);
        }
 
        // Unreference the framebuffer.
@@ -701,12 +752,27 @@ int EmuLoop(const char *rom_filename)
 
        // Save SRAM/EEPROM, if necessary.
        // TODO: Move to EmuContext::~EmuContext()?
-       context->saveData();
+       d->emuContext->saveData();
 
        // Shut down LibGens.
-       delete keyManager;
-       delete context;
-       delete rom;
+       delete d->keyManager;
+       d->keyManager = nullptr;
+       delete d->emuContext;
+       d->emuContext = nullptr;
+       delete d->rom;
+       d->rom = nullptr;
+
+       // Pause audio and wait 50ms for SDL to catch up.
+       m_sdlHandler->pause_audio(true);
+       usleep(50000);
+
+       // NOTE: Deleting sdlHandler can cause crashes on Windows
+       // due to the timer callback trying to post the semaphore
+       // after it's been deleted.
+       // Shut down the SDL functions manually.
+       m_sdlHandler->end_audio();
+       m_sdlHandler->end_video();
+       m_vBackend = nullptr;
 
        // Done running the emulation loop.
        return 0;
index 35e65d0..3d7e7d0 100644 (file)
 #ifndef __GENS_SDL_EMULOOP_HPP__
 #define __GENS_SDL_EMULOOP_HPP__
 
+#include "EventLoop.hpp"
+#include <string>
+
+namespace LibZomg {
+       class ZomgBase;
+}
+
 namespace GensSdl {
 
-// TODO: Make a base "main loop" class?
+class EmuLoopPrivate;
+class EmuLoop : public EventLoop
+{
+       public:
+               EmuLoop();
+               virtual ~EmuLoop();
+
+       private:
+               friend class EmuLoopPrivate;
+               EmuLoopPrivate *const d;
+       private:
+               // Q_DISABLE_COPY() equivalent.
+               // TODO: Add GensSdl-specific version of Q_DISABLE_COPY().
+               EmuLoop(const EmuLoop &);
+               EmuLoop &operator=(const EmuLoop &);
+
+       public:
+               /**
+                * Run the event loop.
+                * @param rom_filename ROM filename. [TODO: Replace with options struct?]
+                * @return Exit code.
+                */
+               virtual int run(const char *rom_filename) final;
+
+       protected:
+               /**
+                * Process an SDL event.
+                * @param event SDL event.
+                * @return 0 if the event was handled; non-zero if it wasn't.
+                */
+               virtual int processSdlEvent(const SDL_Event *event) final;
+
+               /**
+                * Get the modification time string for the specified save file.
+                * @param zomg Save file.
+                * @return String contianing the mtime, or an error message if invalid.
+                */
+               std::string getSaveSlot_mtime(const LibZomg::ZomgBase *zomg);
+
+               /**
+                * Save slot selection.
+                * @param saveSlot Save slot. (0-9)
+                */
+               void doSaveSlot(int saveSlot);
+
+               /**
+                * Load the state in the selected slot.
+                */
+               void doLoadState(void);
+
+               /**
+                * Save the state in the selected slot.
+                */
+               void doSaveState(void);
+
+               /**
+                * Change stretch mode parameters.
+                */
+               void doStretchMode(void);
 
-/**
- * Run the emulation loop.
- * @param rom_filename ROM filename. [TODO: Replace with options struct?]
- * @return Exit code.
- */
-int EmuLoop(const char *rom_filename);
+               /**
+                * Take a screenshot.
+                */
+               void doScreenShot(void);
+};
 
 }
 
diff --git a/src/gens-sdl/EventLoop.cpp b/src/gens-sdl/EventLoop.cpp
new file mode 100644 (file)
index 0000000..cb29a42
--- /dev/null
@@ -0,0 +1,291 @@
+/***************************************************************************
+ * gens-sdl: Gens/GS II basic SDL frontend.                                *
+ * EventLoop.cpp: Event loop base class.                                   *
+ *                                                                         *
+ * Copyright (c) 2015 by David Korth.                                      *
+ *                                                                         *
+ * This program is free software; you can redistribute it and/or modify it *
+ * under the terms of the GNU General Public License as published by the   *
+ * Free Software Foundation; either version 2 of the License, or (at your  *
+ * option) any later version.                                              *
+ *                                                                         *
+ * This program is distributed in the hope that it will be useful, but     *
+ * WITHOUT ANY WARRANTY; without even the implied warranty of              *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
+ * GNU General Public License for more details.                            *
+ *                                                                         *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.           *
+ ***************************************************************************/
+
+// Reentrant functions.
+// MUST be included before everything else due to
+// _POSIX_SOURCE and _POSIX_C_SOURCE definitions.
+#include "libcompat/reentrant.h"
+
+#include "EventLoop.hpp"
+#include "gens-sdl.hpp"
+
+// Configuration.
+#include "Config.hpp"
+
+#include "SdlHandler.hpp"
+#include "VBackend.hpp"
+using GensSdl::SdlHandler;
+using GensSdl::VBackend;
+
+// LibGens
+#include "libgens/lg_main.hpp"
+#include "libgens/Util/MdFb.hpp"
+using LibGens::MdFb;
+
+// OS-specific includes.
+#ifdef _WIN32
+// Windows
+#include <windows.h>
+// Win32 Unicode Translation Layer.
+// Needed for proper Unicode filename support on Windows.
+#include "libcompat/W32U/W32U_mini.h"
+#include "libcompat/W32U/W32U_argv.h"
+#else
+// Linux, Unix, Mac OS X
+#include <unistd.h>
+#endif
+
+// yield(), aka usleep(0) or Sleep(0)
+#ifdef _WIN32
+// Windows
+#define yield() do { Sleep(0); } while (0)
+#define usleep(usec) Sleep((DWORD)((usec) / 1000))
+#else
+// Linux, Unix, Mac OS X
+#define yield() do { usleep(0); } while (0)
+#endif
+
+// C++ includes.
+#include <string>
+using std::string;
+
+namespace GensSdl {
+
+EventLoop::EventLoop()
+       : m_running(false)
+       , m_autoPause(false)
+       , m_lastF1time(0)
+{
+       m_paused.data = 0;
+}
+
+EventLoop::~EventLoop()
+{
+       // Shut down SDL.
+       // TODO
+}
+
+/**
+ * Process an SDL event.
+ * @param event SDL event.
+ * @return 0 if the event was handled; non-zero if it wasn't.
+ */
+int EventLoop::processSdlEvent(const SDL_Event *event) {
+       // NOTE: Some keys are processed in specific event loops
+       // instead of here because they only apply if a ROM is loaded.
+       // gens-qt4 won't make a distinction, since it can run with
+       // both a ROM loaded and not loaded, and you can open and close
+       // ROMs without restarting the program, so it has to be able
+       // to switch modes while allowing options to be change both
+       // when a ROM is loaded and when no ROM is loaded.
+       // In essence, it combines both EmuLoop and CrazyEffectLoop
+       // while maintaining the state of various emulation options.
+
+       int ret = 0;
+       switch (event->type) {
+               case SDL_QUIT:
+                       m_running = false;
+                       break;
+
+               case SDL_KEYDOWN:
+                       // SDL keycodes nearly match GensKey.
+                       // TODO: Split out into a separate function?
+                       // TODO: Check for "no modifiers" for some keys?
+                       switch (event->key.keysym.sym) {
+                               case SDLK_ESCAPE:
+                                       // Pause emulation.
+                                       doPause();
+                                       break;
+
+                               case SDLK_RETURN:
+                                       // Check for Alt+Enter.
+                                       if ((event->key.keysym.mod & KMOD_ALT) &&
+                                           !(event->key.keysym.mod & ~KMOD_ALT))
+                                       {
+                                               // Alt+Enter. Toggle fullscreen.
+                                               m_sdlHandler->toggle_fullscreen();
+                                       } else {
+                                               // Not Alt+Enter.
+                                               // We're not handling this event.
+                                               ret = 1;
+                                       }
+                                       break;
+
+                               case SDLK_F1:
+                                       // Show the "About" message.
+                                       doAboutMessage();
+                                       break;
+
+                               case SDLK_F9:
+                                       // Fast Blur.
+                                       doFastBlur();
+                                       break;
+
+                               case SDLK_F12:
+                                       // FIXME: TEMPORARY KEY BINDING for debugging.
+                                       m_vBackend->setAspectRatioConstraint(!m_vBackend->aspectRatioConstraint());
+                                       break;
+
+                               default:
+                                       // Event not handled.
+                                       ret = 1;
+                                       break;
+                       }
+                       break;
+
+               case SDL_WINDOWEVENT:
+                       switch (event->window.event) {
+                               case SDL_WINDOWEVENT_RESIZED:
+                                       // Resize the video renderer.
+                                       m_sdlHandler->resize_video(event->window.data1, event->window.data2);
+                                       break;
+                               case SDL_WINDOWEVENT_EXPOSED:
+                                       // Window has been exposed.
+                                       // Tell the main loop to update video.
+                                       m_exposed = true;
+                                       break;
+                               case SDL_WINDOWEVENT_FOCUS_LOST:
+                                       // If AutoPause is enabled, pause the emulator.
+                                       if (m_autoPause) {
+                                               doAutoPause(true);
+                                       }
+                                       break;
+                               case SDL_WINDOWEVENT_FOCUS_GAINED:
+                                       // If AutoPause is enabled, unpause the emulator.
+                                       // TODO: Always run this, even if !autoPause?
+                                       if (m_autoPause) {
+                                               doAutoPause(false);
+                                       }
+                                       break;
+                               default:
+                                       // Event not handled.
+                                       ret = 1;
+                                       break;
+                       }
+                       break;
+
+               default:
+                       // Event not handled.
+                       ret = 1;
+                       break;
+       }
+
+       return ret;
+}
+
+/**
+ * Toggle Fast Blur.
+ */
+void EventLoop::doFastBlur(void)
+{
+       bool fastBlur = !m_vBackend->fastBlur();
+       m_vBackend->setFastBlur(fastBlur);
+
+       // Show an OSD message.
+       if (fastBlur) {
+               m_vBackend->osd_print(1500, "Fast Blur enabled.");
+       } else {
+               m_vBackend->osd_print(1500, "Fast Blur disabled.");
+       }
+}
+
+/**
+ * Common pause processing function.
+ * Called by doPause() and doAutoPause().
+ */
+void EventLoop::doPauseProcessing(void)
+{
+       bool manual = m_paused.manual;
+       bool any = !!m_paused.data;
+       // TODO: Option to disable the Paused Effect?
+       // When enabled, it's only used for Manual Pause.
+       m_vBackend->setPausedEffect(manual);
+
+       // Reset the clocks and counters.
+       m_clks.reset();
+       // Pause audio.
+       m_sdlHandler->pause_audio(any);
+
+       // Update the window title.
+       if (manual) {
+               m_sdlHandler->set_window_title("Gens/GS II [SDL] [Paused]");
+       } else {
+               m_sdlHandler->set_window_title("Gens/GS II [SDL]");
+       }
+}
+
+/**
+ * Pause/unpause emulation.
+ */
+void EventLoop::doPause(void)
+{
+       m_paused.manual = !m_paused.manual;
+       doPauseProcessing();
+}
+
+/**
+ * Pause/unpause emulation in response to window focus changes.
+ * @param lostFocus True if window lost focus; false if window gained focus.
+ */
+void EventLoop::doAutoPause(bool lostFocus)
+{
+       m_paused.focus = lostFocus;
+       doPauseProcessing();
+}
+
+/**
+ * Show the "About" message.
+ */
+void EventLoop::doAboutMessage(void)
+{
+       // TODO: OSD Gens logo as preview image, but with drop shadow disabled?
+       const uint64_t curTime = m_clks.timing.getTime();
+       if (m_lastF1time > 0 && (m_lastF1time + 5000000 > curTime)) {
+               // Timer hasn't expired.
+               // Don't show the message.
+               return;
+       }
+
+       // Version string.
+       string ver_str;
+       ver_str.reserve(512);
+       ver_str = "Gens/GS II - SDL2 frontend\n";
+       ver_str += "Version " + string(LibGens::version);
+       if (LibGens::version_vcs) {
+               ver_str += " (" + string(LibGens::version_vcs) + ')';
+       }
+       ver_str += '\n';
+       if (LibGens::version_desc) {
+               ver_str += string(LibGens::version_desc) + '\n';
+       }
+#if !defined(GENS_ENABLE_EMULATION)
+       ver_str += "[NO-EMULATION BUILD]\n";
+#endif
+       ver_str += "(c) 2008-2015 by David Korth.";
+
+       // Show a new message.
+       m_vBackend->osd_print(5000, ver_str.c_str());
+
+       // Save the current time.
+       m_lastF1time = curTime;
+}
+
+}
diff --git a/src/gens-sdl/EventLoop.hpp b/src/gens-sdl/EventLoop.hpp
new file mode 100644 (file)
index 0000000..dc2d505
--- /dev/null
@@ -0,0 +1,157 @@
+/***************************************************************************
+ * gens-sdl: Gens/GS II basic SDL frontend.                                *
+ * EventLoop.hpp: Event loop base class.                                   *
+ *                                                                         *
+ * Copyright (c) 2015 by David Korth.                                      *
+ *                                                                         *
+ * This program is free software; you can redistribute it and/or modify it *
+ * under the terms of the GNU General Public License as published by the   *
+ * Free Software Foundation; either version 2 of the License, or (at your  *
+ * option) any later version.                                              *
+ *                                                                         *
+ * This program is distributed in the hope that it will be useful, but     *
+ * WITHOUT ANY WARRANTY; without even the implied warranty of              *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
+ * GNU General Public License for more details.                            *
+ *                                                                         *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.           *
+ ***************************************************************************/
+
+#ifndef __GENS_SDL_EVENTLOOP_HPP__
+#define __GENS_SDL_EVENTLOOP_HPP__
+
+#include "libgens/Util/Timing.hpp"
+#include <SDL.h>
+
+namespace GensSdl {
+
+class SdlHandler;
+class VBackend;
+
+class EventLoop
+{
+       public:
+               EventLoop();
+               virtual ~EventLoop();
+
+       private:
+               // Q_DISABLE_COPY() equivalent.
+               // TODO: Add GensSdl-specific version of Q_DISABLE_COPY().
+               EventLoop(const EventLoop &);
+               EventLoop &operator=(const EventLoop &);
+
+       public:
+               /**
+                * Run the event loop.
+                * @param rom_filename ROM filename. [TODO: Replace with options struct?]
+                * @return Exit code.
+                */
+               virtual int run(const char *rom_filename) = 0;
+
+       protected:
+               /**
+                * Process an SDL event.
+                * @param event SDL event.
+                * @return 0 if the event was handled; non-zero if it wasn't.
+                */
+               virtual int processSdlEvent(const SDL_Event *event);
+
+               /**
+                * Toggle Fast Blur.
+                */
+               void doFastBlur(void);
+
+               /**
+                * Common pause processing function.
+                * Called by doPause() and doAutoPause().
+                */
+               void doPauseProcessing(void);
+
+               /**
+                * Pause/unpause emulation.
+                */
+               void doPause(void);
+
+               /**
+                * Pause/unpause emulation in response to window focus changes.
+                * @param lostFocus True if window lost focus; false if window gained focus.
+                */
+               void doAutoPause(bool lostFocus);
+
+               /**
+                * Show the "About" message.
+                */
+               void doAboutMessage(void);
+
+       protected:
+               // TODO: Move to a private class?
+               bool m_running;
+               bool m_frameskip;
+
+               union paused_t {
+                       struct {
+                               uint8_t manual  : 1;    // Manual pause.
+                               uint8_t focus   : 1;    // Auto pause when focus is lost.
+                       };
+                       uint8_t data;
+               };
+               paused_t m_paused;
+
+               // Automatically pause when the window loses focus?
+               bool m_autoPause;
+
+               // Window has been exposed.
+               // Video should be updated if emulation is paused.
+               bool m_exposed;
+
+               class clks_t {
+                       public:
+                               // Reset frameskip timers.
+                               void reset(void) {
+                                       start_clk = timing.getTime();
+                                       old_clk = start_clk;
+                                       fps_clk = start_clk;
+                                       fps_clk = start_clk;
+                                       new_clk = start_clk;
+                                       usec_frameskip = 0;
+
+                                       // Frame counter.
+                                       frames = 0;
+                                       frames_old = 0;
+                                       fps = 0;
+                               }
+
+                               // Timing object.
+                               LibGens::Timing timing;
+
+                               // Clocks.
+                               uint64_t start_clk;
+                               uint64_t old_clk;
+                               uint64_t fps_clk;
+                               uint64_t new_clk;
+                               // Microsecond counter for frameskip.
+                               uint64_t usec_frameskip;
+
+                               // Frame counters.
+                               unsigned int frames;
+                               unsigned int frames_old;
+                               unsigned int fps;       // TODO: float or double?
+               };
+               clks_t m_clks;
+
+               // Last time the F1 message was displayed.
+               // This is here to prevent the user from spamming
+               // the display with the message.
+               uint64_t m_lastF1time;
+
+       public:
+               // TODO: Make this protected.
+               SdlHandler *m_sdlHandler;
+               VBackend *m_vBackend;
+};
+
+}
+
+#endif /* __GENS_SDL_EVENTLOOP_HPP__ */
index 34ed54e..d3df829 100644 (file)
@@ -19,9 +19,6 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.           *
  ***************************************************************************/
 
-// TODO BEFORE COMMIT: Remove any more unnecessary includes.
-// Also, after commit, convert emulation loops into classes?
-
 // Reentrant functions.
 // MUST be included before everything else due to
 // _POSIX_SOURCE and _POSIX_C_SOURCE definitions.
@@ -87,17 +84,16 @@ using std::vector;
 
 namespace GensSdl {
 
-SdlHandler *sdlHandler = nullptr;
-VBackend *vBackend = nullptr;
-
 /** Command line parameters. **/
 // TODO: Write a popt-based command line parser with
 // a struct containing all of the options.
 static const char *rom_filename = nullptr;
-static bool autoPause = false;
 // If true, don't emulate anything; just run the Crazy Effect.
 static bool runCrazyEffect = false;
 
+// Event loop.
+static EventLoop *eventLoop = nullptr;
+
 // Startup OSD message queue.
 struct OsdStartup {
        int duration;
@@ -106,11 +102,6 @@ struct OsdStartup {
 };
 static vector<OsdStartup> startup_queue;
 
-// Last time the F1 message was displayed.
-// This is here to prevent the user from spamming
-// the display with the message.
-static uint64_t lastF1time = 0;
-
 /**
  * Onscreen Display handler.
  * @param osd_type OSD type.
@@ -157,8 +148,8 @@ static void gsdl_osd(OsdType osd_type, int param)
        }
 
        if (msg != nullptr) {
-               if (vBackend) {
-                       vBackend->osd_printf(1500, msg, param);
+               if (eventLoop->m_vBackend) {
+                       eventLoop->m_vBackend->osd_printf(1500, msg, param);
                } else {
                        // SDL handler hasn't been created yet.
                        // Store the message for later.
@@ -181,235 +172,11 @@ void checkForStartupMessages(void)
        if (!startup_queue.empty()) {
                for (int i = 0; i < (int)startup_queue.size(); i++) {
                        const OsdStartup &startup = startup_queue.at(i);
-                       vBackend->osd_printf(startup.duration, startup.msg, startup.param);
+                       eventLoop->m_vBackend->osd_printf(startup.duration, startup.msg, startup.param);
                }
        }
 }
 
-// Timing object.
-LibGens::Timing timing;
-
-// Emulation state.
-bool running = true;
-paused_t paused;
-
-// Enable frameskip.
-bool frameskip = true;
-
-// Window has been exposed.
-// Video should be updated if emulation is paused.
-bool exposed = false;
-
-// Frameskip timers.
-clks_t clks;
-
-/**
- * Toggle Fast Blur.
- */
-static void doFastBlur(void)
-{
-       bool fastBlur = !vBackend->fastBlur();
-       vBackend->setFastBlur(fastBlur);
-
-       // Show an OSD message.
-       if (fastBlur) {
-               vBackend->osd_print(1500, "Fast Blur enabled.");
-       } else {
-               vBackend->osd_print(1500, "Fast Blur disabled.");
-       }
-}
-
-/**
- * Common pause processing function.
- * Called by doPause() and doAutoPause().
- */
-static void doPauseProcessing(void)
-{
-       bool manual = paused.manual;
-       bool any = !!paused.data;
-       // TODO: Option to disable the Paused Effect?
-       // When enabled, it's only used for Manual Pause.
-       vBackend->setPausedEffect(manual);
-
-       // Reset the clocks and counters.
-       clks.reset();
-       // Pause audio.
-       sdlHandler->pause_audio(any);
-
-       // Update the window title.
-       if (manual) {
-               sdlHandler->set_window_title("Gens/GS II [SDL] [Paused]");
-       } else {
-               sdlHandler->set_window_title("Gens/GS II [SDL]");
-       }
-}
-
-/**
- * Pause/unpause emulation.
- */
-static void doPause(void)
-{
-       paused.manual = !paused.manual;
-       doPauseProcessing();
-}
-
-/**
- * Pause/unpause emulation in response to window focus changes.
- * @param lostFocus True if window lost focus; false if window gained focus.
- */
-static void doAutoPause(bool lostFocus)
-{
-       paused.focus = lostFocus;
-       doPauseProcessing();
-}
-
-/**
- * Show the "About" message.
- */
-static void doAboutMessage(void)
-{
-       // TODO: OSD Gens logo as preview image, but with drop shadow disabled?
-       const uint64_t curTime = timing.getTime();
-       if (lastF1time > 0 && (lastF1time + 5000000 > curTime)) {
-               // Timer hasn't expired.
-               // Don't show the message.
-               return;
-       }
-
-       // Version string.
-       string ver_str;
-       ver_str.reserve(512);
-       ver_str = "Gens/GS II - SDL2 frontend\n";
-       ver_str += "Version " + string(LibGens::version);
-       if (LibGens::version_vcs) {
-               ver_str += " (" + string(LibGens::version_vcs) + ')';
-       }
-       ver_str += '\n';
-       if (LibGens::version_desc) {
-               ver_str += string(LibGens::version_desc) + '\n';
-       }
-#if !defined(GENS_ENABLE_EMULATION)
-       ver_str += "[NO-EMULATION BUILD]\n";
-#endif
-       ver_str += "(c) 2008-2015 by David Korth.";
-
-       // Show a new message.
-       vBackend->osd_print(5000, ver_str.c_str());
-
-       // Save the current time.
-       lastF1time = curTime;
-}
-
-/**
- * Process an SDL event.
- * EmuLoop processes SDL events first; if it ends up
- * with an event that it can't handle, it goes here.
- * @param event SDL event.
- * @return 0 if the event was handled; non-zero if it wasn't.
- */
-int processSdlEvent_common(const SDL_Event *event) {
-       // NOTE: Some keys are processed in specific event loops
-       // instead of here because they only apply if a ROM is loaded.
-       // gens-qt4 won't make a distinction, since it can run with
-       // both a ROM loaded and not loaded, and you can open and close
-       // ROMs without restarting the program, so it has to be able
-       // to switch modes while allowing options to be change both
-       // when a ROM is loaded and when no ROM is loaded.
-       // In essence, it combines both EmuLoop and CrazyEffectLoop
-       // while maintaining the state of various emulation options.
-
-       int ret = 0;
-       switch (event->type) {
-               case SDL_QUIT:
-                       running = false;
-                       break;
-
-               case SDL_KEYDOWN:
-                       // SDL keycodes nearly match GensKey.
-                       // TODO: Split out into a separate function?
-                       // TODO: Check for "no modifiers" for some keys?
-                       switch (event->key.keysym.sym) {
-                               case SDLK_ESCAPE:
-                                       // Pause emulation.
-                                       doPause();
-                                       break;
-
-                               case SDLK_RETURN:
-                                       // Check for Alt+Enter.
-                                       if ((event->key.keysym.mod & KMOD_ALT) &&
-                                           !(event->key.keysym.mod & ~KMOD_ALT))
-                                       {
-                                               // Alt+Enter. Toggle fullscreen.
-                                               sdlHandler->toggle_fullscreen();
-                                       } else {
-                                               // Not Alt+Enter.
-                                               // We're not handling this event.
-                                               ret = 1;
-                                       }
-                                       break;
-
-                               case SDLK_F1:
-                                       // Show the "About" message.
-                                       doAboutMessage();
-                                       break;
-
-                               case SDLK_F9:
-                                       // Fast Blur.
-                                       doFastBlur();
-                                       break;
-
-                               case SDLK_F12:
-                                       // FIXME: TEMPORARY KEY BINDING for debugging.
-                                       vBackend->setAspectRatioConstraint(!vBackend->aspectRatioConstraint());
-                                       break;
-
-                               default:
-                                       // Event not handled.
-                                       ret = 1;
-                                       break;
-                       }
-                       break;
-
-               case SDL_WINDOWEVENT:
-                       switch (event->window.event) {
-                               case SDL_WINDOWEVENT_RESIZED:
-                                       // Resize the video renderer.
-                                       sdlHandler->resize_video(event->window.data1, event->window.data2);
-                                       break;
-                               case SDL_WINDOWEVENT_EXPOSED:
-                                       // Window has been exposed.
-                                       // Tell the main loop to update video.
-                                       exposed = true;
-                                       break;
-                               case SDL_WINDOWEVENT_FOCUS_LOST:
-                                       // If AutoPause is enabled, pause the emulator.
-                                       if (autoPause) {
-                                               doAutoPause(true);
-                                       }
-                                       break;
-                               case SDL_WINDOWEVENT_FOCUS_GAINED:
-                                       // If AutoPause is enabled, unpause the emulator.
-                                       // TODO: Always run this, even if !autoPause?
-                                       if (autoPause) {
-                                               doAutoPause(false);
-                                       }
-                                       break;
-                               default:
-                                       // Event not handled.
-                                       ret = 1;
-                                       break;
-                       }
-                       break;
-
-               default:
-                       // Event not handled.
-                       ret = 1;
-                       break;
-       }
-
-       return ret;
-}
-
 /**
  * Run the emulator.
  */
@@ -428,37 +195,21 @@ int run(void)
        // Register the LibGens OSD handler.
        lg_set_osd_fn(gsdl_osd);
 
-       int ret;
        if (runCrazyEffect) {
                // Run the Crazy Effect.
-               ret = CrazyEffectLoop();
+               GensSdl::eventLoop = new GensSdl::CrazyEffectLoop();
        } else {
                // Start the emulation loop.
-               ret = EmuLoop(rom_filename);
+               GensSdl::eventLoop = new GensSdl::EmuLoop();
+       }
+       int ret = 0;
+       if (GensSdl::eventLoop) {
+               ret = GensSdl::eventLoop->run(rom_filename);
        }
-
-       // COMMIT CHECK: How many linebreaks were here?
-
-       // Pause audio and wait 50ms for SDL to catch up.
-       sdlHandler->pause_audio(true);
-       usleep(50000);
-
-       // NULL out the VBackend before shutting down SDL.
-       vBackend = nullptr;
 
        // Unregister the LibGens OSD handler.
        lg_set_osd_fn(nullptr);
 
-       if (sdlHandler) {
-               // NOTE: Deleting sdlHandler can cause crashes on Windows
-               // due to the timer callback trying to post the semaphore
-               // after it's been deleted.
-               // Shut down the SDL functions manually.
-               sdlHandler->end_audio();
-               sdlHandler->end_video();
-               //delete sdlHandler;
-       }
-
        // ...and we're done here.
        return ret;
 }
index ea556bb..2dd194d 100644 (file)
 
 namespace GensSdl {
 
-class SdlHandler;
-extern SdlHandler *sdlHandler;
-
-class VBackend;
-extern VBackend *vBackend;
-
-extern bool running;
-
-union paused_t {
-       struct {
-               uint8_t manual  : 1;    // Manual pause.
-               uint8_t focus   : 1;    // Auto pause when focus is lost.
-       };
-       uint8_t data;
-};
-extern paused_t paused;
-
-// Enable frameskip.
-extern bool frameskip;
-
-// Window has been exposed.
-// Video should be updated if emulation is paused.
-extern bool exposed;
-
-// Timing object.
-extern LibGens::Timing timing;
-
-class clks_t {
-       public:
-               // Reset frameskip timers.
-               void reset(void) {
-                       start_clk = timing.getTime();
-                       old_clk = start_clk;
-                       fps_clk = start_clk;
-                       fps_clk = start_clk;
-                       new_clk = start_clk;
-                       usec_frameskip = 0;
-
-                       // Frame counter.
-                       frames = 0;
-                       frames_old = 0;
-                       fps = 0;
-               }
-
-               uint64_t start_clk;
-               uint64_t old_clk;
-               uint64_t fps_clk;
-               uint64_t new_clk;
-               // Microsecond counter for frameskip.
-               uint64_t usec_frameskip;
-
-               // Frame counters.
-               unsigned int frames;
-               unsigned int frames_old;
-               unsigned int fps;       // TODO: float or double?
-};
-extern clks_t clks;
-
 /**
  * Check for any OSD messages that were printed during startup.
  * These messages would have been printed before VBackend
@@ -96,15 +38,6 @@ extern clks_t clks;
  */
 void checkForStartupMessages(void);
 
-/**
- * Process an SDL event.
- * EmuLoop processes SDL events first; if it ends up
- * with an event that it can't handle, it goes here.
- * @param event SDL event.
- * @return 0 if the event was handled; non-zero if it wasn't.
- */
-int processSdlEvent_common(const SDL_Event *event);
-
 }
 
 #endif /* __GENS_SDL_HPP__ */