42using namespace std::string_literals;
58namespace Experimental {
113 std::list<std::shared_ptr<WebCommand>>
fCmds;
133 void ProcessData(
unsigned connid,
const std::string &arg);
162 void NewDisplay(const std::
string &where) final;
168 void Run(
double tm = 0.) final;
181 return std::make_unique<RCanvasPainter>(canv);
189 R__LOG_ERROR(CanvasPainerLog()) <<
"Generator is already set! Skipping second initialization.";
212RCanvasPainter::RCanvasPainter(
RCanvas &canv) : fCanvas(canv)
236 item.fCallback(
false);
246 std::list<std::shared_ptr<WebCommand>> remainingCmds;
248 for (
auto &&cmd :
fCmds) {
249 if (!connid || (cmd->fConnId == connid)) {
250 cmd->CallBack(
false);
253 remainingCmds.emplace_back(std::move(cmd));
257 std::swap(
fCmds, remainingCmds);
265 uint64_t min_delivered = 0;
266 bool is_any_send =
true;
269 while (is_any_send && (++loopcnt < 10)) {
275 if (conn.fDelivered && (!min_delivered || (min_delivered < conn.fDelivered)))
276 min_delivered = conn.fDelivered;
279 bool need_send_snapshot = (conn.fSend !=
fCanvas.
GetModified()) && (conn.fDelivered == conn.fSend);
282 if (need_send_snapshot && (loopcnt == 0))
283 if (std::find(conn.fSendQueue.begin(), conn.fSendQueue.end(),
""s) == conn.fSendQueue.end())
284 conn.fSendQueue.emplace_back(
""s);
287 if (!
fWindow->CanSend(conn.fConnId,
true))
293 ((
fCmds.front()->fConnId == 0) || (
fCmds.front()->fConnId == conn.fConnId))) {
295 auto &cmd =
fCmds.front();
297 cmd->fConnId = conn.fConnId;
303 }
else if (!conn.fSendQueue.empty()) {
305 buf = conn.fSendQueue.front().c_str();
306 conn.fSendQueue.pop_front();
309 if (!need_send_snapshot && (buf.
Length() == 0) && !conn.fSendQueue.empty()) {
310 buf = conn.fSendQueue.front().c_str();
311 conn.fSendQueue.pop_front();
315 if ((buf.
Length() == 0) && need_send_snapshot) {
345 if (item.fVersion > fSnapshotDelivered)
382 fWindow->WaitForTimed([
this, ver](
double) {
411 if (arg ==
"AddPanel") {
413 connid =
fWindow->GetConnectionId();
425 auto cmd = std::make_shared<WebCommand>(std::to_string(++
fCmdsCnt),
name, arg, callback, connid);
426 fCmds.emplace_back(cmd);
432 int res =
fWindow->WaitForTimed([
this, cmd](
double) {
434 R__LOG_DEBUG(0, CanvasPainerLog()) <<
"Command " << cmd->fName <<
" done";
435 return cmd->fResult ? 1 : -1;
439 if (!
fWindow->HasConnection(cmd->fConnId,
false))
449 R__LOG_ERROR(CanvasPainerLog()) <<
name <<
" fail with " << arg <<
" result = " << res;
463 auto len = fname.length();
464 if ((len > 4) && ((fname.compare(len-4,4,
".json") == 0) || (fname.compare(len-4,4,
".JSON") == 0))) {
465 std::ofstream
f(fname);
467 R__LOG_ERROR(CanvasPainerLog()) <<
"Fail to open file " << fname <<
" to store canvas snapshot";
470 R__LOG_INFO(CanvasPainerLog()) <<
"Store canvas in " << fname;
502 auto check_header = [&arg, &cdata](
const std::string &header) {
503 if (arg.compare(0, header.length(), header) != 0)
505 cdata = arg.substr(header.length());
512 if (check_header(
"READY")) {
514 }
else if (check_header(
"SNAPDONE:")) {
515 conn->fDelivered = (uint64_t)std::stoll(cdata);
516 }
else if (arg ==
"QUIT") {
520 }
else if (arg ==
"RELOAD") {
522 }
else if (arg ==
"INTERRUPT") {
523 gROOT->SetInterrupt();
524 }
else if (check_header(
"REPLY:")) {
525 const char *sid = cdata.c_str();
526 const char *separ = strchr(sid,
':');
529 id.append(sid, separ - sid);
531 R__LOG_ERROR(CanvasPainerLog()) <<
"Get REPLY without command";
533 R__LOG_ERROR(CanvasPainerLog()) <<
"Front command is not running when get reply";
534 }
else if (
fCmds.front()->fId !=
id) {
535 R__LOG_ERROR(CanvasPainerLog()) <<
"Mismatch with front command and ID in REPLY";
539 }
else if (check_header(
"SAVE:")) {
541 }
else if (check_header(
"PRODUCE:")) {
542 R__LOG_DEBUG(0, CanvasPainerLog()) <<
"Create file " << cdata;
547 }
else if (check_header(
"REQ:")) {
548 auto req = TBufferJSON::FromJSON<RDrawableRequest>(cdata);
550 std::shared_ptr<RDrawable> drawable;
551 req->GetContext().SetCanvas(&
fCanvas);
552 if (req->GetId().empty() || (req->GetId() ==
"canvas")) {
553 req->GetContext().SetPad(
nullptr);
554 req->GetContext().SetDrawable(&
fCanvas, 0);
558 req->GetContext().SetPad(
const_cast<RPadBase *
>(subpad));
559 req->GetContext().SetDrawable(drawable.get(), 0);
562 req->GetContext().SetConnection(connid, conn ==
fWebConn.begin());
564 auto reply = req->Process();
566 if (req->ShouldBeReplyed()) {
568 reply = std::make_unique<RDrawableReply>();
570 reply->SetRequestId(req->GetRequestId());
573 conn->fSendQueue.emplace_back(
"REPL_REQ:"s + json.Data());
577 if (req->NeedCanvasUpdate())
581 R__LOG_ERROR(CanvasPainerLog()) <<
"Fail to parse RDrawableRequest";
584 R__LOG_ERROR(CanvasPainerLog()) <<
"Got not recognized message" << arg;
599 fWindow->SetDefaultPage(
"file:rootui5sys/canv/canvas.html");
602 [
this](
unsigned connid) {
607 [
this](
unsigned connid,
const std::string &arg) {
ProcessData(connid, arg); },
609 [
this](
unsigned connid) {
634 if ((
width > 10) && (height > 10)) {
652 return fWindow->NumConnections();
670 if (
gROOT->IsWebDisplayBatch())
674 R__LOG_ERROR(CanvasPainerLog()) <<
"Canvas not yet shown in AddPanel";
679 R__LOG_ERROR(CanvasPainerLog()) <<
"Canvas window was not shown to call AddPanel";
683 std::string addr =
fWindow->GetRelativeAddr(win);
685 if (addr.length() == 0) {
686 R__LOG_ERROR(CanvasPainerLog()) <<
"Cannot attach panel to canvas";
693 std::string cmd(
"ADDPANEL:");
709 auto canvitem = std::make_unique<RCanvasDisplayItem>();
716 canvitem->BuildFullId(
"");
717 canvitem->SetObjectID(
"canvas");
722 static std::vector<const TClass *> exclude_classes = {
723 TClass::GetClass<RAttrMap::NoValue_t>(),
724 TClass::GetClass<RAttrMap::BoolValue_t>(),
725 TClass::GetClass<RAttrMap::IntValue_t>(),
726 TClass::GetClass<RAttrMap::DoubleValue_t>(),
727 TClass::GetClass<RAttrMap::StringValue_t>(),
728 TClass::GetClass<RAttrMap>(),
729 TClass::GetClass<RStyle::Block_t>(),
730 TClass::GetClass<RPadPos>(),
731 TClass::GetClass<RPadLength>(),
732 TClass::GetClass<RPadExtent>(),
733 TClass::GetClass<std::unordered_map<std::string,RAttrMap::Value_t*>>()
736 for (
auto cl : exclude_classes)
737 json.SetSkipClassInfo(cl);
739 auto res = json.StoreObject(canvitem.get(), TClass::GetClass<RCanvasDisplayItem>());
741 return std::string(res.Data());
748std::shared_ptr<RDrawable>
751 std::string search =
id;
752 size_t pos = search.find(
"#");
754 if (pos != std::string::npos)
768 size_t pos = reply.find(
":");
769 if ((pos == std::string::npos) || (pos == 0)) {
770 R__LOG_ERROR(CanvasPainerLog()) <<
"SaveCreatedFile does not found ':' separator";
774 std::string fname(reply, 0, pos);
775 reply.erase(0, pos + 1);
777 Bool_t isSvg = (fname.length() > 4) && ((fname.rfind(
".svg") == fname.length()-4) || (fname.rfind(
".SVG") == fname.length()-4));
781 std::ofstream ofs(fname, std::ios::binary);
784 file_len = reply.length();
788 file_len = binary.
Length();
792 R__LOG_INFO(CanvasPainerLog()) <<
" Save file from GUI " << fname <<
" len " << file_len;
800 auto cmd =
fCmds.front();
807 if ((cmd->fName ==
"SVG") || (cmd->fName ==
"PNG") || (cmd->fName ==
"JPEG")) {
808 if (reply.length() == 0) {
809 R__LOG_ERROR(CanvasPainerLog()) <<
"Fail to produce image" << cmd->fArg;
812 std::ofstream ofs(cmd->fArg, std::ios::binary);
815 R__LOG_INFO(CanvasPainerLog()) << cmd->fName <<
" create file " << cmd->fArg <<
" length " << content.
Length();
818 }
else if (cmd->fName.find(
"ADDPANEL:") == 0) {
819 R__LOG_DEBUG(0, CanvasPainerLog()) <<
"get reply for ADDPANEL " << reply;
820 result = (reply ==
"true");
822 R__LOG_ERROR(CanvasPainerLog()) <<
"Unknown command " << cmd->fName;
825 cmd->fResult = result;
826 cmd->CallBack(result);
838 std::this_thread::sleep_for(std::chrono::milliseconds(
int(tm*1000)));
struct TNewCanvasPainterReg newCanvasPainterReg
#define R__LOG_ERROR(...)
#define R__LOG_DEBUG(DEBUGLEVEL,...)
include TDocParser_001 C image html pict1_TDocParser_001 png width
Abstract interface for painting a canvas.
static std::unique_ptr< Generator > & GetGenerator()
generator getter
static void ResetGlobalPainter()
Release the GeneratorImpl object.
std::unique_ptr< RVirtualCanvasPainter > Create(RCanvas &canv) const override
Create a new RCanvasPainter to paint the given RCanvas.
static void SetGlobalPainter()
Set RVirtualCanvasPainter::fgGenerator to a new GeneratorImpl object.
std::list< WebConn > fWebConn
!< configured display
std::list< std::shared_ptr< WebCommand > > fCmds
! list of submitted commands
uint64_t fCmdsCnt
! commands counter
uint64_t fSnapshotDelivered
! minimal version delivered to all connections
bool AddPanel(std::shared_ptr< RWebWindow >) final
Add window as panel inside canvas window.
std::list< WebUpdate > fUpdatesLst
! list of callbacks for canvas update
void CancelCommands(unsigned connid=0)
Cancel command execution on provided connection All commands are cancelled, when connid === 0.
void SaveCreatedFile(std::string &reply)
Method called when GUI sends file to save on local disk File data coded with base64 coding beside SVG...
std::shared_ptr< RWebWindow > fWindow
void CancelUpdates()
Cancel all pending Canvas::Update()
bool ProduceBatchOutput(const std::string &fname, int width, int height) final
Produce batch output, using chrome headless mode with DOM dump.
std::shared_ptr< RDrawable > FindPrimitive(const RCanvas &can, const std::string &id, const RPadBase **subpad=nullptr)
Find drawable in the canvas with specified id Used to communicate with the clients,...
void DoWhenReady(const std::string &name, const std::string &arg, bool async, CanvasCallback_t callback) final
perform special action when drawing is ready
std::vector< Detail::RMenuItem > MenuItemsVector
std::string GetWindowAddr() const final
Returns web window name.
void Run(double tm=0.) final
Run canvas functionality for specified period of time Required when canvas used not from the main thr...
int NumDisplays() const final
Returns number of connected displays.
void FrontCommandReplied(const std::string &reply)
Process reply on the currently active command.
void ProcessData(unsigned connid, const std::string &arg)
Process data from the client.
int fJsonComp
! json compression for data send to client
void CanvasUpdated(uint64_t ver, bool async, CanvasCallback_t callback) final
Method invoked when canvas should be updated on the client side Depending from delivered status,...
void CheckDataToSend()
Check if canvas need to send data to the clients.
virtual ~RCanvasPainter()
Destructor.
void CreateWindow()
Create web window for canvas.
std::string ProduceJSON() final
Produce JSON for the canvas.
bool IsCanvasModified(uint64_t id) const final
return true if canvas modified since last painting
RCanvasPainter & operator=(const RCanvasPainter &)=delete
Disable assignment.
std::string CreateSnapshot(RDrawable::RDisplayContext &ctxt)
Create JSON representation of data, which should be send to the clients Here server-side painting is ...
RCanvasPainter(const RCanvasPainter &)=delete
Disable copy construction.
void NewDisplay(const std::string &where) final
Create new display for the canvas See RWebWindowsManager::Show() docu for more info.
RCanvas & fCanvas
! Canvas we are painting, *this will be owned by canvas
const std::string & GetTitle() const
Get the canvas's title.
int GetHeight() const
Get canvas height.
uint64_t GetModified() const
Get modify counter.
int GetWidth() const
Get canvas width.
void SetConnection(unsigned connid, bool ismain)
Set connection id and ismain flag for connection.
const std::string & GetId() const
A log configuration for a channel, e.g.
Base class for graphic containers for RDrawable-s.
void DisplayPrimitives(RPadBaseDisplayItem &paditem, RDisplayContext &ctxt)
Create display items for all primitives in the pad Each display item gets its special id,...
std::shared_ptr< RDrawable > FindPrimitiveByDisplayId(const std::string &display_id) const
Find primitive with unique id, produce for RDisplayItem Such id used for client-server identification...
const RPadBase * FindPadForPrimitiveWithDisplayId(const std::string &display_id) const
Find subpad which contains primitive with given display id.
Holds different arguments for starting browser with RWebDisplayHandle::Display() method.
RWebDisplayArgs & SetHeight(int h=0)
set preferable web window height
RWebDisplayArgs & SetWidgetKind(const std::string &kind)
set widget kind
RWebDisplayArgs & SetWidth(int w=0)
set preferable web window width
static bool ProduceImage(const std::string &fname, const std::string &json, int width=800, int height=600)
Produce image file using JSON data as source Invokes JSROOT drawing functionality in headless browser...
Represents web window, which can be shown in web browser or any other supported environment.
static std::shared_ptr< RWebWindow > Create()
Create new RWebWindow Using default RWebWindowsManager.
static TString Decode(const char *data)
Decode a base64 string date into a generic TString.
Class for serializing object to and from JavaScript Object Notation (JSON) format.
static TString ToJSON(const T *obj, Int_t compact=0, const char *member_name=nullptr)
@ kNoSpaces
no new lines plus remove all spaces around "," and ":" symbols
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
A ROOT file is a suite of consecutive data records (TKey instances) with a well defined format.
static TFile * Open(const char *name, Option_t *option="", const char *ftitle="", Int_t compress=ROOT::RCompressionSetting::EDefaults::kUseCompiledDefault, Int_t netopt=0)
Create / open a file.
const char * Data() const
static TString ULLtoa(ULong64_t value, Int_t base)
Converts a ULong64_t (twice the range of an Long64_t) to a TString with respect to the base specified...
TString & Append(const char *cs)
std::function< void(bool)> CanvasCallback_t
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
unsigned fConnId
! connection id for the command, when 0 specified command will be submitted to any available connecti...
std::string fArg
! command arguments
bool fResult
! result of command execution
std::string fName
! command name
CanvasCallback_t fCallback
! callback function associated with command
WebCommand(const std::string &id, const std::string &name, const std::string &arg, CanvasCallback_t callback, unsigned connid)
enum ROOT::Experimental::RCanvasPainter::WebCommand::@64 sInit
! true when command submitted
std::string fId
! command identifier
unsigned fConnId
! connection id
std::list< std::string > fSendQueue
! send queue for the connection
RDrawable::Version_t fDelivered
! indicates version confirmed from canvas
RDrawable::Version_t fSend
! indicates version send to connection
uint64_t fVersion
! canvas version
CanvasCallback_t fCallback
! callback function associated with the update
WebUpdate(uint64_t ver, CanvasCallback_t callback)