// Fl_Gl_Window.C #include #if HAVE_GL #include #include #include #include "Fl_Gl_Choice.H" //////////////////////////////////////////////////////////////// // The symbol SWAP_TYPE defines what is in the back buffer after doing // a glXSwapBuffers(). // The OpenGl documentation says that the contents of the backbuffer // are "undefined" after glXSwapBuffers(). However, if we know what // is in the backbuffers then we can save a good deal of time. For // this reason you can define some symbols to describe what is left in // the back buffer. // The default of SWAP_SWAP works on an SGI, and will also work (but // is sub-optimal) on machines that should be SWAP_COPY or SWAP_NODAMAGE. // The win32 emulation of OpenGL can use COPY, but some (all?) OpenGL // cards use SWAP. // contents of back buffer after glXSwapBuffers(): #define UNDEFINED 0 // unknown #define SWAP 1 // former front buffer #define COPY 2 // unchanged #define NODAMAGE 3 // unchanged even by X expose() events #ifdef MESA #define SWAP_TYPE NODAMAGE #else #define SWAP_TYPE SWAP #endif //////////////////////////////////////////////////////////////// int Fl_Gl_Window::can_do(int a, const int *b) { #ifdef WIN32 Fl_Gl_Choice *g = Fl_Gl_Choice::find(a,b); HWND w = GetDesktopWindow(); HDC dc = GetDC(w); int r = ChoosePixelFormat(dc, &g->pfd); ReleaseDC(w,dc); return r != 0; #else return Fl_Gl_Choice::find(a,b) != 0; #endif } void Fl_Gl_Window::show() { #ifndef WIN32 if (!shown()) { if (!g) { g = Fl_Gl_Choice::find(mode_,alist); if (!g) {Fl::error("Insufficient GL support"); return;} } Fl_X::make_xid(this, g->vis, g->colormap); if (overlay && overlay != this) ((Fl_Gl_Window*)overlay)->show(); } #endif Fl_Window::show(); } void Fl_Gl_Window::invalidate() { valid(0); #ifndef WIN32 if (overlay) ((Fl_Gl_Window*)overlay)->valid(0); #endif } extern GLXContext fl_first_context; // in Fl_Gl_Choice.C int Fl_Gl_Window::mode(int m, const int *a) { if (m == mode_ && a == alist) return 0; mode_ = m; alist = a; #ifdef WIN32 // destroy context and g: if (shown()) {hide(); show();} #else // under X, if the visual changes we must make a new X window (!): if (shown()) { Fl_Gl_Choice *g1 = g; g = Fl_Gl_Choice::find(mode_,alist); if (!g || g->vis->visualid != g1->vis->visualid || g->d != g1->d) { hide(); show(); } } #endif return 1; } #ifdef WIN32 extern char fl_direct_paint; // true when responding to WM_PAINT #endif void Fl_Gl_Window::make_current() { #ifdef WIN32 HDC hdc = fl_private_dc(this, mode_,&g); if (!context) { context = wglCreateContext(hdc); if (fl_first_context) wglShareLists(fl_first_context, (GLXContext)context); else fl_first_context = (GLXContext)context; valid(0); } wglMakeCurrent(hdc, (GLXContext)context); #else if (!context) { context = glXCreateContext(fl_display, g->vis, fl_first_context, 1); if (!fl_first_context) fl_first_context = (GLXContext)context; valid(0); } glXMakeCurrent(fl_display, fl_xid(this), (GLXContext)context); #endif glDrawBuffer(GL_BACK); } void Fl_Gl_Window::ortho() { glLoadIdentity(); glViewport(0, 0, w(), h()); glOrtho(0, w(), 0, h(), -1, 1); } void Fl_Gl_Window::swap_buffers() { #ifdef WIN32 SwapBuffers(Fl_X::i(this)->private_dc); #else glXSwapBuffers(fl_display, fl_xid(this)); #endif } #if HAVE_GL_OVERLAY #if WIN32 uchar fl_overlay; // changes how fl_color() works #endif #endif void Fl_Gl_Window::flush() { make_current(); #if HAVE_GL_OVERLAY #ifdef WIN32 uchar save_valid = valid_; if (overlay && overlay!= this && damage() == 8) goto DRAW_OVERLAY_ONLY; #endif #endif if (g->d) { #if SWAP_TYPE == NODAMAGE // don't draw if only overlay damage or expose events: if ((damage()&~0xA0) || !valid()) draw(); swap_buffers(); #elif SWAP_TYPE == COPY // don't draw if only the overlay is damaged: if (damage() != 8 || !valid()) draw(); swap_buffers(); #else // SWAP_TYPE == SWAP || SWAP_TYPE == UNDEFINED if (overlay == this) { // Use CopyPixels to act like SWAP_TYPE == COPY // don't draw if only the overlay is damaged: if (damage1_ || damage() != 8 || !valid()) draw(); // we use a seperate context for the copy because rasterpos must be 0 // and depth test needs to be off: static GLXContext ortho_context; int init = !ortho_context; #ifdef WIN32 if (init) ortho_context = wglCreateContext(Fl_X::i(this)->private_dc); wglMakeCurrent(Fl_X::i(this)->private_dc, ortho_context); #else if (init) ortho_context = glXCreateContext(fl_display,g->vis,fl_first_context,1); glXMakeCurrent(fl_display, fl_xid(this), ortho_context); #endif if (init) { glDisable(GL_DEPTH_TEST); glReadBuffer(GL_BACK); glDrawBuffer(GL_FRONT); } glCopyPixels(0,0,w(),h(),GL_COLOR); make_current(); // set current context back to draw overlay damage1_ = 0; } else { #if SWAP_TYPE == SWAP uchar old_damage = damage(); clear_damage(damage1_|old_damage); draw(); swap_buffers(); damage1_ = old_damage; #else // SWAP_TYPE == UNDEFINED clear_damage(~0); draw(); swap_buffers(); damage1_ = ~0; #endif } #endif if (overlay==this) { // fake overlay in front buffer glDrawBuffer(GL_FRONT); draw_overlay(); glDrawBuffer(GL_BACK); glFlush(); } } else { // single-buffered context is simpler: // this faking of the overlay is incorrect but worked good for // one in-house program: if (overlay != this || damage()!=8 || !Fl::pushed()) draw(); if (overlay == this) draw_overlay(); glFlush(); } #if HAVE_GL_OVERLAY #ifdef WIN32 if (overlay && overlay != this) { DRAW_OVERLAY_ONLY: valid_ = save_valid; wglMakeCurrent(Fl_X::i(this)->private_dc, (GLXContext)overlay); glDisable(GL_SCISSOR_TEST); fl_overlay = 1; glClear(GL_COLOR_BUFFER_BIT); draw_overlay(); wglSwapLayerBuffers(Fl_X::i(this)->private_dc,WGL_SWAP_OVERLAY1); fl_overlay = 0; } #endif #endif valid(1); } void Fl_Gl_Window::resize(int X,int Y,int W,int H) { if (W != w() || H != h()) valid(0); Fl_Window::resize(X,Y,W,H); } void Fl_Gl_Window::hide() { if (context) { #ifdef WIN32 wglMakeCurrent(0, 0); if (context && context != fl_first_context) wglDeleteContext((GLXContext)context); g = 0; #else glXMakeCurrent(fl_display, 0, 0); if (context != fl_first_context) glXDestroyContext(fl_display, (GLXContext)context); #ifdef GLX_MESA_release_buffers glXReleaseBuffersMESA(fl_display, fl_xid(this)); #endif #endif context = 0; } Fl_Window::hide(); } Fl_Gl_Window::~Fl_Gl_Window() { hide(); // delete overlay; this is done by ~Fl_Group } void Fl_Gl_Window::init() { end(); // we probably don't want any children box(FL_NO_BOX); mode_ = FL_RGB | FL_DEPTH | FL_DOUBLE; alist = 0; context = 0; g = 0; overlay = 0; damage1_ = 0; } void Fl_Gl_Window::draw_overlay() {} #endif