musrview: fix the segfault of the interactive "Save graphics"
RenderGraphics() flips gROOT's batch flag so that the export TCanvas is built headless. That is enough for TCanvas to take its batch branch, but not enough to make the session batch: 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 paints the canvas background through SelectDrawable(fPixmapID)/PaintBorder(). With the batch gVirtualX that is a no-op, with TGX11 it selects window -1 and TGX11::DrawBoxW() segfaults. Saving from the musrview window therefore crashed every time, while the --png/--pdf/... batch export was never affected (there gVirtualX already is the batch one). Put the graphics interface into the same state a genuine batch session has by pointing gVirtualX at ROOT's gGXBatch for the duration of the export. Batch flag and gVirtualX are now handled by one RAII guard, which also replaces the three hand-written restore points, and which is declared before the canvas so that it outlives it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVGQWoKNXByP2PSmMgPz3i
This commit is contained in:
1 parent
e7e16dd793
commit
9d1b6eac30
1 file changed
+47
-7
@@ -43,6 +43,7 @@
|
||||
#include <TROOT.h>
|
||||
#include <TStyle.h>
|
||||
#include <TSystem.h>
|
||||
#include <TVirtualX.h>
|
||||
|
||||
#include "PMusrWebCanvas.h"
|
||||
|
||||
@@ -173,6 +174,33 @@ std::string FindWebDir()
|
||||
return std::string();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* <p>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;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user