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
wxDEFINE_EVENT(EVT_SHOW_DIALOG, wxThreadEvent);
wxDEFINE_EVENT(EVT_SHOW_SCRIPT_DIALOG, wxThreadEvent);
ProgressSink::ProgressSink(agi::ProgressSink *impl, BackgroundScriptRunner *bsr)
: impl(impl)
, bsr(bsr)
@ -235,74 +232,35 @@ namespace Automation4 {
void ProgressSink::ShowDialog(ScriptDialog *config_dialog)
{
wxSemaphore sema(0, 1);
wxThreadEvent *evt = new wxThreadEvent(EVT_SHOW_SCRIPT_DIALOG);
evt->SetPayload(std::make_pair(config_dialog, &sema));
bsr->QueueEvent(evt);
sema.Wait();
InvokeOnMainThread([=] {
wxDialog w; // container dialog box
w.SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
w.Create(bsr->GetParentWindow(), -1, bsr->GetTitle());
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 ret = 0;
wxSemaphore sema(0, 1);
wxThreadEvent *evt = new wxThreadEvent(EVT_SHOW_DIALOG);
evt->SetPayload(std::make_tuple(dialog, &sema, &ret));
bsr->QueueEvent(evt);
sema.Wait();
InvokeOnMainThread([&] { ret = dialog->ShowModal(); });
return ret;
}
BackgroundScriptRunner::BackgroundScriptRunner(wxWindow *parent, wxString const& 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()
{
}
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)
{
int prio = OPT_GET("Automation/Thread Priority")->GetInt();
@ -311,7 +269,10 @@ namespace Automation4 {
else if (prio == 2) prio = 10; // lowest
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
@ -319,6 +280,11 @@ namespace Automation4 {
return impl.get();
}
wxString BackgroundScriptRunner::GetTitle() const
{
return impl->GetTitle();
}
// Script
Script::Script(wxString const& filename)
: filename(filename)

View file

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

View file

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

View file

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

View file

@ -39,9 +39,11 @@
#include <cstdint>
#include <algorithm>
#include <functional>
#include <utility>
#include <vector>
#include <wx/app.h>
#include <wx/icon.h>
#include <wx/thread.h>
#endif
@ -182,3 +184,19 @@ struct cast {
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();
}