Jogamp
Fix NativeWindow/NEWT Unique Display Naming, X11 use real NULL Display name
[jogl.git] / src / newt / classes / com / jogamp / newt / opengl / GLWindow.java
CommitLineData
a959c53b
KR
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
2d57c252 34package com.jogamp.newt.opengl;
a959c53b 35
2d57c252 36import com.jogamp.newt.*;
1ad8c39d 37import com.jogamp.newt.event.*;
a959c53b
KR
38import javax.media.nativewindow.*;
39import javax.media.opengl.*;
f3bc93b0 40import com.jogamp.opengl.impl.GLDrawableHelper;
a92906bc 41import java.util.*;
a959c53b
KR
42
43/**
44 * An implementation of {@link Window} which is customized for OpenGL
2268a6ce
SG
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>
a959c53b
KR
54 */
55public class GLWindow extends Window implements GLAutoDrawable {
a92906bc
SG
56 private static List/*GLWindow*/ glwindows = new ArrayList();
57
eab82899 58 private boolean ownerOfWinScrDpy;
a959c53b 59 private Window window;
2268a6ce 60 private boolean runPumpMessages;
a959c53b 61
476d1d75
MB
62 /**
63 * Constructor. Do not call this directly -- use {@link #create()} instead.
64 */
eab82899
SG
65 protected GLWindow(Window window, boolean ownerOfWinScrDpy) {
66 this.ownerOfWinScrDpy = ownerOfWinScrDpy;
a959c53b
KR
67 this.window = window;
68 this.window.setAutoDrawableClient(true);
1c1053c6 69 this.runPumpMessages = ( null == getScreen().getDisplay().getEDTUtil() ) ;
1ad8c39d 70 window.addWindowListener(new WindowAdapter() {
a959c53b
KR
71 public void windowResized(WindowEvent e) {
72 sendReshape = true;
73 }
74
a959c53b
KR
75 public void windowDestroyNotify(WindowEvent e) {
76 sendDestroy = true;
77 }
78 });
a92906bc
SG
79
80 List newglw = (List) ((ArrayList) glwindows).clone();
81 newglw.add(this);
82 glwindows=newglw;
a959c53b
KR
83 }
84
6e599a26
SG
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);
a959c53b
KR
88 }
89
6e599a26
SG
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);
a959c53b
KR
94 }
95
6e599a26
SG
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 */
a959c53b 98 public static GLWindow create(GLCapabilities caps) {
6e599a26 99 return create(null, null, caps, false);
a959c53b
KR
100 }
101
6e599a26
SG
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 */
a959c53b 104 public static GLWindow create(GLCapabilities caps, boolean undecorated) {
6e599a26 105 return create(null, null, caps, undecorated);
a959c53b
KR
106 }
107
3cc7335e 108 /** Either or: window (prio), or caps and undecorated (2nd choice) */
6e599a26 109 private static GLWindow create(Object parentWindowObject, Window window,
3cc7335e
SG
110 GLCapabilities caps,
111 boolean undecorated) {
eab82899 112 boolean ownerOfWinScrDpy=false;
a959c53b 113 if (window == null) {
3cc7335e
SG
114 if (caps == null) {
115 caps = new GLCapabilities(null); // default ..
116 }
eab82899 117 ownerOfWinScrDpy = true;
6e599a26 118 window = NewtFactory.createWindow(parentWindowObject, caps, undecorated);
a959c53b
KR
119 }
120
eab82899 121 return new GLWindow(window, ownerOfWinScrDpy);
a959c53b
KR
122 }
123
6e599a26
SG
124 public boolean isDestroyed() {
125 return null == window ;
126 }
127
128 public Window getWindow() {
129 return window;
130 }
131
a92906bc
SG
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 *
2268a6ce 144 * Enabling local pump messages while using the EDT,
2d57c252 145 * {@link com.jogamp.newt.NewtFactory#setUseEDT(boolean)},
2268a6ce
SG
146 * will result in an exception.
147 *
a92906bc
SG
148 * @deprecated EXPERIMENTAL, semantic is about to be removed after further verification.
149 */
150 public void setRunPumpMessages(boolean onoff) {
1c1053c6 151 if( onoff && null!=getScreen().getDisplay().getEDTUtil() ) {
2268a6ce
SG
152 throw new GLException("GLWindow.setRunPumpMessages(true) - Can't do with EDT on");
153 }
a92906bc
SG
154 runPumpMessages = onoff;
155 }
156
6e599a26 157 protected void createNativeImpl() {
a959c53b
KR
158 shouldNotCallThis();
159 }
160
161 protected void closeNative() {
162 shouldNotCallThis();
163 }
164
eab82899 165 protected void dispose(boolean regenerate, boolean sendEvent) {
a959c53b 166 if(Window.DEBUG_WINDOW_EVENT || window.DEBUG_IMPLEMENTATION) {
eab82899 167 Exception e1 = new Exception("GLWindow.dispose("+regenerate+") "+Thread.currentThread()+", 1");
a959c53b
KR
168 e1.printStackTrace();
169 }
170
eab82899
SG
171 if(sendEvent) {
172 sendDisposeEvent();
173 }
a959c53b
KR
174
175 if (context != null) {
176 context.destroy();
177 }
178 if (drawable != null) {
179 drawable.setRealized(false);
180 }
181
a959c53b
KR
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);
a959c53b
KR
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) {
2268a6ce 201 System.out.println("GLWindow.dispose("+regenerate+") "+Thread.currentThread()+", fin: "+this);
a959c53b
KR
202 }
203 }
204
205 public synchronized void destroy() {
eab82899 206 destroy(true);
2268a6ce
SG
207 }
208
eab82899
SG
209 /** @param sendDisposeEvent should be false in a [time,reliable] critical shutdown */
210 public synchronized void destroy(boolean sendDisposeEvent) {
a92906bc
SG
211 List newglw = (List) ((ArrayList) glwindows).clone();
212 newglw.remove(this);
213 glwindows=newglw;
214
eab82899 215 dispose(false, sendDisposeEvent);
a959c53b 216
a959c53b 217 if(null!=window) {
eab82899
SG
218 if(ownerOfWinScrDpy) {
219 window.destroy(true);
a959c53b 220 }
a959c53b
KR
221 }
222
223 drawable = null;
224 context = null;
eab82899 225 window = null;
a959c53b
KR
226 }
227
228 public boolean getPerfLogEnabled() { return perfLog; }
229
230 public void enablePerfLog(boolean v) {
231 perfLog = v;
232 }
233
6e599a26
SG
234 protected void setVisibleImpl() {
235 shouldNotCallThis();
236 }
237
238 public void setVisible(final boolean visible) {
a959c53b 239 window.setVisible(visible);
6e599a26 240 if (visible && null == context && 0 != window.getWindowHandle()) {
a959c53b
KR
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);
a959c53b
KR
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
ccc30b05
SG
276 public void requestFocus() {
277 window.requestFocus();
278 }
279
a959c53b
KR
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
7bed517f 288 public Insets getInsets() {
289 return window.getInsets();
290 }
291
a959c53b
KR
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
bf584fba
SG
320 public void addSurfaceUpdatedListener(SurfaceUpdatedListener l) {
321 window.addSurfaceUpdatedListener(l);
322 }
323 public void removeSurfaceUpdatedListener(SurfaceUpdatedListener l) {
324 window.removeSurfaceUpdatedListener(l);
325 }
e8f4dc96
SG
326 public void removeAllSurfaceUpdatedListener() {
327 window.removeAllSurfaceUpdatedListener();
328 }
bf584fba
SG
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
ccc30b05
SG
336 public void sendEvent(NEWTEvent e) {
337 window.sendEvent(e);
338 }
339
a959c53b
KR
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() {
1c1053c6 377 return "NEWT-GLWindow[ \n\tHelper: "+helper+", \n\tDrawable: "+drawable + /** ", \n\tWindow: "+window+", \n\tFactory: "+factory+ */ "]";
a959c53b
KR
378 }
379
380 //----------------------------------------------------------------------
381 // OpenGL-related methods and state
382 //
383
a959c53b
KR
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
4e0a5af0 412 public GL setGL(GL gl) {
a959c53b
KR
413 if (context != null) {
414 context.setGL(gl);
4e0a5af0 415 return gl;
a959c53b 416 }
4e0a5af0 417 return null;
a959c53b
KR
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() {
cf4c4037
SG
429 display(false);
430 }
431
432 public void display(boolean forceReshape) {
6e599a26
SG
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 ) {
a92906bc 441 if(runPumpMessages) {
2268a6ce 442 window.getScreen().getDisplay().pumpMessages();
a92906bc 443 }
a959c53b 444 if(window.hasDeviceChanged() && GLAutoDrawable.SCREEN_CHANGE_ACTION_ENABLED) {
eab82899 445 dispose(true, true);
a959c53b
KR
446 }
447 if (sendDestroy) {
448 destroy();
449 sendDestroy=false;
6e599a26 450 } else if ( window.isVisible() ) {
cf4c4037
SG
451 if(forceReshape) {
452 sendReshape = true;
453 }
a959c53b
KR
454 helper.invokeGL(drawable, context, displayAction, initAction);
455 }
456 }
457 }
458
459 private void sendDisposeEvent() {
2268a6ce 460 if(drawable!=null && context != null) {
a959c53b
KR
461 helper.invokeGL(drawable, context, disposeAction, null);
462 }
463 }
464
a92906bc 465 /** This implementation uses a static value */
a959c53b 466 public void setAutoSwapBufferMode(boolean onOrOff) {
2268a6ce 467 helper.setAutoSwapBufferMode(onOrOff);
a959c53b
KR
468 }
469
a92906bc 470 /** This implementation uses a static value */
a959c53b 471 public boolean getAutoSwapBufferMode() {
2268a6ce 472 return helper.getAutoSwapBufferMode();
a959c53b
KR
473 }
474
475 public void swapBuffers() {
2268a6ce
SG
476 if(drawable!=null && context != null) {
477 if (context != GLContext.getCurrent()) {
a959c53b
KR
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 }
a92906bc 535 private DisplayAction displayAction = new DisplayAction();
a959c53b
KR
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;
a959c53b 546
a959c53b
KR
547 class SwapBuffersAction implements Runnable {
548 public void run() {
549 drawable.swapBuffers();
550 }
551 }
a959c53b
KR
552 private SwapBuffersAction swapBuffersAction = new SwapBuffersAction();
553
554 //----------------------------------------------------------------------
6e599a26 555 // NativeWindow/Window methods
a959c53b
KR
556 //
557
a959c53b
KR
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
8883fa88 578 public boolean surfaceSwap() {
579 if(null!=drawable) return drawable.getNativeWindow().surfaceSwap();
580 return super.surfaceSwap();
581 }
582
a959c53b
KR
583 public long getWindowHandle() {
584 if(null!=drawable) return drawable.getNativeWindow().getWindowHandle();
6e599a26 585 return window.getWindowHandle();
a959c53b
KR
586 }
587
588 public long getSurfaceHandle() {
589 if(null!=drawable) return drawable.getNativeWindow().getSurfaceHandle();
6e599a26
SG
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;
a959c53b
KR
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.