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
				
			
		| 
						 | 
				
			
			@ -15,7 +15,6 @@ Missing RGSS3 functionality:
 | 
			
		|||
 | 
			
		||||
* Text outline
 | 
			
		||||
* Movie playback
 | 
			
		||||
* F12 reset
 | 
			
		||||
* Audio fade-in
 | 
			
		||||
 | 
			
		||||
Some other things might be implemented, but simply not bound yet.
 | 
			
		||||
| 
						 | 
				
			
			@ -116,7 +115,6 @@ If a requested font is not found, no error is generated. Instead, a built-in fon
 | 
			
		|||
 | 
			
		||||
* wma audio files
 | 
			
		||||
* The Win32API ruby class (for obvious reasons)
 | 
			
		||||
* Restarting the game with F12
 | 
			
		||||
* Creating Bitmaps with sizes greater than the OpenGL texture size limit (around 8192 on modern cards)*
 | 
			
		||||
 | 
			
		||||
\* There is an exception to this, called *mega surface*. When a Bitmap bigger than the texture limit is created from a file, it is not stored in VRAM, but regular RAM. Its sole purpose is to be used as a tileset bitmap. Any other operation to it (besides blitting to a regular Bitmap) will result in an error.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -97,6 +97,15 @@ DEF_FADE( me )
 | 
			
		|||
 | 
			
		||||
DEF_PLAY_STOP( se )
 | 
			
		||||
 | 
			
		||||
