summaryrefslogtreecommitdiff
path: root/src/Fl_Gl_Window.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/Fl_Gl_Window.cxx')
-rw-r--r--src/Fl_Gl_Window.cxx287
1 files changed, 287 insertions, 0 deletions
diff --git a/src/Fl_Gl_Window.cxx b/src/Fl_Gl_Window.cxx
new file mode 100644
index 000000000..acd08e6f3
--- /dev/null
+++ b/src/Fl_Gl_Window.cxx
@@ -0,0 +1,287 @@
+// Fl_Gl_Window.C
+
+#include <config.h>
+#if HAVE_GL
+
+#include <FL/Fl.H>
+#include <FL/x.H>
+#include <FL/Fl_Gl_Window.H>
+#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