#include <stdexcept>
#include <algorithm>
#include <memory>
#include "TVirtualX.h"
#include "GuiTypes.h"
#include "TString.h"
#include "TError.h"
#include "TROOT.h"
#include "TGLContextPrivate.h"
#include "TGLIncludes.h"
#include "TGLContext.h"
#include "TGLWidget.h"
#include "TGLFormat.h"
#include "TGLUtil.h"
#include "TGLFontManager.h"
ClassImp(TGLContext);
Bool_t TGLContext::fgGlewInitDone = kFALSE;
TGLContext::TGLContext(TGLWidget *wid, Bool_t shareDefault,
const TGLContext *shareList)
: fDevice(wid),
fPimpl(0),
fFromCtor(kTRUE),
fValid(kFALSE),
fIdentity(0)
{
if (shareDefault)
shareList = TGLContextIdentity::GetDefaultContextAny();
if (!gVirtualX->IsCmdThread()) {
gROOT->ProcessLineFast(Form("((TGLContext *)0x%lx)->SetContext((TGLWidget *)0x%lx, (TGLContext *)0x%lx)",
this, wid, shareList));
} else {
SetContext(wid, shareList);
}
if (shareDefault)
fIdentity = TGLContextIdentity::GetDefaultIdentity();
else
fIdentity = shareList ? shareList->GetIdentity() : new TGLContextIdentity;
fIdentity->AddRef(this);
fFromCtor = kFALSE;
}
void TGLContext::GlewInit()
{
GLenum status = glewInit();
if (status != GLEW_OK)
Warning("TGLContext::GlewInit", "GLEW initalization failed.");
else if (gDebug > 0)
Info("TGLContext::GlewInit", "GLEW initalization successful.");
fgGlewInitDone = kTRUE;
}
#ifdef WIN32
namespace {
struct LayoutCompatible_t {
void *fDummy0;
void *fDummy1;
HWND *fPHwnd;
unsigned char fDummy2;
unsigned fDummy3;
unsigned short fDummy4;
unsigned short fDummy5;
void *fDummy6;
unsigned fDummy7:2;
};
}
void TGLContext::SetContext(TGLWidget *widget, const TGLContext *shareList)
{
if (!fFromCtor) {
Error("TGLContext::SetContext", "SetContext must be called only from ctor");
return;
}
std::auto_ptr<TGLContextPrivate> safe_ptr(fPimpl = new TGLContextPrivate);
LayoutCompatible_t *trick =
reinterpret_cast<LayoutCompatible_t *>(widget->GetId());
HWND hWND = *trick->fPHwnd;
HDC hDC = GetWindowDC(hWND);
if (!hDC) {
Error("TGLContext::SetContext", "GetWindowDC failed");
throw std::runtime_error("GetWindowDC failed");
}
const Rgl::TGuardBase &dcGuard = Rgl::make_guard(ReleaseDC, hWND, hDC);
if (HGLRC glContext = wglCreateContext(hDC)) {
if (shareList && !wglShareLists(shareList->fPimpl->fGLContext, glContext)) {
wglDeleteContext(glContext);
Error("TGLContext::SetContext", "Context sharing failed!");
throw std::runtime_error("Context sharing failed");
}
fPimpl->fHWND = hWND;
fPimpl->fHDC = hDC;
fPimpl->fGLContext = glContext;
} else {
Error("TGLContext::SetContext", "wglCreateContext failed");
throw std::runtime_error("wglCreateContext failed");
}
fValid = kTRUE;
fDevice->AddContext(this);
TGLContextPrivate::RegisterContext(this);
dcGuard.Stop();
safe_ptr.release();
}
Bool_t TGLContext::MakeCurrent()
{
if (!fValid) {
Error("TGLContext::MakeCurrent", "This context is invalid.");
return kFALSE;
}
if (!gVirtualX->IsCmdThread())
return Bool_t(gROOT->ProcessLineFast(Form("((TGLContext *)0x%lx)->MakeCurrent()", this)));
else {
Bool_t rez = wglMakeCurrent(fPimpl->fHDC, fPimpl->fGLContext);
if (rez) {
if (!fgGlewInitDone)
GlewInit();
fIdentity->DeleteGLResources();
}
return rez;
}
}
Bool_t TGLContext::ClearCurrent()
{
return wglMakeCurrent(0, 0);
}
void TGLContext::SwapBuffers()
{
if (!fValid) {
Error("TGLContext::SwapBuffers", "This context is invalid.");
return;
}
if (!gVirtualX->IsCmdThread())
gROOT->ProcessLineFast(Form("((TGLContext *)0x%lx)->SwapBuffers()", this));
else {
if (fPimpl->fHWND)
wglSwapLayerBuffers(fPimpl->fHDC, WGL_SWAP_MAIN_PLANE);
else
glFinish();
}
}
void TGLContext::Release()
{
if (!gVirtualX->IsCmdThread()) {
gROOT->ProcessLineFast(Form("((TGLContext *)0x%lx)->Release()", this));
return;
}
if (fPimpl->fHWND)
ReleaseDC(fPimpl->fHWND, fPimpl->fHDC);
TGLContextPrivate::RemoveContext(this);
wglDeleteContext(fPimpl->fGLContext);
fValid = kFALSE;
}
#else // Non WIN32
void TGLContext::SetContext(TGLWidget *widget, const TGLContext *shareList)
{
if (!fFromCtor) {
Error("TGLContext::SetContext", "SetContext must be called only from ctor");
return;
}
std::auto_ptr<TGLContextPrivate> safe_ptr(fPimpl = new TGLContextPrivate);
Display *dpy = static_cast<Display *>(widget->GetInnerData().first);
XVisualInfo *visInfo = static_cast<XVisualInfo *>(widget->GetInnerData().second);
GLXContext glCtx = shareList ? glXCreateContext(dpy, visInfo, shareList->fPimpl->fGLContext, True)
: glXCreateContext(dpy, visInfo, None, True);
if (!glCtx) {
Error("TGLContext::SetContext", "glXCreateContext failed!");
throw std::runtime_error("glXCreateContext failed!");
}
fPimpl->fDpy = dpy;
fPimpl->fVisualInfo = visInfo;
fPimpl->fGLContext = glCtx;
fPimpl->fWindowID = widget->GetId();
fValid = kTRUE;
fDevice->AddContext(this);
TGLContextPrivate::RegisterContext(this);
safe_ptr.release();
}
Bool_t TGLContext::MakeCurrent()
{
if (!fValid) {
Error("TGLContext::MakeCurrent", "This context is invalid.");
return kFALSE;
}
if (fPimpl->fWindowID != 0) {
const Bool_t rez = glXMakeCurrent(fPimpl->fDpy, fPimpl->fWindowID,
fPimpl->fGLContext);
if (rez) {
if (!fgGlewInitDone)
GlewInit();
fIdentity->DeleteGLResources();
}
return rez;
}
return kFALSE;
}
Bool_t TGLContext::ClearCurrent()
{
return glXMakeCurrent(fPimpl->fDpy, None, 0);
}
void TGLContext::SwapBuffers()
{
if (!fValid) {
Error("TGLContext::SwapCurrent", "This context is invalid.");
return;
}
if (fPimpl->fWindowID != 0)
glXSwapBuffers(fPimpl->fDpy, fPimpl->fWindowID);
else
glFinish();
}
void TGLContext::Release()
{
TGLContextPrivate::RemoveContext(this);
glXDestroyContext(fPimpl->fDpy, fPimpl->fGLContext);
fValid = kFALSE;
}
#endif
TGLContext::~TGLContext()
{
if (fValid) {
Release();
fDevice->RemoveContext(this);
}
fIdentity->Release(this);
delete fPimpl;
}
TGLContextIdentity *TGLContext::GetIdentity()const
{
return fIdentity;
}
TGLContext *TGLContext::GetCurrent()
{
return TGLContextPrivate::GetCurrentContext();
}
ClassImp(TGLContextIdentity)
TGLContextIdentity* TGLContextIdentity::fgDefaultIdentity = new TGLContextIdentity;
TGLContextIdentity::TGLContextIdentity():
fFontManager(0), fCnt(0), fClientCnt(0)
{
}
TGLContextIdentity::~TGLContextIdentity()
{
if (fFontManager) delete fFontManager;
}
void TGLContextIdentity::AddRef(TGLContext* ctx)
{
++fCnt;
fCtxs.push_back(ctx);
}
void TGLContextIdentity::Release(TGLContext* ctx)
{
CtxList_t::iterator i = std::find(fCtxs.begin(), fCtxs.end(), ctx);
if (i != fCtxs.end())
{
fCtxs.erase(i);
--fCnt;
CheckDestroy();
}
else
{
Error("TGLContextIdentity::Release", "unregistered context.");
}
}
void TGLContextIdentity::RegisterDLNameRangeToWipe(UInt_t base, Int_t size)
{
fDLTrash.push_back(DLRange_t(base, size));
}
void TGLContextIdentity::DeleteGLResources()
{
if (!fDLTrash.empty())
{
for (DLTrashIt_t it = fDLTrash.begin(), e = fDLTrash.end(); it != e; ++it)
glDeleteLists(it->first, it->second);
fDLTrash.clear();
}
if (fFontManager)
fFontManager->ClearFontTrash();
}
TGLContextIdentity* TGLContextIdentity::GetCurrent()
{
TGLContext* ctx = TGLContext::GetCurrent();
return ctx ? ctx->GetIdentity() : 0;
}
TGLContextIdentity* TGLContextIdentity::GetDefaultIdentity()
{
if (fgDefaultIdentity == 0)
fgDefaultIdentity = new TGLContextIdentity;
return fgDefaultIdentity;
}
TGLContext* TGLContextIdentity::GetDefaultContextAny()
{
if (fgDefaultIdentity == 0 || fgDefaultIdentity->fCtxs.empty())
return 0;
return fgDefaultIdentity->fCtxs.front();
}
TGLFontManager* TGLContextIdentity::GetFontManager()
{
if(!fFontManager) fFontManager = new TGLFontManager();
return fFontManager;
}
void TGLContextIdentity::CheckDestroy()
{
if (fCnt <= 0 && fClientCnt <= 0)
{
if (this == fgDefaultIdentity)
fgDefaultIdentity = 0;
delete this;
}
}