Implement F12 game reset (MRI only)
Can be disabled with "enableReset=false". While at it, also replace the flakey volatile bool flags with proper atomics.
This commit is contained in:
parent
3a2e560139
commit
d223d83cbf
23 changed files with 343 additions and 60 deletions
|
@ -327,4 +327,12 @@ float Audio::bgsPos()
|
|||
return p->bgs.playingOffset();
|
||||
}
|
||||
|
||||
void Audio::reset()
|
||||
{
|
||||
p->bgm.stop();
|
||||
p->bgs.stop();
|
||||
p->me.stop();
|
||||
p->se.stop();
|
||||
}
|
||||
|
||||
Audio::~Audio() { delete p; }
|
||||
|
|
|
@ -67,6 +67,8 @@ public:
|
|||
float bgmPos();
|
||||
float bgsPos();
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
Audio(const Config &conf);
|
||||
~Audio();
|
||||
|
|
|
@ -35,6 +35,10 @@ struct ScriptBinding
|
|||
* function will perform a longjmp instead of returning,
|
||||
* so be careful about any variables with local storage */
|
||||
void (*terminate) (void);
|
||||
|
||||
/* Instructs the binding to issue a game reset.
|
||||
* Same conditions as for terminate apply */
|
||||
void (*reset) (void);
|
||||
};
|
||||
|
||||
/* VTable defined in the binding source */
|
||||
|
|
|
@ -138,6 +138,7 @@ Config::Config()
|
|||
solidFonts(false),
|
||||
gameFolder("."),
|
||||
anyAltToggleFS(false),
|
||||
enableReset(true),
|
||||
allowSymlinks(false),
|
||||
pathCache(true),
|
||||
useScriptNames(false)
|
||||
|
@ -164,6 +165,7 @@ void Config::read(int argc, char *argv[])
|
|||
PO_DESC(solidFonts, bool) \
|
||||
PO_DESC(gameFolder, std::string) \
|
||||
PO_DESC(anyAltToggleFS, bool) \
|
||||
PO_DESC(enableReset, bool) \
|
||||
PO_DESC(allowSymlinks, bool) \
|
||||
PO_DESC(iconPath, std::string) \
|
||||
PO_DESC(titleLanguage, std::string) \
|
||||
|
|
|
@ -47,6 +47,7 @@ struct Config
|
|||
|
||||
std::string gameFolder;
|
||||
bool anyAltToggleFS;
|
||||
bool enableReset;
|
||||
bool allowSymlinks;
|
||||
bool pathCache;
|
||||
|
||||
|
|
|
@ -22,7 +22,10 @@
|
|||
#ifndef DISPOSABLE_H
|
||||
#define DISPOSABLE_H
|
||||
|
||||
#include "intrulist.h"
|
||||
#include "exception.h"
|
||||
#include "sharedstate.h"
|
||||
#include "graphics.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <sigc++/signal.h>
|
||||
|
@ -32,12 +35,15 @@ class Disposable
|
|||
{
|
||||
public:
|
||||
Disposable()
|
||||
: disposed(false)
|
||||
{}
|
||||
: disposed(false),
|
||||
link(this)
|
||||
{
|
||||
shState->graphics().addDisposable(this);
|
||||
}
|
||||
|
||||
virtual ~Disposable()
|
||||
{
|
||||
assert(disposed);
|
||||
shState->graphics().remDisposable(this);
|
||||
}
|
||||
|
||||
void dispose()
|
||||
|
@ -69,7 +75,10 @@ private:
|
|||
virtual void releaseResources() = 0;
|
||||
virtual const char *klassName() const = 0;
|
||||
|
||||
friend class Graphics;
|
||||
|
||||
bool disposed;
|
||||
IntruListLink<Disposable> link;
|
||||
};
|
||||
|
||||
template<class C>
|
||||
|
|
|
@ -106,6 +106,8 @@ void EventThread::process(RGSSThreadData &rtData)
|
|||
char pendingTitle[128];
|
||||
bool havePendingTitle = false;
|
||||
|
||||
bool resetting = false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!SDL_WaitEvent(&event))
|
||||
|
@ -206,10 +208,34 @@ void EventThread::process(RGSSThreadData &rtData)
|
|||
break;
|
||||
}
|
||||
|
||||
if (event.key.keysym.scancode == SDL_SCANCODE_F12)
|
||||
{
|
||||
if (!rtData.config.enableReset)
|
||||
break;
|
||||
|
||||
if (resetting)
|
||||
break;
|
||||
|
||||
resetting = true;
|
||||
rtData.rqResetFinish.clear();
|
||||
rtData.rqReset.set();
|
||||
break;
|
||||
}
|
||||
|
||||
keyStates[event.key.keysym.scancode] = true;
|
||||
break;
|
||||
|
||||
case SDL_KEYUP :
|
||||
if (event.key.keysym.scancode == SDL_SCANCODE_F12)
|
||||
{
|
||||
if (!rtData.config.enableReset)
|
||||
break;
|
||||
|
||||
resetting = false;
|
||||
rtData.rqResetFinish.set();
|
||||
break;
|
||||
}
|
||||
|
||||
keyStates[event.key.keysym.scancode] = false;
|
||||
break;
|
||||
|
||||
|
@ -271,7 +297,7 @@ void EventThread::process(RGSSThreadData &rtData)
|
|||
rtData.config.game.title.c_str(),
|
||||
(const char*) event.user.data1, win);
|
||||
free(event.user.data1);
|
||||
msgBoxDone = true;
|
||||
msgBoxDone.set();
|
||||
break;
|
||||
|
||||
case REQUEST_SETCURSORVISIBLE :
|
||||
|
@ -377,7 +403,7 @@ void EventThread::requestShowCursor(bool mode)
|
|||
|
||||
void EventThread::showMessageBox(const char *body, int flags)
|
||||
{
|
||||
msgBoxDone = false;
|
||||
msgBoxDone.clear();
|
||||
|
||||
SDL_Event event;
|
||||
event.user.code = flags;
|
||||
|
@ -386,7 +412,7 @@ void EventThread::showMessageBox(const char *body, int flags)
|
|||
SDL_PushEvent(&event);
|
||||
|
||||
/* Keep repainting screen while box is open */
|
||||
shState->graphics().repaintWait(&msgBoxDone);
|
||||
shState->graphics().repaintWait(msgBoxDone);
|
||||
/* Prevent endless loops */
|
||||
resetInputStates();
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <SDL_joystick.h>
|
||||
#include <SDL_mouse.h>
|
||||
#include <SDL_mutex.h>
|
||||
#include <SDL_atomic.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -38,6 +39,32 @@ struct RGSSThreadData;
|
|||
struct SDL_Thread;
|
||||
struct SDL_Window;
|
||||
|
||||
struct AtomicFlag
|
||||
{
|
||||
AtomicFlag()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void set()
|
||||
{
|
||||
SDL_AtomicSet(&atom, 1);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
SDL_AtomicSet(&atom, 0);
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return SDL_AtomicGet(&atom);
|
||||
}
|
||||
|
||||
private:
|
||||
mutable SDL_atomic_t atom;
|
||||
};
|
||||
|
||||
class EventThread
|
||||
{
|
||||
public:
|
||||
|
@ -91,7 +118,7 @@ private:
|
|||
|
||||
bool fullscreen;
|
||||
bool showCursor;
|
||||
volatile bool msgBoxDone;
|
||||
AtomicFlag msgBoxDone;
|
||||
|
||||
struct
|
||||
{
|
||||
|
@ -111,13 +138,12 @@ struct WindowSizeNotify
|
|||
{
|
||||
SDL_mutex *mutex;
|
||||
|
||||
volatile bool changedFlag;
|
||||
volatile int w, h;
|
||||
AtomicFlag changed;
|
||||
int w, h;
|
||||
|
||||
WindowSizeNotify()
|
||||
{
|
||||
mutex = SDL_CreateMutex();
|
||||
changedFlag = false;
|
||||
w = h = 0;
|
||||
}
|
||||
|
||||
|
@ -133,7 +159,7 @@ struct WindowSizeNotify
|
|||
|
||||
this->w = w;
|
||||
this->h = h;
|
||||
changedFlag = true;
|
||||
changed.set();
|
||||
|
||||
SDL_UnlockMutex(mutex);
|
||||
}
|
||||
|
@ -141,14 +167,14 @@ struct WindowSizeNotify
|
|||
/* Done from the receiving side */
|
||||
bool pollChange(int *w, int *h)
|
||||
{
|
||||
if (!changedFlag)
|
||||
if (!changed)
|
||||
return false;
|
||||
|
||||
SDL_LockMutex(mutex);
|
||||
|
||||
*w = this->w;
|
||||
*h = this->h;
|
||||
changedFlag = false;
|
||||
changed.clear();
|
||||
|
||||
SDL_UnlockMutex(mutex);
|
||||
|
||||
|
@ -159,10 +185,16 @@ struct WindowSizeNotify
|
|||
struct RGSSThreadData
|
||||
{
|
||||
/* Main thread sets this to request RGSS thread to terminate */
|
||||
volatile bool rqTerm;
|
||||
AtomicFlag rqTerm;
|
||||
/* In response, RGSS thread sets this to confirm
|
||||
* that it received the request and isn't stuck */
|
||||
volatile bool rqTermAck;
|
||||
AtomicFlag rqTermAck;
|
||||
|
||||
/* Set when F12 is pressed */
|
||||
AtomicFlag rqReset;
|
||||
|
||||
/* Set when F12 is released */
|
||||
AtomicFlag rqResetFinish;
|
||||
|
||||
EventThread *ethread;
|
||||
WindowSizeNotify windowSizeMsg;
|
||||
|
@ -182,9 +214,7 @@ struct RGSSThreadData
|
|||
const char *argv0,
|
||||
SDL_Window *window,
|
||||
const Config& newconf)
|
||||
: rqTerm(false),
|
||||
rqTermAck(false),
|
||||
ethread(ethread),
|
||||
: ethread(ethread),
|
||||
argv0(argv0),
|
||||
window(window),
|
||||
sizeResoRatio(1, 1),
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
#include "texpool.h"
|
||||
#include "bitmap.h"
|
||||
#include "etc-internal.h"
|
||||
#include "disposable.h"
|
||||
#include "intrulist.h"
|
||||
#include "binding.h"
|
||||
#include "debugwriter.h"
|
||||
|
||||
|
@ -109,6 +111,19 @@ struct PingPong
|
|||
bind();
|
||||
}
|
||||
|
||||
void clearBuffers()
|
||||
{
|
||||
glState.clearColor.pushSet(Vec4(0, 0, 0, 1));
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
FBO::bind(rt[i].fbo);
|
||||
FBO::clear();
|
||||
}
|
||||
|
||||
glState.clearColor.pop();
|
||||
}
|
||||
|
||||
private:
|
||||
void bind()
|
||||
{
|
||||
|
@ -388,6 +403,10 @@ struct GraphicsPrivate
|
|||
Quad screenQuad;
|
||||
TEXFBO transBuffer;
|
||||
|
||||
/* Global list of all live Disposables
|
||||
* (disposed on reset) */
|
||||
IntruList<Disposable> dispList;
|
||||
|
||||
GraphicsPrivate(RGSSThreadData *rtData)
|
||||
: scRes(DEF_SCREEN_W, DEF_SCREEN_H),
|
||||
scSize(scRes),
|
||||
|
@ -472,9 +491,15 @@ struct GraphicsPrivate
|
|||
}
|
||||
}
|
||||
|
||||
void checkShutDownReset()
|
||||
{
|
||||
shState->checkShutdown();
|
||||
shState->checkReset();
|
||||
}
|
||||
|
||||
void shutdown()
|
||||
{
|
||||
threadData->rqTermAck = true;
|
||||
threadData->rqTermAck.set();
|
||||
shState->texPool().disable();
|
||||
|
||||
scriptBinding->terminate();
|
||||
|
@ -540,7 +565,7 @@ Graphics::~Graphics()
|
|||
|
||||
void Graphics::update()
|
||||
{
|
||||
shState->checkShutdown();
|
||||
p->checkShutDownReset();
|
||||
|
||||
if (p->frozen)
|
||||
return;
|
||||
|
@ -571,7 +596,7 @@ void Graphics::freeze()
|
|||
{
|
||||
p->frozen = true;
|
||||
|
||||
shState->checkShutdown();
|
||||
p->checkShutDownReset();
|
||||
p->checkResize();
|
||||
|
||||
/* Capture scene into frozen buffer */
|
||||
|
@ -623,10 +648,23 @@ void Graphics::transition(int duration,
|
|||
|
||||
for (int i = 0; i < duration; ++i)
|
||||
{
|
||||
/* We need to clean up transMap properly before
|
||||
* a possible longjmp, so we manually test for
|
||||
* shutdown/reset here */
|
||||
if (p->threadData->rqTerm)
|
||||
{
|
||||
glState.blend.pop();
|
||||
delete transMap;
|
||||
p->shutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
if (p->threadData->rqReset)
|
||||
{
|
||||
glState.blend.pop();
|
||||
delete transMap;
|
||||
scriptBinding->reset();
|
||||
return;
|
||||
}
|
||||
|
||||
const float prog = i * (1.0 / duration);
|
||||
|
@ -694,8 +732,7 @@ void Graphics::wait(int duration)
|
|||
{
|
||||
for (int i = 0; i < duration; ++i)
|
||||
{
|
||||
shState->checkShutdown();
|
||||
p->checkResize();
|
||||
p->checkShutDownReset();
|
||||
p->redrawScreen();
|
||||
}
|
||||
}
|
||||
|
@ -823,6 +860,29 @@ void Graphics::setBrightness(int value)
|
|||
p->screen.setBrightness(value / 255.0);
|
||||
}
|
||||
|
||||
void Graphics::reset()
|
||||
{
|
||||
/* Dispose all live Disposables */
|
||||
IntruListLink<Disposable> *iter;
|
||||
|
||||
for (iter = p->dispList.begin();
|
||||
iter != p->dispList.end();
|
||||
iter = iter->next)
|
||||
{
|
||||
iter->data->dispose();
|
||||
}
|
||||
|
||||
p->dispList.clear();
|
||||
|
||||
/* Reset attributes (frame count not included) */
|
||||
p->fpsLimiter.resetFrameAdjust();
|
||||
p->frozen = false;
|
||||
p->screen.getPP().clearBuffers();
|
||||
|
||||
setFrameRate(DEF_FRAMERATE);
|
||||
setBrightness(255);
|
||||
}
|
||||
|
||||
bool Graphics::getFullscreen() const
|
||||
{
|
||||
return p->threadData->ethread->getFullscreen();
|
||||
|
@ -848,9 +908,9 @@ Scene *Graphics::getScreen() const
|
|||
return &p->screen;
|
||||
}
|
||||
|
||||
void Graphics::repaintWait(volatile bool *exitCond)
|
||||
void Graphics::repaintWait(const AtomicFlag &exitCond, bool checkReset)
|
||||
{
|
||||
if (*exitCond)
|
||||
if (exitCond)
|
||||
return;
|
||||
|
||||
/* Repaint the screen with the last good frame we drew */
|
||||
|
@ -858,10 +918,13 @@ void Graphics::repaintWait(volatile bool *exitCond)
|
|||
GLMeta::blitBeginScreen(p->winSize);
|
||||
GLMeta::blitSource(lastFrame);
|
||||
|
||||
while (!*exitCond)
|
||||
while (!exitCond)
|
||||
{
|
||||
shState->checkShutdown();
|
||||
|
||||
if (checkReset)
|
||||
shState->checkReset();
|
||||
|
||||
FBO::clear();
|
||||
p->metaBlitBufferFlippedScaled();
|
||||
SDL_GL_SwapWindow(p->threadData->window);
|
||||
|
@ -872,3 +935,13 @@ void Graphics::repaintWait(volatile bool *exitCond)
|
|||
|
||||
GLMeta::blitEnd();
|
||||
}
|
||||
|
||||
void Graphics::addDisposable(Disposable *d)
|
||||
{
|
||||
p->dispList.append(d->link);
|
||||
}
|
||||
|
||||
void Graphics::remDisposable(Disposable *d)
|
||||
{
|
||||
p->dispList.remove(d->link);
|
||||
}
|
||||
|
|
|
@ -26,8 +26,10 @@
|
|||
|
||||
class Scene;
|
||||
class Bitmap;
|
||||
class Disposable;
|
||||
struct RGSSThreadData;
|
||||
struct GraphicsPrivate;
|
||||
struct AtomicFlag;
|
||||
|
||||
class Graphics
|
||||
{
|
||||
|
@ -53,6 +55,8 @@ public:
|
|||
int height() const;
|
||||
void resizeScreen(int width, int height);
|
||||
|
||||
void reset();
|
||||
|
||||
/* Non-standard extension */
|
||||
DECL_ATTR( Fullscreen, bool )
|
||||
DECL_ATTR( ShowCursor, bool )
|
||||
|
@ -60,14 +64,20 @@ public:
|
|||
/* <internal> */
|
||||
Scene *getScreen() const;
|
||||
/* Repaint screen with static image until exitCond
|
||||
* turns true. Used in EThread::showMessageBox() */
|
||||
void repaintWait(volatile bool *exitCond);
|
||||
* is set. Observes reset flag on top of shutdown
|
||||
* if "checkReset" */
|
||||
void repaintWait(const AtomicFlag &exitCond,
|
||||
bool checkReset = true);
|
||||
|
||||
private:
|
||||
Graphics(RGSSThreadData *data);
|
||||
~Graphics();
|
||||
|
||||
void addDisposable(Disposable *);
|
||||
void remDisposable(Disposable *);
|
||||
|
||||
friend struct SharedStatePrivate;
|
||||
friend class Disposable;
|
||||
|
||||
GraphicsPrivate *p;
|
||||
};
|
||||
|
|
|
@ -105,6 +105,15 @@ public:
|
|||
size--;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
remove(root);
|
||||
root.prev = &root;
|
||||
root.next = &root;
|
||||
|
||||
size = 0;
|
||||
}
|
||||
|
||||
T *tail() const
|
||||
{
|
||||
IntruListLink<T> *node = root.prev;
|
||||
|
|
|
@ -46,7 +46,7 @@ rgssThreadError(RGSSThreadData *rtData, const std::string &msg)
|
|||
{
|
||||
rtData->rgssErrorMsg = msg;
|
||||
rtData->ethread->requestTerminate();
|
||||
rtData->rqTermAck = true;
|
||||
rtData->rqTermAck.set();
|
||||
}
|
||||
|
||||
static inline const char*
|
||||
|
@ -147,7 +147,7 @@ int rgssThreadFun(void *userdata)
|
|||
/* Start script execution */
|
||||
scriptBinding->execute();
|
||||
|
||||
threadData->rqTermAck = true;
|
||||
threadData->rqTermAck.set();
|
||||
threadData->ethread->requestTerminate();
|
||||
|
||||
SharedState::finiInstance();
|
||||
|
@ -276,7 +276,7 @@ int main(int argc, char *argv[])
|
|||
eventThread.process(rtData);
|
||||
|
||||
/* Request RGSS thread to stop */
|
||||
rtData.rqTerm = true;
|
||||
rtData.rqTerm.set();
|
||||
|
||||
/* Wait for RGSS thread response */
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
|
|
|
@ -330,11 +330,20 @@ void SharedState::checkShutdown()
|
|||
if (!p->rtData.rqTerm)
|
||||
return;
|
||||
|
||||
p->rtData.rqTermAck = true;
|
||||
p->rtData.rqTermAck.set();
|
||||
p->texPool.disable();
|
||||
scriptBinding->terminate();
|
||||
}
|
||||
|
||||
void SharedState::checkReset()
|
||||
{
|
||||
if (!p->rtData.rqReset)
|
||||
return;
|
||||
|
||||
p->rtData.rqReset.clear();
|
||||
scriptBinding->reset();
|
||||
}
|
||||
|
||||
Font &SharedState::defaultFont() const
|
||||
{
|
||||
return *p->defaultFont;
|
||||
|
|
|
@ -109,6 +109,8 @@ struct SharedState
|
|||
* function will most likely not return */
|
||||
void checkShutdown();
|
||||
|
||||
void checkReset();
|
||||
|
||||
static SharedState *instance;
|
||||
static int rgssVersion;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue