/*] Copyright (c) 2009-2010, Charles McGarvey [************************** **] All rights reserved. * * vi:ts=4 sw=4 tw=75 * * Distributable under the terms and conditions of the 2-clause BSD license; * see the file COPYING for a complete text of the license. * **************************************************************************/ #include #include // exit, srand #include // time #include #include #include #include "fastevents.h" #include "Core.hh" #include "Event.hh" #include "Log.hh" #include "Math.hh" #include "ModalDialog.hh" #include "Settings.hh" #include "Timer.hh" #include "Video.hh" namespace Mf { class Core::Impl { public: Impl() : mError(Error::NONE), mTimestep(0.01), mFramerate(0.02), mShowFps(false) {} void init() { unsigned randomSeed; if (settings.get("rngseed", randomSeed)) srand(randomSeed); else srand(time(0)); Scalar timestep = 80.0; settings.get("timestep", timestep); mTimestep = 1.0 / timestep; Scalar framerate = 40.0; settings.get("framerate", framerate); mFramerate = 1.0 / framerate; mShowFps = false; settings.get("showfps", mShowFps); } /** * The main loop. This just calls dispatchEvents(), update(), and * draw() over and over again. The timing of the update and draw are * decoupled. The actual frame rate is also calculated here. This * function will return the exit code used to stop the loop. */ void run() { init(); Scalar totalTime = 0.0; Scalar ticks = Timer::getTicks(); Scalar nextUpdate = ticks; Scalar nextDraw = ticks; Scalar nextSecond = ticks + SCALAR(1.0); mFps = 0; int frames = 0; const int MAX_FRAMESKIP = 15; const Scalar inverseTimestep = SCALAR(1.0) / mTimestep; ASSERT(video && "cannot run core without a current video context"); do { Timer::fireIfExpired(); dispatchEvents(); int i = 0; while (nextUpdate < Timer::getTicks() && i < MAX_FRAMESKIP) { totalTime += mTimestep; update(totalTime, mTimestep); nextUpdate += mTimestep; ++i; } if (nextDraw < (ticks = Timer::getTicks())) { ++frames; draw((ticks + mTimestep - nextUpdate) * inverseTimestep); video->swap(); nextDraw += mFramerate; if (mShowFps && nextSecond < ticks) { mFps = frames; frames = 0; logInfo << mFps << " fps" << std::endl; nextSecond += SCALAR(1.0); } } // be a good citizen and give back what you don't need Timer::sleep(0.0); } while (!mStack.empty()); mDispatch.dispatch("engine.stopping"); } void dispatchEvents() { SDL_Event event; while (FE_PollEvent(&event) == 1) { switch (event.type) { case SDL_KEYDOWN: if (event.key.keysym.sym == SDLK_ESCAPE && (SDL_GetModState() & KMOD_CTRL) ) { // emergency escape logWarning("escape forced"); exit(1); } break; case SDL_VIDEORESIZE: video->resize(event.resize.w, event.resize.h); break; } handleEvent(event); } } void update(Scalar t, Scalar dt) { for (mStackIt = mStack.begin(); mStackIt != mStack.end(); ++mStackIt) { (*mStackIt)->update(t, dt); } } void draw(Scalar alpha) { // FIXME - this will crash if the layer being drawn pops itself std::list::reverse_iterator it; for (it = mStack.rbegin(); it != mStack.rend(); ++it) { (*it)->draw(alpha); } } void handleEvent(const Event& event) { for (mStackIt = mStack.begin(); mStackIt != mStack.end(); ++mStackIt) { if ((*mStackIt)->handleEvent(event)) break; } } void push(LayerP layer) { ASSERT(layer && "cannot push null layer"); mStack.push_front(layer); logInfo << "stack: " << mStack.size() << " [pushed " << layer.get() << "]" << std::endl; layer->addedToCore(); } LayerP pop() { bool fixIt = false; if (mStack.begin() == mStackIt) fixIt = true; LayerP layer = mStack.front(); mStack.pop_front(); logInfo << "stack: " << mStack.size() << " [popped " << layer.get() << "]" << std::endl; layer->removedFromCore(); if (fixIt) mStackIt = --mStack.begin(); return layer; } LayerP pop(Layer* layer) { bool fixIt = false; std::list layers; std::list::iterator it; for (it = mStack.begin(); it != mStack.end(); ++it) { layers.push_back(*it); if (it == mStackIt) fixIt = true; if ((*it).get() == layer) { ++it; mStack.erase(mStack.begin(), it); for (it = layers.begin(); it != layers.end(); ++it) { (*it)->removedFromCore(); logInfo << "stack: " << mStack.size() << " [popped " << (*it).get() << "]" << std::endl; } if (fixIt) mStackIt = --mStack.begin(); return layers.back(); } } return LayerP(); } void clear() { mStack.clear(); mStackIt = mStack.begin(); logInfo("stack: 0 [cleared]"); } Error mError; Dispatch mDispatch; std::list mStack; std::list::iterator mStackIt; Scalar mTimestep; Scalar mFramerate; int mFps; bool mShowFps; }; Core::Core() : // pass through mImpl(new Core::Impl) {} void Core::init() { // pass through mImpl->init(); } int Core::getFps() const { return mImpl->mFps; } void Core::push(LayerP layer) { // pass through mImpl->push(layer); } LayerP Core::pop() { // pass through return mImpl->pop(); } LayerP Core::pop(Layer* layer) { // pass through return mImpl->pop(layer); } void Core::clear() { // pass through mImpl->clear(); } int Core::getSize() const { return mImpl->mStack.size(); } void Core::run() { // pass through return mImpl->run(); } Dispatch::Handler Core::addHandler(const std::string& event, const Dispatch::Function& callback) { return mImpl->mDispatch.addHandler(event, callback); } Dispatch::Handler Core::addHandler(const std::string& event, const Dispatch::Function& callback, Dispatch::Handler handler) { return mImpl->mDispatch.addHandler(event, callback, handler); } void Core::dispatch(const std::string& event, const Dispatch::Message* message) { mImpl->mDispatch.dispatch(event, message); } Core core; class Backend_; typedef boost::shared_ptr BackendP; class Backend_ { public: Backend_() { #if defined(_WIN32) || defined(__WIN32__) if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) #else if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTTHREAD) != 0) #endif { const char* error = SDL_GetError(); gError.init(Error::SDL_INIT, error); return; // fatal } else { char name[128]; SDL_VideoDriverName(name, sizeof(name)); logInfo << "initialized SDL; using video driver `" << name << "'" << std::endl; } if (FE_Init() != 0) { const char* error = FE_GetError(); gError.init(Error::FASTEVENTS_INIT, error); return; // fatal } gError.init(Error::NONE); } ~Backend_() { FE_Quit(); SDL_Quit(); } static void retain() { if (gRetainCount++ == 0) { gInstance = BackendP(new Backend_); } } static void release() { if (--gRetainCount == 0) { gInstance.reset(); gError.reset(); } } static const Error& getError() { return gError; } private: static Error gError; static int gRetainCount; static BackendP gInstance; }; Error Backend_::gError(Error::UNINITIALIZED); int Backend_::gRetainCount = 0; BackendP Backend_::gInstance; Backend::Backend() { Backend_::retain(); } Backend::~Backend() { Backend_::release(); } bool Backend::isInitialized() { return getError().code() == Error::NONE; } const Error& Backend::getError() { return Backend_::getError(); } } // namespace Mf