fltk/src/Fl_Gl_Window.cxx

294 lines
7.3 KiB
C++
Raw Normal View History

// 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
////////////////////////////////////////////////////////////////
HDC fl_GetDC(HWND);
int Fl_Gl_Window::can_do(int a, const int *b) {
#ifdef WIN32
Fl_Gl_Choice *g = Fl_Gl_Choice::find(a,b);
/*
Is this necessary? Don't all windows have the same
support for pixel formats?
HWND w = GetDesktopWindow();
HDC dc = GetDC(w);
*/
if (!fl_gc) fl_GetDC(0);
int r = ChoosePixelFormat(fl_gc, &g->pfd);
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