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