Jogamp
Cleanup enqueueEvent and remove PaintEvent and it's Listener
[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 final Window getInnerWindow() {
124         return window.getInnerWindow();
125     }
126
127     public final Object getWrappedWindow() {
128         return window.getWrappedWindow();
129     }
130
131     /** 
132      * EXPERIMENTAL<br> 
133      * Enable or disables running the {@link Display#pumpMessages} in the {@link #display()} call.<br>
134      * The default behavior is to run {@link Display#pumpMessages}.<P>
135      *
136      * The idea was that in a single threaded environment with one {@link Display} and many {@link Window}'s,
137      * a performance benefit was expected while disabling the implicit {@link Display#pumpMessages} and
138      * do it once via {@link GLWindow#runCurrentThreadPumpMessage()} <br>
139      * This could not have been verified. No measurable difference could have been recognized.<P>
140      *
141      * Best performance has been achieved with one GLWindow per thread.<br> 
142      *
143      * Enabling local pump messages while using the EDT, 
144      * {@link com.jogamp.newt.NewtFactory#setUseEDT(boolean)},
145      * will result in an exception.
146      *
147      * @deprecated EXPERIMENTAL, semantic is about to be removed after further verification.
148      */
149     public void setRunPumpMessages(boolean onoff) {
150         if( onoff && null!=getScreen().getDisplay().getEDTUtil() ) {
151             throw new GLException("GLWindow.setRunPumpMessages(true) - Can't do with EDT on");
152         }
153         runPumpMessages = onoff;
154     }
155
156     protected void createNativeImpl() {
157         shouldNotCallThis();
158     }
159
160     protected void closeNative() {
161         shouldNotCallThis();
162     }
163
164     class DisposeAction implements Runnable {
165         public void run() {
166             // Lock: Covered by DestroyAction ..
167             helper.dispose(GLWindow.this);
168         }
169     }
170     private DisposeAction disposeAction = new DisposeAction();
171
172     class DestroyAction implements Runnable {
173         boolean deep;
174         public DestroyAction(boolean deep) {
175             this.deep = deep;
176         }
177         public void run() {
178             // Lock: Have to cover whole workflow (dispose all, context, drawable and window)
179             windowLock();
180             try {
181                 if(null==window || window.isDestroyed()) {
182                     return; // nop
183                 }
184                 if(Window.DEBUG_WINDOW_EVENT || window.DEBUG_IMPLEMENTATION) {
185                     Exception e1 = new Exception("GLWindow.destroy("+deep+") "+Thread.currentThread()+", start: "+GLWindow.this);
186                     e1.printStackTrace();
187                 }
188
189                 if ( null != context && context.isCreated() && null != drawable && drawable.isRealized() ) {
190                     // Catch dispose GLExceptions by GLEventListener, just 'print' them
191                     // so we can continue with the destruction.
192                     try {
193                         helper.invokeGL(drawable, context, disposeAction, null);
194                     } catch (GLException gle) {
195                         gle.printStackTrace();
196                     }
197                 }
198
199                 if (context != null && null != drawable && drawable.isRealized() ) {
200                     context.destroy();
201                     context = null;
202                 }
203                 if (drawable != null) {
204                     drawable.setRealized(false);
205                     drawable = null;
206                 }
207
208                 if(null!=window) {
209                     window.destroy(deep);
210                 }
211
212                 if(deep) {
213                     helper=null;
214                 }
215                 if(Window.DEBUG_WINDOW_EVENT || window.DEBUG_IMPLEMENTATION) {
216                     System.out.println("GLWindow.destroy("+deep+") "+Thread.currentThread()+", fin: "+GLWindow.this);
217                 }
218             } finally {
219                 windowUnlock();
220             }
221         }
222     }
223
224     /** 
225      * @param deep If true, all resources, ie listeners, parent handles, size, position 
226      * and the referenced NEWT screen and display, will be destroyed as well. Be aware that if you call
227      * this method with deep = true, you will not be able to regenerate the Window.
228      * @see #destroy()
229      */
230     public void destroy(boolean deep) {
231         if(!isDestroyed()) {
232             runOnEDTIfAvail(true, new DestroyAction(deep));
233         }
234     }
235
236     public boolean getPerfLogEnabled() { return perfLog; }
237
238     public void enablePerfLog(boolean v) {
239         perfLog = v;
240     }
241
242     protected void setVisibleImpl(boolean visible) {
243         shouldNotCallThis();
244     }
245
246     public void reparentWindow(NativeWindow newParent, Screen newScreen) {
247         window.reparentWindow(newParent, newScreen);
248     }
249
250     class VisibleAction implements Runnable {
251         boolean visible;
252         public VisibleAction(boolean visible) {
253             this.visible = visible;
254         }
255         public void run() {
256             // Lock: Have to cover whole workflow (window, may do nativeCreation, drawable and context)
257             windowLock();
258             try{
259                 window.setVisible(visible);
260                 if (null == context && visible && 0 != window.getWindowHandle() && 0<getWidth()*getHeight()) {
261                     NativeWindow nw;
262                     if (getWrappedWindow() != null) {
263                         nw = NativeWindowFactory.getNativeWindow(getWrappedWindow(), window.getGraphicsConfiguration());
264                     } else {
265                         nw = window;
266                     }
267                     GLCapabilities glCaps = (GLCapabilities) nw.getGraphicsConfiguration().getNativeGraphicsConfiguration().getChosenCapabilities();
268                     if(null==factory) {
269                         factory = GLDrawableFactory.getFactory(glCaps.getGLProfile());
270                     }
271                     if(null==drawable) {
272                         drawable = factory.createGLDrawable(nw);
273                     }    
274                     drawable.setRealized(true);
275                     context = drawable.createContext(null);
276                     sendReshape = true; // ensure a reshape event is send ..
277                 }
278             } finally {
279                 windowUnlock();
280             }
281         }
282     }
283
284     public void setVisible(boolean visible) {
285         if(!isDestroyed()) {
286             runOnEDTIfAvail(true, new VisibleAction(visible));
287         }
288     }
289
290     public Capabilities getRequestedCapabilities() {
291         return window.getRequestedCapabilities();
292     }
293
294     public NativeWindow getParentNativeWindow() {
295         return window.getParentNativeWindow();
296     }
297
298     public Screen getScreen() {
299         return window.getScreen();
300     }
301
302     public void setTitle(String title) {
303         window.setTitle(title);
304     }
305
306     public String getTitle() {
307         return window.getTitle();
308     }
309
310     public void setUndecorated(boolean value) {
311         window.setUndecorated(value);
312     }
313
314     public boolean isUndecorated() {
315         return window.isUndecorated();
316     }
317
318     public void requestFocus() {
319         window.requestFocus();
320     }
321
322     public Insets getInsets() {
323         return window.getInsets();
324     }
325
326     public void setSize(int width, int height) {
327         window.setSize(width, height);
328     }
329     protected void setSizeImpl(int width, int height) {
330         shouldNotCallThis();
331     }
332
333     public void setPosition(int x, int y) {
334         window.setPosition(x, y);
335     }
336     protected void setPositionImpl(int x, int y) {
337         shouldNotCallThis();
338     }
339
340     public boolean setFullscreen(boolean fullscreen) {
341         return window.setFullscreen(fullscreen);
342     }
343     protected boolean setFullscreenImpl(boolean fullscreen, int x, int y, int w, int h) {
344         shouldNotCallThis();
345         return false;
346     }
347
348     public boolean isVisible() {
349         return window.isVisible();
350     }
351
352     public int getX() {
353         return window.getX();
354     }
355
356     public int getY() {
357         return window.getY();
358     }
359
360     public int getWidth() {
361         return window.getWidth();
362     }
363
364     public int getHeight() {
365         return window.getHeight();
366     }
367
368     public boolean isFullscreen() {
369         return window.isFullscreen();
370     }
371
372     public void addSurfaceUpdatedListener(int index, SurfaceUpdatedListener l) {
373         window.addSurfaceUpdatedListener(index, l);
374     }
375     public void removeSurfaceUpdatedListener(SurfaceUpdatedListener l) {
376         window.removeSurfaceUpdatedListener(l);
377     }
378     public void removeAllSurfaceUpdatedListener() {
379         window.removeAllSurfaceUpdatedListener();
380     }
381     public SurfaceUpdatedListener getSurfaceUpdatedListener(int index) {
382         return window.getSurfaceUpdatedListener(index);
383     }
384     public SurfaceUpdatedListener[] getSurfaceUpdatedListeners() {
385         return window.getSurfaceUpdatedListeners();
386     }
387     public void surfaceUpdated(Object updater, NativeWindow window0, long when) { 
388         window.surfaceUpdated(updater, window, when);
389     }
390
391     public void addMouseListener(int index, MouseListener l) {
392         window.addMouseListener(index, l);
393     }
394     public void removeMouseListener(MouseListener l) {
395         window.removeMouseListener(l);
396     }
397     public MouseListener getMouseListener(int index) {
398         return window.getMouseListener(index);
399     }
400     public MouseListener[] getMouseListeners() {
401         return window.getMouseListeners();
402     }
403
404     public void addKeyListener(int index, KeyListener l) {
405         window.addKeyListener(index, l);
406     }
407     public void removeKeyListener(KeyListener l) {
408         window.removeKeyListener(l);
409     }
410     public KeyListener getKeyListener(int index) {
411         return window.getKeyListener(index);
412     }
413     public KeyListener[] getKeyListeners() {
414         return window.getKeyListeners();
415     }
416
417     public void addWindowListener(int index, WindowListener l) {
418         window.addWindowListener(index, l);
419     }
420     public void removeWindowListener(WindowListener l) {
421         window.removeWindowListener(l);
422     }
423     public WindowListener getWindowListener(int index) {
424         return window.getWindowListener(index);
425     }
426     public WindowListener[] getWindowListeners() {
427         return window.getWindowListeners();
428     }
429
430     public String toString() {
431         return "NEWT-GLWindow[ \n\tHelper: "+helper+", \n\tDrawable: "+drawable + /** ", \n\tWindow: "+window+", \n\tFactory: "+factory+ */ "]";
432     }
433
434     //----------------------------------------------------------------------
435     // OpenGL-related methods and state
436     //
437
438     private GLDrawableFactory factory;
439     private GLDrawable drawable;
440     private GLContext context;
441     private GLDrawableHelper helper = new GLDrawableHelper();
442     // To make reshape events be sent immediately before a display event
443     private boolean sendReshape=false;
444     private boolean sendDestroy=false;
445     private boolean perfLog = false;
446
447     public GLDrawableFactory getFactory() {
448         return factory;
449     }
450
451     public void setContext(GLContext newCtx) {
452         context = newCtx;
453     }
454
455     public GLContext getContext() {
456         return context;
457     }
458
459     public GL getGL() {
460         if (context == null) {
461             return null;
462         }
463         return context.getGL();
464     }
465
466     public GL setGL(GL gl) {
467         if (context != null) {
468             context.setGL(gl);
469             return gl;
470         }
471         return null;
472     }
473
474     public void addGLEventListener(GLEventListener listener) {
475         helper.addGLEventListener(listener);
476     }
477
478     public void addGLEventListener(int index, GLEventListener listener) {
479         helper.addGLEventListener(index, listener);
480     }
481
482     public void removeGLEventListener(GLEventListener listener) {
483         helper.removeGLEventListener(listener);
484     }
485
486     public void invoke(boolean wait, GLRunnable glRunnable) {
487         helper.invoke(wait, glRunnable);
488     }
489
490     public void display() {
491         display(false);
492     }
493
494     public void display(boolean forceReshape) {
495         if( null == window ) { return; }
496
497         if( null == context && window.isVisible() ) {
498             // retry native window and drawable/context creation 
499             setVisible(true);
500         }
501
502         if( window.isNativeWindowValid() && null != context ) {
503             if(runPumpMessages) {
504                 window.getScreen().getDisplay().pumpMessages();
505             }
506             if(sendDestroy || window.hasDeviceChanged() && GLAutoDrawable.SCREEN_CHANGE_ACTION_ENABLED) {
507                 destroy();
508                 sendDestroy=false;
509             } else if ( window.isVisible() ) {
510                 if(forceReshape) {
511                     sendReshape = true;
512                 }
513                 windowLock();
514                 try{
515                     helper.invokeGL(drawable, context, displayAction, initAction);
516                 } finally {
517                     windowUnlock();
518                 }
519             }
520         }
521     }
522
523     /** This implementation uses a static value */
524     public void setAutoSwapBufferMode(boolean onOrOff) {
525         helper.setAutoSwapBufferMode(onOrOff);
526     }
527
528     /** This implementation uses a static value */
529     public boolean getAutoSwapBufferMode() {
530         return helper.getAutoSwapBufferMode();
531     }
532
533     public void swapBuffers() {
534         if(drawable!=null && context != null) {
535             // Lock: Locked Surface/Window by MakeCurrent/Release
536             if (context != GLContext.getCurrent()) {
537                 // Assume we should try to make the context current before swapping the buffers
538                 helper.invokeGL(drawable, context, swapBuffersAction, initAction);
539             } else {
540                 drawable.swapBuffers();
541             }
542         }
543     }
544
545     class InitAction implements Runnable {
546         public void run() {
547             // Lock: Locked Surface/Window by MakeCurrent/Release
548             helper.init(GLWindow.this);
549             startTime = System.currentTimeMillis();
550             curTime   = startTime;
551             if(perfLog) {
552                 lastCheck  = startTime;
553                 totalFrames = 0; lastFrames = 0;
554             }
555         }
556     }
557     private InitAction initAction = new InitAction();
558
559     class DisplayAction implements Runnable {
560         public void run() {
561             // Lock: Locked Surface/Window by display _and_ MakeCurrent/Release
562             if (sendReshape) {
563                 int width = getWidth();
564                 int height = getHeight();
565                 getGL().glViewport(0, 0, width, height);
566                 helper.reshape(GLWindow.this, 0, 0, width, height);
567                 sendReshape = false;
568             }
569
570             helper.display(GLWindow.this);
571
572             curTime = System.currentTimeMillis();
573             totalFrames++;
574
575             if(perfLog) {
576                 long dt0, dt1;
577                 lastFrames++;
578                 dt0 = curTime-lastCheck;
579                 if ( dt0 > 5000 ) {
580                     dt1 = curTime-startTime;
581                     System.out.println(dt0/1000 +"s: "+ lastFrames + "f, " + (lastFrames*1000)/dt0 + " fps, "+dt0/lastFrames+" ms/f; "+
582                                        "total: "+ dt1/1000+"s, "+(totalFrames*1000)/dt1 + " fps, "+dt1/totalFrames+" ms/f");
583                     lastCheck=curTime;
584                     lastFrames=0;
585                 }
586             }
587         }
588     }
589     private DisplayAction displayAction = new DisplayAction();
590
591     public long getStartTime()   { return startTime; }
592     public long getCurrentTime() { return curTime; }
593     public long getDuration()    { return curTime-startTime; }
594     public int  getTotalFrames() { return totalFrames; }
595
596     private long startTime = 0;
597     private long curTime = 0;
598     private long lastCheck  = 0;
599     private int  totalFrames = 0, lastFrames = 0;
600
601     class SwapBuffersAction implements Runnable {
602         public void run() {
603             drawable.swapBuffers();
604         }
605     }
606     private SwapBuffersAction swapBuffersAction = new SwapBuffersAction();
607
608     //----------------------------------------------------------------------
609     // NativeWindow/Window methods
610     //
611
612     public int lockSurface() throws NativeWindowException {
613         if(null!=drawable) return drawable.getNativeWindow().lockSurface();
614         return window.lockSurface();
615     }
616
617     public void unlockSurface() {
618         if(null!=drawable) drawable.getNativeWindow().unlockSurface();
619         else window.unlockSurface();
620     }
621
622     public boolean isSurfaceLocked() {
623         if(null!=drawable) return drawable.getNativeWindow().isSurfaceLocked();
624         return window.isSurfaceLocked();
625     }
626
627     public Exception getLockedStack() {
628         if(null!=drawable) return drawable.getNativeWindow().getLockedStack();
629         return window.getLockedStack();
630     }
631
632     public boolean surfaceSwap() { 
633         if(null!=drawable) return drawable.getNativeWindow().surfaceSwap();
634         return super.surfaceSwap();
635     }
636
637     public long getWindowHandle() {
638         if(null!=drawable) return drawable.getNativeWindow().getWindowHandle();
639         return window.getWindowHandle();
640     }
641
642     public long getSurfaceHandle() {
643         if(null!=drawable) return drawable.getNativeWindow().getSurfaceHandle();
644         return window.getSurfaceHandle();
645     }
646
647     public AbstractGraphicsConfiguration getGraphicsConfiguration() {
648         if(null!=drawable) return drawable.getNativeWindow().getGraphicsConfiguration();
649         return window.getGraphicsConfiguration();
650     }
651
652     //----------------------------------------------------------------------
653     // GLDrawable methods
654     //
655
656     public NativeWindow getNativeWindow() {
657         return null!=drawable ? drawable.getNativeWindow() : null;
658     }
659
660     public long getHandle() {
661         return null!=drawable ? drawable.getHandle() : 0;
662     }
663
664     //----------------------------------------------------------------------
665     // GLDrawable methods that are not really needed
666     //
667
668     public GLContext createContext(GLContext shareWith) {
669         return drawable.createContext(shareWith);
670     }
671
672     public void setRealized(boolean realized) {
673     }
674
675     public boolean isRealized() {
676         return ( null != drawable ) ? drawable.isRealized() : false;
677     }
678
679     public GLCapabilities getChosenGLCapabilities() {
680         if (drawable == null) {
681             throw new GLException("No drawable yet");
682         }
683
684         return drawable.getChosenGLCapabilities();
685     }
686
687     public GLProfile getGLProfile() {
688         if (drawable == null) {
689             throw new GLException("No drawable yet");
690         }
691
692         return drawable.getGLProfile();
693     }
694 }
http://JogAmp.org git info: FAQ, tutorial and man pages.