Jogamp
GLAutoDrawable: setAnimator/getAnimator/invoke/display changes; NEWT: Adding native...
[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.newt.util.*;
39 import com.jogamp.nativewindow.impl.RecursiveToolkitLock;
40 import javax.media.nativewindow.*;
41 import javax.media.opengl.*;
42 import com.jogamp.opengl.impl.GLDrawableHelper;
43 import java.util.*;
44
45 /**
46  * An implementation of {@link Window} which is customized for OpenGL
47  * use, and which implements the {@link javax.media.opengl.GLAutoDrawable} interface.
48  * <P>
49  * This implementation does not make the OpenGL context current<br>
50  * before calling the various input EventListener callbacks (MouseListener, KeyListener,
51  * etc.).<br>
52  * This design decision is made to favor a more performant and simplified
53  * implementation, as well as the event dispatcher shall be allowed
54  * not having a notion about OpenGL.
55  * <p>
56  */
57 public class GLWindow extends Window implements GLAutoDrawable {
58     private Window window;
59
60     /**
61      * Constructor. Do not call this directly -- use {@link #create()} instead.
62      */
63     protected GLWindow(Window window) {
64         this.startTime = System.currentTimeMillis();
65         this.window = window;
66         this.window.setHandleDestroyNotify(false);
67         window.addWindowListener(new WindowAdapter() {
68                 public void windowRepaint(WindowUpdateEvent e) {
69                     if( !windowIsLocked() && null == getAnimator() ) {
70                         display();
71                     }
72                 }
73
74                 public void windowResized(WindowEvent e) {
75                     sendReshape = true;
76                     if( !windowIsLocked() && null == getAnimator() ) {
77                         display();
78                     }
79                 }
80
81                 public void windowDestroyNotify(WindowEvent e) {
82                     if( !windowIsLocked() && null == getAnimator() ) {
83                         destroy();
84                     } else {
85                         sendDestroy = true;
86                     }
87                 }
88             });
89     }
90
91     /** Creates a new GLWindow attaching the given window - not owning the Window. */
92     public static GLWindow create(Window window) {
93         return create(null, window, null, false);
94     }
95
96     /** Creates a new GLWindow attaching a new native child Window of the given <code>parentNativeWindow</code>
97         with the given GLCapabilities - owning the Window */
98     public static GLWindow create(NativeWindow parentNativeWindow, GLCapabilities caps) {
99         return create(parentNativeWindow, null, caps, false);
100     }
101
102     /** Creates a new GLWindow attaching a new decorated Window on the local display, screen 0, with a
103         dummy visual ID and given GLCapabilities - owning the window */
104     public static GLWindow create(GLCapabilities caps) {
105         return create(null, null, caps, false);
106     }
107
108     /** Creates a new GLWindow attaching a new Window on the local display, screen 0, with a
109         dummy visual ID and given GLCapabilities - owning the window */
110     public static GLWindow create(GLCapabilities caps, boolean undecorated) {
111         return create(null, null, caps, undecorated);
112     }
113
114     /** Either or: window (prio), or caps and undecorated (2nd choice) */
115     private static GLWindow create(NativeWindow parentNativeWindow, Window window, 
116                                    GLCapabilities caps,
117                                    boolean undecorated) {
118         if (window == null) {
119             if (caps == null) {
120                 caps = new GLCapabilities(null); // default ..
121             }
122             window = NewtFactory.createWindow(parentNativeWindow, caps, undecorated);
123         }
124
125         return new GLWindow(window);
126     }
127     
128     public boolean isNativeWindowValid() {
129         return (null!=window)?window.isNativeWindowValid():false;
130     }
131
132     public boolean isDestroyed() {
133         return (null!=window)?window.isDestroyed():true;
134     }
135
136     public final Window getInnerWindow() {
137         return window.getInnerWindow();
138     }
139
140     public final Object getWrappedWindow() {
141         return window.getWrappedWindow();
142     }
143
144     protected void createNativeImpl() {
145         shouldNotCallThis();
146     }
147
148     protected void closeNative() {
149         shouldNotCallThis();
150     }
151
152     class DisposeAction implements Runnable {
153         public void run() {
154             // Lock: Covered by DestroyAction ..
155             helper.dispose(GLWindow.this);
156         }
157     }
158     private DisposeAction disposeAction = new DisposeAction();
159
160     class DestroyAction implements Runnable {
161         boolean deep;
162         public DestroyAction(boolean deep) {
163             this.deep = deep;
164         }
165         public void run() {
166             // Lock: Have to cover whole workflow (dispose all, context, drawable and window)
167             windowLock();
168             try {
169                 if( isDestroyed() ) {
170                     return; // nop
171                 }
172                 if(Window.DEBUG_WINDOW_EVENT || window.DEBUG_IMPLEMENTATION) {
173                     Exception e1 = new Exception("GLWindow.destroy("+deep+") "+Thread.currentThread()+", start: "+GLWindow.this);
174                     e1.printStackTrace();
175                 }
176
177                 if( window.isNativeWindowValid() && null != drawable && drawable.isRealized() ) {
178                     if( null != context && context.isCreated() ) {
179                         // Catch dispose GLExceptions by GLEventListener, just 'print' them
180                         // so we can continue with the destruction.
181                         try {
182                             helper.invokeGL(drawable, context, disposeAction, null);
183                         } catch (GLException gle) {
184                             gle.printStackTrace();
185                         }
186
187                         context.destroy();
188                         context = null;
189                     }
190
191                     drawable.setRealized(false);
192                     drawable = null;
193                 }
194
195                 if(null!=window) {
196                     window.destroy(deep);
197                 }
198
199                 if(deep) {
200                     helper=null;
201                 }
202                 if(Window.DEBUG_WINDOW_EVENT || window.DEBUG_IMPLEMENTATION) {
203                     System.out.println("GLWindow.destroy("+deep+") "+Thread.currentThread()+", fin: "+GLWindow.this);
204                 }
205             } finally {
206                 windowUnlock();
207             }
208         }
209     }
210
211     /** 
212      * @param deep If true, all resources, ie listeners, parent handles, size, position 
213      * and the referenced NEWT screen and display, will be destroyed as well. Be aware that if you call
214      * this method with deep = true, you will not be able to regenerate the Window.
215      * @see #destroy()
216      */
217     public void destroy(boolean deep) {
218         if( !isDestroyed() ) {
219             runOnEDTIfAvail(true, new DestroyAction(deep));
220         }
221     }
222
223     public boolean getPerfLogEnabled() { return perfLog; }
224
225     public void enablePerfLog(boolean v) {
226         perfLog = v;
227     }
228
229     protected void setVisibleImpl(boolean visible) {
230         shouldNotCallThis();
231     }
232
233     public void reparentWindow(NativeWindow newParent, Screen newScreen) {
234         window.reparentWindow(newParent, newScreen);
235     }
236
237     class VisibleAction implements Runnable {
238         boolean visible;
239         public VisibleAction(boolean visible) {
240             this.visible = visible;
241         }
242         public void run() {
243             // Lock: Have to cover whole workflow (window, may do nativeCreation, drawable and context)
244             windowLock();
245             try{
246                 window.setVisible(visible);
247                 if (null == context && visible && 0 != window.getWindowHandle() && 0<getWidth()*getHeight()) {
248                     NativeWindow nw;
249                     if (getWrappedWindow() != null) {
250                         nw = NativeWindowFactory.getNativeWindow(getWrappedWindow(), window.getGraphicsConfiguration());
251                     } else {
252                         nw = window;
253                     }
254                     GLCapabilities glCaps = (GLCapabilities) nw.getGraphicsConfiguration().getNativeGraphicsConfiguration().getChosenCapabilities();
255                     if(null==factory) {
256                         factory = GLDrawableFactory.getFactory(glCaps.getGLProfile());
257                     }
258                     if(null==drawable) {
259                         drawable = factory.createGLDrawable(nw);
260                     }    
261                     drawable.setRealized(true);
262                     context = drawable.createContext(null);
263                     sendReshape = true; // ensure a reshape event is send ..
264                 }
265             } finally {
266                 windowUnlock();
267             }
268         }
269     }
270
271     public void setVisible(boolean visible) {
272         if(!isDestroyed()) {
273             runOnEDTIfAvail(true, new VisibleAction(visible));
274         }
275     }
276
277     public Capabilities getRequestedCapabilities() {
278         return window.getRequestedCapabilities();
279     }
280
281     public NativeWindow getParentNativeWindow() {
282         return window.getParentNativeWindow();
283     }
284
285     public Screen getScreen() {
286         return window.getScreen();
287     }
288
289     public void setTitle(String title) {
290         window.setTitle(title);
291     }
292
293     public String getTitle() {
294         return window.getTitle();
295     }
296
297     public void setUndecorated(boolean value) {
298         window.setUndecorated(value);
299     }
300
301     public boolean isUndecorated() {
302         return window.isUndecorated();
303     }
304
305     public void requestFocus() {
306         window.requestFocus();
307     }
308     public void setFocusAction(FocusRunnable focusAction) {
309         window.setFocusAction(focusAction);
310     }
311
312     public Insets getInsets() {
313         return window.getInsets();
314     }
315
316     public void setSize(int width, int height) {
317         window.setSize(width, height);
318     }
319     protected void setSizeImpl(int width, int height) {
320         shouldNotCallThis();
321     }
322
323     public void setPosition(int x, int y) {
324         window.setPosition(x, y);
325     }
326     protected void setPositionImpl(int x, int y) {
327         shouldNotCallThis();
328     }
329
330     public boolean setFullscreen(boolean fullscreen) {
331         return window.setFullscreen(fullscreen);
332     }
333     protected void setFullscreenImpl(boolean fullscreen, int x, int y, int w, int h) {
334         shouldNotCallThis();
335     }
336
337     public boolean isVisible() {
338         return window.isVisible();
339     }
340
341     public int getX() {
342         return window.getX();
343     }
344
345     public int getY() {
346         return window.getY();
347     }
348
349     public int getWidth() {
350         return window.getWidth();
351     }
352
353     public int getHeight() {
354         return window.getHeight();
355     }
356
357     public boolean isFullscreen() {
358         return window.isFullscreen();
359     }
360
361     public void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event) {
362         window.enqueueEvent(wait, event);
363     }
364     public boolean consumeEvent(NEWTEvent e) {
365         return window.consumeEvent(e);
366     }
367
368     public void addSurfaceUpdatedListener(int index, SurfaceUpdatedListener l) {
369         window.addSurfaceUpdatedListener(index, l);
370     }
371     public void removeSurfaceUpdatedListener(SurfaceUpdatedListener l) {
372         window.removeSurfaceUpdatedListener(l);
373     }
374     public void removeAllSurfaceUpdatedListener() {
375         window.removeAllSurfaceUpdatedListener();
376     }
377     public SurfaceUpdatedListener getSurfaceUpdatedListener(int index) {
378         return window.getSurfaceUpdatedListener(index);
379     }
380     public SurfaceUpdatedListener[] getSurfaceUpdatedListeners() {
381         return window.getSurfaceUpdatedListeners();
382     }
383     public void surfaceUpdated(Object updater, NativeWindow window0, long when) { 
384         window.surfaceUpdated(updater, window, when);
385     }
386
387     public void addMouseListener(int index, MouseListener l) {
388         window.addMouseListener(index, l);
389     }
390     public void removeMouseListener(MouseListener l) {
391         window.removeMouseListener(l);
392     }
393     public MouseListener getMouseListener(int index) {
394         return window.getMouseListener(index);
395     }
396     public MouseListener[] getMouseListeners() {
397         return window.getMouseListeners();
398     }
399
400     public void addKeyListener(int index, KeyListener l) {
401         window.addKeyListener(index, l);
402     }
403     public void removeKeyListener(KeyListener l) {
404         window.removeKeyListener(l);
405     }
406     public KeyListener getKeyListener(int index) {
407         return window.getKeyListener(index);
408     }
409     public KeyListener[] getKeyListeners() {
410         return window.getKeyListeners();
411     }
412
413     public void sendWindowEvent(int eventType) {
414         window.sendWindowEvent(eventType);
415     }
416     public void enqueueWindowEvent(boolean wait, int eventType) {
417         window.enqueueWindowEvent(wait, eventType);
418     }
419     public void addWindowListener(int index, WindowListener l) {
420         window.addWindowListener(index, l);
421     }
422     public void removeWindowListener(WindowListener l) {
423         window.removeWindowListener(l);
424     }
425     public WindowListener getWindowListener(int index) {
426         return window.getWindowListener(index);
427     }
428     public WindowListener[] getWindowListeners() {
429         return window.getWindowListeners();
430     }
431     public void setPropagateRepaint(boolean v) {
432         window.setPropagateRepaint(v);
433     }
434     public void windowRepaint(int x, int y, int width, int height) {
435         window.windowRepaint(x, y, width, height);
436     }
437
438     public String toString() {
439         return "NEWT-GLWindow[ \n\tHelper: "+helper+", \n\tDrawable: "+drawable + /** ", \n\tWindow: "+window+", \n\tFactory: "+factory+ */ "]";
440     }
441
442     //----------------------------------------------------------------------
443     // OpenGL-related methods and state
444     //
445
446     private GLDrawableFactory factory;
447     private GLDrawable drawable;
448     private GLContext context;
449     private GLDrawableHelper helper = new GLDrawableHelper();
450     // To make reshape events be sent immediately before a display event
451     private boolean sendReshape=false;
452     private boolean sendDestroy=false;
453     private boolean perfLog = false;
454
455     public GLDrawableFactory getFactory() {
456         return factory;
457     }
458
459     public void setContext(GLContext newCtx) {
460         context = newCtx;
461     }
462
463     public GLContext getContext() {
464         return context;
465     }
466
467     public GL getGL() {
468         if (context == null) {
469             return null;
470         }
471         return context.getGL();
472     }
473
474     public GL setGL(GL gl) {
475         if (context != null) {
476             context.setGL(gl);
477             return gl;
478         }
479         return null;
480     }
481
482     public void addGLEventListener(GLEventListener listener) {
483         helper.addGLEventListener(listener);
484     }
485
486     public void addGLEventListener(int index, GLEventListener listener) {
487         helper.addGLEventListener(index, listener);
488     }
489
490     public void removeGLEventListener(GLEventListener listener) {
491         helper.removeGLEventListener(listener);
492     }
493
494     public void setAnimator(Thread animator) {
495         helper.setAnimator(animator);
496         window.setPropagateRepaint(null==animator);
497     }
498
499     public Thread getAnimator() {
500         return helper.getAnimator();
501     }
502
503     public void invoke(boolean wait, GLRunnable glRunnable) {
504         helper.invoke(this, wait, glRunnable);
505     }
506
507     public void display() {
508         display(false);
509     }
510
511     public void display(boolean forceReshape) {
512         if( null == window ) { return; }
513
514         if(sendDestroy || ( null!=window && window.hasDeviceChanged() && GLAutoDrawable.SCREEN_CHANGE_ACTION_ENABLED ) ) {
515           sendDestroy=false;
516           destroy();
517           return;
518         }
519
520         if( null == context && window.isVisible() ) {
521             // retry native window and drawable/context creation 
522             setVisible(true);
523         }
524
525         if( window.isVisible() && window.isNativeWindowValid() && null != context ) {
526             if(forceReshape) {
527                 sendReshape = true;
528             }
529             windowLock();
530             try{
531                 helper.invokeGL(drawable, context, displayAction, initAction);
532             } finally {
533                 windowUnlock();
534             }
535         }
536     }
537
538     /** This implementation uses a static value */
539     public void setAutoSwapBufferMode(boolean onOrOff) {
540         helper.setAutoSwapBufferMode(onOrOff);
541     }
542
543     /** This implementation uses a static value */
544     public boolean getAutoSwapBufferMode() {
545         return helper.getAutoSwapBufferMode();
546     }
547
548     public void swapBuffers() {
549         if(drawable!=null && context != null) {
550             // Lock: Locked Surface/Window by MakeCurrent/Release
551             if (context != GLContext.getCurrent()) {
552                 // Assume we should try to make the context current before swapping the buffers
553                 helper.invokeGL(drawable, context, swapBuffersAction, initAction);
554             } else {
555                 drawable.swapBuffers();
556             }
557         }
558     }
559
560     class InitAction implements Runnable {
561         public void run() {
562             // Lock: Locked Surface/Window by MakeCurrent/Release
563             helper.init(GLWindow.this);
564             startTime = System.currentTimeMillis(); // overwrite startTime to real init one
565             curTime   = startTime;
566             if(perfLog) {
567                 lastCheck  = startTime;
568                 totalFrames = 0; lastFrames = 0;
569             }
570         }
571     }
572     private InitAction initAction = new InitAction();
573
574     class DisplayAction implements Runnable {
575         public void run() {
576             // Lock: Locked Surface/Window by display _and_ MakeCurrent/Release
577             if (sendReshape) {
578                 helper.reshape(GLWindow.this, 0, 0, getWidth(), getHeight());
579                 sendReshape = false;
580             }
581
582             helper.display(GLWindow.this);
583
584             curTime = System.currentTimeMillis();
585             totalFrames++;
586
587             if(perfLog) {
588                 long dt0, dt1;
589                 lastFrames++;
590                 dt0 = curTime-lastCheck;
591                 if ( dt0 > 5000 ) {
592                     dt1 = curTime-startTime;
593                     System.out.println(dt0/1000 +"s: "+ lastFrames + "f, " + (lastFrames*1000)/dt0 + " fps, "+dt0/lastFrames+" ms/f; "+
594                                        "total: "+ dt1/1000+"s, "+(totalFrames*1000)/dt1 + " fps, "+dt1/totalFrames+" ms/f");
595                     lastCheck=curTime;
596                     lastFrames=0;
597                 }
598             }
599         }
600     }
601     private DisplayAction displayAction = new DisplayAction();
602
603     public long getStartTime()   { return startTime; }
604     public long getCurrentTime() { curTime = System.currentTimeMillis(); return curTime; }
605     public long getDuration()    { return getCurrentTime()-startTime; }
606     public int  getTotalFrames() { return totalFrames; }
607
608     private long startTime = 0;
609     private long curTime = 0;
610     private long lastCheck  = 0;
611     private int  totalFrames = 0, lastFrames = 0;
612
613     class SwapBuffersAction implements Runnable {
614         public void run() {
615             drawable.swapBuffers();
616         }
617     }
618     private SwapBuffersAction swapBuffersAction = new SwapBuffersAction();
619
620     //----------------------------------------------------------------------
621     // NativeWindow/Window methods
622     //
623
624     public int lockSurface() throws NativeWindowException {
625         if(null!=drawable) return drawable.getNativeWindow().lockSurface();
626         return window.lockSurface();
627     }
628
629     public void unlockSurface() {
630         if(null!=drawable) drawable.getNativeWindow().unlockSurface();
631         else window.unlockSurface();
632     }
633
634     public boolean isSurfaceLocked() {
635         if(null!=drawable) return drawable.getNativeWindow().isSurfaceLocked();
636         return window.isSurfaceLocked();
637     }
638
639     public Exception getLockedStack() {
640         if(null!=drawable) return drawable.getNativeWindow().getLockedStack();
641         return window.getLockedStack();
642     }
643
644     public boolean surfaceSwap() { 
645         if(null!=drawable) return drawable.getNativeWindow().surfaceSwap();
646         return super.surfaceSwap();
647     }
648
649     public long getWindowHandle() {
650         if(null!=drawable) return drawable.getNativeWindow().getWindowHandle();
651         return window.getWindowHandle();
652     }
653
654     public long getSurfaceHandle() {
655         if(null!=drawable) return drawable.getNativeWindow().getSurfaceHandle();
656         return window.getSurfaceHandle();
657     }
658
659     public AbstractGraphicsConfiguration getGraphicsConfiguration() {
660         if(null!=drawable) return drawable.getNativeWindow().getGraphicsConfiguration();
661         return window.getGraphicsConfiguration();
662     }
663
664     //----------------------------------------------------------------------
665     // GLDrawable methods
666     //
667
668     public NativeWindow getNativeWindow() {
669         return null!=drawable ? drawable.getNativeWindow() : null;
670     }
671
672     public long getHandle() {
673         return null!=drawable ? drawable.getHandle() : 0;
674     }
675
676     //----------------------------------------------------------------------
677     // GLDrawable methods that are not really needed
678     //
679
680     public GLContext createContext(GLContext shareWith) {
681         return drawable.createContext(shareWith);
682     }
683
684     public void setRealized(boolean realized) {
685     }
686
687     public boolean isRealized() {
688         return ( null != drawable ) ? drawable.isRealized() : false;
689     }
690
691     public GLCapabilities getChosenGLCapabilities() {
692         if (drawable == null) {
693             throw new GLException("No drawable yet");
694         }
695
696         return drawable.getChosenGLCapabilities();
697     }
698
699     public GLProfile getGLProfile() {
700         if (drawable == null) {
701             throw new GLException("No drawable yet");
702         }
703
704         return drawable.getGLProfile();
705     }
706 }
http://JogAmp.org git info: FAQ, tutorial and man pages.