Add a generic function for invoking functions on the GUI thread

This commit is contained in:
Thomas Goyne 2012-11-25 19:06:18 -08:00
parent 1d020b851e
commit 947dc537d1
5 changed files with 46 additions and 58 deletions

View file

@ -223,9 +223,6 @@ namespace Automation4 {
} }
// ProgressSink // ProgressSink
wxDEFINE_EVENT(EVT_SHOW_DIALOG, wxThreadEvent);
wxDEFINE_EVENT(EVT_SHOW_SCRIPT_DIALOG, wxThreadEvent);
ProgressSink::ProgressSink(agi::ProgressSink *impl, BackgroundScriptRunner *bsr) ProgressSink::ProgressSink(agi::ProgressSink *impl, BackgroundScriptRunner *bsr)
: impl(impl) : impl(impl)
, bsr(bsr) , bsr(bsr)
@ -235,74 +232,35 @@ namespace Automation4 {
void ProgressSink::ShowDialog(ScriptDialog *config_dialog) void ProgressSink::ShowDialog(ScriptDialog *config_dialog)
{ {
wxSemaphore sema(0, 1); InvokeOnMainThread([=] {
wxThreadEvent *evt = new wxThreadEvent(EVT_SHOW_SCRIPT_DIALOG); wxDialog w; // container dialog box
evt->SetPayload(std::make_pair(config_dialog, &sema)); w.SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
bsr->QueueEvent(evt); w.Create(bsr->GetParentWindow(), -1, bsr->GetTitle());
sema.Wait(); wxBoxSizer *s = new wxBoxSizer(wxHORIZONTAL); // sizer for putting contents in
wxWindow *ww = config_dialog->CreateWindow(&w); // generate actual dialog contents
s->Add(ww, 0, wxALL, 5); // add contents to dialog
w.SetSizerAndFit(s);
w.CenterOnParent();
w.ShowModal();
});
} }
int ProgressSink::ShowDialog(wxDialog *dialog) int ProgressSink::ShowDialog(wxDialog *dialog)
{ {
int ret = 0; int ret = 0;
wxSemaphore sema(0, 1); InvokeOnMainThread([&] { ret = dialog->ShowModal(); });
wxThreadEvent *evt = new wxThreadEvent(EVT_SHOW_DIALOG);
evt->SetPayload(std::make_tuple(dialog, &sema, &ret));
bsr->QueueEvent(evt);
sema.Wait();
return ret; return ret;
} }
BackgroundScriptRunner::BackgroundScriptRunner(wxWindow *parent, wxString const& title) BackgroundScriptRunner::BackgroundScriptRunner(wxWindow *parent, wxString const& title)
: impl(new DialogProgress(parent, title)) : impl(new DialogProgress(parent, title))
{ {
impl->Bind(EVT_SHOW_DIALOG, &BackgroundScriptRunner::OnDialog, this);
impl->Bind(EVT_SHOW_SCRIPT_DIALOG, &BackgroundScriptRunner::OnScriptDialog, this);
} }
BackgroundScriptRunner::~BackgroundScriptRunner() BackgroundScriptRunner::~BackgroundScriptRunner()
{ {
} }
void BackgroundScriptRunner::OnScriptDialog(wxThreadEvent &evt)
{
std::pair<ScriptDialog*, wxSemaphore*> payload = evt.GetPayload<std::pair<ScriptDialog*, wxSemaphore*> >();
wxDialog w; // container dialog box
w.SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
w.Create(impl.get(), -1, impl->GetTitle());
wxBoxSizer *s = new wxBoxSizer(wxHORIZONTAL); // sizer for putting contents in
wxWindow *ww = payload.first->CreateWindow(&w); // generate actual dialog contents
s->Add(ww, 0, wxALL, 5); // add contents to dialog
w.SetSizerAndFit(s);
w.CenterOnParent();
w.ShowModal();
// Tell the calling thread it can wake up now
payload.second->Post();
}
void BackgroundScriptRunner::OnDialog(wxThreadEvent &evt)
{
using namespace std;
tuple<wxDialog*, wxSemaphore*, int*> payload = evt.GetPayload<tuple<wxDialog*, wxSemaphore*, int*> >();
*get<2>(payload) = get<0>(payload)->ShowModal();
get<1>(payload)->Post();
}
void BackgroundScriptRunner::QueueEvent(wxEvent *evt)
{
wxQueueEvent(impl.get(), evt);
}
// Convert a function taking an Automation4::ProgressSink to one taking an
// agi::ProgressSink so that we can pass it to an agi::BackgroundWorker
static void progress_sink_wrapper(std::function<void (ProgressSink*)> task, agi::ProgressSink *ps, BackgroundScriptRunner *bsr)
{
ProgressSink aps(ps, bsr);
task(&aps);
}
void BackgroundScriptRunner::Run(std::function<void (ProgressSink*)> task) void BackgroundScriptRunner::Run(std::function<void (ProgressSink*)> task)
{ {
int prio = OPT_GET("Automation/Thread Priority")->GetInt(); int prio = OPT_GET("Automation/Thread Priority")->GetInt();
@ -311,7 +269,10 @@ namespace Automation4 {
else if (prio == 2) prio = 10; // lowest else if (prio == 2) prio = 10; // lowest
else prio = 50; // fallback normal else prio = 50; // fallback normal
impl->Run(bind(progress_sink_wrapper, task, std::placeholders::_1, this), prio); impl->Run([&](agi::ProgressSink *ps) {
ProgressSink aps(ps, this);
task(&aps);
}, prio);
} }
wxWindow *BackgroundScriptRunner::GetParentWindow() const wxWindow *BackgroundScriptRunner::GetParentWindow() const
@ -319,6 +280,11 @@ namespace Automation4 {
return impl.get(); return impl.get();
} }
wxString BackgroundScriptRunner::GetTitle() const
{
return impl->GetTitle();
}
// Script // Script
Script::Script(wxString const& filename) Script::Script(wxString const& filename)
: filename(filename) : filename(filename)

View file

@ -124,11 +124,9 @@ namespace Automation4 {
class BackgroundScriptRunner { class BackgroundScriptRunner {
agi::scoped_ptr<DialogProgress> impl; agi::scoped_ptr<DialogProgress> impl;
void OnDialog(wxThreadEvent &evt);
void OnScriptDialog(wxThreadEvent &evt);
public: public:
void QueueEvent(wxEvent *evt);
wxWindow *GetParentWindow() const; wxWindow *GetParentWindow() const;
wxString GetTitle() const;
void Run(std::function<void(ProgressSink*)> task); void Run(std::function<void(ProgressSink*)> task);

View file

@ -144,6 +144,10 @@ bool AegisubApp::OnInit() {
SetAppName("aegisub"); SetAppName("aegisub");
#endif #endif
Bind(EVT_CALL_THUNK, [](wxThreadEvent &evt) {
evt.GetPayload<std::function<void()>>()();
});
// logging. // logging.
agi::log::log = new agi::log::LogSink; agi::log::log = new agi::log::LogSink;

View file

@ -58,6 +58,8 @@
#include "compat.h" #include "compat.h"
#include "main.h" #include "main.h"
wxDEFINE_EVENT(EVT_CALL_THUNK, wxThreadEvent);
wxString MakeRelativePath(wxString _path, wxString reference) { wxString MakeRelativePath(wxString _path, wxString reference) {
if (_path.empty() || _path[0] == '?') return _path; if (_path.empty() || _path[0] == '?') return _path;
wxFileName path(_path); wxFileName path(_path);

View file

@ -39,9 +39,11 @@
#include <cstdint> #include <cstdint>
#include <algorithm> #include <algorithm>
#include <functional>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <wx/app.h>
#include <wx/icon.h> #include <wx/icon.h>
#include <wx/thread.h> #include <wx/thread.h>
#endif #endif
@ -182,3 +184,19 @@ struct cast {
return dynamic_cast<Out>(&in); return dynamic_cast<Out>(&in);
} }
}; };
wxDECLARE_EVENT(EVT_CALL_THUNK, wxThreadEvent);
template<typename Function>
void InvokeOnMainThreadAsync(Function const& f) {
wxThreadEvent *evt = new wxThreadEvent(EVT_CALL_THUNK);
evt->SetPayload<std::function<void()>>(f);
wxTheApp->QueueEvent(evt);
}
template<typename Function>
void InvokeOnMainThread(Function const& f) {
wxSemaphore sema(0, 1);
InvokeOnMainThreadAsync([&] { f(); sema.Post(); });
sema.Wait();
}