themes: Workaround for bug where a background color of RGB 0,0,0 in Black color schem...
[ntk.git] / src / Fl_Gl_Window.cxx
blob6d71c384de6e8a7870129c32feed6c092cacfee5
1 //
2 // "$Id: Fl_Gl_Window.cxx 8657 2011-05-12 11:50:43Z manolo $"
3 //
4 // OpenGL window code for the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 1998-2010 by Bill Spitzak and others.
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Library General Public License for more details.
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 // USA.
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
28 #include "flstring.h"
29 #if HAVE_GL
31 extern int fl_gl_load_plugin;
33 static int temp = fl_gl_load_plugin;
35 #include <FL/Fl.H>
36 #include <FL/x.H>
37 #ifdef __APPLE__
38 #include <FL/gl.h>
39 #endif
40 #include "Fl_Gl_Choice.H"
41 #include <FL/Fl_Gl_Window.H>
42 #include <stdlib.h>
43 #include <FL/fl_utf8.h>
45 ////////////////////////////////////////////////////////////////
47 // The symbol SWAP_TYPE defines what is in the back buffer after doing
48 // a glXSwapBuffers().
50 // The OpenGl documentation says that the contents of the backbuffer
51 // are "undefined" after glXSwapBuffers(). However, if we know what
52 // is in the backbuffers then we can save a good deal of time. For
53 // this reason you can define some symbols to describe what is left in
54 // the back buffer.
56 // Having not found any way to determine this from glx (or wgl) I have
57 // resorted to letting the user specify it with an environment variable,
58 // GL_SWAP_TYPE, it should be equal to one of these symbols:
60 // contents of back buffer after glXSwapBuffers():
61 #define UNDEFINED 1 // anything
62 #define SWAP 2 // former front buffer (same as unknown)
63 #define COPY 3 // unchanged
64 #define NODAMAGE 4 // unchanged even by X expose() events
66 static char SWAP_TYPE = 0 ; // 0 = determine it from environment variable
68 ////////////////////////////////////////////////////////////////
70 /** Returns non-zero if the hardware supports the given or current OpenGL mode. */
71 int Fl_Gl_Window::can_do(int a, const int *b) {
72 return Fl_Gl_Choice::find(a,b) != 0;
75 void Fl_Gl_Window::show() {
76 #if defined(__APPLE__)
77 int need_redraw = 0;
78 #endif
79 if (!shown()) {
80 if (!g) {
81 g = Fl_Gl_Choice::find(mode_,alist);
83 if (!g && (mode_ & FL_DOUBLE) == FL_SINGLE) {
84 g = Fl_Gl_Choice::find(mode_ | FL_DOUBLE,alist);
85 if (g) mode_ |= FL_FAKE_SINGLE;
88 if (!g) {
89 Fl::error("Insufficient GL support");
90 return;
93 #if !defined(WIN32) && !defined(__APPLE__)
94 Fl_X::make_xid(this, g->vis, g->colormap);
95 if (overlay && overlay != this) ((Fl_Gl_Window*)overlay)->show();
96 #elif defined(__APPLE__)
97 if( ! parent() ) need_redraw=1;
98 #endif
100 Fl_Window::show();
102 #ifdef __APPLE__
103 set_visible();
104 if(need_redraw) redraw();//necessary only after creation of a top-level GL window
105 #endif /* __APPLE__ */
109 The invalidate() method turns off valid() and is
110 equivalent to calling value(0).
112 void Fl_Gl_Window::invalidate() {
113 valid(0);
114 context_valid(0);
115 #ifndef WIN32
116 if (overlay) {
117 ((Fl_Gl_Window*)overlay)->valid(0);
118 ((Fl_Gl_Window*)overlay)->context_valid(0);
120 #endif
124 See const int Fl_Gl_Window::mode() const
126 int Fl_Gl_Window::mode(int m, const int *a) {
127 if (m == mode_ && a == alist) return 0;
128 #ifndef __APPLE__
129 int oldmode = mode_;
130 #endif // !__APPLE__
131 #if !defined(WIN32) && !defined(__APPLE__)
132 Fl_Gl_Choice* oldg = g;
133 #endif // !WIN32 && !__APPLE__
134 context(0);
135 mode_ = m; alist = a;
136 if (shown()) {
137 g = Fl_Gl_Choice::find(m, a);
139 #if defined(USE_X11)
140 // under X, if the visual changes we must make a new X window (yuck!):
141 if (!g || g->vis->visualid!=oldg->vis->visualid || (oldmode^m)&FL_DOUBLE) {
142 hide();
143 show();
145 #elif defined(WIN32)
146 if (!g || (oldmode^m)&(FL_DOUBLE|FL_STEREO)) {
147 hide();
148 show();
150 #elif defined(__APPLE_QUARTZ__)
151 // warning: the Quartz version should probably use Core GL (CGL) instead of AGL
152 redraw();
153 #else
154 # error unsupported platform
155 #endif
156 } else {
157 g = 0;
159 return 1;
162 #define NON_LOCAL_CONTEXT 0x80000000
165 The make_current() method selects the OpenGL context for the
166 widget. It is called automatically prior to the draw() method
167 being called and can also be used to implement feedback and/or
168 selection within the handle() method.
171 void Fl_Gl_Window::make_current() {
172 // puts("Fl_Gl_Window::make_current()");
173 // printf("make_current: context_=%p\n", context_);
174 if (!context_) {
175 mode_ &= ~NON_LOCAL_CONTEXT;
176 context_ = fl_create_gl_context(this, g);
177 valid(0);
178 context_valid(0);
180 fl_set_gl_context(this, context_);
182 #ifdef __APPLE__
183 // Set the buffer rectangle here, since in resize() we won't have the
184 // correct parent window size to work with...
185 GLint xywh[4];
187 if (window()) {
188 xywh[0] = x();
189 xywh[1] = window()->h() - y() - h();
190 } else {
191 xywh[0] = 0;
192 xywh[1] = 0;
195 xywh[2] = w();
196 xywh[3] = h();
198 aglSetInteger(context_, AGL_BUFFER_RECT, xywh);
199 aglEnable(context_, AGL_BUFFER_RECT);
200 // printf("make_current: xywh=[%d %d %d %d]\n", xywh[0], xywh[1], xywh[2], xywh[3]);
201 #endif // __APPLE__
203 #if defined(WIN32) && USE_COLORMAP
204 if (fl_palette) {
205 fl_GetDC(fl_xid(this));
206 SelectPalette(fl_gc, fl_palette, FALSE);
207 RealizePalette(fl_gc);
209 #endif // USE_COLORMAP
210 if (mode_ & FL_FAKE_SINGLE) {
211 glDrawBuffer(GL_FRONT);
212 glReadBuffer(GL_FRONT);
214 current_ = this;
218 Sets the projection so 0,0 is in the lower left of the window and each
219 pixel is 1 unit wide/tall. If you are drawing 2D images, your
220 draw() method may want to call this if valid() is false.
222 void Fl_Gl_Window::ortho() {
223 // Alpha NT seems to have a broken OpenGL that does not like negative coords:
224 #ifdef _M_ALPHA
225 glLoadIdentity();
226 glViewport(0, 0, w(), h());
227 glOrtho(0, w(), 0, h(), -1, 1);
228 #else
229 GLint v[2];
230 glGetIntegerv(GL_MAX_VIEWPORT_DIMS, v);
231 glLoadIdentity();
232 glViewport(w()-v[0], h()-v[1], v[0], v[1]);
233 glOrtho(w()-v[0], w(), h()-v[1], h(), -1, 1);
234 #endif
238 The swap_buffers() method swaps the back and front buffers.
239 It is called automatically after the draw() method is called.
241 void Fl_Gl_Window::swap_buffers() {
242 #if defined(USE_X11)
243 glXSwapBuffers(fl_display, fl_xid(this));
244 #elif defined(WIN32)
245 # if HAVE_GL_OVERLAY
246 // Do not swap the overlay, to match GLX:
247 BOOL ret = wglSwapLayerBuffers(Fl_X::i(this)->private_dc, WGL_SWAP_MAIN_PLANE);
248 DWORD err = GetLastError();;
249 # else
250 SwapBuffers(Fl_X::i(this)->private_dc);
251 # endif
252 #elif defined(__APPLE_QUARTZ__)
253 if(overlay != NULL) {
254 //aglSwapBuffers does not work well with overlays under cocoa
255 glReadBuffer(GL_BACK);
256 glDrawBuffer(GL_FRONT);
257 glCopyPixels(0,0,w(),h(),GL_COLOR);
259 else
260 aglSwapBuffers((AGLContext)context_);
261 #else
262 # error unsupported platform
263 #endif
266 #if HAVE_GL_OVERLAY && defined(WIN32)
267 uchar fl_overlay; // changes how fl_color() works
268 int fl_overlay_depth = 0;
269 #endif
272 void Fl_Gl_Window::flush() {
273 uchar save_valid = valid_f_ & 1;
274 #if HAVE_GL_OVERLAY && defined(WIN32)
275 uchar save_valid_f = valid_f_;
276 #endif
278 #if defined(__APPLE_QUARTZ__)
279 // warning: the Quartz version should probably use Core GL (CGL) instead of AGL
280 //: clear previous clipping in this shared port
281 #if ! __LP64__
282 /*GrafPtr port = GetWindowPort( Fl_X::i(this)->window_ref() );
283 Rect rect; SetRect( &rect, 0, 0, 0x7fff, 0x7fff );
284 GrafPtr old; GetPort( &old );
285 SetPort( port );
286 ClipRect( &rect );
287 SetPort( old );*/
288 #endif
289 #endif
291 #if HAVE_GL_OVERLAY && defined(WIN32)
293 bool fixcursor = false; // for fixing the SGI 320 bug
295 // Draw into hardware overlay planes if they are damaged:
296 if (overlay && overlay != this
297 && (damage()&(FL_DAMAGE_OVERLAY|FL_DAMAGE_EXPOSE) || !save_valid)) {
298 // SGI 320 messes up overlay with user-defined cursors:
299 if (Fl_X::i(this)->cursor && Fl_X::i(this)->cursor != fl_default_cursor) {
300 fixcursor = true; // make it restore cursor later
301 SetCursor(0);
303 fl_set_gl_context(this, (GLContext)overlay);
304 if (fl_overlay_depth)
305 wglRealizeLayerPalette(Fl_X::i(this)->private_dc, 1, TRUE);
306 glDisable(GL_SCISSOR_TEST);
307 glClear(GL_COLOR_BUFFER_BIT);
308 fl_overlay = 1;
309 draw_overlay();
310 fl_overlay = 0;
311 valid_f_ = save_valid_f;
312 wglSwapLayerBuffers(Fl_X::i(this)->private_dc, WGL_SWAP_OVERLAY1);
313 // if only the overlay was damaged we are done, leave main layer alone:
314 if (damage() == FL_DAMAGE_OVERLAY) {
315 if (fixcursor) SetCursor(Fl_X::i(this)->cursor);
316 return;
319 #endif
321 make_current();
323 if (mode_ & FL_DOUBLE) {
325 glDrawBuffer(GL_BACK);
327 if (!SWAP_TYPE) {
328 #if defined (__APPLE_QUARTZ__) || defined (USE_X11)
329 SWAP_TYPE = COPY;
330 #else
331 SWAP_TYPE = UNDEFINED;
332 #endif
333 const char* c = fl_getenv("GL_SWAP_TYPE");
334 if (c) {
335 if (!strcmp(c,"COPY")) SWAP_TYPE = COPY;
336 else if (!strcmp(c, "NODAMAGE")) SWAP_TYPE = NODAMAGE;
337 else if (!strcmp(c, "SWAP")) SWAP_TYPE = SWAP;
338 else SWAP_TYPE = UNDEFINED;
342 if (SWAP_TYPE == NODAMAGE) {
344 // don't draw if only overlay damage or expose events:
345 if ((damage()&~(FL_DAMAGE_OVERLAY|FL_DAMAGE_EXPOSE)) || !save_valid)
346 draw();
347 swap_buffers();
349 } else if (SWAP_TYPE == COPY) {
351 // don't draw if only the overlay is damaged:
352 if (damage() != FL_DAMAGE_OVERLAY || !save_valid) draw();
353 swap_buffers();
355 } else if (SWAP_TYPE == SWAP){
356 damage(FL_DAMAGE_ALL);
357 draw();
358 if (overlay == this) draw_overlay();
359 swap_buffers();
360 } else if (SWAP_TYPE == UNDEFINED){ // SWAP_TYPE == UNDEFINED
362 // If we are faking the overlay, use CopyPixels to act like
363 // SWAP_TYPE == COPY. Otherwise overlay redraw is way too slow.
364 if (overlay == this) {
365 // don't draw if only the overlay is damaged:
366 if (damage1_ || damage() != FL_DAMAGE_OVERLAY || !save_valid) draw();
367 // we use a separate context for the copy because rasterpos must be 0
368 // and depth test needs to be off:
369 static GLContext ortho_context = 0;
370 static Fl_Gl_Window* ortho_window = 0;
371 int orthoinit = !ortho_context;
372 if (orthoinit) ortho_context = fl_create_gl_context(this, g);
373 fl_set_gl_context(this, ortho_context);
374 if (orthoinit || !save_valid || ortho_window != this) {
375 glDisable(GL_DEPTH_TEST);
376 glReadBuffer(GL_BACK);
377 glDrawBuffer(GL_FRONT);
378 glLoadIdentity();
379 glViewport(0, 0, w(), h());
380 glOrtho(0, w(), 0, h(), -1, 1);
381 glRasterPos2i(0,0);
382 ortho_window = this;
384 glCopyPixels(0,0,w(),h(),GL_COLOR);
385 make_current(); // set current context back to draw overlay
386 damage1_ = 0;
388 } else {
389 damage1_ = damage();
390 clear_damage(0xff); draw();
391 swap_buffers();
396 if (overlay==this && SWAP_TYPE != SWAP) { // fake overlay in front buffer
397 glDrawBuffer(GL_FRONT);
398 draw_overlay();
399 glDrawBuffer(GL_BACK);
400 glFlush();
403 } else { // single-buffered context is simpler:
405 draw();
406 if (overlay == this) draw_overlay();
407 glFlush();
411 #if HAVE_GL_OVERLAY && defined(WIN32)
412 if (fixcursor) SetCursor(Fl_X::i(this)->cursor);
413 #endif
414 valid(1);
415 context_valid(1);
418 void Fl_Gl_Window::resize(int X,int Y,int W,int H) {
419 // printf("Fl_Gl_Window::resize(X=%d, Y=%d, W=%d, H=%d)\n", X, Y, W, H);
420 // printf("current: x()=%d, y()=%d, w()=%d, h()=%d\n", x(), y(), w(), h());
422 if (W != w() || H != h()) valid(0);
424 #ifdef __APPLE__
425 if (X != x() || Y != y() || W != w() || H != h()) aglUpdateContext(context_);
426 #elif !defined(WIN32)
427 if ((W != w() || H != h()) && !resizable() && overlay && overlay != this) {
428 ((Fl_Gl_Window*)overlay)->resize(0,0,W,H);
430 #endif
432 Fl_Window::resize(X,Y,W,H);
436 Returns or sets a pointer to the GLContext that this window is
437 using. This is a system-dependent structure, but it is portable to copy
438 the context from one window to another. You can also set it to NULL,
439 which will force FLTK to recreate the context the next time make_current()
440 is called, this is useful for getting around bugs in OpenGL implementations.
442 If <i>destroy_flag</i> is true the context will be destroyed by
443 fltk when the window is destroyed, or when the mode() is changed,
444 or the next time context(x) is called.
446 void Fl_Gl_Window::context(void* v, int destroy_flag) {
447 if (context_ && !(mode_&NON_LOCAL_CONTEXT)) fl_delete_gl_context(context_);
448 context_ = (GLContext)v;
449 if (destroy_flag) mode_ &= ~NON_LOCAL_CONTEXT;
450 else mode_ |= NON_LOCAL_CONTEXT;
454 Hides the window and destroys the OpenGL context.
456 void Fl_Gl_Window::hide() {
457 context(0);
458 #if HAVE_GL_OVERLAY && defined(WIN32)
459 if (overlay && overlay != this) {
460 fl_delete_gl_context((GLContext)overlay);
461 overlay = 0;
463 #endif
464 Fl_Window::hide();
468 The destructor removes the widget and destroys the OpenGL context
469 associated with it.
471 Fl_Gl_Window::~Fl_Gl_Window() {
472 hide();
473 // delete overlay; this is done by ~Fl_Group
474 #ifdef __APPLE__
475 // resets the pile of string textures used to draw strings
476 extern void gl_texture_reset();
477 gl_texture_reset();
478 #endif
481 void Fl_Gl_Window::init() {
482 end(); // we probably don't want any children
483 box(FL_NO_BOX);
485 mode_ = FL_RGB | FL_DEPTH | FL_DOUBLE;
486 alist = 0;
487 context_ = 0;
488 g = 0;
489 overlay = 0;
490 valid_f_ = 0;
491 damage1_ = 0;
493 #if 0 // This breaks resizing on Linux/X11
494 int H = h();
495 h(1); // Make sure we actually do something in resize()...
496 resize(x(), y(), w(), H);
497 #endif // 0
501 You must implement this virtual function if you want to draw into the
502 overlay. The overlay is cleared before this is called. You should
503 draw anything that is not clear using OpenGL. You must use
504 gl_color(i) to choose colors (it allocates them from the colormap
505 using system-specific calls), and remember that you are in an indexed
506 OpenGL mode and drawing anything other than flat-shaded will probably
507 not work.
509 Both this function and Fl_Gl_Window::draw() should check
510 Fl_Gl_Window::valid() and set the same transformation. If you
511 don't your code may not work on other systems. Depending on the OS,
512 and on whether overlays are real or simulated, the OpenGL context may
513 be the same or different between the overlay and main window.
515 void Fl_Gl_Window::draw_overlay() {}
517 #endif
520 You \e \b must subclass Fl_Gl_Window and provide an implementation for
521 draw(). You may also provide an implementation of draw_overlay()
522 if you want to draw into the overlay planes. You can avoid
523 reinitializing the viewport and lights and other things by checking
524 valid() at the start of draw() and only doing the
525 initialization if it is false.
527 The draw() method can <I>only</I> use OpenGL calls. Do not
528 attempt to call X, any of the functions in <FL/fl_draw.H>, or glX
529 directly. Do not call gl_start() or gl_finish().
531 If double-buffering is enabled in the window, the back and front
532 buffers are swapped after this function is completed.
534 void Fl_Gl_Window::draw() {
535 Fl::fatal("Fl_Gl_Window::draw() *must* be overriden. Please refer to the documentation.");
540 Handle some FLTK events as needed.
542 int Fl_Gl_Window::handle(int event)
544 #ifdef __APPLE_QUARTZ__
545 /*if (event==FL_HIDE) {
546 // if we are not hidden, just the parent was hidden, so we must throw away the context
547 if (!visible_r())
548 context(0); // remove context without setting the hidden flags
550 if (event==FL_SHOW) {
551 // if we are not hidden, just the parent was shown, so we must create a new context
552 if (visible_r())
553 show(); //
555 #endif
556 return Fl_Window::handle(event);
560 // End of "$Id: Fl_Gl_Window.cxx 8657 2011-05-12 11:50:43Z manolo $".