RB_METHOD(audioReset)
 | 
			
		||||
{
 | 
			
		||||
	RB_UNUSED_PARAM;
 | 
			
		||||
 | 
			
		||||
	shState->audio().reset();
 | 
			
		||||
 | 
			
		||||
	return Qnil;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define BIND_PLAY_STOP(entity) \
 | 
			
		||||
	_rb_define_module_function(module, #entity "_play", audio_##entity##Play); \
 | 
			
		||||
| 
						 | 
				
			
			@ -129,4 +138,6 @@ audioBindingInit()
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	BIND_PLAY_STOP( se )
 | 
			
		||||
 | 
			
		||||
	_rb_define_module_function(module, "__reset__", audioReset);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,7 @@
 | 
			
		|||
#include "util.h"
 | 
			
		||||
#include "debugwriter.h"
 | 
			
		||||
#include "graphics.h"
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
#include "boost-hash.h"
 | 
			
		||||
 | 
			
		||||
#include <ruby.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -44,11 +45,13 @@ extern const char module_rpg3[];
 | 
			
		|||
 | 
			
		||||
static void mriBindingExecute();
 | 
			
		||||
static void mriBindingTerminate();
 | 
			
		||||
static void mriBindingReset();
 | 
			
		||||
 | 
			
		||||
ScriptBinding scriptBindingImpl =
 | 
			
		||||
{
 | 
			
		||||
	mriBindingExecute,
 | 
			
		||||
	mriBindingTerminate
 | 
			
		||||
	mriBindingTerminate,
 | 
			
		||||
	mriBindingReset
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ScriptBinding *scriptBinding = &scriptBindingImpl;
 | 
			
		||||
| 
						 | 
				
			
			@ -213,12 +216,51 @@ RB_METHOD(mriDataDirectory)
 | 
			
		|||
	return pathStr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE rgssMainCb(VALUE block)
 | 
			
		||||
{
 | 
			
		||||
	rb_funcall2(block, rb_intern("call"), 0, 0);
 | 
			
		||||
	return Qnil;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE rgssMainRescue(VALUE arg, VALUE exc)
 | 
			
		||||
{
 | 
			
		||||
	VALUE *excRet = (VALUE*) arg;
 | 
			
		||||
 | 
			
		||||
	*excRet = exc;
 | 
			
		||||
 | 
			
		||||
	return Qnil;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void processReset()
 | 
			
		||||
{
 | 
			
		||||
	shState->graphics().reset();
 | 
			
		||||
	shState->audio().reset();
 | 
			
		||||
 | 
			
		||||
	shState->rtData().rqReset.clear();
 | 
			
		||||
	shState->graphics().repaintWait(shState->rtData().rqResetFinish,
 | 
			
		||||
	                                false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RB_METHOD(mriRgssMain)
 | 
			
		||||
{
 | 
			
		||||
	RB_UNUSED_PARAM;
 | 
			
		||||
 | 
			
		||||
	// TODO: Implement F12 reset
 | 
			
		||||
	rb_yield(Qnil);
 | 
			
		||||
	while (true)
 | 
			
		||||
	{
 | 
			
		||||
		VALUE exc = Qnil;
 | 
			
		||||
 | 
			
		||||
		rb_rescue2((VALUE(*)(ANYARGS)) rgssMainCb, rb_block_proc(),
 | 
			
		||||
		           (VALUE(*)(ANYARGS)) rgssMainRescue, (VALUE) &exc,
 | 
			
		||||
		           rb_eException, (VALUE) 0);
 | 
			
		||||
 | 
			
		||||
		if (NIL_P(exc))
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		if (rb_obj_class(exc) == getRbData()->exc[Reset])
 | 
			
		||||
			processReset();
 | 
			
		||||
		else
 | 
			
		||||
			rb_exc_raise(exc);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return Qnil;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -395,6 +437,8 @@ static void runRMXPScripts(BacktraceData &btData)
 | 
			
		|||
	for (size_t i = 0; i < conf.preloadScripts.size(); ++i)
 | 
			
		||||
		runCustomScript(conf.preloadScripts[i]);
 | 
			
		||||
 | 
			
		||||
	while (true)
 | 
			
		||||
	{
 | 
			
		||||
		for (long i = 0; i < scriptCount; ++i)
 | 
			
		||||
		{
 | 
			
		||||
			VALUE script = rb_ary_entry(scriptArray, i);
 | 
			
		||||
| 
						 | 
				
			
			@ -420,6 +464,13 @@ static void runRMXPScripts(BacktraceData &btData)
 | 
			
		|||
			if (state)
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		VALUE exc = rb_gv_get("$!");
 | 
			
		||||
		if (rb_obj_class(exc) != getRbData()->exc[Reset])
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		processReset();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void showExc(VALUE exc, const BacktraceData &btData)
 | 
			
		||||
| 
						 | 
				
			
			@ -520,10 +571,15 @@ static void mriBindingExecute()
 | 
			
		|||
 | 
			
		||||
	ruby_cleanup(0);
 | 
			
		||||
 | 
			
		||||
	shState->rtData().rqTermAck = true;
 | 
			
		||||
	shState->rtData().rqTermAck.set();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mriBindingTerminate()
 | 
			
		||||
{
 | 
			
		||||
	rb_raise(rb_eSystemExit, " ");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mriBindingReset()
 | 
			
		||||
{
 | 
			
		||||
	rb_raise(getRbData()->exc[Reset], " ");
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,6 +51,8 @@ RbData::RbData()
 | 
			
		|||
	for (size_t i = 0; i < ARRAY_SIZE(customExc); ++i)
 | 
			
		||||
		exc[customExc[i].id] = rb_define_class(customExc[i].name, rb_eException);
 | 
			
		||||
 | 
			
		||||
	exc[Reset] = rb_define_class(rgssVer >= 3 ? "RGSSReset" : "Reset", rb_eException);
 | 
			
		||||
 | 
			
		||||
	exc[ErrnoENOENT] = rb_const_get(rb_const_get(rb_cObject, rb_intern("Errno")), rb_intern("ENOENT"));
 | 
			
		||||
	exc[IOError] = rb_eIOError;
 | 
			
		||||
	exc[TypeError] = rb_eTypeError;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,6 +29,7 @@
 | 
			
		|||
enum RbException
 | 
			
		||||
{
 | 
			
		||||
	RGSS = 0,
 | 
			
		||||
	Reset,
 | 
			
		||||
	PHYSFS,
 | 
			
		||||
	SDL,
 | 
			
		||||
	MKXP,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -174,6 +174,15 @@ RB_METHOD(graphicsResizeScreen)
 | 
			
		|||
	return Qnil;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RB_METHOD(graphicsReset)
 | 
			
		||||
{
 | 
			
		||||
	RB_UNUSED_PARAM;
 | 
			
		||||
 | 
			
		||||
	shState->graphics().reset();
 | 
			
		||||
 | 
			
		||||
	return Qnil;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEF_GRA_PROP_I(FrameRate)
 | 
			
		||||
DEF_GRA_PROP_I(FrameCount)
 | 
			
		||||
DEF_GRA_PROP_I(Brightness)
 | 
			
		||||
| 
						 | 
				
			
			@ -196,6 +205,8 @@ void graphicsBindingInit()
 | 
			
		|||
	_rb_define_module_function(module, "transition", graphicsTransition);
 | 
			
		||||
	_rb_define_module_function(module, "frame_reset", graphicsFrameReset);
 | 
			
		||||
 | 
			
		||||
	_rb_define_module_function(module, "__reset__", graphicsReset);
 | 
			
		||||
 | 
			
		||||
	INIT_GRA_PROP_BIND( FrameRate,  "frame_rate"  );
 | 
			
		||||
	INIT_GRA_PROP_BIND( FrameCount, "frame_count" );
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,11 +51,13 @@
 | 
			
		|||
 | 
			
		||||
static void mrbBindingExecute();
 | 
			
		||||
static void mrbBindingTerminate();
 | 
			
		||||
static void mrbBindingReset();
 | 
			
		||||
 | 
			
		||||
ScriptBinding scriptBindingImpl =
 | 
			
		||||
{
 | 
			
		||||
    mrbBindingExecute,
 | 
			
		||||
    mrbBindingTerminate
 | 
			
		||||
    mrbBindingTerminate,
 | 
			
		||||
    mrbBindingReset
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ScriptBinding *scriptBinding = &scriptBindingImpl;
 | 
			
		||||
| 
						 | 
				
			
			@ -384,7 +386,7 @@ static void mrbBindingExecute()
 | 
			
		|||
 | 
			
		||||
	checkException(mrb);
 | 
			
		||||
 | 
			
		||||
	shState->rtData().rqTermAck = true;
 | 
			
		||||
	shState->rtData().rqTermAck.set();
 | 
			
		||||
	shState->texPool().disable();
 | 
			
		||||
 | 
			
		||||
	mrbc_context_free(mrb, ctx);
 | 
			
		||||
| 
						 | 
				
			
			@ -398,3 +400,8 @@ static void mrbBindingTerminate()
 | 
			
		|||
 | 
			
		||||
	mrb_raise(mrb, data->exc[Shutdown], "");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mrbBindingReset()
 | 
			
		||||
{
 | 
			
		||||
	// No idea how to do this with mruby yet
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,7 +27,7 @@
 | 
			
		|||
static void nullBindingExecute()
 | 
			
		||||
{
 | 
			
		||||
	Debug() << "The null binding doesn't do anything, so we're done!";
 | 
			
		||||
	shState->rtData().rqTermAck = true;
 | 
			
		||||
	shState->rtData().rqTermAck.set();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void nullBindingTerminate()
 | 
			
		||||
| 
						 | 
				
			
			@ -35,10 +35,16 @@ static void nullBindingTerminate()
 | 
			
		|||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void nullBindingReset()
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ScriptBinding scriptBindingImpl =
 | 
			
		||||
{
 | 
			
		||||
    nullBindingExecute,
 | 
			
		||||
    nullBindingTerminate
 | 
			
		||||
    nullBindingTerminate,
 | 
			
		||||
    nullBindingReset
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ScriptBinding *scriptBinding = &scriptBindingImpl;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -99,6 +99,12 @@
 | 
			
		|||
# anyAltToggleFS=false
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Enable F12 game reset
 | 
			
		||||
# (default: enabled)
 | 
			
		||||
#
 | 
			
		||||
# enableReset=true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Allow symlinks for game assets to be followed
 | 
			
		||||
# (default: disabled)
 | 
			
		||||
#
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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