Jogamp
NEWT: Fix AWT Parenting ; Multithreading Issues ; Semantics: destroy(), .. ; Misc.
[jogl.git] / src / newt / classes / com / jogamp / newt / opengl / GLWindow.java
1 /*
2  * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved.
3  * 
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  * 
8  * - Redistribution of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * 
11  * - Redistribution in binary form must reproduce the above copyright
12  *   notice, this list of conditions and the following disclaimer in the
13  *   documentation and/or other materials provided with the distribution.
14  * 
15  * Neither the name of Sun Microsystems, Inc. or the names of
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  * 
19  * This software is provided "AS IS," without a warranty of any kind. ALL
20  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
21  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
22  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
23  * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
24  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
25  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
26  * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
27  * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
28  * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
29  * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
30  * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
31  * 
32  */
33
34 package com.jogamp.newt.opengl;
35
36 import com.jogamp.newt.*;
37 import com.jogamp.newt.event.*;
38 import com.jogamp.nativewindow.impl.RecursiveToolkitLock;
39 import javax.media.nativewindow.*;
40 import javax.media.opengl.*;
41 import com.jogamp.opengl.impl.GLDrawableHelper;
42 import java.util.*;
43
44 /**
45  * An implementation of {@link Window} which is customized for OpenGL
46  * use, and which implements the {@link javax.media.opengl.GLAutoDrawable} interface.
47  * <P>
48  * This implementation does not make the OpenGL context current<br>
49  * before calling the various input EventListener callbacks (MouseListener, KeyListener,
50  * etc.).<br>
51  * This design decision is made to favor a more performant and simplified
52  * implementation, as well as the event dispatcher shall be allowed
53  * not having a notion about OpenGL.
54  * <p>
55  */
56 public class GLWindow extends Window implements GLAutoDrawable {
57     private Window window;
58     private boolean runPumpMessages;
59
60     /**
61      * Constructor. Do not call this directly -- use {@link #create()} instead.
62      */
63     protected GLWindow(Window window) {
64         this.window = window;
65         this.window.setHandleDestroyNotify(false);
66         this.runPumpMessages = ( null == getScreen().getDisplay().getEDTUtil() ) ;
67         window.addWindowListener(new WindowAdapter() {
68                 public void windowResized(WindowEvent e) {
69                     sendReshape = true;
70                 }
71
72                 public void windowDestroyNotify(WindowEvent e) {
73                     sendDestroy = true;
74                 }
75             });
76     }
77
78     /** Creates a new GLWindow attaching the given window - not owning the Window. */
79     public static GLWindow create(Window window) {
80         return create(null, window, null, false);
81     }
82
83     /** Creates a new GLWindow attaching a new native child Window of the given <code>parentNativeWindow</code>
84         with the given GLCapabilities - owning the Window */
85     public static GLWindow create(NativeWindow parentNativeWindow, GLCapabilities caps) {
86         return create(parentNativeWindow, null, caps, false);
87     }
88
89     /** Creates a new GLWindow attaching a new decorated Window on the local display, screen 0, with a
90         dummy visual ID and given GLCapabilities - owning the window */
91     public static GLWindow create(GLCapabilities caps) {
92         return create(null, null, caps, false);
93     }
94
95     /** Creates a new GLWindow attaching a new Window on the local display, screen 0, with a
96         dummy visual ID and given GLCapabilities - owning the window */
97     public static GLWindow create(GLCapabilities caps, boolean undecorated) {
98         return create(null, null, caps, undecorated);
99     }
100
101     /** Either or: window (prio), or caps and undecorated (2nd choice) */
102     private static GLWindow create(NativeWindow parentNativeWindow, Window window, 
103                                    GLCapabilities caps,
104                                    boolean undecorated) {
105         if (window == null) {
106             if (caps == null) {
107                 caps = new GLCapabilities(null); // default ..
108             }
109             window = NewtFactory.createWindow(parentNativeWindow, caps, undecorated);
110         }
111
112         return new GLWindow(window);
113     }
114     
115     public boolean isNativeWindowValid() {
116         return (null!=window)?window.isNativeWindowValid():false;
117     }
118
119     public boolean isDestroyed() {
120         return (null!=window)?window.isDestroyed():true;
121     }
122
123     public Window getInnerWindow() {
124         return window.getInnerWindow();
125     }
126
127     /** 
128      * EXPERIMENTAL<br> 
129      * Enable or disables running the {@link Display#pumpMessages} in the {@link #display()} call.<br>
130      * The default behavior is to run {@link Display#pumpMessages}.<P>
131      *
132      * The idea was that in a single threaded environment with one {@link Display} and many {@link Window}'s,
133      * a performance benefit was expected while disabling the implicit {@link Display#pumpMessages} and
134      * do it once via {@link GLWindow#runCurrentThreadPumpMessage()} <br>
135      * This could not have been verified. No measurable difference could have been recognized.<P>
136      *
137      * Best performance has been achieved with one GLWindow per thread.<br> 
138      *
139      * Enabling local pump messages while using the EDT, 
140      * {@link com.jogamp.newt.NewtFactory#setUseEDT(boolean)},
141      * will result in an exception.
142      *
143      * @deprecated EXPERIMENTAL, semantic is about to be removed after further verification.
144      */
145     public void setRunPumpMessages(boolean onoff) {
146         if( onoff && null!=getScreen().getDisplay().getEDTUtil() ) {
147             throw new GLException("GLWindow.setRunPumpMessages(true) - Can't do with EDT on");
148         }
149         runPumpMessages = onoff;
150     }
151
152     protected void createNativeImpl() {
153         shouldNotCallThis();
154     }
155
156     protected void closeNative() {
157         shouldNotCallThis();
158     }
159
160     protected void dispose() {
161         if(Window.DEBUG_WINDOW_EVENT || window.DEBUG_IMPLEMENTATION) {
162             Exception e1 = new Exception("GLWindow.dispose() "+Thread.currentThread()+", start: "+this);
163             e1.printStackTrace();
164         }
165
166         if ( null != context && null != drawable && drawable.isRealized() ) {
167             helper.invokeGL(drawable, context, disposeAction, null);
168         }
169
170         if (context != null) {
171             context.destroy();
172             context = null;
173         }
174         if (drawable != null) {
175             drawable.setRealized(false);
176             drawable = null;
177         }
178
179         if(Window.DEBUG_WINDOW_EVENT || window.DEBUG_IMPLEMENTATION) {
180             System.out.println("GLWindow.dispose() "+Thread.currentThread()+", fin: "+this);
181         }
182     }
183
184     class DestroyAction implements Runnable {
185         boolean deep;
186         public DestroyAction(boolean deep) {
187             this.deep = deep;
188         }
189         public void run() {
190             windowLock();
191             try {
192                 if(null==window || window.isDestroyed()) {
193                     return; // nop
194                 }
195                 dispose();
196
197                 if(null!=window) {
198                     window.destroy(deep);
199                 }
200
201                 if(deep) {
202                     helper=null;
203                 }
204             } finally {
205                 windowUnlock();
206             }
207         }
208     }
209
210     /** 
211      * @param deep If true, all resources, ie listeners, parent handles, size, position 
212      * and the referenced NEWT screen and display, will be destroyed as well. Be aware that if you call
213      * this method with deep = true, you will not be able to regenerate the Window.
214      * @see #destroy()
215      */
216     public void destroy(boolean deep) {
217         if(!isDestroyed()) {
218             runOnEDTIfAvail(true, new DestroyAction(deep));
219         }
220     }
221
222     public boolean getPerfLogEnabled() { return perfLog; }
223
224     public void enablePerfLog(boolean v) {
225         perfLog = v;
226     }
227
228     protected void setVisibleImpl(boolean visible) {
229         shouldNotCallThis();
230     }
231
232     public void reparentWindow(NativeWindow newParent, Screen newScreen) {
233         window.reparentWindow(newParent, newScreen);
234     }
235
236     class VisibleAction implements Runnable {
237         boolean visible;
238         public VisibleAction(boolean visible) {
239             this.visible = visible;
240         }
241         public void run() {
242             windowLock();
243             try{
244                 window.setVisible(visible);
245                 if (null == context && visible && 0 != window.getWindowHandle() && 0<getWidth()*getHeight()) {
246                     NativeWindow nw;
247                     if (window.getWrappedWindow() != null) {
248                         nw = NativeWindowFactory.getNativeWindow(window.getWrappedWindow(), window.getGraphicsConfiguration());
249                     } else {
250                         nw = window;
251                     }
252                     GLCapabilities glCaps = (GLCapabilities) nw.getGraphicsConfiguration().getNativeGraphicsConfiguration().getChosenCapabilities();
253                     if(null==factory) {
254                         factory = GLDrawableFactory.getFactory(glCaps.getGLProfile());
255                     }
256                     if(null==drawable) {
257                         drawable = factory.createGLDrawable(nw);
258                     }    
259                     drawable.setRealized(true);
260                     context = drawable.createContext(null);
261                     sendReshape = true; // ensure a reshape event is send ..
262                 }
263             } finally {
264                 windowUnlock();
265             }
266         }
267     }
268
269     public void setVisible(boolean visible) {
270         if(!isDestroyed()) {
271             runOnEDTIfAvail(true, new VisibleAction(visible));
272         }
273     }
274
275     public Capabilities getRequestedCapabilities() {
276         return window.getRequestedCapabilities();
277     }
278
279     public NativeWindow getParentNativeWindow() {
280         return window.getParentNativeWindow();
281     }
282
283     public Screen getScreen() {
284         return window.getScreen();
285     }
286
287     public void setTitle(String title) {
288         window.setTitle(title);
289     }
290
291     public String getTitle() {
292         return window.getTitle();
293     }
294
295     public void setUndecorated(boolean value) {
296         window.setUndecorated(value);
297     }
298
299     public boolean isUndecorated() {
300         return window.isUndecorated();
301     }
302
303     public void requestFocus() {
304         window.requestFocus();
305     }
306
307     public Insets getInsets() {
308         return window.getInsets();
309     }
310
311     public void setSize(int width, int height) {
312         window.setSize(width, height);
313     }
314     protected void setSizeImpl(int width, int height) {
315         shouldNotCallThis();
316     }
317
318     public void setPosition(int x, int y) {
319         window.setPosition(x, y);
320     }
321     protected void setPositionImpl(int x, int y) {
322         shouldNotCallThis();
323     }
324
325     public boolean setFullscreen(boolean fullscreen) {
326         return window.setFullscreen(fullscreen);
327     }
328     protected boolean setFullscreenImpl(boolean fullscreen, int x, int y, int w, int h) {
329         shouldNotCallThis();
330         return false;
331     }
332
333     public boolean isVisible() {
334         return window.isVisible();
335     }
336
337     public int getX() {
338         return window.getX();
339     }
340
341     public int getY() {
342         return window.getY();
343     }
344
345     public int getWidth() {
346         return window.getWidth();
347     }
348
349     public int getHeight() {
350         return window.getHeight();
351     }
352
353     public boolean isFullscreen() {
354         return window.isFullscreen();
355     }
356
357     public void sendEvent(NEWTEvent e) {
358         window.sendEvent(e);
359     }
360
361     public void addSurfaceUpdatedListener(SurfaceUpdatedListener l) {
362         window.addSurfaceUpdatedListener(l);
363     }
364     public void removeSurfaceUpdatedListener(SurfaceUpdatedListener l) {
365         window.removeSurfaceUpdatedListener(l);
366     }
367     public void removeAllSurfaceUpdatedListener() {
368         window.removeAllSurfaceUpdatedListener();
369     }
370     public SurfaceUpdatedListener[] getSurfaceUpdatedListener() {
371         return window.getSurfaceUpdatedListener();
372     }
373     public void surfaceUpdated(Object updater, NativeWindow window0, long when) { 
374         window.surfaceUpdated(updater, window, when);
375     }
376
377     public void addMouseListener(MouseListener l) {
378         window.addMouseListener(l);
379     }
380
381     public void removeMouseListener(MouseListener l) {
382         window.removeMouseListener(l);
383     }
384
385     public MouseListener[] getMouseListeners() {
386         return window.getMouseListeners();
387     }
388
389     public void addKeyListener(KeyListener l) {
390         window.addKeyListener(l);
391     }
392
393     public void removeKeyListener(KeyListener l) {
394         window.removeKeyListener(l);
395     }
396
397     public KeyListener[] getKeyListeners() {
398         return window.getKeyListeners();
399     }
400
401     public void addWindowListener(WindowListener l) {
402         window.addWindowListener(l);
403     }
404
405     public void removeWindowListener(WindowListener l) {
406         window.removeWindowListener(l);
407     }
408
409     public WindowListener[] getWindowListeners() {
410         return window.getWindowListeners();
411     }
412
413     public String toString() {
414         return "NEWT-GLWindow[ \n\tHelper: "+helper+", \n\tDrawable: "+drawable + /** ", \n\tWindow: "+window+", \n\tFactory: "+factory+ */ "]";
415     }
416
417     //----------------------------------------------------------------------
418     // OpenGL-related methods and state
419     //
420
421     private GLDrawableFactory factory;
422     private GLDrawable drawable;
423     private GLContext context;
424     private GLDrawableHelper helper = new GLDrawableHelper();
425     // To make reshape events be sent immediately before a display event
426     private boolean sendReshape=false;
427     private boolean sendDestroy=false;
428     private boolean perfLog = false;
429
430     public GLDrawableFactory getFactory() {
431         return factory;
432     }
433
434     public void setContext(GLContext newCtx) {
435         context = newCtx;
436     }
437
438     public GLContext getContext() {
439         return context;
440     }
441
442     public GL getGL() {
443         if (context == null) {
444             return null;
445         }
446         return context.getGL();
447     }
448
449     public GL setGL(GL gl) {
450         if (context != null) {
451             context.setGL(gl);
452             return gl;
453         }
454         return null;
455     }
456
457     public void addGLEventListener(GLEventListener listener) {
458         helper.addGLEventListener(listener);
459     }
460
461     public void removeGLEventListener(GLEventListener listener) {
462         helper.removeGLEventListener(listener);
463     }
464
465     public void display() {
466         display(false);
467     }
468
469     public void display(boolean forceReshape) {
470         if( null == window ) { return; }
471
472         if( null == context && window.isVisible() ) {
473             // retry native window and drawable/context creation 
474             setVisible(true);
475         }
476
477         if( window.isNativeWindowValid() && null != context ) {
478             if(runPumpMessages) {
479                 window.getScreen().getDisplay().pumpMessages();
480             }
481             if(sendDestroy || window.hasDeviceChanged() && GLAutoDrawable.SCREEN_CHANGE_ACTION_ENABLED) {
482                 destroy();
483                 sendDestroy=false;
484             } else if ( window.isVisible() ) {
485                 if(forceReshape) {
486                     sendReshape = true;
487                 }
488                 helper.invokeGL(drawable, context, displayAction, initAction);
489             }
490         }
491     }
492
493     /** This implementation uses a static value */
494     public void setAutoSwapBufferMode(boolean onOrOff) {
495         helper.setAutoSwapBufferMode(onOrOff);
496     }
497
498     /** This implementation uses a static value */
499     public boolean getAutoSwapBufferMode() {
500         return helper.getAutoSwapBufferMode();
501     }
502
503     public void swapBuffers() {
504         if(drawable!=null && context != null) {
505             if (context != GLContext.getCurrent()) {
506                 // Assume we should try to make the context current before swapping the buffers
507                 helper.invokeGL(drawable, context, swapBuffersAction, initAction);
508             } else {
509                 drawable.swapBuffers();
510             }
511         }
512     }
513
514     class InitAction implements Runnable {
515         public void run() {
516             helper.init(GLWindow.this);
517             startTime = System.currentTimeMillis();
518             curTime   = startTime;
519             if(perfLog) {
520                 lastCheck  = startTime;
521                 totalFrames = 0; lastFrames = 0;
522             }
523         }
524     }
525     private InitAction initAction = new InitAction();
526
527     class DisposeAction implements Runnable {
528         public void run() {
529             helper.dispose(GLWindow.this);
530         }
531     }
532     private DisposeAction disposeAction = new DisposeAction();
533
534     class DisplayAction implements Runnable {
535         public void run() {
536             if (sendReshape) {
537                 int width = getWidth();
538                 int height = getHeight();
539                 getGL().glViewport(0, 0, width, height);
540                 helper.reshape(GLWindow.this, 0, 0, width, height);
541                 sendReshape = false;
542             }
543
544             helper.display(GLWindow.this);
545
546             curTime = System.currentTimeMillis();
547             totalFrames++;
548
549             if(perfLog) {
550                 long dt0, dt1;
551                 lastFrames++;
552                 dt0 = curTime-lastCheck;
553                 if ( dt0 > 5000 ) {
554                     dt1 = curTime-startTime;
555                     System.out.println(dt0/1000 +"s: "+ lastFrames + "f, " + (lastFrames*1000)/dt0 + " fps, "+dt0/lastFrames+" ms/f; "+
556                                        "total: "+ dt1/1000+"s, "+(totalFrames*1000)/dt1 + " fps, "+dt1/totalFrames+" ms/f");
557                     lastCheck=curTime;
558                     lastFrames=0;
559                 }
560             }
561         }
562     }
563     private DisplayAction displayAction = new DisplayAction();
564
565     public long getStartTime()   { return startTime; }
566     public long getCurrentTime() { return curTime; }
567     public long getDuration()    { return curTime-startTime; }
568     public int  getTotalFrames() { return totalFrames; }
569
570     private long startTime = 0;
571     private long curTime = 0;
572     private long lastCheck  = 0;
573     private int  totalFrames = 0, lastFrames = 0;
574
575     class SwapBuffersAction implements Runnable {
576         public void run() {
577             drawable.swapBuffers();
578         }
579     }
580     private SwapBuffersAction swapBuffersAction = new SwapBuffersAction();
581
582     //----------------------------------------------------------------------
583     // NativeWindow/Window methods
584     //
585
586     public int lockSurface() throws NativeWindowException {
587         if(null!=drawable) return drawable.getNativeWindow().lockSurface();
588         return window.lockSurface();
589     }
590
591     public void unlockSurface() {
592         if(null!=drawable) drawable.getNativeWindow().unlockSurface();
593         else window.unlockSurface();
594     }
595
596     public boolean isSurfaceLocked() {
597         if(null!=drawable) return drawable.getNativeWindow().isSurfaceLocked();
598         return window.isSurfaceLocked();
599     }
600
601     public Exception getLockedStack() {
602         if(null!=drawable) return drawable.getNativeWindow().getLockedStack();
603         return window.getLockedStack();
604     }
605
606     public boolean surfaceSwap() { 
607         if(null!=drawable) return drawable.getNativeWindow().surfaceSwap();
608         return super.surfaceSwap();
609     }
610
611     public long getWindowHandle() {
612         if(null!=drawable) return drawable.getNativeWindow().getWindowHandle();
613         return window.getWindowHandle();
614     }
615
616     public long getSurfaceHandle() {
617         if(null!=drawable) return drawable.getNativeWindow().getSurfaceHandle();
618         return window.getSurfaceHandle();
619     }
620
621     public AbstractGraphicsConfiguration getGraphicsConfiguration() {
622         if(null!=drawable) return drawable.getNativeWindow().getGraphicsConfiguration();
623         return window.getGraphicsConfiguration();
624     }
625
626     //----------------------------------------------------------------------
627     // GLDrawable methods
628     //
629
630     public NativeWindow getNativeWindow() {
631         return null!=drawable ? drawable.getNativeWindow() : null;
632     }
633
634     //----------------------------------------------------------------------
635     // GLDrawable methods that are not really needed
636     //
637
638     public GLContext createContext(GLContext shareWith) {
639         return drawable.createContext(shareWith);
640     }
641
642     public void setRealized(boolean realized) {
643     }
644
645     public boolean isRealized() {
646         return ( null != drawable ) ? drawable.isRealized() : false;
647     }
648
649     public GLCapabilities getChosenGLCapabilities() {
650         if (drawable == null) {
651             throw new GLException("No drawable yet");
652         }
653
654         return drawable.getChosenGLCapabilities();
655     }
656
657     public GLProfile getGLProfile() {
658         if (drawable == null) {
659             throw new GLException("No drawable yet");
660         }
661
662         return drawable.getGLProfile();
663     }
664 }
http://JogAmp.org git info: FAQ, tutorial and man pages.