Skip to content
Snippets Groups Projects
Commit 74c05a78 authored by Sergey Linev's avatar Sergey Linev Committed by Bertrand Bellenot
Browse files

webgui: let specify callback in Canvas::Update() call

Invoked when current canvas version delivered to all clients.
parent 4d7e2058
No related branches found
No related tags found
No related merge requests found
......@@ -136,7 +136,7 @@ public:
bool IsModified() const;
/// update drawing
void Update(bool async = false);
void Update(bool async = false, CanvasCallback_t callback = nullptr);
/// Save canvas in image file
void SaveAs(const std::string &filename, bool async = false, CanvasCallback_t callback = nullptr);
......
......@@ -57,7 +57,7 @@ public:
virtual void AddDisplayItem(TDisplayItem *item) = 0;
/// indicate that canvas changed, provides current version of the canvas
virtual void CanvasUpdated(uint64_t, bool) = 0;
virtual void CanvasUpdated(uint64_t, bool, CanvasCallback_t) = 0;
/// return true if canvas modified since last painting
virtual bool IsCanvasModified(uint64_t) const = 0;
......
......@@ -48,10 +48,10 @@ bool ROOT::Experimental::TCanvas::IsModified() const
return fPainter ? fPainter->IsCanvasModified(fModified) : fModified;
}
void ROOT::Experimental::TCanvas::Update(bool async)
void ROOT::Experimental::TCanvas::Update(bool async, CanvasCallback_t callback)
{
if (fPainter)
fPainter->CanvasUpdated(fModified, async);
fPainter->CanvasUpdated(fModified, async, callback);
// SnapshotList_t lst;
// for (auto&& drw: fPrimitives) {
......@@ -97,7 +97,7 @@ void ROOT::Experimental::TCanvas::Show(const std::string &where)
fPainter = Internal::TVirtualCanvasPainter::Create(*this, batch_mode);
if (fPainter) {
fPainter->NewDisplay(where);
fPainter->CanvasUpdated(fModified, true); // trigger async display
fPainter->CanvasUpdated(fModified, true, nullptr); // trigger async display
}
}
......
......@@ -173,18 +173,26 @@ private:
};
struct WebCommand {
std::string fId; ///<! command identifier
std::string fName; ///<! command name
std::string fArg; ///<! command arg
bool fRunning; ///<! true when command submitted
std::string fId; ///<! command identifier
std::string fName; ///<! command name
std::string fArg; ///<! command arg
bool fRunning; ///<! true when command submitted
ROOT::Experimental::CanvasCallback_t fCallback; ///<! callback function associated with command
WebCommand() : fId(), fName(), fArg(), fRunning(false), fCallback() {}
};
struct WebUpdate {
uint64_t fVersion; ///<! canvas version
ROOT::Experimental::CanvasCallback_t fCallback; ///<! callback function associated with command
WebUpdate() : fVersion(0), fCallback() {}
};
typedef std::list<WebConn> WebConnList;
typedef std::list<WebCommand> WebCommandsList;
typedef std::list<WebUpdate> WebUpdatesList;
typedef std::vector<ROOT::Experimental::Detail::TMenuItem> MenuItemsVector;
/// The canvas we are painting. It might go out of existence while painting.
......@@ -201,6 +209,7 @@ private:
uint64_t fSnapshotVersion; ///!< version of snapshot
std::string fSnapshot; ///!< last produced snapshot
uint64_t fSnapshotDelivered; ///!< minimal version delivered to all connections
WebUpdatesList fUpdatesLst; ///!< list of callbacks for canvas update
static std::string fAddr; ///<! real http address (when assigned)
static THttpServer *gServer; ///<! server
......@@ -234,7 +243,7 @@ public:
/// The painter observes it; it needs to know should the TCanvas be deleted.
TCanvasPainter(const std::string &name, const ROOT::Experimental::TCanvas &canv, bool batch_mode)
: THttpWSHandler(name.c_str(), "title"), fCanvas(canv), fBatchMode(batch_mode), fWebConn(), fDisplayList(),
fCmds(), fCmdsCnt(0), fWaitingCmdId(), fSnapshotVersion(0), fSnapshot(), fSnapshotDelivered(0)
fCmds(), fCmdsCnt(0), fWaitingCmdId(), fSnapshotVersion(0), fSnapshot(), fSnapshotDelivered(0), fUpdatesLst()
{
CreateHttpServer();
gServer->Register("/web7gui", this);
......@@ -244,12 +253,13 @@ public:
virtual void AddDisplayItem(ROOT::Experimental::TDisplayItem *item) final;
virtual void CanvasUpdated(uint64_t, bool) override;
virtual void CanvasUpdated(uint64_t, bool, ROOT::Experimental::CanvasCallback_t) override;
virtual bool IsCanvasModified(uint64_t) const override;
/// perform special action when drawing is ready
virtual void DoWhenReady(const std::string &cmd, const std::string &arg, bool async, ROOT::Experimental::CanvasCallback_t callback) final;
virtual void DoWhenReady(const std::string &cmd, const std::string &arg, bool async,
ROOT::Experimental::CanvasCallback_t callback) final;
// open new display for the canvas
virtual void NewDisplay(const std::string &where) override;
......@@ -380,8 +390,8 @@ void TCanvasPainter::NewDisplay(const std::string &where)
TString exec;
if (!is_native && !ic_cef && !is_qt5 && (where!="browser")) {
if (where.find("$url")!=std::string::npos) {
if (!is_native && !ic_cef && !is_qt5 && (where != "browser")) {
if (where.find("$url") != std::string::npos) {
exec = where.c_str();
exec.ReplaceAll("$url", addr);
} else {
......@@ -397,13 +407,26 @@ void TCanvasPainter::NewDisplay(const std::string &where)
gSystem->Exec(exec);
}
void TCanvasPainter::CanvasUpdated(uint64_t ver, bool async)
void TCanvasPainter::CanvasUpdated(uint64_t ver, bool async, ROOT::Experimental::CanvasCallback_t callback)
{
if (ver && fSnapshotDelivered && (ver <= fSnapshotDelivered)) {
// if given canvas version was already delivered to clients, can return immediately
if (callback) callback(true);
return;
}
fSnapshotVersion = ver;
fSnapshot = CreateSnapshot(fCanvas);
CheckDataToSend();
if (callback) {
WebUpdate item;
item.fVersion = ver;
item.fCallback = callback;
fUpdatesLst.push_back(item);
}
if (!async)
WaitWhenCanvasPainted(ver);
}
......@@ -431,7 +454,8 @@ bool TCanvasPainter::WaitWhenCanvasPainted(uint64_t ver)
return false;
}
void TCanvasPainter::DoWhenReady(const std::string &name, const std::string &arg, bool async, ROOT::Experimental::CanvasCallback_t callback)
void TCanvasPainter::DoWhenReady(const std::string &name, const std::string &arg, bool async,
ROOT::Experimental::CanvasCallback_t callback)
{
if (!async && !fWaitingCmdId.empty()) {
Error("DoWhenReady", "Fail to submit sync command when previous is still awaited - use async");
......@@ -446,11 +470,13 @@ void TCanvasPainter::DoWhenReady(const std::string &name, const std::string &arg
cmd.fCallback = callback;
fCmds.push_back(cmd);
if (!async) fWaitingCmdId = cmd.fId;
if (!async)
fWaitingCmdId = cmd.fId;
CheckDataToSend();
if (async) return;
if (async)
return;
uint64_t cnt = 0;
bool had_connection = false;
......@@ -467,7 +493,6 @@ void TCanvasPainter::DoWhenReady(const std::string &name, const std::string &arg
gSystem->ProcessEvents();
gSystem->Sleep((++cnt < 500) ? 1 : 100); // increase sleep interval when do very often
}
}
/////////////////////////////////////////////////////////////////////////////////////////////
......@@ -476,12 +501,15 @@ void TCanvasPainter::DoWhenReady(const std::string &name, const std::string &arg
void TCanvasPainter::PopFrontCommand(bool result)
{
if (fCmds.size() == 0) return;
if (fCmds.size() == 0)
return;
// simple condition, which will be checked in waiting loop
if (!fWaitingCmdId.empty() && (fWaitingCmdId == fCmds.front().fId)) fWaitingCmdId.clear();
if (!fWaitingCmdId.empty() && (fWaitingCmdId == fCmds.front().fId))
fWaitingCmdId.clear();
if (fCmds.front().fCallback) fCmds.front().fCallback(result);
if (fCmds.front().fCallback)
fCmds.front().fCallback(result);
fCmds.pop_front();
}
......@@ -619,7 +647,8 @@ Bool_t TCanvasPainter::ProcessWS(THttpCallArg *arg)
const char *sid = cdata + 6;
const char *separ = strchr(sid, ':');
std::string id;
if (separ) id.append(sid, separ - sid);
if (separ)
id.append(sid, separ - sid);
if (fCmds.size() == 0) {
Error("ProcessWS", "Get REPLY without command");
} else if (!fCmds.front().fRunning) {
......@@ -627,7 +656,7 @@ Bool_t TCanvasPainter::ProcessWS(THttpCallArg *arg)
} else if (fCmds.front().fId != id) {
Error("ProcessWS", "Mismatch with front command and ID in REPLY");
} else {
bool res = FrontCommandReplied(separ+1);
bool res = FrontCommandReplied(separ + 1);
PopFrontCommand(res);
}
conn->fReady = kTRUE;
......@@ -666,7 +695,7 @@ void TCanvasPainter::CheckDataToSend()
TString buf;
if (conn.fDrawReady && (fCmds.size()>0) && !fCmds.front().fRunning) {
if (conn.fDrawReady && (fCmds.size() > 0) && !fCmds.front().fRunning) {
WebCommand &cmd = fCmds.front();
cmd.fRunning = true;
buf = "CMD:";
......@@ -710,6 +739,16 @@ void TCanvasPainter::CheckDataToSend()
if (fSnapshotDelivered != min_delivered) {
fSnapshotDelivered = min_delivered;
auto iter = fUpdatesLst.begin();
while (iter != fUpdatesLst.end()) {
auto curr = iter; iter++;
if (curr->fVersion <= fSnapshotDelivered) {
curr->fCallback(true);
fUpdatesLst.erase(curr);
}
}
// one could call-back canvas methods here
}
}
......
......@@ -40,7 +40,11 @@ void draw_v6()
// canvas->Show("/usr/bin/opera"); // one could specify program name which should show canvas
// canvas->Show("firefox"); // it could be firefox, opera, chromium; canvas can be shown several times
canvas->Update(); // synchronous, wait until painting is finished
// synchronous, wait until painting is finished
canvas->Update(false, [](bool res) { std::cout << "First Update done = " << (res ? "true" : "false") << std::endl; });
// call again, should return immediately
canvas->Update(false, [](bool res) { std::cout << "Second Update done = " << (res ? "true" : "false") << std::endl; });
// request to create PNG file in asynchronous mode and specify lambda function as callback
// when request processed by the client, callback called with result value
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment