diff --git a/aegisub/auto4_base.cpp b/aegisub/auto4_base.cpp index 9674cea98..f802edc41 100644 --- a/aegisub/auto4_base.cpp +++ b/aegisub/auto4_base.cpp @@ -366,9 +366,7 @@ namespace Automation4 { if (script_finished) { if (!debug_visible) { - if(IsModal()) - EndModal(0); - else Show(false); + EndModal(0); } else { cancel_button->Enable(true); cancel_button->SetLabel(_("Close")); @@ -452,10 +450,7 @@ namespace Automation4 { cancelled = true; cancel_button->Enable(false); } else { - if(this->IsModal()) - EndModal(0); - else - Show(false); + EndModal(0); } } diff --git a/aegisub/auto4_base.h b/aegisub/auto4_base.h index 963ca0ce9..f5dd180f0 100644 --- a/aegisub/auto4_base.h +++ b/aegisub/auto4_base.h @@ -232,6 +232,7 @@ namespace Automation4 { void OnIdle(wxIdleEvent &evt); void OnConfigDialog(ShowConfigDialogEvent &evt); + void DoUpdateDisplay(); protected: volatile bool cancelled; @@ -241,7 +242,6 @@ namespace Automation4 { virtual ~ProgressSink(); public: - void DoUpdateDisplay(); void SetProgress(float _progress); void SetTask(const wxString &_task); void SetTitle(const wxString &_title); diff --git a/aegisub/auto4_ruby.cpp b/aegisub/auto4_ruby.cpp index 110e0691b..7fd0f31ff 100644 --- a/aegisub/auto4_ruby.cpp +++ b/aegisub/auto4_ruby.cpp @@ -56,7 +56,7 @@ namespace Automation4 { RubyScript * RubyScript::inst = NULL; // current Ruby Script RubyProgressSink* RubyProgressSink::inst = NULL; VALUE RubyScript::RubyAegisub; - RubyAssFile *RubyAssFile::raf = NULL; +// RubyAssFile *RubyAssFile::raf = NULL; // RubyScriptReader RubyScriptReader::RubyScriptReader(const wxString &filename) @@ -137,7 +137,8 @@ namespace Automation4 { rb_protect(rbLoadWrapper, rb_str_new2(t), &status); 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"); @@ -281,11 +282,20 @@ namespace Automation4 { return true; try { + RubyProgressSink::inst = NULL; RubyAssFile *subsobj = new RubyAssFile(subs, true, true); - VALUE sel = CreateIntegerArray(selected); // selected items - RubyObjects::Get()->Register(sel); - VALUE result = rbFunCall(rb_mKernel, rb_to_id(validation_fun), 3, subsobj->rbAssFile, sel, rb_int2inum(active)); - RubyObjects::Get()->Unregister(sel); + VALUE *argv = ALLOCA_N(VALUE, 3); + argv[0] = subsobj->rbAssFile; + argv[1] = CreateIntegerArray(selected); // selected items; + 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) return true; }catch (const char* e) { @@ -302,26 +312,67 @@ namespace Automation4 { delete RubyProgressSink::inst; RubyProgressSink::inst = new RubyProgressSink(progress_parent, false); RubyProgressSink::inst->SetTitle(GetName()); - RubyProgressSink::inst->Show(true); // do call RubyAssFile *subsobj = new RubyAssFile(subs, true, true); - VALUE sel = CreateIntegerArray(selected); // selected items - RubyObjects::Get()->Register(sel); - VALUE result = rbFunCall(rb_mKernel, rb_to_id(macro_fun), 3, subsobj->rbAssFile, sel, rb_int2inum(active)); - RubyObjects::Get()->Unregister(sel); - if(result != Qnil && result != Qfalse) + VALUE *argv = ALLOCA_N(VALUE, 3); + argv[0] = subsobj->rbAssFile; + argv[1] = CreateIntegerArray(selected); // selected items; + argv[2] = INT2FIX(active); + 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); } + delete subsobj; } catch (const char* e) { wxString *err = new wxString(e, wxConvUTF8); 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(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(const wxString &_name, const wxString &_description, int merit, VALUE _filter_fun, VALUE _dialog_fun) @@ -363,19 +414,32 @@ namespace Automation4 { delete RubyProgressSink::inst; RubyProgressSink::inst = new RubyProgressSink(export_dialog, false); RubyProgressSink::inst->SetTitle(GetName()); - RubyProgressSink::inst->Show(true); RubyAssFile *subsobj = new RubyAssFile(subs, true/*modify*/, false/*undo*/); - VALUE result = rbFunCall(rb_mKernel, rb_to_id(filter_fun), 2, subsobj->rbAssFile, Qnil /* config */); - if(result != Qnil && result != Qfalse) + VALUE *argv = ALLOCA_N(VALUE, 2); + 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); } + delete subsobj; } catch (const char* e) { 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) @@ -422,8 +486,6 @@ namespace Automation4 { { float _progr = rb_num2dbl(progress); RubyProgressSink::inst->SetProgress(_progr); - RubyProgressSink::inst->DoUpdateDisplay(); - wxSafeYield(RubyProgressSink::inst); return Qtrue; } @@ -431,7 +493,6 @@ namespace Automation4 { { wxString _t(StringValueCStr(task), wxConvUTF8); RubyProgressSink::inst->SetTask(_t); - RubyProgressSink::inst->DoUpdateDisplay(); return Qtrue; } @@ -439,8 +500,6 @@ namespace Automation4 { { wxString _t(StringValueCStr(title), wxConvUTF8); RubyProgressSink::inst->SetTitle(_t); - //wxSafeYield(RubyProgressSink::inst); - RubyProgressSink::inst->DoUpdateDisplay(); return Qtrue; } @@ -461,8 +520,6 @@ namespace Automation4 { else args[1] = args[0]; wxString _m(StringValueCStr(args[1]), wxConvUTF8); RubyProgressSink::inst->AddDebugOutput(_m); - RubyProgressSink::inst->DoUpdateDisplay(); - wxSafeYield(RubyProgressSink::inst); return Qtrue; } @@ -564,7 +621,8 @@ namespace Automation4 { VALUE result; result = rb_protect(rbCallWrapper, reinterpret_cast(&arg), &error); if(error) { - //throw StringValueCStr(ruby_errinfo); + VALUE err = rb_errinfo(); + throw StringValueCStr(err); } return result; } diff --git a/aegisub/auto4_ruby.h b/aegisub/auto4_ruby.h index 7762e2dde..f5fa5b6b7 100644 --- a/aegisub/auto4_ruby.h +++ b/aegisub/auto4_ruby.h @@ -76,12 +76,12 @@ namespace Automation4 { static int RubyUnparseTagData(); static int RubySetUndoPoint(); - ~RubyAssFile(); public: void RubyUpdateAssFile(VALUE subtitles); 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 RubyAssFile(AssFile *_ass, bool _can_modify, bool _can_set_undo); + ~RubyAssFile(); static RubyAssFile *raf; VALUE rbAssFile; @@ -245,6 +245,17 @@ namespace Automation4 { 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 rbExecWrapper(VALUE arg); VALUE rbLoadWrapper(VALUE arg); diff --git a/aegisub/auto4_ruby_assfile.cpp b/aegisub/auto4_ruby_assfile.cpp index 4298390e9..dd7b78a4e 100644 --- a/aegisub/auto4_ruby_assfile.cpp +++ b/aegisub/auto4_ruby_assfile.cpp @@ -337,8 +337,8 @@ namespace Automation4 { // If the first line is dialogue we leave header from the original (styles, info, etc) void RubyAssFile::RubyUpdateAssFile(VALUE subtitles) { - RubyObjects::Get()->Register(subtitles); - int size = rb_num2long(rb_funcall(subtitles, rb_to_id(rb_str_new2("size")), 0)); + //RubyObjects::Get()->Register(subtitles); + int size = RARRAY(subtitles)->len; if(size <= 0) return; // empty - leave the original @@ -378,7 +378,7 @@ namespace Automation4 { rb_set_errinfo(Qnil); } } - RubyObjects::Get()->Unregister(subtitles); + //RubyObjects::Get()->Unregister(subtitles); } int RubyAssFile::RubyParseTagData() @@ -401,7 +401,6 @@ namespace Automation4 { RubyAssFile::~RubyAssFile() { - RubyObjects::Get()->Unregister(rbAssFile); } RubyAssFile::RubyAssFile(AssFile *_ass, bool _can_modify, bool _can_set_undo) @@ -409,12 +408,8 @@ namespace Automation4 { , can_modify(_can_modify) , 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()); - RubyObjects::Get()->Register(rbAssFile); std::list::iterator entry; int status;