// Copyright (C) 2010, Guy Barrand. All rights reserved. // See the file tools.license for terms. #ifndef tools_Xt_OpenGLArea #define tools_Xt_OpenGLArea #include #include #include #include #include #include #include #include namespace tools { namespace Xt { /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// typedef struct _OpenGLAreaClassRec* OpenGLAreaWidgetClass; typedef struct _OpenGLAreaRec* OpenGLAreaWidget; typedef struct { int reason; XEvent* event; } XoAnyCallbackStruct; #define XoNdoubleBufferOn "doubleBufferOn" #define XoNpaintCallback "paintCallback" #define XoNeventCallback "eventCallback" #define XoCR_PAINT 1 #define XoCR_EVENT 2 /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// typedef struct { void* extension; } OpenGLAreaClassPart; typedef struct _OpenGLAreaClassRec { CoreClassPart core_class; CompositeClassPart composite_class; OpenGLAreaClassPart openGLArea_class; } OpenGLAreaClassRec; typedef struct { Boolean doubleBufferOn; XtCallbackList paintCallback; XtCallbackList eventCallback; Visual* visual; Boolean installColormap; GLXContext glContext; } OpenGLAreaPart; typedef struct _OpenGLAreaRec { CorePart core; CompositePart composite; OpenGLAreaPart openGLArea; } OpenGLAreaRec; /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// class OpenGLArea { public: static void paint(Widget a_this) { if(!XtIsRealized(a_this)) return; if(make_current(a_this)==1) { XoAnyCallbackStruct value; value.reason = XoCR_PAINT; value.event = 0; ::XtCallCallbacks(a_this,XoNpaintCallback,(XtPointer)&value); ::glXSwapBuffers(XtDisplay(a_this),XtWindow(a_this)); ::glXMakeCurrent(XtDisplay(a_this),None,NULL); } } protected: static void initialize_class(void) {} static void initialize_widget(Widget a_request,Widget a_this,ArgList,Cardinal*) { if(a_request->core.width<=0) a_this->core.width = 100; if(a_request->core.height<=0) a_this->core.height = 100; #ifdef OPENGLAREA_DEBUG ::printf ("debug : OpenGLArea : InitializeWidget : %s\n",::XtName(a_this)); #endif OpenGLAreaPart& athis = _athis(a_this); athis.visual = CopyFromParent; athis.installColormap = False; athis.glContext = NULL; Display* display; Screen* screen; int iscreen; XVisualInfo* vinfo; display = XtDisplay(a_this); screen = XtScreen(a_this); iscreen = XScreenNumberOfScreen(screen); {int error,event; if(::glXQueryExtension(display,&error,&event)==0) { CWarn ("X server does not have OpenGL extensions.\n"); }} if(athis.doubleBufferOn==True) { int atbs_1[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_ALPHA_SIZE, 1, GLX_DEPTH_SIZE, 1, GLX_DOUBLEBUFFER, None }; vinfo = ::glXChooseVisual (display,iscreen,atbs_1); if(vinfo==NULL) { //GLX_ALPHA_SIZE : problem with Xming. Try without it. int atbs_2[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_DEPTH_SIZE, 1, GLX_DOUBLEBUFFER, None }; vinfo = ::glXChooseVisual (display,iscreen,atbs_2); } } else { int atbs_1[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_ALPHA_SIZE, 1, GLX_DEPTH_SIZE, 1, None }; vinfo = ::glXChooseVisual (display,iscreen,atbs_1); if(vinfo==NULL) { //GLX_ALPHA_SIZE : problem with Xming. Try without it. int atbs_2[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_DEPTH_SIZE, 1, None }; vinfo = ::glXChooseVisual (display,iscreen,atbs_2); } } if(vinfo==NULL) { CWarn ("Can't choose a visual.\n"); } else { a_this->core.depth = vinfo->depth; athis.visual = vinfo->visual; if( (vinfo->depth ==DefaultDepth (display,iscreen)) && (vinfo->visual==DefaultVisual(display,iscreen)) ) { a_this->core.colormap = XDefaultColormap (display,iscreen); athis.installColormap = False; } else { a_this->core.colormap = XCreateColormap (display,XRootWindow(display,iscreen),vinfo->visual, AllocNone); athis.installColormap = True; } if(a_this->core.colormap==0L) { CWarn ("Can't get/create a X colormap.\n"); } athis.glContext = ::glXCreateContext (display,vinfo,NULL,GL_TRUE); if(athis.glContext==NULL) { athis.glContext = ::glXCreateContext (display,vinfo,NULL,GL_FALSE); if(athis.glContext==NULL) { CWarn ("Can't create a GLX context.\n"); } } ::XFree(vinfo); } ::XtAddEventHandler (a_this,ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|KeyPressMask|KeyReleaseMask,0,event_handler,NULL); #ifdef OPENGLAREA_DEBUG printf("debug : OpenGLArea : InitializeWidget : end\n"); #endif } static void realize_widget(Widget a_this,XtValueMask* a_mask,XSetWindowAttributes* a_watbs) { #ifdef OPENGLAREA_DEBUG printf("debug : OpenGLArea : realize_widget : %s\n",XtName(a_this)); #endif // Have to create window ourselves due to OpenGL that compells it's visual. // In principle colormap is correctly set in a_watbsy. OpenGLAreaPart& athis = _athis(a_this); ::XtCreateWindow(a_this,(unsigned int)InputOutput,athis.visual,*a_mask,a_watbs); // Call the Realize procedure (XtInheritRealize) : if(widget_class()->core_class.superclass->core_class.realize!=NULL) (widget_class()->core_class.superclass->core_class.realize)(a_this, a_mask, a_watbs); // If window is delete, all seems ok : if(athis.installColormap==True) install_colormap(a_this); //make_current(a_this); #ifdef OPENGLAREA_DEBUG printf("debug : OpenGLArea : realize_widget : end\n"); #endif } static void destroy_widget(Widget a_this) { #ifdef OPENGLAREA_DEBUG printf("debug : OpenGLArea : destroy_widget : begin\n"); #endif OpenGLAreaPart& athis = _athis(a_this); if(athis.installColormap==True) { uninstall_colormap (a_this); athis.installColormap = False; ::XFreeColormap(XtDisplay(a_this),a_this->core.colormap); } if(athis.glContext!=NULL) { ::glXMakeCurrent(XtDisplay(a_this),None,NULL); ::glXDestroyContext(XtDisplay(a_this),athis.glContext); athis.glContext = NULL; } #ifdef OPENGLAREA_DEBUG printf("debug : OpenGLArea : destroy_widget : end\n"); #endif } static Boolean set_values(Widget a_current,Widget,Widget a_this,ArgList,Cardinal*) { OpenGLAreaPart& athis = _athis(a_this); if(athis.doubleBufferOn != ((OpenGLAreaWidget)a_current)->openGLArea.doubleBufferOn) { // Can't change buffering here if X window is created. // With OpenGL, buffering fix parameter of the X window. // Buffering must be choosen before the execution of the // Realize method that create the window. if(XtIsRealized(a_this) && (athis.installColormap==True)) { CWarn("Can't change buffering after \"realization\" of the widget.\n"); athis.doubleBufferOn = ((OpenGLAreaWidget)a_current)->openGLArea.doubleBufferOn; } } return False; } static void change_widget_size(Widget a_this) { #ifdef OPENGLAREA_DEBUG printf("debug : OpenGLArea : change_widget_size : %s\n",XtName(a_this)); #endif // Call the Resize procedure (XtInheritResize) : if(widget_class()->core_class.superclass->core_class.resize!=NULL) (widget_class()->core_class.superclass->core_class.resize)(a_this); #ifdef OPENGLAREA_DEBUG printf("debug : OpenGLArea : change_widget_size : end\n"); #endif } static void draw_widget(Widget a_this,XEvent* a_event,Region a_region) { #ifdef OPENGLAREA_DEBUG printf("debug : OpenGLArea : draw_widget : %s\n",XtName(a_this)); #endif if(widget_class()->core_class.superclass->core_class.expose!=NULL) (widget_class()->core_class.superclass->core_class.expose)(a_this,a_event,a_region); if(make_current(a_this)==1) { #ifdef OPENGLAREA_DEBUG printf("debug : OpenGLArea : draw_widget : %s : make_current ok : call paintCallback...\n",XtName(a_this)); #endif XoAnyCallbackStruct value; value.reason = XoCR_PAINT; value.event = a_event; ::XtCallCallbacks(a_this,XoNpaintCallback,(XtPointer)&value); ::glXSwapBuffers(XtDisplay(a_this),XtWindow(a_this)); ::glXMakeCurrent(XtDisplay(a_this),None,NULL); } #ifdef OPENGLAREA_DEBUG printf("debug : OpenGLArea : draw_widget : end\n"); #endif } protected: static OpenGLAreaPart& _athis(Widget a_this) {return *(&(((OpenGLAreaWidget)a_this)->openGLArea));} static void CWarn(const char* a_msg) { ::printf("%s",a_msg); } static void CWarnF(const char* a_format,...) { va_list args; va_start(args,a_format); ::vprintf(a_format,args); va_end(args); } static int make_current(Widget a_this) { if(!XtIsRealized(a_this)) return 0; OpenGLAreaPart& athis = _athis(a_this); if(athis.glContext==NULL) return 0; return (int)::glXMakeCurrent(XtDisplay(a_this),XtWindow(a_this),athis.glContext); } static void event_handler(Widget a_this,XtPointer,XEvent* a_event ,Boolean*) { XoAnyCallbackStruct value; value.reason = XoCR_EVENT; value.event = a_event; ::XtCallCallbacks(a_this,XoNeventCallback,(XtPointer)&value); } static void install_colormap(Widget a_this) { // From Mesa/widgets/GLwDrawingArea.c/post_colormap routine. // Could use also XtSetWMColormapWindows. if( !XtIsWidget(a_this) || !XtIsRealized(a_this) ) return; Widget shell = get_shell(a_this); if(shell==NULL) return; Display* display = XtDisplay (a_this); Window wthis = XtWindow (a_this); Window wshell = XtWindow (shell); Window* ws = NULL; int wn = 0; ::XGetWMColormapWindows (display,wshell, &ws, &wn); // Check if colormap of this is a colormap of a Window in list : XWindowAttributes watbs; XGetWindowAttributes (display,wthis,&watbs); Colormap cmapthis = watbs.colormap; int found = -1; for(int count=0;count