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