2 // "$Id: Fl_Gl_Window.cxx 8657 2011-05-12 11:50:43Z manolo $"
4 // OpenGL window code for the Fast Light Tool Kit (FLTK).
6 // Copyright 1998-2010 by Bill Spitzak and others.
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
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
31 extern int fl_gl_load_plugin
;
33 static int temp
= fl_gl_load_plugin
;
40 #include "Fl_Gl_Choice.H"
41 #include <FL/Fl_Gl_Window.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
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__)
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
;
89 Fl::error("Insufficient GL support");
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;
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() {
117 ((Fl_Gl_Window
*)overlay
)->valid(0);
118 ((Fl_Gl_Window
*)overlay
)->context_valid(0);
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;
131 #if !defined(WIN32) && !defined(__APPLE__)
132 Fl_Gl_Choice
* oldg
= g
;
133 #endif // !WIN32 && !__APPLE__
135 mode_
= m
; alist
= a
;
137 g
= Fl_Gl_Choice::find(m
, a
);
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
) {
146 if (!g
|| (oldmode
^m
)&(FL_DOUBLE
|FL_STEREO
)) {
150 #elif defined(__APPLE_QUARTZ__)
151 // warning: the Quartz version should probably use Core GL (CGL) instead of AGL
154 # error unsupported platform
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_);
175 mode_
&= ~NON_LOCAL_CONTEXT
;
176 context_
= fl_create_gl_context(this, g
);
180 fl_set_gl_context(this, context_
);
183 // Set the buffer rectangle here, since in resize() we won't have the
184 // correct parent window size to work with...
189 xywh
[1] = window()->h() - y() - 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]);
203 #if defined(WIN32) && USE_COLORMAP
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
);
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:
226 glViewport(0, 0, w(), h());
227 glOrtho(0, w(), 0, h(), -1, 1);
230 glGetIntegerv(GL_MAX_VIEWPORT_DIMS
, v
);
232 glViewport(w()-v
[0], h()-v
[1], v
[0], v
[1]);
233 glOrtho(w()-v
[0], w(), h()-v
[1], h(), -1, 1);
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() {
243 glXSwapBuffers(fl_display
, fl_xid(this));
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();;
250 SwapBuffers(Fl_X::i(this)->private_dc
);
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
);
260 aglSwapBuffers((AGLContext
)context_
);
262 # error unsupported platform
266 #if HAVE_GL_OVERLAY && defined(WIN32)
267 uchar fl_overlay
; // changes how fl_color() works
268 int fl_overlay_depth
= 0;
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_
;
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
282 /*GrafPtr port = GetWindowPort( Fl_X::i(this)->window_ref() );
283 Rect rect; SetRect( &rect, 0, 0, 0x7fff, 0x7fff );
284 GrafPtr old; GetPort( &old );
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
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
);
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
);
323 if (mode_
& FL_DOUBLE
) {
325 glDrawBuffer(GL_BACK
);
328 #if defined (__APPLE_QUARTZ__) || defined (USE_X11)
331 SWAP_TYPE
= UNDEFINED
;
333 const char* c
= fl_getenv("GL_SWAP_TYPE");
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
)
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();
355 } else if (SWAP_TYPE
== SWAP
){
356 damage(FL_DAMAGE_ALL
);
358 if (overlay
== this) draw_overlay();
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
);
379 glViewport(0, 0, w(), h());
380 glOrtho(0, w(), 0, h(), -1, 1);
384 glCopyPixels(0,0,w(),h(),GL_COLOR
);
385 make_current(); // set current context back to draw overlay
390 clear_damage(0xff); draw();
396 if (overlay
==this && SWAP_TYPE
!= SWAP
) { // fake overlay in front buffer
397 glDrawBuffer(GL_FRONT
);
399 glDrawBuffer(GL_BACK
);
403 } else { // single-buffered context is simpler:
406 if (overlay
== this) draw_overlay();
411 #if HAVE_GL_OVERLAY && defined(WIN32)
412 if (fixcursor
) SetCursor(Fl_X::i(this)->cursor
);
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);
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
);
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() {
458 #if HAVE_GL_OVERLAY && defined(WIN32)
459 if (overlay
&& overlay
!= this) {
460 fl_delete_gl_context((GLContext
)overlay
);
468 The destructor removes the widget and destroys the OpenGL context
471 Fl_Gl_Window::~Fl_Gl_Window() {
473 // delete overlay; this is done by ~Fl_Group
475 // resets the pile of string textures used to draw strings
476 extern void gl_texture_reset();
481 void Fl_Gl_Window::init() {
482 end(); // we probably don't want any children
485 mode_
= FL_RGB
| FL_DEPTH
| FL_DOUBLE
;
493 #if 0 // This breaks resizing on Linux/X11
495 h(1); // Make sure we actually do something in resize()...
496 resize(x(), y(), w(), H
);
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
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() {}
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
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
556 return Fl_Window::handle(event
);
560 // End of "$Id: Fl_Gl_Window.cxx 8657 2011-05-12 11:50:43Z manolo $".