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