Logo ROOT  
Reference Guide
RWebWindowsManager.cxx
Go to the documentation of this file.
1// Author: Sergey Linev <s.linev@gsi.de>
2// Date: 2017-10-16
3// Warning: This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is welcome!
4
5/*************************************************************************
6 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
7 * All rights reserved. *
8 * *
9 * For the licensing terms see $ROOTSYS/LICENSE. *
10 * For the list of contributors see $ROOTSYS/README/CREDITS. *
11 *************************************************************************/
12
14
15#include <ROOT/RLogger.hxx>
18
20
21#include "THttpServer.h"
22
23#include "TSystem.h"
24#include "TRandom3.h"
25#include "TString.h"
26#include "TApplication.h"
27#include "TTimer.h"
28#include "TROOT.h"
29#include "TEnv.h"
30#include "TExec.h"
31
32#include <thread>
33#include <chrono>
34#include <iostream>
35
36using namespace ROOT::Experimental;
37
38///////////////////////////////////////////////////////////////
39/// Parse boolean gEnv variable which should be "yes" or "no"
40/// \return 1 for true or 0 for false
41/// Returns \param dflt if result is not defined
42/// \param name name of the env variable
43
44int RWebWindowWSHandler::GetBoolEnv(const std::string &name, int dflt)
45{
46 const char *undef = "<undefined>";
47 const char *value = gEnv->GetValue(name.c_str(), undef);
48 if (!value) return dflt;
49 std::string svalue = value;
50 if (svalue == undef) return dflt;
51
52 if (svalue == "yes") return 1;
53 if (svalue == "no") return 0;
54
55 R__LOG_ERROR(WebGUILog()) << name << " has to be yes or no";
56 return dflt;
57}
58
59
60/** \class ROOT::Experimental::RWebWindowsManager
61\ingroup webdisplay
62
63Central instance to create and show web-based windows like Canvas or FitPanel.
64
65Manager responsible to creating THttpServer instance, which is used for RWebWindow's
66communication with clients.
67
68Method RWebWindows::Show() used to show window in specified location.
69*/
70
71//////////////////////////////////////////////////////////////////////////////////////////
72/// Returns default window manager
73/// Used to display all standard ROOT elements like TCanvas or TFitPanel
74
75std::shared_ptr<RWebWindowsManager> &RWebWindowsManager::Instance()
76{
77 static std::shared_ptr<RWebWindowsManager> sInstance = std::make_shared<RWebWindowsManager>();
78 return sInstance;
79}
80
81//////////////////////////////////////////////////////////////////
82/// This thread id used to identify main application thread, where ROOT event processing runs
83/// To inject code in that thread, one should use TTimer (like THttpServer does)
84/// In other threads special run methods have to be invoked like RWebWindow::Run()
85///
86/// TODO: probably detection of main thread should be delivered by central ROOT instances like gApplication or gROOT
87/// Main thread can only make sense if special processing runs there and one can inject own functionality there
88
89static std::thread::id gWebWinMainThrd = std::this_thread::get_id();
90static bool gWebWinMainThrdSet = true;
91
92//////////////////////////////////////////////////////////////////////////////////////////
93/// Returns true when called from main process
94/// Main process recognized at the moment when library is loaded
95/// It supposed to be a thread where gApplication->Run() will be called
96/// If application runs in separate thread, one have to use AssignMainThrd() method
97/// to let RWebWindowsManager correctly recognize such situation
98
100{
101 return gWebWinMainThrdSet && (std::this_thread::get_id() == gWebWinMainThrd);
102}
103
104//////////////////////////////////////////////////////////////////////////////////////////
105/// Re-assigns main thread id
106/// Normally main thread id recognized at the moment when library is loaded
107/// It supposed to be a thread where gApplication->Run() will be called
108/// If application runs in separate thread, one have to call this method
109/// to let RWebWindowsManager correctly recognize such situation
110
112{
113 gWebWinMainThrdSet = true;
114 gWebWinMainThrd = std::this_thread::get_id();
115}
116
117//////////////////////////////////////////////////////////////////////////////////////////
118/// window manager constructor
119/// Required here for correct usage of unique_ptr<THttpServer>
120
122{
123 fExternalProcessEvents = RWebWindowWSHandler::GetBoolEnv("WebGui.ExternalProcessEvents") == 1;
125 gWebWinMainThrdSet = false;
126 fAssgnExec = std::make_unique<TExec>("init_threadid", "ROOT::Experimental::RWebWindowsManager::AssignMainThrd();");
127 TTimer::SingleShot(0, "TExec", fAssgnExec.get(), "Exec()");
128 }
129}
130
131//////////////////////////////////////////////////////////////////////////////////////////
132/// window manager destructor
133/// Required here for correct usage of unique_ptr<THttpServer>
134
136{
137 if (gApplication && fServer && !fServer->IsTerminated()) {
138 gApplication->Disconnect("Terminate(Int_t)", fServer.get(), "SetTerminate()");
139 fServer->SetTerminate();
140 }
141}
142
143//////////////////////////////////////////////////////////////////////////////////////////
144/// Assign thread id for window
145/// Required in case of external process events
146
148{
150 win.fUseServerThreads = false;
151 win.fProcessMT = false;
152 win.fCallbacksThrdIdSet = true;
153 win.fCallbacksThrdId = gWebWinMainThrd;
154 }
155}
156
157//////////////////////////////////////////////////////////////////////////////////////////
158/// Creates http server, if required - with real http engine (civetweb)
159/// One could configure concrete HTTP port, which should be used for the server,
160/// provide following entry in rootrc file:
161///
162/// WebGui.HttpPort: 8088
163///
164/// or specify range of http ports, which can be used:
165///
166/// WebGui.HttpPortMin: 8800
167/// WebGui.HttpPortMax: 9800
168///
169/// By default range [8800..9800] is used
170///
171/// One also can bind HTTP server socket to loopback address,
172/// In that case only connection from localhost will be available:
173///
174/// WebGui.HttpLoopback: yes
175///
176/// Or one could specify hostname which should be used for binding of server socket
177///
178/// WebGui.HttpBind: hostname | ipaddress
179///
180/// To use secured protocol, following parameter should be specified
181///
182/// WebGui.UseHttps: yes
183/// WebGui.ServerCert: sertificate_filename.pem
184///
185/// All incoming requests processed in THttpServer in timer handler with 10 ms timeout.
186/// One may decrease value to improve latency or increase value to minimize CPU load
187///
188/// WebGui.HttpTimer: 10
189///
190/// To processing incoming http requests and websockets, THttpServer allocate 10 threads
191/// One have to increase this number if more simultaneous connections are expected:
192///
193/// WebGui.HttpThrds: 10
194///
195/// One also can configure usage of special thread of processing of http server requests
196///
197/// WebGui.HttpThrd: no
198///
199/// Extra threads can be used to send data to different clients via websocket (default no)
200///
201/// WebGui.SenderThrds: no
202///
203/// If required, one could change websocket timeouts (default is 10000 ms)
204///
205/// WebGui.HttpWSTmout: 10000
206///
207/// By default, THttpServer created in restricted mode which only allows websocket handlers
208/// and processes only very few other related http requests. For security reasons such mode
209/// should be always enabled. Only if it is really necessary to process all other kinds
210/// of HTTP requests, one could specify no for following parameter (default yes):
211///
212/// WebGui.WSOnly: yes
213///
214/// In some applications one may need to force longpoll websocket emulations from the beginning,
215/// for instance when clients connected via proxys. Although JSROOT should automatically fallback
216/// to longpoll engine, one can configure this directly (default no)
217///
218/// WebGui.WSLongpoll: no
219///
220/// Following parameter controls browser max-age caching parameter for files (default 3600)
221/// When 0 is specified, browser cache will be disabled
222///
223/// WebGui.HttpMaxAge: 3600
224///
225/// Also one can provide extra URL options for, see TCivetweb::Create for list of supported options
226///
227/// WebGui.HttpExtraArgs: winsymlinks=no
228///
229/// One also can configure usage of FastCGI server for web windows:
230///
231/// WebGui.FastCgiPort: 4000
232/// WebGui.FastCgiThreads: 10
233///
234/// To be able start web browser for such windows, one can provide real URL of the
235/// web server which will connect with that FastCGI instance:
236///
237/// WebGui.FastCgiServer: https://your_apache_server.com/root_cgi_path
238///
239
241{
242 if (gROOT->GetWebDisplay() == "off")
243 return false;
244
245 // explicitly protect server creation
246 std::lock_guard<std::recursive_mutex> grd(fMutex);
247
248 if (!fServer) {
249
250 fServer = std::make_unique<THttpServer>("basic_sniffer");
251
253 fUseHttpThrd = false;
254 } else {
255 auto serv_thrd = RWebWindowWSHandler::GetBoolEnv("WebGui.HttpThrd");
256 if (serv_thrd != -1)
257 fUseHttpThrd = serv_thrd != 0;
258 }
259
260 auto send_thrds = RWebWindowWSHandler::GetBoolEnv("WebGui.SenderThrds");
261 if (send_thrds != -1)
262 fUseSenderThreads = send_thrds != 0;
263
264 if (IsUseHttpThread())
265 fServer->CreateServerThread();
266
267 if (gApplication)
268 gApplication->Connect("Terminate(Int_t)", "THttpServer", fServer.get(), "SetTerminate()");
269
270 fServer->SetWSOnly(RWebWindowWSHandler::GetBoolEnv("WebGui.WSOnly", 1) != 0);
271
272 // this is location where all ROOT UI5 sources are collected
273 // normally it is $ROOTSYS/ui5 or <prefix>/ui5 location
274 TString ui5dir = gSystem->Getenv("ROOTUI5SYS");
275 if (ui5dir.Length() == 0)
276 ui5dir = gEnv->GetValue("WebGui.RootUi5Path","");
277
278 if (ui5dir.Length() == 0)
279 ui5dir.Form("%s/ui5", TROOT::GetDataDir().Data());
280
281 if (gSystem->ExpandPathName(ui5dir)) {
282 R__LOG_ERROR(WebGUILog()) << "Path to ROOT ui5 sources " << ui5dir << " not found, set ROOTUI5SYS correctly";
283 ui5dir = ".";
284 }
285
286 fServer->AddLocation("rootui5sys/", ui5dir.Data());
287 }
288
289 if (!with_http || fServer->IsAnyEngine())
290 return true;
291
292 int http_port = gEnv->GetValue("WebGui.HttpPort", 0);
293 int http_min = gEnv->GetValue("WebGui.HttpPortMin", 8800);
294 int http_max = gEnv->GetValue("WebGui.HttpPortMax", 9800);
295 int http_timer = gEnv->GetValue("WebGui.HttpTimer", 10);
296 int http_thrds = gEnv->GetValue("WebGui.HttpThreads", 10);
297 int http_wstmout = gEnv->GetValue("WebGui.HttpWSTmout", 10000);
298 int http_maxage = gEnv->GetValue("WebGui.HttpMaxAge", -1);
299 const char *extra_args = gEnv->GetValue("WebGui.HttpExtraArgs", "");
300 int fcgi_port = gEnv->GetValue("WebGui.FastCgiPort", 0);
301 int fcgi_thrds = gEnv->GetValue("WebGui.FastCgiThreads", 10);
302 const char *fcgi_serv = gEnv->GetValue("WebGui.FastCgiServer", "");
303 fLaunchTmout = gEnv->GetValue("WebGui.LaunchTmout", 30.);
304 bool assign_loopback = RWebWindowWSHandler::GetBoolEnv("WebGui.HttpLoopback", 1) == 1;
305 const char *http_bind = gEnv->GetValue("WebGui.HttpBind", "");
306 bool use_secure = RWebWindowWSHandler::GetBoolEnv("WebGui.UseHttps", 0) == 1;
307 const char *ssl_cert = gEnv->GetValue("WebGui.ServerCert", "rootserver.pem");
308
309 int ntry = 100;
310
311 if ((http_port < 0) && (fcgi_port <= 0)) {
312 R__LOG_ERROR(WebGUILog()) << "Not allowed to create HTTP server, check WebGui.HttpPort variable";
313 return false;
314 }
315
316 if ((http_timer > 0) && !IsUseHttpThread())
317 fServer->SetTimer(http_timer);
318
319 if (http_port < 0) {
320 ntry = 0;
321 } else {
322
323 if (http_port == 0)
324 gRandom->SetSeed(0);
325
326 if (http_max - http_min < ntry)
327 ntry = http_max - http_min;
328 }
329
330 if (fcgi_port > 0)
331 ntry++;
332
333 while (ntry-- >= 0) {
334 if ((http_port == 0) && (fcgi_port <= 0)) {
335 if ((http_min <= 0) || (http_max <= http_min)) {
336 R__LOG_ERROR(WebGUILog()) << "Wrong HTTP range configuration, check WebGui.HttpPortMin/Max variables";
337 return false;
338 }
339
340 http_port = (int)(http_min + (http_max - http_min) * gRandom->Rndm(1));
341 }
342
343 TString engine, url;
344
345 if (fcgi_port > 0) {
346 engine.Form("fastcgi:%d?thrds=%d", fcgi_port, fcgi_thrds);
347 if (!fServer->CreateEngine(engine)) return false;
348 if (fcgi_serv && (strlen(fcgi_serv) > 0)) fAddr = fcgi_serv;
349 if (http_port < 0) return true;
350 fcgi_port = 0;
351 } else if (http_port > 0) {
352 url = use_secure ? "https://" : "http://";
353 engine.Form("%s:%d?thrds=%d&websocket_timeout=%d", (use_secure ? "https" : "http"), http_port, http_thrds, http_wstmout);
354 if (assign_loopback) {
355 engine.Append("&loopback");
356 url.Append("localhost");
357 } else if (http_bind && (strlen(http_bind) > 0)) {
358 engine.Append("&bind=");
359 engine.Append(http_bind);
360 url.Append(http_bind);
361 } else {
362 url.Append("localhost");
363 }
364
365 if (http_maxage >= 0)
366 engine.Append(TString::Format("&max_age=%d", http_maxage));
367
368 if (use_secure) {
369 engine.Append("&ssl_cert=");
370 engine.Append(ssl_cert);
371 }
372
373 if (extra_args && strlen(extra_args) > 0) {
374 engine.Append("&");
375 engine.Append(extra_args);
376 }
377
378 if (fServer->CreateEngine(engine)) {
379 fAddr = url.Data();
380 fAddr.append(":");
381 fAddr.append(std::to_string(http_port));
382 return true;
383 }
384 http_port = 0;
385 }
386 }
387
388 return false;
389}
390
391//////////////////////////////////////////////////////////////////////////////////////////
392/// Creates new window
393/// To show window, RWebWindow::Show() have to be called
394
395std::shared_ptr<RWebWindow> RWebWindowsManager::CreateWindow()
396{
397 // we book manager mutex for a longer operation, locked again in server creation
398 std::lock_guard<std::recursive_mutex> grd(fMutex);
399
400 if (!CreateServer()) {
401 R__LOG_ERROR(WebGUILog()) << "Cannot create server when creating window";
402 return nullptr;
403 }
404
405 std::shared_ptr<RWebWindow> win = std::make_shared<RWebWindow>();
406
407 if (!win) {
408 R__LOG_ERROR(WebGUILog()) << "Fail to create RWebWindow instance";
409 return nullptr;
410 }
411
412 double dflt_tmout = gEnv->GetValue("WebGui.OperationTmout", 50.);
413
414 auto wshandler = win->CreateWSHandler(Instance(), ++fIdCnt, dflt_tmout);
415
416 if (gEnv->GetValue("WebGui.RecordData", 0) > 0) {
417 std::string fname, prefix;
418 if (fIdCnt > 1) {
419 prefix = std::string("f") + std::to_string(fIdCnt) + "_";
420 fname = std::string("protcol") + std::to_string(fIdCnt) + ".json";
421 } else {
422 fname = "protocol.json";
423 }
424 win->RecordData(fname, prefix);
425 }
426
430 else
431 win->UseServerThreads(); // let run window until thread is obtained
432 } else if (IsUseHttpThread())
433 win->UseServerThreads();
434
435 const char *token = gEnv->GetValue("WebGui.ConnToken", "");
436 if (token && *token)
437 win->SetConnToken(token);
438
439 fServer->RegisterWS(wshandler);
440
441 return win;
442}
443
444//////////////////////////////////////////////////////////////////////////////////////////
445/// Release all references to specified window
446/// Called from RWebWindow destructor
447
449{
450 if (win.fWSHandler)
451 fServer->UnregisterWS(win.fWSHandler);
452}
453
454//////////////////////////////////////////////////////////////////////////
455/// Provide URL address to access specified window from inside or from remote
456
457std::string RWebWindowsManager::GetUrl(const RWebWindow &win, bool remote)
458{
459 if (!fServer) {
460 R__LOG_ERROR(WebGUILog()) << "Server instance not exists when requesting window URL";
461 return "";
462 }
463
464 std::string addr = "/";
465
466 addr.append(win.fWSHandler->GetName());
467
468 addr.append("/");
469
470 if (remote) {
471 if (!CreateServer(true)) {
472 R__LOG_ERROR(WebGUILog()) << "Fail to start real HTTP server when requesting URL";
473 return "";
474 }
475
476 addr = fAddr + addr;
477 }
478
479 return addr;
480}
481
482///////////////////////////////////////////////////////////////////////////////////////////////////
483/// Show web window in specified location.
484///
485/// \param[inout] win web window by reference
486/// \param user_args specifies where and how display web window
487///
488/// As display args one can use string like "firefox" or "chrome" - these are two main supported web browsers.
489/// See RWebDisplayArgs::SetBrowserKind() for all available options. Default value for the browser can be configured
490/// when starting root with --web argument like: "root --web=chrome". When root started in web server mode "root --web=server",
491/// no any web browser will be started - just URL will be printout, which can be entered in any running web browser
492///
493/// If allowed, same window can be displayed several times (like for RCanvas or TCanvas)
494///
495/// Following parameters can be configured in rootrc file:
496///
497/// WebGui.Display: kind of display like chrome or firefox or browser, can be overwritten by --web=value command line argument
498/// WebGui.OnetimeKey: if configured requires unique key every time window is connected (default no)
499/// WebGui.Chrome: full path to Google Chrome executable
500/// WebGui.ChromeBatch: command to start chrome in batch, used for image production, like "$prog --headless --disable-gpu $geometry $url"
501/// WebGui.ChromeHeadless: command to start chrome in headless mode, like "fork: --headless --disable-gpu $geometry $url"
502/// WebGui.ChromeInteractive: command to start chrome in interactive mode, like "$prog $geometry --app=\'$url\' &"
503/// WebGui.Firefox: full path to Mozilla Firefox executable
504/// WebGui.FirefoxHeadless: command to start Firefox in headless mode, like "fork:--headless --private-window --no-remote $profile $url"
505/// WebGui.FirefoxInteractive: command to start Firefox in interactive mode, like "$prog --private-window \'$url\' &"
506/// WebGui.FirefoxProfile: name of Firefox profile to use
507/// WebGui.FirefoxProfilePath: file path to Firefox profile
508/// WebGui.FirefoxRandomProfile: usage of random Firefox profile -1 never, 0 - only for headless mode (dflt), 1 - always
509/// WebGui.LaunchTmout: time required to start process in seconds (default 30 s)
510/// WebGui.OperationTmout: time required to perform WebWindow operation like execute command or update drawings
511/// WebGui.RecordData: if specified enables data recording for each web window 0 - off, 1 - on
512/// WebGui.JsonComp: compression factor for JSON conversion, if not specified - each widget uses own default values
513/// WebGui.ForceHttp: 0 - off (default), 1 - always create real http server to run web window
514/// WebGui.Console: -1 - output only console.error(), 0 - add console.warn(), 1 - add console.log() output
515/// WebGui.ConnCredits: 10 - number of packets which can be send by server or client without acknowledge from receiving side
516/// WebGui.openui5src: alternative location for openui5 like https://openui5.hana.ondemand.com/
517/// WebGui.openui5libs: list of pre-loaded ui5 libs like sap.m, sap.ui.layout, sap.ui.unified
518/// WebGui.openui5theme: openui5 theme like sap_belize (default) or sap_fiori_3
519///
520/// THttpServer-related parameters documented in \ref CreateServer method
521
523{
524 // silently ignore regular Show() calls in batch mode
525 if (!user_args.IsHeadless() && gROOT->IsWebDisplayBatch())
526 return 0;
527
528 // for embedded window no any browser need to be started
529 // also when off is specified, no browser should be started
530 if ((user_args.GetBrowserKind() == RWebDisplayArgs::kEmbedded) || (user_args.GetBrowserKind() == RWebDisplayArgs::kOff))
531 return 0;
532
533 // catch window showing, used by the RBrowser to embed some of ROOT widgets
534 if (fShowCallback)
535 if (fShowCallback(win, user_args))
536 return 0;
537
538 // place here while involves conn mutex
539 auto token = win.GetConnToken();
540
541 // we book manager mutex for a longer operation,
542 std::lock_guard<std::recursive_mutex> grd(fMutex);
543
544 if (!fServer) {
545 R__LOG_ERROR(WebGUILog()) << "Server instance not exists to show window";
546 return 0;
547 }
548
549 std::string key;
550 int ntry = 100000;
551 TRandom3 rnd;
552 rnd.SetSeed();
553
554 do {
555 key = std::to_string(rnd.Integer(0x100000));
556 } while ((--ntry > 0) && win.HasKey(key));
557 if (ntry == 0) {
558 R__LOG_ERROR(WebGUILog()) << "Fail to create unique key for the window";
559 return 0;
560 }
561
562 RWebDisplayArgs args(user_args);
563
564 if (args.IsHeadless() && !args.IsSupportHeadless()) {
565 R__LOG_ERROR(WebGUILog()) << "Cannot use batch mode with " << args.GetBrowserName();
566 return 0;
567 }
568
569 if (args.GetWidth() <= 0) args.SetWidth(win.GetWidth());
570 if (args.GetHeight() <= 0) args.SetHeight(win.GetHeight());
571
572 bool normal_http = !args.IsLocalDisplay();
573 if (!normal_http && (gEnv->GetValue("WebGui.ForceHttp", 0) == 1))
574 normal_http = true;
575
576 std::string url = GetUrl(win, normal_http);
577 if (url.empty()) {
578 R__LOG_ERROR(WebGUILog()) << "Cannot create URL for the window";
579 return 0;
580 }
581 if (normal_http && fAddr.empty()) {
582 R__LOG_WARNING(WebGUILog()) << "Full URL cannot be produced for window " << url << " to start web browser";
583 return 0;
584 }
585
586 args.SetUrl(url);
587
588 args.AppendUrlOpt(std::string("key=") + key);
589 if (args.IsHeadless()) args.AppendUrlOpt("headless"); // used to create holder request
590 if (!token.empty())
591 args.AppendUrlOpt(std::string("token=") + token);
592
593 if (!args.IsHeadless() && (args.GetBrowserKind() == RWebDisplayArgs::kServer) && (RWebWindowWSHandler::GetBoolEnv("WebGui.OnetimeKey") != 1)) {
594 std::cout << "New web window: " << args.GetUrl() << std::endl;
595 return 0;
596 }
597
598#if not(defined(R__MACOSX)) and not(defined(R__WIN32))
599 if (args.IsInteractiveBrowser()) {
600 const char *varname = "WebGui.CheckRemoteDisplay";
601 if (RWebWindowWSHandler::GetBoolEnv(varname, 1) == 1) {
602 const char *displ = gSystem->Getenv("DISPLAY");
603 if (displ && *displ && (*displ != ':')) {
604 gEnv->SetValue(varname, "no");
605 std::cout << "\n"
606 "ROOT web-based widget started in the session where DISPLAY set to " << displ << "\n" <<
607 "Means web browser will be displayed on remote X11 server which is usually very inefficient\n"
608 "One can start ROOT session in server mode like \"root -b --web=server:8877\" and forward http port to display node\n"
609 "Find more info on https://root.cern/for_developers/root7/#rbrowser\n"
610 "This message can be disabled by setting \"" << varname << ": no\" in .rootrc file\n";
611 }
612 }
613 }
614#endif
615
616 if (!normal_http)
617 args.SetHttpServer(GetServer());
618
619 auto handle = RWebDisplayHandle::Display(args);
620
621 if (!handle) {
622 R__LOG_ERROR(WebGUILog()) << "Cannot display window in " << args.GetBrowserName();
623 return 0;
624 }
625
626 return win.AddDisplayHandle(args.IsHeadless(), key, handle);
627}
628
629//////////////////////////////////////////////////////////////////////////
630/// Waits until provided check function or lambdas returns non-zero value
631/// Regularly calls WebWindow::Sync() method to let run event loop
632/// If call from the main thread, runs system events processing
633/// Check function has following signature: int func(double spent_tm)
634/// Parameter spent_tm is time in seconds, which already spent inside function
635/// Waiting will be continued, if function returns zero.
636/// First non-zero value breaks waiting loop and result is returned (or 0 if time is expired).
637/// If parameter timed is true, timelimit (in seconds) defines how long to wait
638
639int RWebWindowsManager::WaitFor(RWebWindow &win, WebWindowWaitFunc_t check, bool timed, double timelimit)
640{
641 int res = 0, cnt = 0;
642 double spent = 0.;
643
644 auto start = std::chrono::high_resolution_clock::now();
645
646 win.Sync(); // in any case call sync once to ensure
647
648 auto is_main_thread = IsMainThrd();
649
650 while ((res = check(spent)) == 0) {
651
652 if (is_main_thread)
654
655 win.Sync();
656
657 // only when first 1000 events processed, invoke sleep
658 if (++cnt > 1000)
659 std::this_thread::sleep_for(std::chrono::milliseconds(cnt > 5000 ? 10 : 1));
660
661 std::chrono::duration<double, std::milli> elapsed = std::chrono::high_resolution_clock::now() - start;
662
663 spent = elapsed.count() * 1e-3; // use ms precision
664
665 if (timed && (spent > timelimit))
666 return -3;
667 }
668
669 return res;
670}
671
672//////////////////////////////////////////////////////////////////////////
673/// Terminate http server and ROOT application
674
676{
677 if (fServer)
678 fServer->SetTerminate();
679
680 if (gApplication)
681 TTimer::SingleShot(100, "TApplication", gApplication, "Terminate()");
682}
#define R__LOG_WARNING(...)
Definition: RLogger.hxx:363
#define R__LOG_ERROR(...)
Definition: RLogger.hxx:362
#define e(i)
Definition: RSha256.hxx:103
static bool gWebWinMainThrdSet
static std::thread::id gWebWinMainThrd
This thread id used to identify main application thread, where ROOT event processing runs To inject c...
R__EXTERN TApplication * gApplication
Definition: TApplication.h:165
R__EXTERN TEnv * gEnv
Definition: TEnv.h:170
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 value
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
char name[80]
Definition: TGX11.cxx:110
#define gROOT
Definition: TROOT.h:404
R__EXTERN TRandom * gRandom
Definition: TRandom.h:62
R__EXTERN TSystem * gSystem
Definition: TSystem.h:559
Holds different arguments for starting browser with RWebDisplayHandle::Display() method.
std::string GetBrowserName() const
Returns configured browser name.
bool IsSupportHeadless() const
returns true if browser supports headless mode
bool IsHeadless() const
returns headless mode
RWebDisplayArgs & SetUrl(const std::string &url)
set window url
bool IsInteractiveBrowser() const
returns true if interactive browser window supposed to be started
int GetHeight() const
returns preferable web window height
void SetHttpServer(THttpServer *serv)
set http server instance, used for window display
EBrowserKind GetBrowserKind() const
returns configured browser kind, see EBrowserKind for supported values
void AppendUrlOpt(const std::string &opt)
append extra url options, add "&" as separator if required
bool IsLocalDisplay() const
returns true if local display like CEF or Qt5 QWebEngine should be used
@ kEmbedded
window will be embedded into other, no extra browser need to be started
@ kServer
indicates that ROOT runs as server and just printouts window URL, browser should be started by the us...
@ kOff
disable web display, do not start any browser
RWebDisplayArgs & SetHeight(int h=0)
set preferable web window height
const std::string & GetUrl() const
returns window url
int GetWidth() const
returns preferable web window width
RWebDisplayArgs & SetWidth(int w=0)
set preferable web window width
static std::unique_ptr< RWebDisplayHandle > Display(const RWebDisplayArgs &args)
Create web display.
static int GetBoolEnv(const std::string &name, int dfl=-1)
Parse boolean gEnv variable which should be "yes" or "no".
Represents web window, which can be shown in web browser or any other supported environment.
Definition: RWebWindow.hxx:53
void AssignWindowThreadId(RWebWindow &win)
Assign thread id for window Required in case of external process events.
std::unique_ptr< THttpServer > fServer
! central communication with the all used displays
std::string GetUrl(const RWebWindow &win, bool remote=false)
Provide URL address to access specified window from inside or from remote.
bool CreateServer(bool with_http=false)
Creates http server, if required - with real http engine (civetweb) One could configure concrete HTTP...
RWebWindowsManager()
window manager constructor Required here for correct usage of unique_ptr<THttpServer>
int WaitFor(RWebWindow &win, WebWindowWaitFunc_t check, bool timed=false, double tm=-1)
Waits until provided check function or lambdas returns non-zero value Regularly calls WebWindow::Sync...
std::recursive_mutex fMutex
! main mutex, used for window creations
unsigned fIdCnt
! counter for identifiers
unsigned ShowWindow(RWebWindow &win, const RWebDisplayArgs &args)
Show window in specified location, see Show() method for more details.
std::string fAddr
! HTTP address of the server
bool fUseSenderThreads
! use extra threads for sending data from RWebWindow to clients
void Terminate()
Terminate http server and ROOT application.
~RWebWindowsManager()
window manager destructor Required here for correct usage of unique_ptr<THttpServer>
THttpServer * GetServer() const
Returns THttpServer instance.
WebWindowShowCallback_t fShowCallback
! function called for each RWebWindow::Show call
static void AssignMainThrd()
Re-assigns main thread id Normally main thread id recognized at the moment when library is loaded It ...
std::unique_ptr< TExec > fAssgnExec
! special exec to assign thread id via ProcessEvents
bool IsUseHttpThread() const
Returns true if http server use special thread for requests processing (default off)
static bool IsMainThrd()
Returns true when called from main process Main process recognized at the moment when library is load...
static std::shared_ptr< RWebWindowsManager > & Instance()
Returns default window manager Used to display all standard ROOT elements like TCanvas or TFitPanel.
bool fUseHttpThrd
! use special thread for THttpServer
void Unregister(RWebWindow &win)
Release all references to specified window Called from RWebWindow destructor.
float fLaunchTmout
! timeout in seconds to start browser process, default 30s
bool fExternalProcessEvents
! indicate that there are external process events engine
std::shared_ptr< RWebWindow > CreateWindow()
Creates new window To show window, RWebWindow::Show() have to be called.
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
Definition: TEnv.cxx:491
virtual void SetValue(const char *name, const char *value, EEnvLevel level=kEnvChange, const char *type=nullptr)
Set the value of a resource or create a new resource.
Definition: TEnv.cxx:736
Bool_t Connect(const char *signal, const char *receiver_class, void *receiver, const char *slot)
Non-static method is used to connect from the signal of this object to the receiver slot.
Definition: TQObject.cxx:869
Bool_t Disconnect(const char *signal=nullptr, void *receiver=nullptr, const char *slot=nullptr)
Disconnects signal of this object from slot of receiver.
Definition: TQObject.cxx:1027
static const TString & GetDataDir()
Get the data directory in the installation. Static utility function.
Definition: TROOT.cxx:2973
Random number generator class based on M.
Definition: TRandom3.h:27
void SetSeed(ULong_t seed=0) override
Set the random generator sequence if seed is 0 (default value) a TUUID is generated and used to fill ...
Definition: TRandom3.cxx:206
virtual void SetSeed(ULong_t seed=0)
Set the random generator seed.
Definition: TRandom.cxx:608
Double_t Rndm() override
Machine independent random number generator.
Definition: TRandom.cxx:552
virtual UInt_t Integer(UInt_t imax)
Returns a random integer uniformly distributed on the interval [ 0, imax-1 ].
Definition: TRandom.cxx:360
Basic string class.
Definition: TString.h:136
Ssiz_t Length() const
Definition: TString.h:410
const char * Data() const
Definition: TString.h:369
TString & Append(const char *cs)
Definition: TString.h:564
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
Definition: TString.cxx:2345
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Definition: TString.cxx:2323
virtual Bool_t ExpandPathName(TString &path)
Expand a pathname getting rid of special shell characters like ~.
Definition: TSystem.cxx:1274
virtual const char * Getenv(const char *env)
Get environment variable.
Definition: TSystem.cxx:1663
virtual Bool_t ProcessEvents()
Process pending events (GUI, timers, sockets).
Definition: TSystem.cxx:419
static void SingleShot(Int_t milliSec, const char *receiver_class, void *receiver, const char *method)
This static function calls a slot after a given time interval.
Definition: TTimer.cxx:258
RLogChannel & WebGUILog()
Log channel for WebGUI diagnostics.
std::function< int(double)> WebWindowWaitFunc_t
function signature for waiting call-backs Such callback used when calling thread need to waits for so...
Definition: RWebWindow.hxx:48
const char * cnt
Definition: TXMLSetup.cxx:75