Ruby: scripts run in a separate thread

Originally committed to SVN as r921.
This commit is contained in:
pomyk 2007-02-02 19:40:50 +00:00
parent 6da6f1bc57
commit 2f52b36910
5 changed files with 102 additions and 43 deletions

View File

@ -366,9 +366,7 @@ namespace Automation4 {
if (script_finished) { if (script_finished) {
if (!debug_visible) { if (!debug_visible) {
if(IsModal()) EndModal(0);
EndModal(0);
else Show(false);
} else { } else {
cancel_button->Enable(true); cancel_button->Enable(true);
cancel_button->SetLabel(_("Close")); cancel_button->SetLabel(_("Close"));
@ -452,10 +450,7 @@ namespace Automation4 {
cancelled = true; cancelled = true;
cancel_button->Enable(false); cancel_button->Enable(false);
} else { } else {
if(this->IsModal()) EndModal(0);
EndModal(0);
else
Show(false);
} }
} }

View File

@ -232,6 +232,7 @@ namespace Automation4 {
void OnIdle(wxIdleEvent &evt); void OnIdle(wxIdleEvent &evt);
void OnConfigDialog(ShowConfigDialogEvent &evt); void OnConfigDialog(ShowConfigDialogEvent &evt);
void DoUpdateDisplay();
protected: protected:
volatile bool cancelled; volatile bool cancelled;
@ -241,7 +242,6 @@ namespace Automation4 {
virtual ~ProgressSink(); virtual ~ProgressSink();
public: public:
void DoUpdateDisplay();
void SetProgress(float _progress); void SetProgress(float _progress);
void SetTask(const wxString &_task); void SetTask(const wxString &_task);
void SetTitle(const wxString &_title); void SetTitle(const wxString &_title);

View File

@ -56,7 +56,7 @@ namespace Automation4 {
RubyScript * RubyScript::inst = NULL; // current Ruby Script RubyScript * RubyScript::inst = NULL; // current Ruby Script
RubyProgressSink* RubyProgressSink::inst = NULL; RubyProgressSink* RubyProgressSink::inst = NULL;
VALUE RubyScript::RubyAegisub; VALUE RubyScript::RubyAegisub;
RubyAssFile *RubyAssFile::raf = NULL; // RubyAssFile *RubyAssFile::raf = NULL;
// RubyScriptReader // RubyScriptReader
RubyScriptReader::RubyScriptReader(const wxString &filename) RubyScriptReader::RubyScriptReader(const wxString &filename)
@ -137,7 +137,8 @@ namespace Automation4 {
rb_protect(rbLoadWrapper, rb_str_new2(t), &status); rb_protect(rbLoadWrapper, rb_str_new2(t), &status);
if(status > 0) // something bad happened (probably parsing error) if(status > 0) // something bad happened (probably parsing error)
{ {
//throw StringValueCStr(ruby_errinfo); VALUE err = rb_errinfo();
throw StringValueCStr(err);
} }
VALUE global_var = rb_gv_get("$script_name"); VALUE global_var = rb_gv_get("$script_name");
@ -281,11 +282,20 @@ namespace Automation4 {
return true; return true;
try { try {
RubyProgressSink::inst = NULL;
RubyAssFile *subsobj = new RubyAssFile(subs, true, true); RubyAssFile *subsobj = new RubyAssFile(subs, true, true);
VALUE sel = CreateIntegerArray(selected); // selected items VALUE *argv = ALLOCA_N(VALUE, 3);
RubyObjects::Get()->Register(sel); argv[0] = subsobj->rbAssFile;
VALUE result = rbFunCall(rb_mKernel, rb_to_id(validation_fun), 3, subsobj->rbAssFile, sel, rb_int2inum(active)); argv[1] = CreateIntegerArray(selected); // selected items;
RubyObjects::Get()->Unregister(sel); argv[2] = INT2FIX(active);
RubyCallArguments arg(rb_mKernel, rb_to_id(validation_fun), 3, argv);
VALUE result;
RubyThreadedCall call(&arg, &result);
wxThread::ExitCode code = call.Wait();
if(code)
{
return false;
}
if(result != Qnil && result != Qfalse) if(result != Qnil && result != Qfalse)
return true; return true;
}catch (const char* e) { }catch (const char* e) {
@ -302,26 +312,67 @@ namespace Automation4 {
delete RubyProgressSink::inst; delete RubyProgressSink::inst;
RubyProgressSink::inst = new RubyProgressSink(progress_parent, false); RubyProgressSink::inst = new RubyProgressSink(progress_parent, false);
RubyProgressSink::inst->SetTitle(GetName()); RubyProgressSink::inst->SetTitle(GetName());
RubyProgressSink::inst->Show(true);
// do call // do call
RubyAssFile *subsobj = new RubyAssFile(subs, true, true); RubyAssFile *subsobj = new RubyAssFile(subs, true, true);
VALUE sel = CreateIntegerArray(selected); // selected items VALUE *argv = ALLOCA_N(VALUE, 3);
RubyObjects::Get()->Register(sel); argv[0] = subsobj->rbAssFile;
VALUE result = rbFunCall(rb_mKernel, rb_to_id(macro_fun), 3, subsobj->rbAssFile, sel, rb_int2inum(active)); argv[1] = CreateIntegerArray(selected); // selected items;
RubyObjects::Get()->Unregister(sel); argv[2] = INT2FIX(active);
if(result != Qnil && result != Qfalse) RubyCallArguments arg(rb_mKernel, rb_to_id(macro_fun), 3, argv);
VALUE result;
RubyThreadedCall call(&arg, &result);
RubyProgressSink::inst->ShowModal();
wxThread::ExitCode code = call.Wait();
RubyProgressSink::inst = NULL;
if(code)
{
if(TYPE(result) == T_STRING)
throw StringValueCStr(result);
else throw "Unknown Error";
}
else if(result != Qnil && result != Qfalse)
{ {
subsobj->RubyUpdateAssFile(result); subsobj->RubyUpdateAssFile(result);
} }
delete subsobj;
} catch (const char* e) { } catch (const char* e) {
wxString *err = new wxString(e, wxConvUTF8); wxString *err = new wxString(e, wxConvUTF8);
wxMessageBox(*err, _T("Error running macro"),wxICON_ERROR | wxOK); wxMessageBox(*err, _T("Error running macro"),wxICON_ERROR | wxOK);
} }
RubyProgressSink::inst->script_finished = true;
} }
RubyThreadedCall::RubyThreadedCall(RubyCallArguments *a, VALUE *res)
: wxThread(wxTHREAD_JOINABLE)
,args(a), result(res)
{
int prio = Options.AsInt(_T("Automation Thread Priority"));
if (prio == 0) prio = 50; // normal
else if (prio == 1) prio = 30; // below normal
else if (prio == 2) prio = 10; // lowest
else prio = 50; // fallback normal
Create();
SetPriority(prio);
Run();
}
wxThread::ExitCode RubyThreadedCall::Entry()
{
int error = 0;
*result = rb_protect(rbCallWrapper, reinterpret_cast<VALUE>(args), &error);
if(RubyProgressSink::inst)
{
RubyProgressSink::inst->script_finished = true;
wxWakeUpIdle();
}
if(error) {
*result = rb_errinfo();
return (wxThread::ExitCode)1;
}
return (wxThread::ExitCode)0;
}
// RubyFeatureFilter // RubyFeatureFilter
RubyFeatureFilter::RubyFeatureFilter(const wxString &_name, const wxString &_description, RubyFeatureFilter::RubyFeatureFilter(const wxString &_name, const wxString &_description,
int merit, VALUE _filter_fun, VALUE _dialog_fun) int merit, VALUE _filter_fun, VALUE _dialog_fun)
@ -363,19 +414,32 @@ namespace Automation4 {
delete RubyProgressSink::inst; delete RubyProgressSink::inst;
RubyProgressSink::inst = new RubyProgressSink(export_dialog, false); RubyProgressSink::inst = new RubyProgressSink(export_dialog, false);
RubyProgressSink::inst->SetTitle(GetName()); RubyProgressSink::inst->SetTitle(GetName());
RubyProgressSink::inst->Show(true);
RubyAssFile *subsobj = new RubyAssFile(subs, true/*modify*/, false/*undo*/); RubyAssFile *subsobj = new RubyAssFile(subs, true/*modify*/, false/*undo*/);
VALUE result = rbFunCall(rb_mKernel, rb_to_id(filter_fun), 2, subsobj->rbAssFile, Qnil /* config */); VALUE *argv = ALLOCA_N(VALUE, 2);
if(result != Qnil && result != Qfalse) argv[0] = subsobj->rbAssFile;
argv[1] = Qnil; // config
RubyCallArguments arg(rb_mKernel, rb_to_id(filter_fun), 2, argv);
VALUE result;
RubyThreadedCall call(&arg, &result);
RubyProgressSink::inst->ShowModal();
wxThread::ExitCode code = call.Wait();
RubyProgressSink::inst = NULL;
if(code)
{
if(TYPE(result) == T_STRING)
throw StringValueCStr(result);
else throw "Unknown Error";
}
else if(result != Qnil && result != Qfalse)
{ {
subsobj->RubyUpdateAssFile(result); subsobj->RubyUpdateAssFile(result);
} }
delete subsobj;
} catch (const char* e) { } catch (const char* e) {
wxString *err = new wxString(e, wxConvUTF8); wxString *err = new wxString(e, wxConvUTF8);
wxMessageBox(*err, _T("Error running filter"),wxICON_ERROR | wxOK); wxMessageBox(*err, _T("Error running filter"),wxICON_ERROR | wxOK);
} }
RubyProgressSink::inst->script_finished = true;
} }
ScriptConfigDialog* RubyFeatureFilter::GenerateConfigDialog(wxWindow *parent) ScriptConfigDialog* RubyFeatureFilter::GenerateConfigDialog(wxWindow *parent)
@ -422,8 +486,6 @@ namespace Automation4 {
{ {
float _progr = rb_num2dbl(progress); float _progr = rb_num2dbl(progress);
RubyProgressSink::inst->SetProgress(_progr); RubyProgressSink::inst->SetProgress(_progr);
RubyProgressSink::inst->DoUpdateDisplay();
wxSafeYield(RubyProgressSink::inst);
return Qtrue; return Qtrue;
} }
@ -431,7 +493,6 @@ namespace Automation4 {
{ {
wxString _t(StringValueCStr(task), wxConvUTF8); wxString _t(StringValueCStr(task), wxConvUTF8);
RubyProgressSink::inst->SetTask(_t); RubyProgressSink::inst->SetTask(_t);
RubyProgressSink::inst->DoUpdateDisplay();
return Qtrue; return Qtrue;
} }
@ -439,8 +500,6 @@ namespace Automation4 {
{ {
wxString _t(StringValueCStr(title), wxConvUTF8); wxString _t(StringValueCStr(title), wxConvUTF8);
RubyProgressSink::inst->SetTitle(_t); RubyProgressSink::inst->SetTitle(_t);
//wxSafeYield(RubyProgressSink::inst);
RubyProgressSink::inst->DoUpdateDisplay();
return Qtrue; return Qtrue;
} }
@ -461,8 +520,6 @@ namespace Automation4 {
else args[1] = args[0]; else args[1] = args[0];
wxString _m(StringValueCStr(args[1]), wxConvUTF8); wxString _m(StringValueCStr(args[1]), wxConvUTF8);
RubyProgressSink::inst->AddDebugOutput(_m); RubyProgressSink::inst->AddDebugOutput(_m);
RubyProgressSink::inst->DoUpdateDisplay();
wxSafeYield(RubyProgressSink::inst);
return Qtrue; return Qtrue;
} }
@ -564,7 +621,8 @@ namespace Automation4 {
VALUE result; VALUE result;
result = rb_protect(rbCallWrapper, reinterpret_cast<VALUE>(&arg), &error); result = rb_protect(rbCallWrapper, reinterpret_cast<VALUE>(&arg), &error);
if(error) { if(error) {
//throw StringValueCStr(ruby_errinfo); VALUE err = rb_errinfo();
throw StringValueCStr(err);
} }
return result; return result;
} }

View File

@ -76,12 +76,12 @@ namespace Automation4 {
static int RubyUnparseTagData(); static int RubyUnparseTagData();
static int RubySetUndoPoint(); static int RubySetUndoPoint();
~RubyAssFile();
public: public:
void RubyUpdateAssFile(VALUE subtitles); void RubyUpdateAssFile(VALUE subtitles);
static VALUE AssEntryToRuby(AssEntry *e); // makes a Ruby representation of AssEntry static VALUE AssEntryToRuby(AssEntry *e); // makes a Ruby representation of AssEntry
static AssEntry *RubyToAssEntry(VALUE ass_entry); // creates an AssEntry object from a Ruby representation static AssEntry *RubyToAssEntry(VALUE ass_entry); // creates an AssEntry object from a Ruby representation
RubyAssFile(AssFile *_ass, bool _can_modify, bool _can_set_undo); RubyAssFile(AssFile *_ass, bool _can_modify, bool _can_set_undo);
~RubyAssFile();
static RubyAssFile *raf; static RubyAssFile *raf;
VALUE rbAssFile; VALUE rbAssFile;
@ -245,6 +245,17 @@ namespace Automation4 {
RubyCallArguments(VALUE _recv, ID _id, int _n, VALUE *_argv); RubyCallArguments(VALUE _recv, ID _id, int _n, VALUE *_argv);
}; };
// A single call to a Ruby function, run inside a separate thread.
// This object should be created on the stack in the function that does the call.
class RubyThreadedCall : public wxThread {
private:
RubyCallArguments *args;
VALUE *result;
public:
RubyThreadedCall(RubyCallArguments *args, VALUE *result);
virtual ExitCode Entry();
};
VALUE rbCallWrapper(VALUE arg); VALUE rbCallWrapper(VALUE arg);
VALUE rbExecWrapper(VALUE arg); VALUE rbExecWrapper(VALUE arg);
VALUE rbLoadWrapper(VALUE arg); VALUE rbLoadWrapper(VALUE arg);

View File

@ -337,8 +337,8 @@ namespace Automation4 {
// If the first line is dialogue we leave header from the original (styles, info, etc) // If the first line is dialogue we leave header from the original (styles, info, etc)
void RubyAssFile::RubyUpdateAssFile(VALUE subtitles) void RubyAssFile::RubyUpdateAssFile(VALUE subtitles)
{ {
RubyObjects::Get()->Register(subtitles); //RubyObjects::Get()->Register(subtitles);
int size = rb_num2long(rb_funcall(subtitles, rb_to_id(rb_str_new2("size")), 0)); int size = RARRAY(subtitles)->len;
if(size <= 0) return; // empty - leave the original if(size <= 0) return; // empty - leave the original
@ -378,7 +378,7 @@ namespace Automation4 {
rb_set_errinfo(Qnil); rb_set_errinfo(Qnil);
} }
} }
RubyObjects::Get()->Unregister(subtitles); //RubyObjects::Get()->Unregister(subtitles);
} }
int RubyAssFile::RubyParseTagData() int RubyAssFile::RubyParseTagData()
@ -401,7 +401,6 @@ namespace Automation4 {
RubyAssFile::~RubyAssFile() RubyAssFile::~RubyAssFile()
{ {
RubyObjects::Get()->Unregister(rbAssFile);
} }
RubyAssFile::RubyAssFile(AssFile *_ass, bool _can_modify, bool _can_set_undo) RubyAssFile::RubyAssFile(AssFile *_ass, bool _can_modify, bool _can_set_undo)
@ -409,12 +408,8 @@ namespace Automation4 {
, can_modify(_can_modify) , can_modify(_can_modify)
, can_set_undo(_can_set_undo) , can_set_undo(_can_set_undo)
{ {
if(RubyAssFile::raf)
delete RubyAssFile::raf; // delete previous if there is one
RubyAssFile::raf = this; // set pointer to this obj
rbAssFile = rb_ary_new2(ass->Line.size()); rbAssFile = rb_ary_new2(ass->Line.size());
RubyObjects::Get()->Register(rbAssFile);
std::list<AssEntry*>::iterator entry; std::list<AssEntry*>::iterator entry;
int status; int status;