diff --git a/src/classes/PMusrWebCanvas.cpp b/src/classes/PMusrWebCanvas.cpp index 1ad17884..58de4655 100644 --- a/src/classes/PMusrWebCanvas.cpp +++ b/src/classes/PMusrWebCanvas.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include "PMusrWebCanvas.h" @@ -173,6 +174,33 @@ std::string FindWebDir() return std::string(); } +//------------------------------------------------------------------------ +/** + *

Puts ROOT into the state a genuine batch session has, and restores the + * previous state when it goes out of scope: gROOT's batch flag plus the + * graphics interface, see PMusrWebCanvas::RenderGraphics() for why both are + * needed. + */ +//------------------------------------------------------------------------ +class PBatchGraphicsGuard { + public: + PBatchGraphicsGuard() : fWasBatch(gROOT->IsBatch()), fSavedVirtualX(gVirtualX) { + gROOT->SetBatch(kTRUE); + if (gGXBatch != nullptr) + gVirtualX = gGXBatch; + } + ~PBatchGraphicsGuard() { + gVirtualX = fSavedVirtualX; + gROOT->SetBatch(fWasBatch); + } + PBatchGraphicsGuard(const PBatchGraphicsGuard &) = delete; + PBatchGraphicsGuard &operator=(const PBatchGraphicsGuard &) = delete; + + private: + Bool_t fWasBatch; + TVirtualX *fSavedVirtualX; +}; + } // anonymous namespace //-------------------------------------------------------------------------- @@ -1117,8 +1145,24 @@ Bool_t PMusrWebCanvas::RenderGraphics(const std::string &fileName, const PanelVi // function is safe here because this canvas is the only one that ever // exists in the process: the interactive display never builds a TCanvas of // its own (see the class comment above). - const Bool_t wasBatch = gROOT->IsBatch(); - gROOT->SetBatch(kTRUE); + // + // The batch flag alone is not enough though. An interactive musrview is a + // *non-batch* ROOT session, hence TApplication has loaded the X11 graphics + // and gVirtualX points at TGX11. A batch TCanvas never obtains a drawable + // (its fPixmapID stays -1), yet TCanvas::Build() unconditionally does + // + // GetCanvasPainter()->SelectDrawable(fPixmapID); + // PaintBorder(GetFillColor(), kTRUE); + // + // to paint the canvas background. With the batch gVirtualX that is a no-op, + // with TGX11 it selects window -1 and the following TGX11::DrawBoxW() + // segfaults. So the graphics interface has to be put into the same state a + // genuine batch session has, which is what gGXBatch is: the TVirtualX + // instance ROOT installs when it starts up without graphics. + // + // The guard is declared before the canvas on purpose: it has to outlive it, + // since the TCanvas destructor uses the graphics interface as well. + PBatchGraphicsGuard batchGuard; std::string name = "musrviewBatch" + std::to_string(fPlotNumber); TCanvas canvas(name.c_str(), fTitle.c_str(), exportWidth, exportHeight); @@ -1193,10 +1237,8 @@ Bool_t PMusrWebCanvas::RenderGraphics(const std::string &fileName, const PanelVi infoPad->Draw(); } - if (!FillPlotPad(&dataTheoryPad)) { - gROOT->SetBatch(wasBatch); + if (!FillPlotPad(&dataTheoryPad)) return false; - } dataTheoryPad.Update(); canvas.cd(); @@ -1209,8 +1251,6 @@ Bool_t PMusrWebCanvas::RenderGraphics(const std::string &fileName, const PanelVi dataTheoryPad.GetListOfPrimitives()->Clear(); canvas.GetListOfPrimitives()->Clear(); - gROOT->SetBatch(wasBatch); - return true; }