// @(#)root/gl:$Id$ // Author: Timur Pocheptsov, Jun 2007 /************************************************************************* * Copyright (C) 1995-2004, Rene Brun and Fons Rademakers. * * All rights reserved. * * * * For the licensing terms see $ROOTSYS/LICENSE. * * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ #include #include #include "TVirtualX.h" #include "TGClient.h" #include "TError.h" #include "TROOT.h" #include "TVirtualMutex.h" #include "TGLWidget.h" #include "TGLIncludes.h" #include "TGLWSIncludes.h" #include "TGLUtil.h" #include "TGLEventHandler.h" #include "RConfigure.h" /******************************************************************************/ // TGLWidget /******************************************************************************/ //______________________________________________________________________________ // // GL window with context. _Must_ _have_ a parent window // (the 'parent' parameter of ctors). The current version inherits // TGCanvas (I'm not sure about future versions), probably, in future // multiple inheritance will be added - the second // base class will be TGLPaintDevice or something like this. // // Usage: // - Simply create TGLWidget as an embedded widget, and // connect your slots to signals you need: HandleExpose, HandleConfigureNotify, etc. // In your slots you can use gl API directly - under Win32 TGLWidget switches // between threads internally (look TGLPShapeObjEditor for such usage). // - You can write your own class, derived from TGLWidget, with PaintGL and InitGL // overriden. // // Resources (and invariants): // -fContainer (TGLWidgetContainer) - controlled by std::auto_ptr // -fWindowIndex - controlled manually (see CreateWidget and dtor) // -fGLContext - controlled manually (see CreateWidget and dtor) // -visual info for X11 version, controlled manually (see CreateGLContainer and dtor) // // Exceptions: // -can be thrown only during construction. // -under win32 class does not throw itself (but some internal operations can throw) // -under X11 can throw std::runtime_error (from CreateGLContext). // -In case of exceptions resources will be freed. // // TGLWidget object is immutable as far as it was created. // // Boolean parameter defines, if you want to grab user's input or not. // By default you want, but for example when not - see TGLPShapeObjEditor. // // Non-copyable. ClassImp(TGLWidget); //============================================================================== // TGLWidget - system-independent methods //============================================================================== //______________________________________________________________________________ TGLWidget* TGLWidget::CreateDummy() { // Static constructor for creating widget with default pixel format. TGLFormat format(Rgl::kNone); return Create(format, gClient->GetDefaultRoot(), kFALSE, kFALSE, 0, 1, 1); } //______________________________________________________________________________ TGLWidget* TGLWidget::Create(const TGWindow* parent, Bool_t selectInput, Bool_t shareDefault, const TGLPaintDevice *shareDevice, UInt_t width, UInt_t height) { // Static constructor for creating widget with default pixel format. TGLFormat format; return Create(format, parent, selectInput, shareDefault, shareDevice, width, height); } //______________________________________________________________________________ TGLWidget* TGLWidget::Create(const TGLFormat &format, const TGWindow* parent, Bool_t selectInput, Bool_t shareDefault, const TGLPaintDevice *shareDevice, UInt_t width, UInt_t height) { // Static constructor for creating widget with given pixel format. // Make sure window-system dependent part of GL-util is initialized. TGLUtil::InitializeIfNeeded(); std::pair innerData; Window_t wid = CreateWindow(parent, format, width, height, innerData); TGLWidget* glw = new TGLWidget(wid, parent, selectInput); #ifdef WIN32 glw->fWindowIndex = (Int_t) innerData.second; #elif defined(R__HAS_COCOA) glw->fWindowIndex = wid; #else glw->fWindowIndex = gVirtualX->AddWindow(wid, width, height); glw->fInnerData = innerData; #endif glw->fGLFormat = format; try { glw->SetFormat(); glw->fGLContext = new TGLContext (glw, shareDefault, shareDevice && !shareDefault ? shareDevice->GetContext() : 0); } catch (const std::exception &) { delete glw; throw; } glw->fFromInit = kFALSE; return glw; } //______________________________________________________________________________ TGLWidget::TGLWidget(Window_t glw, const TGWindow* p, Bool_t selectInput) : TGFrame(gClient, glw, p), fGLContext(0), fWindowIndex(-1), fGLFormat(Rgl::kNone), fFromInit(kTRUE), fEventHandler(0) { // Creates widget with default pixel format. if (selectInput) { gVirtualX->GrabButton(GetId(), kAnyButton, kAnyModifier, kButtonPressMask | kButtonReleaseMask, kNone, kNone); gVirtualX->SelectInput(GetId(), kKeyPressMask | kKeyReleaseMask | kExposureMask | kPointerMotionMask | kStructureNotifyMask | kFocusChangeMask | kEnterWindowMask | kLeaveWindowMask); gVirtualX->SetInputFocus(GetId()); } } //______________________________________________________________________________ TGLWidget::~TGLWidget() { //Destructor. Deletes window ???? and XVisualInfo #ifndef WIN32 #ifndef R__HAS_COCOA XFree(fInnerData.second);//free XVisualInfo #endif #endif if (fValidContexts.size() > 1u) { Warning("~TGLWidget", "There are some gl-contexts connected to this gl device" "which have longer lifetime than lifetime of gl-device"); } std::set::iterator it = fValidContexts.begin(); for (; it != fValidContexts.end(); ++it) { (*it)->Release(); } delete fGLContext; gVirtualX->SelectWindow(fWindowIndex); gVirtualX->CloseWindow(); } //______________________________________________________________________________ void TGLWidget::InitGL() { //Call glEnable(... in overrider of InitGL. } //______________________________________________________________________________ void TGLWidget::PaintGL() { //Do actual drawing in overrider of PaintGL. } //______________________________________________________________________________ Bool_t TGLWidget::MakeCurrent() { //Make the gl-context current. return fGLContext->MakeCurrent(); } //______________________________________________________________________________ Bool_t TGLWidget::ClearCurrent() { //Clear the current gl-context. return fGLContext->ClearCurrent(); } //______________________________________________________________________________ void TGLWidget::SwapBuffers() { //Swap buffers. fGLContext->SwapBuffers(); } //______________________________________________________________________________ const TGLContext *TGLWidget::GetContext()const { //Get gl context. return fGLContext; } //______________________________________________________________________________ const TGLFormat *TGLWidget::GetPixelFormat()const { //Pixel format. return &fGLFormat; } //______________________________________________________________________________ std::pair TGLWidget::GetInnerData()const { //Dpy*, XVisualInfo * return fInnerData; } //______________________________________________________________________________ void TGLWidget::AddContext(TGLContext *ctx) { //Register gl-context created for this window. fValidContexts.insert(ctx); } //______________________________________________________________________________ void TGLWidget::RemoveContext(TGLContext *ctx) { //Remove context (no real deletion, done by TGLContex dtor). std::set::iterator it = fValidContexts.find(ctx); if (it != fValidContexts.end()) fValidContexts.erase(it); } //______________________________________________________________________________ void TGLWidget::ExtractViewport(Int_t *vp)const { //For camera. vp[0] = 0; vp[1] = 0; vp[2] = GetWidth(); vp[3] = GetHeight(); } //============================================================================== // System specific methods and helper functions //============================================================================== //============================================================================== #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 fill_pfd(PIXELFORMATDESCRIPTOR *pfd, const TGLFormat &request) { pfd->nSize = sizeof(PIXELFORMATDESCRIPTOR); pfd->nVersion = 1; pfd->dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; if (request.IsDoubleBuffered()) pfd->dwFlags |= PFD_DOUBLEBUFFER; pfd->iPixelType = PFD_TYPE_RGBA; pfd->cColorBits = 24; if (UInt_t acc = request.GetAccumSize()) pfd->cAccumBits = acc; if (UInt_t depth = request.GetDepthSize()) pfd->cDepthBits = depth; if (UInt_t stencil = request.GetStencilSize()) pfd->cStencilBits = stencil; } void check_pixel_format(Int_t pixIndex, HDC hDC, TGLFormat &request) { PIXELFORMATDESCRIPTOR pfd = {}; if (!DescribePixelFormat(hDC, pixIndex, sizeof pfd, &pfd)) { Warning("TGLContext::SetContext", "DescribePixelFormat failed"); return; } if (pfd.cAccumBits) request.SetAccumSize(pfd.cAccumBits); if (pfd.cDepthBits) request.SetDepthSize(pfd.cDepthBits); if (pfd.cStencilBits) request.SetStencilSize(pfd.cStencilBits); } } //______________________________________________________________________________ Window_t TGLWidget::CreateWindow(const TGWindow* parent, const TGLFormat& /*format*/, UInt_t width, UInt_t height, std::pair& innerData) { // CreateWidget. // Static function called prior to widget construction, // I've extracted this code from ctors to make WIN32/X11 // separation simpler and because of gInterpreter usage. // new, TGLContext can throw // std::bad_alloc and std::runtime_error. Before try block, the only // resource allocated is pointed by fWindowIndex (InitWindow cannot throw). // In try block (and after successful constraction) // resources are controlled by std::auto_ptrs and dtor. Int_t widx = gVirtualX->InitWindow((ULong_t)parent->GetId()); innerData.second = (void*) widx; Window_t win = gVirtualX->GetWindowID(widx); gVirtualX->ResizeWindow(win, width, height); return win; } //______________________________________________________________________________ void TGLWidget::SetFormat() { // Set pixel format. // Resource - hDC, owned and freed by guard object. if (!fFromInit) { Error("TGLWidget::SetFormat", "Sorry, you should not call this function"); return; } if (!gVirtualX->IsCmdThread()) gROOT->ProcessLineFast(Form("((TGLWidget *)0x%lx)->SetFormat()", this)); R__LOCKGUARD2(gROOTMutex); LayoutCompatible_t *trick = reinterpret_cast(GetId()); HWND hWND = *trick->fPHwnd; HDC hDC = GetWindowDC(hWND); if (!hDC) { Error("TGLWidget::SetFormat", "GetWindowDC failed"); throw std::runtime_error("GetWindowDC failed"); } const Rgl::TGuardBase &dcGuard = Rgl::make_guard(ReleaseDC, hWND, hDC); PIXELFORMATDESCRIPTOR pfd = {}; fill_pfd(&pfd, fGLFormat); if (const Int_t pixIndex = ChoosePixelFormat(hDC, &pfd)) { check_pixel_format(pixIndex, hDC, fGLFormat); if (!SetPixelFormat(hDC, pixIndex, &pfd)) { Error("TGLWidget::SetFormat", "SetPixelFormat failed"); throw std::runtime_error("SetPixelFormat failed"); } } else { Error("TGLWidget::SetFormat", "ChoosePixelFormat failed"); throw std::runtime_error("ChoosePixelFormat failed"); } } //============================================================================== #elif defined(R__HAS_COCOA) //MacOSX with Cocoa enabled. //============================================================================== //______________________________________________________________________________ Window_t TGLWidget::CreateWindow(const TGWindow* parent, const TGLFormat &format, UInt_t width, UInt_t height, std::pair& /*internalData*/) { // CreateWidget - MacOSX/Cocoa version. // Static function called prior to construction. typedef std::pair component_type; std::vectorformatComponents; if (format.HasDepth()) formatComponents.push_back(component_type(Rgl::kDepth, format.GetDepthSize())); if (format.HasStencil()) formatComponents.push_back(component_type(Rgl::kStencil, format.GetStencilSize())); if (format.HasAccumBuffer()) formatComponents.push_back(component_type(Rgl::kAccum, format.GetAccumSize())); if (format.IsDoubleBuffered()) formatComponents.push_back(component_type(Rgl::kDoubleBuffer, 0)); if (format.IsStereo()) formatComponents.push_back(component_type(Rgl::kStereo, 0)); if (format.HasMultiSampling()) formatComponents.push_back(component_type(Rgl::kMultiSample, format.GetSamples())); return gVirtualX->CreateOpenGLWindow(parent->GetId(), width, height, formatComponents); } //______________________________________________________________________________ void TGLWidget::SetFormat() { // Set pixel format. // Empty version for X11. } //============================================================================== #else // X11 //============================================================================== namespace { void fill_format(std::vector &format, const TGLFormat &request) { format.push_back(GLX_RGBA); format.push_back(GLX_RED_SIZE); format.push_back(8); format.push_back(GLX_GREEN_SIZE); format.push_back(8); format.push_back(GLX_BLUE_SIZE); format.push_back(8); if (request.IsDoubleBuffered()) format.push_back(GLX_DOUBLEBUFFER); if (request.HasDepth()) { format.push_back(GLX_DEPTH_SIZE); format.push_back(request.GetDepthSize()); } if (request.HasStencil()) { format.push_back(GLX_STENCIL_SIZE); format.push_back(request.GetStencilSize()); } if (request.HasAccumBuffer()) { format.push_back(GLX_ACCUM_RED_SIZE); format.push_back(8); format.push_back(GLX_ACCUM_GREEN_SIZE); format.push_back(8); format.push_back(GLX_ACCUM_BLUE_SIZE); format.push_back(8); } if (request.IsStereo()) { format.push_back(GLX_STEREO); } if (request.HasMultiSampling()) { format.push_back(GLX_SAMPLE_BUFFERS_ARB); format.push_back(1); format.push_back(GLX_SAMPLES_ARB); format.push_back(request.GetSamples()); } format.push_back(None); } } //______________________________________________________________________________ Window_t TGLWidget::CreateWindow(const TGWindow* parent, const TGLFormat &format, UInt_t width, UInt_t height, std::pair& innerData) { // CreateWidget - X11 version. // Static function called prior to construction. // Can throw std::bad_alloc and std::runtime_error. // This version is bad - I do not check the results of // X11 calls. std::vector glxfmt; fill_format(glxfmt, format); Display *dpy = reinterpret_cast(gVirtualX->GetDisplay()); if (!dpy) { ::Error("TGLWidget::CreateWindow", "Display is not set!"); throw std::runtime_error("Display is not set!"); } XVisualInfo *visInfo = glXChooseVisual(dpy, DefaultScreen(dpy), &glxfmt[0]); if (!visInfo) { ::Error("TGLWidget::CreateWindow", "No good OpenGL visual found!"); throw std::runtime_error("No good OpenGL visual found!"); } Window_t winID = parent->GetId(); XSetWindowAttributes attr; attr.colormap = XCreateColormap(dpy, winID, visInfo->visual, AllocNone); // Can fail? attr.background_pixel = 0; attr.event_mask = NoEventMask; attr.backing_store = Always; attr.bit_gravity = NorthWestGravity; ULong_t mask = CWBackPixel | CWColormap | CWEventMask | CWBackingStore | CWBitGravity; Window glWin = XCreateWindow(dpy, winID, 0, 0, width, height, 0, visInfo->depth, InputOutput, visInfo->visual, mask, &attr); innerData.first = dpy; innerData.second = visInfo; return glWin; } //______________________________________________________________________________ void TGLWidget::SetFormat() { // Set pixel format. // Empty version for X11. } //============================================================================== #endif //============================================================================== //============================================================================== // Event handling //============================================================================== //______________________________________________________________________________ void TGLWidget::SetEventHandler(TGEventHandler *eh) { //Set event-handler. All events are passed to this object. fEventHandler = eh; } //______________________________________________________________________________ Bool_t TGLWidget::HandleCrossing(Event_t *ev) { // Handle mouse crossing event. if (!gVirtualX->IsCmdThread()) { gROOT->ProcessLineFast(Form("((TGLWidget *)0x%lx)->HandleCrossing((Event_t *)0x%lx)", (ULong_t)this, (ULong_t)ev)); return kTRUE; } R__LOCKGUARD2(gROOTMutex); if ((ev->fType == kEnterNotify) && (!gVirtualX->InheritsFrom("TGX11")) && (gVirtualX->GetInputFocus() != GetId())) { gVirtualX->SetInputFocus(GetId()); } if (fEventHandler) return fEventHandler->HandleCrossing(ev); return kFALSE; } //______________________________________________________________________________ Bool_t TGLWidget::HandleButton(Event_t *ev) { //Delegate call to the owner. if (!gVirtualX->IsCmdThread()) { gROOT->ProcessLineFast(Form("((TGLWidget *)0x%lx)->HandleButton((Event_t *)0x%lx)", (ULong_t)this, (ULong_t)ev)); return kTRUE; } R__LOCKGUARD2(gROOTMutex); if (fEventHandler) return fEventHandler->HandleButton(ev); return kFALSE; } //______________________________________________________________________________ Bool_t TGLWidget::HandleDoubleClick(Event_t *ev) { //Delegate call to the owner. if (!gVirtualX->IsCmdThread()) { gROOT->ProcessLineFast(Form("((TGLWidget *)0x%lx)->HandleDoubleClick((Event_t *)0x%lx)", (ULong_t)this, (ULong_t)ev)); return kTRUE; } R__LOCKGUARD2(gROOTMutex); if (fEventHandler) return fEventHandler->HandleDoubleClick(ev); return kFALSE; } //______________________________________________________________________________ Bool_t TGLWidget::HandleConfigureNotify(Event_t *ev) { //Delegate call to the owner. if (!gVirtualX->IsCmdThread()) { gROOT->ProcessLineFast(Form("((TGLWidget *)0x%lx)->HandleConfigureNotify((Event_t *)0x%lx)", (ULong_t)this, (ULong_t)ev)); return kTRUE; } R__LOCKGUARD2(gROOTMutex); if (fEventHandler && fEventHandler->HandleConfigureNotify(ev)) { TGFrame::HandleConfigureNotify(ev); return kTRUE; } return kFALSE; } //______________________________________________________________________________ Bool_t TGLWidget::HandleFocusChange(Event_t *ev) { //Delegate call to the owner. if (!gVirtualX->IsCmdThread()) { gROOT->ProcessLineFast(Form("((TGLWidget *)0x%lx)->HandleFocusChange((Event_t *)0x%lx)", (ULong_t)this, (ULong_t)ev)); return kTRUE; } R__LOCKGUARD2(gROOTMutex); if (fEventHandler) return fEventHandler->HandleFocusChange(ev); return kFALSE; } //______________________________________________________________________________ Bool_t TGLWidget::HandleKey(Event_t *ev) { //Delegate call to the owner. if (!gVirtualX->IsCmdThread()) { gROOT->ProcessLineFast(Form("((TGLWidget *)0x%lx)->HandleKey((Event_t *)0x%lx)", (ULong_t)this, (ULong_t)ev)); return kTRUE; } R__LOCKGUARD2(gROOTMutex); if (fEventHandler) return fEventHandler->HandleKey(ev); return kFALSE; } //______________________________________________________________________________ Bool_t TGLWidget::HandleMotion(Event_t *ev) { //Delegate call to the owner. if (!gVirtualX->IsCmdThread()) { gROOT->ProcessLineFast(Form("((TGLWidget *)0x%lx)->HandleMotion((Event_t *)0x%lx)", (ULong_t)this, (ULong_t)ev)); return kTRUE; } R__LOCKGUARD2(gROOTMutex); if (fEventHandler) return fEventHandler->HandleMotion(ev); return kFALSE; } //______________________________________________________________________________ void TGLWidget::DoRedraw() { //Delegate call to the owner. // if (!gVirtualX->IsCmdThread()) { // gROOT->ProcessLineFast(Form("((TGLWidget *)0x%lx)->DoRedraw()", this)); // return; // } // R__LOCKGUARD2(gROOTMutex); if (fEventHandler) return fEventHandler->Repaint(); }