42using namespace std::string_literals;
58namespace Experimental {
113 std::list<std::shared_ptr<WebCommand>>
fCmds;
135 void ProcessData(
unsigned connid,
const std::string &arg);
164 void NewDisplay(const std::
string &where) final;
172 void Run(
double tm = 0.) final;
187 return std::make_unique<RCanvasPainter>(canv);
195 R__LOG_ERROR(CanvasPainerLog()) <<
"Generator is already set! Skipping second initialization.";
218RCanvasPainter::RCanvasPainter(
RCanvas &canv) : fCanvas(canv)
242 item.fCallback(
false);
252 std::list<std::shared_ptr<WebCommand>> remainingCmds;
254 for (
auto &&cmd :
fCmds) {
255 if (!connid || (cmd->fConnId == connid)) {
256 cmd->CallBack(
false);
259 remainingCmds.emplace_back(std::move(cmd));
263 std::swap(
fCmds, remainingCmds);
271 uint64_t min_delivered = 0;
272 bool is_any_send =
true;
275 while (is_any_send && (++loopcnt < 10)) {
281 if (conn.fDelivered && (!min_delivered || (min_delivered < conn.fDelivered)))
282 min_delivered = conn.fDelivered;
285 bool need_send_snapshot = (conn.fSend !=
fCanvas.
GetModified()) && (conn.fDelivered == conn.fSend);
288 if (need_send_snapshot && (loopcnt == 0))
289 if (std::find(conn.fSendQueue.begin(), conn.fSendQueue.end(),
""s) == conn.fSendQueue.end())
290 conn.fSendQueue.emplace_back(
""s);
293 if (!
fWindow->CanSend(conn.fConnId,
true))
299 ((
fCmds.front()->fConnId == 0) || (
fCmds.front()->fConnId == conn.fConnId))) {
301 auto &cmd =
fCmds.front();
303 cmd->fConnId = conn.fConnId;
309 }
else if (!conn.fSendQueue.empty()) {
311 buf = conn.fSendQueue.front().c_str();
312 conn.fSendQueue.pop_front();
315 if (!need_send_snapshot && (buf.
Length() == 0) && !conn.fSendQueue.empty()) {
316 buf = conn.fSendQueue.front().c_str();
317 conn.fSendQueue.pop_front();
321 if ((buf.
Length() == 0) && need_send_snapshot) {
351 if (item.fVersion > fSnapshotDelivered)
388 fWindow->WaitForTimed([
this, ver](
double) {
417 if (arg ==
"AddPanel") {
419 connid =
fWindow->GetConnectionId();
431 auto cmd = std::make_shared<WebCommand>(std::to_string(++
fCmdsCnt),
name, arg, callback, connid);
432 fCmds.emplace_back(cmd);
438 int res =
fWindow->WaitForTimed([
this, cmd](
double) {
440 R__LOG_DEBUG(0, CanvasPainerLog()) <<
"Command " << cmd->fName <<
" done";
441 return cmd->fResult ? 1 : -1;
445 if (!
fWindow->HasConnection(cmd->fConnId,
false))
455 R__LOG_ERROR(CanvasPainerLog()) <<
name <<
" fail with " << arg <<
" result = " << res;
464 auto len = fname.length();
465 bool is_json = (
len > 4) && ((fname.compare(
len-4,4,
".json") == 0) || (fname.compare(
len-4,4,
".JSON") == 0));
477 std::ofstream
f(fname);
479 R__LOG_ERROR(CanvasPainerLog()) <<
"Fail to open file " << fname <<
" to store canvas snapshot";
482 R__LOG_INFO(CanvasPainerLog()) <<
"Store canvas in " << fname;
514 auto check_header = [&arg, &cdata](
const std::string &header) {
515 if (arg.compare(0, header.length(), header) != 0)
517 cdata = arg.substr(header.length());
524 if (check_header(
"READY")) {
526 }
else if (check_header(
"SNAPDONE:")) {
527 conn->fDelivered = (uint64_t)std::stoll(cdata);
528 }
else if (arg ==
"QUIT") {
532 }
else if (arg ==
"START_BROWSER") {
533 gROOT->ProcessLine(
"auto br = std::make_shared<ROOT::RBrowser>();br->ClearOnClose(br);");
535 }
else if (arg ==
"RELOAD") {
537 }
else if (arg ==
"INTERRUPT") {
538 gROOT->SetInterrupt();
539 }
else if (check_header(
"REPLY:")) {
540 const char *sid = cdata.c_str();
541 const char *separ = strchr(sid,
':');
544 id.append(sid, separ - sid);
546 R__LOG_ERROR(CanvasPainerLog()) <<
"Get REPLY without command";
548 R__LOG_ERROR(CanvasPainerLog()) <<
"Front command is not running when get reply";
549 }
else if (
fCmds.front()->fId !=
id) {
550 R__LOG_ERROR(CanvasPainerLog()) <<
"Mismatch with front command and ID in REPLY";
554 }
else if (check_header(
"SAVE:")) {
556 }
else if (check_header(
"PRODUCE:")) {
557 R__LOG_DEBUG(0, CanvasPainerLog()) <<
"Create file " << cdata;
566 }
else if (check_header(
"REQ:")) {
567 auto req = TBufferJSON::FromJSON<RDrawableRequest>(cdata);
569 std::shared_ptr<RDrawable> drawable;
570 req->GetContext().SetCanvas(&
fCanvas);
571 if (req->GetId().empty() || (req->GetId() ==
"canvas")) {
572 req->GetContext().SetPad(
nullptr);
573 req->GetContext().SetDrawable(&
fCanvas, 0);
577 req->GetContext().SetPad(
const_cast<RPadBase *
>(subpad));
578 req->GetContext().SetDrawable(drawable.get(), 0);
581 req->GetContext().SetConnection(connid, conn ==
fWebConn.begin());
583 auto reply = req->Process();
585 if (req->ShouldBeReplyed()) {
587 reply = std::make_unique<RDrawableReply>();
589 reply->SetRequestId(req->GetRequestId());
592 conn->fSendQueue.emplace_back(
"REPL_REQ:"s +
json.Data());
596 if (req->NeedCanvasUpdate())
600 R__LOG_ERROR(CanvasPainerLog()) <<
"Fail to parse RDrawableRequest";
602 }
else if (check_header(
"RESIZED:")) {
603 auto sz = TBufferJSON::FromJSON<std::vector<int>>(cdata);
604 if (sz && sz->size() == 2) {
608 }
else if (check_header(
"CLEAR")) {
611 }
else if (check_header(
"SHOWURL:")) {
617 R__LOG_ERROR(CanvasPainerLog()) <<
"Got not recognized message" << arg;
632 fWindow->SetDefaultPage(
"file:rootui5sys/canv/canvas.html");
635 [
this](
unsigned connid) {
640 [
this](
unsigned connid,
const std::string &arg) {
ProcessData(connid, arg); },
642 [
this](
unsigned connid) {
685 return fWindow->NumConnections();
705 return fWindow->GetUrl(remote);
714 if (
gROOT->IsWebDisplayBatch())
718 R__LOG_ERROR(CanvasPainerLog()) <<
"Canvas not yet shown in AddPanel";
723 R__LOG_ERROR(CanvasPainerLog()) <<
"Canvas window was not shown to call AddPanel";
727 if (
win->GetManager() !=
fWindow->GetManager()) {
728 R__LOG_ERROR(CanvasPainerLog()) <<
"Cannot embed window from other windows manager";
733 std::string addr =
win->GetUrl(
false);
736 R__LOG_ERROR(CanvasPainerLog()) <<
"Cannot attach panel to canvas";
743 std::string cmd(
"ADDPANEL:");
759 fWindow->SetClearOnClose(handle);
769 auto canvitem = std::make_unique<RCanvasDisplayItem>();
776 canvitem->BuildFullId(
"");
777 canvitem->SetObjectID(
"canvas");
782 static std::vector<const TClass *> exclude_classes = {
783 TClass::GetClass<RAttrMap::NoValue_t>(),
784 TClass::GetClass<RAttrMap::BoolValue_t>(),
785 TClass::GetClass<RAttrMap::IntValue_t>(),
786 TClass::GetClass<RAttrMap::DoubleValue_t>(),
787 TClass::GetClass<RAttrMap::StringValue_t>(),
788 TClass::GetClass<RAttrMap>(),
789 TClass::GetClass<RStyle::Block_t>(),
790 TClass::GetClass<RPadPos>(),
791 TClass::GetClass<RPadLength>(),
792 TClass::GetClass<RPadExtent>(),
793 TClass::GetClass<std::unordered_map<std::string,RAttrMap::Value_t*>>()
796 for (
auto cl : exclude_classes)
797 json.SetSkipClassInfo(cl);
799 auto res =
json.StoreObject(canvitem.get(), TClass::GetClass<RCanvasDisplayItem>());
801 return std::string(res.Data());
808std::shared_ptr<RDrawable>
811 std::string search =
id;
812 size_t pos = search.find(
"#");
814 if (pos != std::string::npos)
828 size_t pos = reply.find(
":");
829 if ((pos == std::string::npos) || (pos == 0)) {
830 R__LOG_ERROR(CanvasPainerLog()) <<
"SaveCreatedFile does not found ':' separator";
834 std::string fname(reply, 0, pos);
835 reply.erase(0, pos + 1);
837 Bool_t isSvg = (fname.length() > 4) && ((fname.rfind(
".svg") == fname.length()-4) || (fname.rfind(
".SVG") == fname.length()-4));
841 std::ofstream ofs(fname, std::ios::binary);
844 file_len = reply.length();
848 file_len = binary.
Length();
852 R__LOG_INFO(CanvasPainerLog()) <<
" Save file from GUI " << fname <<
" len " << file_len;
860 auto cmd =
fCmds.front();
867 if ((cmd->fName ==
"SVG") || (cmd->fName ==
"PNG") || (cmd->fName ==
"JPEG")) {
868 if (reply.length() == 0) {
869 R__LOG_ERROR(CanvasPainerLog()) <<
"Fail to produce image" << cmd->fArg;
872 std::ofstream ofs(cmd->fArg, std::ios::binary);
875 R__LOG_INFO(CanvasPainerLog()) << cmd->fName <<
" create file " << cmd->fArg <<
" length " << content.
Length();
878 }
else if (cmd->fName.find(
"ADDPANEL:") == 0) {
879 R__LOG_DEBUG(0, CanvasPainerLog()) <<
"get reply for ADDPANEL " << reply;
880 result = (reply ==
"true");
882 R__LOG_ERROR(CanvasPainerLog()) <<
"Unknown command " << cmd->fName;
898 std::this_thread::sleep_for(std::chrono::milliseconds(
int(tm*1000)));
struct TNewCanvasPainterReg newCanvasPainterReg
#define R__LOG_ERROR(...)
#define R__LOG_DEBUG(DEBUGLEVEL,...)
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize id
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t UChar_t len
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t win
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t height
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
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...
void CancelUpdates()
Cancel all pending Canvas::Update()
std::string GetWindowUrl(bool remote) final
Returns connection URL for web window.
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::shared_ptr< ROOT::RWebWindow > fWindow
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...
std::vector< std::unique_ptr< ROOT::RWebDisplayHandle > > fHelpHandles
! array of handles for help widgets
int NumDisplays() const final
Returns number of connected displays.
bool AddPanel(std::shared_ptr< ROOT::RWebWindow >) final
Add window as panel inside canvas window.
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 SetClearOnClose(const std::shared_ptr< void > &) final
Set handle to window which will be cleared when connection is closed.
void NewDisplay(const std::string &where) final
Create new display for the canvas See ROOT::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.
void SetHeight(int height)
Set canvas height.
void SetWidth(int width)
Set canvas width.
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.
void Wipe()
Wipe the pad by clearing the list of primitives.
Holds different arguments for starting browser with RWebDisplayHandle::Display() method.
void SetStandalone(bool on=true)
Set standalone mode for running browser, default on When disabled, normal browser window (or just tab...
RWebDisplayArgs & SetWidgetKind(const std::string &kind)
set widget kind
RWebDisplayArgs & SetUrl(const std::string &url)
set window url
RWebDisplayArgs & SetWidth(int w=0)
set preferable web window width
RWebDisplayArgs & SetHeight(int h=0)
set preferable web window height
static bool ProduceImage(const std::string &fname, const std::string &json, int width=800, int height=600, const char *batch_file=nullptr)
Produce image file using JSON data as source Invokes JSROOT drawing functionality in headless browser...
static bool CanProduceImages(const std::string &browser="")
Returns true if image production for specified browser kind is supported If browser not specified - u...
static std::unique_ptr< RWebDisplayHandle > Display(const RWebDisplayArgs &args)
Create web display.
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 bool EmbedFileDialog(const std::shared_ptr< RWebWindow > &window, unsigned connid, const std::string &args)
Create dialog instance to use as embedded dialog inside provided widget Loads libROOTBrowserv7 and tr...
static bool IsFileDialogMessage(const std::string &msg)
Check if this could be the message send by client to start new file dialog If returns true,...
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
void SetCompact(int level)
Set level of space/newline/array compression Lower digit of compact parameter define formatting rules...
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
A ROOT file is an on-disk file, usually with extension .root, that stores objects in a file-system-li...
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)