Jogamp
Changed NEWT NativeWindow creation contract - AWT/NEWT Parenting - Misc Fixes
[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 javax.media.nativewindow.*;
39 import javax.media.opengl.*;
40 import com.jogamp.opengl.impl.GLDrawableHelper;
41 import java.util.*;
42
43 /**
44  * An implementation of {@link Window} which is customized for OpenGL
45  * use, and which implements the {@link javax.media.opengl.GLAutoDrawable} interface.
46  * <P>
47  * This implementation does not make the OpenGL context current<br>
48  * before calling the various input EventListener callbacks (MouseListener, KeyListener,
49  * etc.).<br>
50  * This design decision is made to favor a more performant and simplified
51  * implementation, as well as the event dispatcher shall be allowed
52  * not having a notion about OpenGL.
53  * <p>
54  */
55 public class GLWindow extends Window implements GLAutoDrawable {
56     private static List/*GLWindow*/ glwindows = new ArrayList();
57
58     private boolean ownerOfWinScrDpy;
59     private Window window;
60     private boolean runPumpMessages;
61
62     /**
63      * Constructor. Do not call this directly -- use {@link #create()} instead.
64      */
65     protected GLWindow(Window window, boolean ownerOfWinScrDpy) {
66         this.ownerOfWinScrDpy = ownerOfWinScrDpy;
67         this.window = window;
68         this.window.setAutoDrawableClient(true);
69         this.runPumpMessages = ( null == getScreen().getDisplay().getEDTUtil() ) ;
70         window.addWindowListener(new WindowAdapter() {
71                 public void windowResized(WindowEvent e) {
72                     sendReshape = true;
73                 }
74
75                 public void windowDestroyNotify(WindowEvent e) {
76                     sendDestroy = true;
77                 }
78             });
79
80         List newglw = (List) ((ArrayList) glwindows).clone();
81         newglw.add(this);
82         glwindows=newglw;
83     }
84
85     /** Creates a new GLWindow attaching the given window - not owning the Window. */
86     public static GLWindow create(Window window) {
87         return create(null, window, null, false);
88     }
89
90     /** Creates a new GLWindow attaching a new native child Window of the given <code>parentWindowObject</code>
91         with the given GLCapabilities - owning the Window */
92     public static GLWindow create(Object parentWindowObject, GLCapabilities caps) {
93         return create(parentWindowObject, null, caps, true);
94     }
95
96     /** Creates a new GLWindow attaching a new decorated Window on the local display, screen 0, with a
97         dummy visual ID and given GLCapabilities - owning the window */
98     public static GLWindow create(GLCapabilities caps) {
99         return create(null, null, caps, false);
100     }
101
102     /** Creates a new GLWindow attaching a new 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, boolean undecorated) {
105         return create(null, null, caps, undecorated);
106     }
107
108     /** Either or: window (prio), or caps and undecorated (2nd choice) */
109     private static GLWindow create(Object parentWindowObject, Window window, 
110                                    GLCapabilities caps,
111                                    boolean undecorated) {
112         boolean ownerOfWinScrDpy=false;
113         if (window == null) {
114             if (caps == null) {
115                 caps = new GLCapabilities(null); // default ..
116             }
117             ownerOfWinScrDpy = true;
118             window = NewtFactory.createWindow(parentWindowObject, caps, undecorated);
119         }
120
121         return new GLWindow(window, ownerOfWinScrDpy);
122     }
123     
124     public boolean isDestroyed() {
125         return null == window ;
126     }
127
128     public Window getWindow() {
129         return window;
130     }
131
132     /** 
133      * EXPERIMENTAL<br> 
134      * Enable or disables running the {@link Display#pumpMessages} in the {@link #display()} call.<br>
135      * The default behavior is to run {@link Display#pumpMessages}.<P>
136      *
137      * The idea was that in a single threaded environment with one {@link Display} and many {@link Window}'s,
138      * a performance benefit was expected while disabling the implicit {@link Display#pumpMessages} and
139      * do it once via {@link GLWindow#runCurrentThreadPumpMessage()} <br>
140      * This could not have been verified. No measurable difference could have been recognized.<P>
141      *
142      * Best performance has been achieved with one GLWindow per thread.<br> 
143      *
144      * Enabling local pump messages while using the EDT, 
145      * {@link com.jogamp.newt.NewtFactory#setUseEDT(boolean)},
146      * will result in an exception.
147      *
148      * @deprecated EXPERIMENTAL, semantic is about to be removed after further verification.
149      */
150     public void setRunPumpMessages(boolean onoff) {
151         if( onoff && null!=getScreen().getDisplay().getEDTUtil() ) {
152             throw new GLException("GLWindow.setRunPumpMessages(true) - Can't do with EDT on");
153         }
154         runPumpMessages = onoff;
155     }
156
157     protected void createNativeImpl() {
158         shouldNotCallThis();
159     }
160
161     protected void closeNative() {
162         shouldNotCallThis();
163     }
164
165     protected void dispose(boolean regenerate, boolean sendEvent) {
166         if(Window.DEBUG_WINDOW_EVENT || window.DEBUG_IMPLEMENTATION) {
167             Exception e1 = new Exception("GLWindow.dispose("+regenerate+") "+Thread.currentThread()+", 1");
168             e1.printStackTrace();
169         }
170
171         if(sendEvent) {
172             sendDisposeEvent();
173         }
174
175         if (context != null) {
176             context.destroy();
177         }
178         if (drawable != null) {
179             drawable.setRealized(false);
180         }
181
182         if(regenerate) {
183             if(null==window) {
184                 throw new GLException("GLWindow.dispose(true): null window");
185             }
186
187             // recreate GLDrawable, to reflect the new graphics configurations
188             NativeWindow nw;
189             if (window.getWrappedWindow() != null) {
190                 nw = NativeWindowFactory.getNativeWindow(window.getWrappedWindow(), window.getGraphicsConfiguration());
191             } else {
192                 nw = window;
193             }
194             drawable = factory.createGLDrawable(nw);
195             drawable.setRealized(true);
196             context = drawable.createContext(null);
197             sendReshape = true; // ensure a reshape event is send ..
198         }
199
200         if(Window.DEBUG_WINDOW_EVENT || window.DEBUG_IMPLEMENTATION) {
201             System.out.println("GLWindow.dispose("+regenerate+") "+Thread.currentThread()+", fin: "+this);
202         }
203     }
204
205     public synchronized void destroy() {
206         destroy(true);
207     }
208
209     /** @param sendDisposeEvent should be false in a [time,reliable] critical shutdown */
210     public synchronized void destroy(boolean sendDisposeEvent) {
211         List newglw = (List) ((ArrayList) glwindows).clone();
212         newglw.remove(this);
213         glwindows=newglw;
214
215         dispose(false, sendDisposeEvent);
216
217         if(null!=window) {
218             if(ownerOfWinScrDpy) {
219                 window.destroy(true);
220             }
221         }
222
223         drawable = null;
224         context = null;
225         window = null;
226     }
227
228     public boolean getPerfLogEnabled() { return perfLog; }
229
230     public void enablePerfLog(boolean v) {
231         perfLog = v;
232     }
233
234     protected void setVisibleImpl() {
235         shouldNotCallThis();
236     }
237
238     public void setVisible(final boolean visible) {
239         window.setVisible(visible);
240         if (visible && null == context && 0 != window.getWindowHandle()) {
241             NativeWindow nw;
242             if (window.getWrappedWindow() != null) {
243                 nw = NativeWindowFactory.getNativeWindow(window.getWrappedWindow(), window.getGraphicsConfiguration());
244             } else {
245                 nw = window;
246             }
247             GLCapabilities glCaps = (GLCapabilities) nw.getGraphicsConfiguration().getNativeGraphicsConfiguration().getChosenCapabilities();
248             factory = GLDrawableFactory.getFactory(glCaps.getGLProfile());
249             drawable = factory.createGLDrawable(nw);
250             drawable.setRealized(true);
251             context = drawable.createContext(null);
252             sendReshape = true; // ensure a reshape event is send ..
253         }
254     }
255
256     public Screen getScreen() {
257         return window.getScreen();
258     }
259
260     public void setTitle(String title) {
261         window.setTitle(title);
262     }
263
264     public String getTitle() {
265         return window.getTitle();
266     }
267
268     public void setUndecorated(boolean value) {
269         window.setUndecorated(value);
270     }
271
272     public boolean isUndecorated() {
273         return window.isUndecorated();
274     }
275
276     public void requestFocus() {
277         window.requestFocus();
278     }
279
280     public void setSize(int width, int height) {
281         window.setSize(width, height);
282     }
283
284     public void setPosition(int x, int y) {
285         window.setPosition(x, y);
286     }
287
288     public Insets getInsets() {
289         return window.getInsets();
290     }
291
292     public boolean setFullscreen(boolean fullscreen) {
293         return window.setFullscreen(fullscreen);
294     }
295
296     public boolean isVisible() {
297         return window.isVisible();
298     }
299
300     public int getX() {
301         return window.getX();
302     }
303
304     public int getY() {
305         return window.getY();
306     }
307
308     public int getWidth() {
309         return window.getWidth();
310     }
311
312     public int getHeight() {
313         return window.getHeight();
314     }
315
316     public boolean isFullscreen() {
317         return window.isFullscreen();
318     }
319
320     public void addSurfaceUpdatedListener(SurfaceUpdatedListener l) {
321         window.addSurfaceUpdatedListener(l);
322     }
323     public void removeSurfaceUpdatedListener(SurfaceUpdatedListener l) {
324         window.removeSurfaceUpdatedListener(l);
325     }
326     public void removeAllSurfaceUpdatedListener() {
327         window.removeAllSurfaceUpdatedListener();
328     }
329     public SurfaceUpdatedListener[] getSurfaceUpdatedListener() {
330         return window.getSurfaceUpdatedListener();
331     }
332     public void surfaceUpdated(Object updater, NativeWindow window0, long when) { 
333         window.surfaceUpdated(updater, window, when);
334     }
335
336     public void sendEvent(NEWTEvent e) {
337         window.sendEvent(e);
338     }
339
340     public void addMouseListener(MouseListener l) {
341         window.addMouseListener(l);
342     }
343
344     public void removeMouseListener(MouseListener l) {
345         window.removeMouseListener(l);
346     }
347
348     public MouseListener[] getMouseListeners() {
349         return window.getMouseListeners();
350     }
351
352     public void addKeyListener(KeyListener l) {
353         window.addKeyListener(l);
354     }
355
356     public void removeKeyListener(KeyListener l) {
357         window.removeKeyListener(l);
358     }
359
360     public KeyListener[] getKeyListeners() {
361         return window.getKeyListeners();
362     }
363
364     public void addWindowListener(WindowListener l) {
365         window.addWindowListener(l);
366     }
367
368     public void removeWindowListener(WindowListener l) {
369         window.removeWindowListener(l);
370     }
371
372     public WindowListener[] getWindowListeners() {
373         return window.getWindowListeners();
374     }
375
376     public String toString() {
377         return "NEWT-GLWindow[ \n\tHelper: "+helper+", \n\tDrawable: "+drawable + /** ", \n\tWindow: "+window+", \n\tFactory: "+factory+ */ "]";
378     }
379
380     //----------------------------------------------------------------------
381     // OpenGL-related methods and state
382     //
383
384     private GLDrawableFactory factory;
385     private GLDrawable drawable;
386     private GLContext context;
387     private GLDrawableHelper helper = new GLDrawableHelper();
388     // To make reshape events be sent immediately before a display event
389     private boolean sendReshape=false;
390     private boolean sendDestroy=false;
391     private boolean perfLog = false;
392
393     public GLDrawableFactory getFactory() {
394         return factory;
395     }
396
397     public void setContext(GLContext newCtx) {
398         context = newCtx;
399     }
400
401     public GLContext getContext() {
402         return context;
403     }
404
405     public GL getGL() {
406         if (context == null) {
407             return null;
408         }
409         return context.getGL();
410     }
411
412     public GL setGL(GL gl) {
413         if (context != null) {
414             context.setGL(gl);
415             return gl;
416         }
417         return null;
418     }
419
420     public void addGLEventListener(GLEventListener listener) {
421         helper.addGLEventListener(listener);
422     }
423
424     public void removeGLEventListener(GLEventListener listener) {
425         helper.removeGLEventListener(listener);
426     }
427
428     public void display() {
429         display(false);
430     }
431
432     public void display(boolean forceReshape) {
433         if( null == window ) { return; }
434
435         if( null == context && window.isVisible() ) {
436             // retry native window and drawable/context creation 
437             setVisible(true);
438         }
439
440         if( null != context ) {
441             if(runPumpMessages) {
442                 window.getScreen().getDisplay().pumpMessages();
443             }
444             if(window.hasDeviceChanged() && GLAutoDrawable.SCREEN_CHANGE_ACTION_ENABLED) {
445                 dispose(true, true);
446             }
447             if (sendDestroy) {
448                 destroy();
449                 sendDestroy=false;
450             } else if ( window.isVisible() ) {
451                 if(forceReshape) {
452                     sendReshape = true;
453                 }
454                 helper.invokeGL(drawable, context, displayAction, initAction);
455             }
456         }
457     }
458
459     private void sendDisposeEvent() {
460         if(drawable!=null && context != null) {
461             helper.invokeGL(drawable, context, disposeAction, null);
462         }
463     }
464
465     /** This implementation uses a static value */
466     public void setAutoSwapBufferMode(boolean onOrOff) {
467         helper.setAutoSwapBufferMode(onOrOff);
468     }
469
470     /** This implementation uses a static value */
471     public boolean getAutoSwapBufferMode() {
472         return helper.getAutoSwapBufferMode();
473     }
474
475     public void swapBuffers() {
476         if(drawable!=null && context != null) {
477             if (context != GLContext.getCurrent()) {
478                 // Assume we should try to make the context current before swapping the buffers
479                 helper.invokeGL(drawable, context, swapBuffersAction, initAction);
480             } else {
481                 drawable.swapBuffers();
482             }
483         }
484     }
485
486     class InitAction implements Runnable {
487         public void run() {
488             helper.init(GLWindow.this);
489             startTime = System.currentTimeMillis();
490             curTime   = startTime;
491             if(perfLog) {
492                 lastCheck  = startTime;
493                 totalFrames = 0; lastFrames = 0;
494             }
495         }
496     }
497     private InitAction initAction = new InitAction();
498
499     class DisposeAction implements Runnable {
500         public void run() {
501             helper.dispose(GLWindow.this);
502         }
503     }
504     private DisposeAction disposeAction = new DisposeAction();
505
506     class DisplayAction implements Runnable {
507         public void run() {
508             if (sendReshape) {
509                 int width = getWidth();
510                 int height = getHeight();
511                 getGL().glViewport(0, 0, width, height);
512                 helper.reshape(GLWindow.this, 0, 0, width, height);
513                 sendReshape = false;
514             }
515
516             helper.display(GLWindow.this);
517
518             curTime = System.currentTimeMillis();
519             totalFrames++;
520
521             if(perfLog) {
522                 long dt0, dt1;
523                 lastFrames++;
524                 dt0 = curTime-lastCheck;
525                 if ( dt0 > 5000 ) {
526                     dt1 = curTime-startTime;
527                     System.out.println(dt0/1000 +"s: "+ lastFrames + "f, " + (lastFrames*1000)/dt0 + " fps, "+dt0/lastFrames+" ms/f; "+
528                                        "total: "+ dt1/1000+"s, "+(totalFrames*1000)/dt1 + " fps, "+dt1/totalFrames+" ms/f");
529                     lastCheck=curTime;
530                     lastFrames=0;
531                 }
532             }
533         }
534     }
535     private DisplayAction displayAction = new DisplayAction();
536
537     public long getStartTime()   { return startTime; }
538     public long getCurrentTime() { return curTime; }
539     public long getDuration()    { return curTime-startTime; }
540     public int  getTotalFrames() { return totalFrames; }
541
542     private long startTime = 0;
543     private long curTime = 0;
544     private long lastCheck  = 0;
545     private int  totalFrames = 0, lastFrames = 0;
546
547     class SwapBuffersAction implements Runnable {
548         public void run() {
549             drawable.swapBuffers();
550         }
551     }
552     private SwapBuffersAction swapBuffersAction = new SwapBuffersAction();
553
554     //----------------------------------------------------------------------
555     // NativeWindow/Window methods
556     //
557
558     public synchronized int lockSurface() throws NativeWindowException {
559         if(null!=drawable) return drawable.getNativeWindow().lockSurface();
560         return NativeWindow.LOCK_SURFACE_NOT_READY;
561     }
562
563     public synchronized void unlockSurface() {
564         if(null!=drawable) drawable.getNativeWindow().unlockSurface();
565         else throw new NativeWindowException("NEWT-GLWindow not locked");
566     }
567
568     public synchronized boolean isSurfaceLocked() {
569         if(null!=drawable) return drawable.getNativeWindow().isSurfaceLocked();
570         return false;
571     }
572
573     public synchronized Exception getLockedStack() {
574         if(null!=drawable) return drawable.getNativeWindow().getLockedStack();
575         return null;
576     }
577
578     public boolean surfaceSwap() { 
579         if(null!=drawable) return drawable.getNativeWindow().surfaceSwap();
580         return super.surfaceSwap();
581     }
582
583     public long getWindowHandle() {
584         if(null!=drawable) return drawable.getNativeWindow().getWindowHandle();
585         return window.getWindowHandle();
586     }
587
588     public long getSurfaceHandle() {
589         if(null!=drawable) return drawable.getNativeWindow().getSurfaceHandle();
590         return window.getSurfaceHandle();
591     }
592
593     public AbstractGraphicsConfiguration getGraphicsConfiguration() {
594         if(null!=drawable) return drawable.getNativeWindow().getGraphicsConfiguration();
595         return window.getGraphicsConfiguration();
596     }
597
598     //----------------------------------------------------------------------
599     // GLDrawable methods
600     //
601
602     public NativeWindow getNativeWindow() {
603         return null!=drawable ? drawable.getNativeWindow() : null;
604     }
605
606     //----------------------------------------------------------------------
607     // GLDrawable methods that are not really needed
608     //
609
610     public GLContext createContext(GLContext shareWith) {
611         return drawable.createContext(shareWith);
612     }
613
614     public void setRealized(boolean realized) {
615     }
616
617     public GLCapabilities getChosenGLCapabilities() {
618         if (drawable == null) {
619             throw new GLException("No drawable yet");
620         }
621
622         return drawable.getChosenGLCapabilities();
623     }
624
625     public GLProfile getGLProfile() {
626         if (drawable == null) {
627             throw new GLException("No drawable yet");
628         }
629
630         return drawable.getGLProfile();
631     }
632
633     //----------------------------------------------------------------------
634     // Internals only below this point
635     //
636
637     private void shouldNotCallThis() {
638         throw new NativeWindowException("Should not call this");
639     }
640 }
http://JogAmp.org git info: FAQ, tutorial and man pages.