diff options
author | Petr Skramovsky <[email protected]> | 2013-07-17 09:20:46 +0200 |
---|---|---|
committer | Petr Skramovsky <[email protected]> | 2013-07-17 09:20:46 +0200 |
commit | 9fce91044649c9f97bd94c5791a10270afad7570 (patch) | |
tree | a69fa244ddeb78e52248d4306a4ecc6b27e924da /src/newt/classes | |
parent | 5116d72f0150bdd6353ee664ef76e414bd61f87e (diff) | |
parent | bfb10d309d97c19a33f9b6758f647186f8e0ddd6 (diff) |
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'src/newt/classes')
44 files changed, 1310 insertions, 676 deletions
diff --git a/src/newt/classes/com/jogamp/newt/Display.java b/src/newt/classes/com/jogamp/newt/Display.java index 993aa33eb..c618405c2 100644 --- a/src/newt/classes/com/jogamp/newt/Display.java +++ b/src/newt/classes/com/jogamp/newt/Display.java @@ -31,6 +31,7 @@ package com.jogamp.newt; import com.jogamp.newt.util.EDTUtil; import jogamp.newt.Debug; +import java.lang.ref.WeakReference; import java.util.*; import javax.media.nativewindow.AbstractGraphicsDevice; @@ -153,41 +154,40 @@ public abstract class Display { /** * Sets a new {@link EDTUtil} and returns the previous one. * <p> - * If <code>newEDTUtil</code> is <code>null</code>, + * If <code>usrEDTUtil</code> is <code>null</code>, * the device's default EDTUtil is created and used. * </p> * <p> - * If a previous one exists and it differs from the new one, - * it's being stopped, wait-until-idle and reset to allow a restart at a later time. + * If a previous one exists and it differs from <code>usrEDTUtil</code>, + * it's being stopped, wait-until-idle. * </p> * <p> - * If <code>newEDTUtil</code> is not null and equals the previous one, + * If <code>usrEDTUtil</code> is not null and equals the previous one, * no change is being made. * </p> - * <p> - * Note that <code>newEDTUtil</code> will be started by this method, - * if it is not running yet. - * </p> */ - public abstract EDTUtil setEDTUtil(EDTUtil newEDTUtil); + public abstract EDTUtil setEDTUtil(EDTUtil usrEDTUtil); public abstract EDTUtil getEDTUtil(); + /** + * @return true if EDT is running and not subject to be stopped, otherwise false. + */ public abstract boolean isEDTRunning(); public abstract void dispatchMessages(); // Global Displays - protected static ArrayList<Display> displayList = new ArrayList<Display>(); + protected static final ArrayList<WeakReference<Display>> displayList = new ArrayList<WeakReference<Display>>(); protected static int displaysActive = 0; public static void dumpDisplayList(String prefix) { synchronized(displayList) { - Iterator<Display> i = displayList.iterator(); System.err.println(prefix+" DisplayList[] entries: "+displayList.size()+" - "+getThreadName()); - for(int j=0; i.hasNext(); j++) { - Display d = i.next(); - System.err.println(" ["+j+"] : "+d); + final Iterator<WeakReference<Display>> ri = displayList.iterator(); + for(int j=0; ri.hasNext(); j++) { + final Display d = ri.next().get(); + System.err.println(" ["+j+"] : "+d+", GC'ed "+(null==d)); } } } @@ -216,29 +216,62 @@ public abstract class Display { return getDisplayOfImpl(type, name, fromIndex, -1, shared); } - private static Display getDisplayOfImpl(String type, String name, int fromIndex, int incr, boolean shared) { + private static Display getDisplayOfImpl(String type, String name, final int fromIndex, final int incr, boolean shared) { synchronized(displayList) { int i = fromIndex >= 0 ? fromIndex : displayList.size() - 1 ; while( ( incr > 0 ) ? i < displayList.size() : i >= 0 ) { - Display display = (Display) displayList.get(i); - if( display.getType().equals(type) && - display.getName().equals(name) && - ( !shared || shared && !display.isExclusive() ) - ) { - return display; + final Display display = (Display) displayList.get(i).get(); + if( null == display ) { + // Clear GC'ed dead reference entry! + displayList.remove(i); + if( incr < 0 ) { + // decrease + i+=incr; + } // else nop - remove shifted subsequent elements to the left + } else { + if( display.getType().equals(type) && + display.getName().equals(name) && + ( !shared || shared && !display.isExclusive() ) + ) { + return display; + } + i+=incr; } - i+=incr; } } return null; } - + + protected static void addDisplay2List(Display display) { + synchronized(displayList) { + // GC before add + int i=0; + while( i < displayList.size() ) { + if( null == displayList.get(i).get() ) { + displayList.remove(i); + } else { + i++; + } + } + displayList.add(new WeakReference<Display>(display)); + } + } + /** Returns the global display collection */ - @SuppressWarnings("unchecked") public static Collection<Display> getAllDisplays() { ArrayList<Display> list; synchronized(displayList) { - list = (ArrayList<Display>) displayList.clone(); + list = new ArrayList<Display>(); + int i = 0; + while( i < displayList.size() ) { + final Display d = displayList.get(i).get(); + if( null == d ) { + displayList.remove(i); + } else { + list.add( displayList.get(i).get() ); + i++; + } + } } return list; } diff --git a/src/newt/classes/com/jogamp/newt/MonitorDevice.java b/src/newt/classes/com/jogamp/newt/MonitorDevice.java index 4b0a760a4..8bc7f40e3 100644 --- a/src/newt/classes/com/jogamp/newt/MonitorDevice.java +++ b/src/newt/classes/com/jogamp/newt/MonitorDevice.java @@ -129,6 +129,10 @@ public abstract class MonitorDevice { /** * Returns a list of immutable {@link MonitorMode}s supported by this monitor. * <p> + * The list is ordered in descending order, + * see {@link MonitorMode#compareTo(MonitorMode)}. + * </p> + * <p> * Use w/ care, it's not a copy! * </p> */ diff --git a/src/newt/classes/com/jogamp/newt/MonitorMode.java b/src/newt/classes/com/jogamp/newt/MonitorMode.java index 914aa880f..218cd8bd5 100644 --- a/src/newt/classes/com/jogamp/newt/MonitorMode.java +++ b/src/newt/classes/com/jogamp/newt/MonitorMode.java @@ -28,6 +28,8 @@ package com.jogamp.newt; +import java.util.Comparator; + import javax.media.nativewindow.util.DimensionImmutable; import javax.media.nativewindow.util.RectangleImmutable; import javax.media.nativewindow.util.SurfaceSize; @@ -108,22 +110,36 @@ import com.jogamp.newt.util.MonitorModeUtil; monitor.setCurrentMode(mm); * </pre> */ -public class MonitorMode { +public class MonitorMode implements Comparable<MonitorMode> { + + /** Comparator for 2 {@link MonitorMode}s, following comparison order as described in {@link MonitorMode#compareTo(MonitorMode)}, returning the ascending order. */ + public static final Comparator<MonitorMode> monitorModeComparator = new Comparator<MonitorMode>() { + public int compare(MonitorMode mm1, MonitorMode mm2) { + return mm1.compareTo(mm2); + } }; + + /** Comparator for 2 {@link MonitorMode}s, following comparison order as described in {@link MonitorMode#compareTo(MonitorMode)}, returning the descending order. */ + public static final Comparator<MonitorMode> monitorModeComparatorInv = new Comparator<MonitorMode>() { + public int compare(MonitorMode mm1, MonitorMode mm2) { + return mm2.compareTo(mm1); + } }; + /** - * Immutable <i>surfaceSize and refreshRate</i> Class, consisting of it's read only components:<br> + * Immutable <i>surfaceSize, flags and refreshRate</i> Class, consisting of it's read only components:<br> * <ul> * <li>nativeId</li> * <li>{@link SurfaceSize} surface memory size</li> + * <li><code>flags</code></li> * <li><code>refresh rate</code></li> * </ul> */ - public static class SizeAndRRate { + public static class SizeAndRRate implements Comparable<SizeAndRRate> { /** Non rotated surface size */ public final SurfaceSize surfaceSize; - /** Vertical refresh rate */ - public final float refreshRate; /** Mode bitfield flags, i.e. {@link #FLAG_DOUBLESCAN}, {@link #FLAG_INTERLACE}, .. */ public final int flags; + /** Vertical refresh rate */ + public final float refreshRate; public final int hashCode; public SizeAndRRate(SurfaceSize surfaceSize, float refreshRate, int flags) { @@ -131,8 +147,8 @@ public class MonitorMode { throw new IllegalArgumentException("surfaceSize must be set ("+surfaceSize+")"); } this.surfaceSize=surfaceSize; - this.refreshRate=refreshRate; this.flags = flags; + this.refreshRate=refreshRate; this.hashCode = getHashCode(); } @@ -140,8 +156,8 @@ public class MonitorMode { private final static String STR_DOUBLESCAN = "DoubleScan"; private final static String STR_SEP = ", "; - public static final StringBuffer flags2String(int flags) { - final StringBuffer sb = new StringBuffer(); + public static final StringBuilder flags2String(int flags) { + final StringBuilder sb = new StringBuilder(); boolean sp = false; if( 0 != ( flags & FLAG_INTERLACE ) ) { sb.append(STR_INTERLACE); @@ -161,6 +177,49 @@ public class MonitorMode { } /** + * <p> + * Compares {@link SurfaceSize#compareTo(SurfaceSize) surfaceSize} 1st, then {@link #flags}, then {@link #refreshRate}. + * </p> + * <p> + * Flags are compared as follows: + * <pre> + * NONE > DOUBLESCAN > INTERLACE + * </pre> + * </p> + * <p> + * Refresh rate differences of < 0.01 are considered equal (epsilon). + * </p> + * {@inheritDoc} + */ + @Override + public int compareTo(final SizeAndRRate sszr) { + final int rssz = surfaceSize.compareTo(sszr.surfaceSize); + if( 0 != rssz ) { + return rssz; + } + final int tflags = 0 == flags ? Integer.MAX_VALUE : flags; // normalize NONE + final int xflags = 0 == sszr.flags ? Integer.MAX_VALUE : sszr.flags; // normalize NONE + if( tflags == xflags ) { + final float refreshEpsilon = 0.01f; // reasonable sorting granularity of refresh rate + final float drate = refreshRate - sszr.refreshRate; + if( Math.abs(drate) < refreshEpsilon ) { + return 0; + } else if( drate > refreshEpsilon ) { + return 1; + } else { + return -1; + } + } else { + if(tflags > xflags) { + return 1; + } else if(tflags < xflags) { + return -1; + } + return 0; + } + } + + /** * Tests equality of two {@link SizeAndRRate} objects * by evaluating equality of it's components:<br/> * <ul> @@ -174,8 +233,8 @@ public class MonitorMode { if (obj instanceof SizeAndRRate) { final SizeAndRRate p = (SizeAndRRate)obj; return surfaceSize.equals(p.surfaceSize) && - refreshRate == p.refreshRate && - flags == p.flags ; + flags == p.flags && + refreshRate == p.refreshRate ; } return false; } @@ -184,8 +243,8 @@ public class MonitorMode { * Returns a combined hash code of it's elements:<br/> * <ul> * <li><code>surfaceSize</code></li> - * <li><code>refreshRate</code></li> * <li><code>flags</code></li> + * <li><code>refreshRate</code></li> * </ul> */ public final int hashCode() { @@ -194,8 +253,8 @@ public class MonitorMode { private final int getHashCode() { // 31 * x == (x << 5) - x int hash = 31 + surfaceSize.hashCode(); - hash = ((hash << 5) - hash) + (int)(refreshRate*100.0f); hash = ((hash << 5) - hash) + flags; + hash = ((hash << 5) - hash) + (int)(refreshRate*100.0f); return hash; } } @@ -306,6 +365,42 @@ public class MonitorMode { } /** + * <p> + * Compares {@link SizeAndRRate#compareTo(SizeAndRRate) sizeAndRRate} 1st, then {@link #rotation}. + * </p> + * <p> + * Rotation is compared inverted, i.e. <code>360 - rotation</code>, + * so the lowest rotation reflects a higher value. + * </p> + * <p> + * Order of comparing MonitorMode: + * <ul> + * <li>resolution</li> + * <li>bits per pixel</li> + * <li>flags</li> + * <li>refresh rate</li> + * <li>rotation</li> + * </ul> + * </p> + * {@inheritDoc} + */ + @Override + public int compareTo(final MonitorMode mm) { + final int c = sizeAndRRate.compareTo(mm.sizeAndRRate); + if( 0 != c ) { + return c; + } + final int trot = 360 - rotation; // normalize rotation + final int xrot = 360 - mm.rotation; // normalize rotation + if(trot > xrot) { + return 1; + } else if(trot < xrot) { + return -1; + } + return 0; + } + + /** * Tests equality of two {@link MonitorMode} objects * by evaluating equality of it's components:<br/> * <ul> diff --git a/src/newt/classes/com/jogamp/newt/Screen.java b/src/newt/classes/com/jogamp/newt/Screen.java index f56aff964..f56ee344b 100644 --- a/src/newt/classes/com/jogamp/newt/Screen.java +++ b/src/newt/classes/com/jogamp/newt/Screen.java @@ -29,6 +29,8 @@ package com.jogamp.newt; import com.jogamp.newt.event.MonitorModeListener; import jogamp.newt.Debug; + +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -64,7 +66,7 @@ public abstract class Screen { } /** - * Manual trigger the native creation, if it is not done yet..<br> + * Manual trigger the native creation, if not done yet..<br> * This is useful to be able to request the {@link javax.media.nativewindow.AbstractGraphicsScreen}, via * {@link #getGraphicsScreen()}.<br> * Otherwise the abstract device won't be available before the dependent component (Window) is realized. @@ -164,6 +166,10 @@ public abstract class Screen { /** * Return a list of all {@link MonitorMode}s for all {@link MonitorDevice}s. + * <p> + * The list is ordered in descending order, + * see {@link MonitorMode#compareTo(MonitorMode)}. + * </p> */ public abstract List<MonitorMode> getMonitorModes(); @@ -220,7 +226,7 @@ public abstract class Screen { public abstract void removeMonitorModeListener(MonitorModeListener sml); // Global Screens - protected static ArrayList<Screen> screenList = new ArrayList<Screen>(); + protected static final ArrayList<WeakReference<Screen>> screenList = new ArrayList<WeakReference<Screen>>(); protected static int screensActive = 0; /** @@ -249,26 +255,60 @@ public abstract class Screen { synchronized(screenList) { int i = fromIndex >= 0 ? fromIndex : screenList.size() - 1 ; while( ( incr > 0 ) ? i < screenList.size() : i >= 0 ) { - Screen screen = (Screen) screenList.get(i); - if( screen.getDisplay().equals(display) && - screen.getIndex() == idx ) { - return screen; + final Screen screen = (Screen) screenList.get(i).get(); + if( null == screen ) { + // Clear GC'ed dead reference entry! + screenList.remove(i); + if( incr < 0 ) { + // decrease + i+=incr; + } // else nop - remove shifted subsequent elements to the left + } else { + if( screen.getDisplay().equals(display) && + screen.getIndex() == idx ) { + return screen; + } + i+=incr; } - i+=incr; } } return null; } - /** Returns the global display collection */ - @SuppressWarnings("unchecked") + + protected static void addScreen2List(Screen screen) { + synchronized(screenList) { + // GC before add + int i=0; + while( i < screenList.size() ) { + if( null == screenList.get(i).get() ) { + screenList.remove(i); + } else { + i++; + } + } + screenList.add(new WeakReference<Screen>(screen)); + } + } + + /** Returns the global screen collection */ public static Collection<Screen> getAllScreens() { ArrayList<Screen> list; synchronized(screenList) { - list = (ArrayList<Screen>) screenList.clone(); + list = new ArrayList<Screen>(); + int i = 0; + while( i < screenList.size() ) { + final Screen s = screenList.get(i).get(); + if( null == s ) { + screenList.remove(i); + } else { + list.add( screenList.get(i).get() ); + i++; + } + } } return list; } - + public static int getActiveScreenNumber() { synchronized(screenList) { return screensActive; diff --git a/src/newt/classes/com/jogamp/newt/Window.java b/src/newt/classes/com/jogamp/newt/Window.java index 0bebf330a..f63c03738 100644 --- a/src/newt/classes/com/jogamp/newt/Window.java +++ b/src/newt/classes/com/jogamp/newt/Window.java @@ -406,7 +406,11 @@ public interface Window extends NativeWindow, WindowClosingProtocol { * Sets a {@link KeyListener} allowing focus traversal with a covered window toolkit like AWT. * <p> * The {@link KeyListener} methods are invoked prior to all other {@link KeyListener}'s - * allowing to suppress the {@link KeyEvent} via the {@link InputEvent#consumedTag}. + * allowing to suppress the {@link KeyEvent} via the {@link InputEvent#consumedTag} + * and to perform focus traversal with a 3rd party toolkit. + * </p> + * <p> + * The {@link KeyListener} methods are not invoked for {@link KeyEvent#isAutoRepeat() auto-repeat} events. * </p> * @param l */ diff --git a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java index d0a4b7e98..e4b5a25c4 100644 --- a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java +++ b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java @@ -31,6 +31,7 @@ package com.jogamp.newt.awt; import java.awt.AWTKeyStroke; import java.awt.Canvas; +import java.awt.Component; import java.awt.Graphics; import java.awt.GraphicsConfiguration; import java.awt.KeyboardFocusManager; @@ -201,8 +202,6 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto }; class FocusTraversalKeyListener implements KeyListener { - boolean suppress = false; - public void keyPressed(KeyEvent e) { if( isParent() && !isFullscreen() ) { handleKey(e, false); @@ -213,34 +212,31 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto handleKey(e, true); } } - public void keyTyped(KeyEvent e) { - if(suppress) { - e.setConsumed(true); - suppress = false; // reset - } - } void handleKey(KeyEvent evt, boolean onRelease) { if(null == keyboardFocusManager) { throw new InternalError("XXX"); } final AWTKeyStroke ks = AWTKeyStroke.getAWTKeyStroke(evt.getKeyCode(), evt.getModifiers(), onRelease); + boolean suppress = false; if(null != ks) { final Set<AWTKeyStroke> fwdKeys = keyboardFocusManager.getDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); - final Set<AWTKeyStroke> bwdKeys = keyboardFocusManager.getDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + final Set<AWTKeyStroke> bwdKeys = keyboardFocusManager.getDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); if(fwdKeys.contains(ks)) { + final Component nextFocus = AWTMisc.getNextFocus(NewtCanvasAWT.this, true /* forward */); if(DEBUG) { - System.err.println("NewtCanvasAWT.focusKey (fwd): "+ks+", current focusOwner "+keyboardFocusManager.getFocusOwner()); + System.err.println("NewtCanvasAWT.focusKey (fwd): "+ks+", current focusOwner "+keyboardFocusManager.getFocusOwner()+", hasFocus: "+hasFocus()+", nextFocus "+nextFocus); } // Newt-EDT -> AWT-EDT may freeze Window's native peer requestFocus. - NewtCanvasAWT.this.transferFocus(); + nextFocus.requestFocus(); suppress = true; } else if(bwdKeys.contains(ks)) { + final Component prevFocus = AWTMisc.getNextFocus(NewtCanvasAWT.this, false /* forward */); if(DEBUG) { - System.err.println("NewtCanvasAWT.focusKey (bwd): "+ks+", current focusOwner "+keyboardFocusManager.getFocusOwner()); + System.err.println("NewtCanvasAWT.focusKey (bwd): "+ks+", current focusOwner "+keyboardFocusManager.getFocusOwner()+", hasFocus: "+hasFocus()+", prevFocus "+prevFocus); } // Newt-EDT -> AWT-EDT may freeze Window's native peer requestFocus. - NewtCanvasAWT.this.transferFocusBackward(); + prevFocus.requestFocus(); suppress = true; } } @@ -355,8 +351,8 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto // after native peer is valid: Windows disableBackgroundErase(); - - jawtWindow = NewtFactoryAWT.getNativeWindow(this, newtChild.getRequestedCapabilities()); + + jawtWindow = NewtFactoryAWT.getNativeWindow(this, null != newtChild ? newtChild.getRequestedCapabilities() : null); jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer); if(DEBUG) { @@ -540,6 +536,9 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto newtChildCloseOp = newtChild.setDefaultCloseOperation(WindowClosingMode.DO_NOTHING_ON_CLOSE); keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); keyboardFocusManager.addPropertyChangeListener("focusOwner", focusPropertyChangeListener); + // force this AWT Canvas to be focus-able, + // since this it is completely covered by the newtChild (z-order). + setFocusable(true); if(isOnscreen) { // onscreen newt child needs to fwd AWT focus newtChild.setKeyboardFocusHandler(newtFocusTraversalKeyListener); @@ -552,6 +551,7 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto newtChild.removeWindowListener(clearAWTMenusOnNewtFocus); newtChild.setFocusAction(null); newtChild.setDefaultCloseOperation(newtChildCloseOp); + setFocusable(false); } } } @@ -576,7 +576,9 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto } final int w = getWidth(); final int h = getHeight(); - System.err.println("NewtCanvasAWT.attachNewtChild.2: size "+w+"x"+h); + if(DEBUG) { + System.err.println("NewtCanvasAWT.attachNewtChild.2: size "+w+"x"+h); + } newtChild.setVisible(false); newtChild.setSize(w, h); newtChild.reparentWindow(jawtWindow); @@ -588,9 +590,6 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto configureNewtChild(true); newtChild.sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout to listener - // force this AWT Canvas to be focus-able, - // since this it is completely covered by the newtChild (z-order). - setFocusable(true); if(DEBUG) { System.err.println("NewtCanvasAWT.attachNewtChild.X: win "+newtWinHandleToHexString(newtChild)+", EDTUtil: cur "+newtChild.getScreen().getDisplay().getEDTUtil()+", comp "+this); } diff --git a/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtApplet1Run.java b/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtApplet1Run.java index d06aca039..e8cd71514 100644 --- a/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtApplet1Run.java +++ b/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtApplet1Run.java @@ -42,6 +42,7 @@ import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLProfile; +import com.jogamp.common.os.Platform; import com.jogamp.newt.awt.NewtCanvasAWT; import com.jogamp.newt.opengl.GLWindow; @@ -94,12 +95,11 @@ import com.jogamp.newt.opengl.GLWindow; public class JOGLNewtApplet1Run extends Applet { public static final boolean DEBUG = JOGLNewtAppletBase.DEBUG; - GLWindow glWindow; - NewtCanvasAWT newtCanvasAWT; - JOGLNewtAppletBase base; + GLWindow glWindow = null; + NewtCanvasAWT newtCanvasAWT = null; + JOGLNewtAppletBase base = null; /** if valid glStandalone:=true (own window) ! */ int glXd=Integer.MAX_VALUE, glYd=Integer.MAX_VALUE, glWidth=Integer.MAX_VALUE, glHeight=Integer.MAX_VALUE; - boolean glStandalone = false; public void init() { if(DEBUG) { @@ -108,7 +108,7 @@ public class JOGLNewtApplet1Run extends Applet { if(!(this instanceof Container)) { throw new RuntimeException("This Applet is not a AWT Container"); } - Container container = (Container) this; + final Container container = (Container) this; String glEventListenerClazzName=null; String glProfileName=null; @@ -147,7 +147,7 @@ public class JOGLNewtApplet1Run extends Applet { if(null==glEventListenerClazzName) { throw new RuntimeException("No applet parameter 'gl_event_listener_class'"); } - glStandalone = Integer.MAX_VALUE>glXd && Integer.MAX_VALUE>glYd && Integer.MAX_VALUE>glWidth && Integer.MAX_VALUE>glHeight; + final boolean glStandalone = Integer.MAX_VALUE>glXd && Integer.MAX_VALUE>glYd && Integer.MAX_VALUE>glWidth && Integer.MAX_VALUE>glHeight; if(DEBUG) { System.err.println("JOGLNewtApplet1Run Configuration:"); System.err.println("glStandalone: "+glStandalone); @@ -209,9 +209,7 @@ public class JOGLNewtApplet1Run extends Applet { addKeyListener((KeyListener)glEventListener); } } - if(glStandalone) { - newtCanvasAWT = null; - } else { + if( !glStandalone ) { newtCanvasAWT = new NewtCanvasAWT(glWindow); container.add(newtCanvasAWT, BorderLayout.CENTER); container.validate(); @@ -226,13 +224,15 @@ public class JOGLNewtApplet1Run extends Applet { public void start() { if(DEBUG) { - System.err.println("JOGLNewtApplet1Run.start() START"); + System.err.println("JOGLNewtApplet1Run.start() START (isVisible "+isVisible()+", isDisplayable "+isDisplayable()+")"); } - this.validate(); this.setVisible(true); - final java.awt.Point p0 = this.getLocationOnScreen(); - if(glStandalone) { + if( null != newtCanvasAWT ) { + newtCanvasAWT.setFocusable(true); + newtCanvasAWT.requestFocus(); + } else { + glWindow.requestFocus(); glWindow.setSize(glWidth, glHeight); glWindow.setPosition(p0.x+glXd, p0.y+glYd); } @@ -251,6 +251,13 @@ public class JOGLNewtApplet1Run extends Applet { System.err.println("GLWindow: "+glWindow); } base.start(); + if( null != newtCanvasAWT && Platform.OSType.MACOS == Platform.getOSType() && newtCanvasAWT.isOffscreenLayerSurfaceEnabled() ) { + // force relayout + final int cW = newtCanvasAWT.getWidth(); + final int cH = newtCanvasAWT.getHeight(); + newtCanvasAWT.setSize(cW+1, cH+1); + newtCanvasAWT.setSize(cW, cH); + } if(DEBUG) { System.err.println("JOGLNewtApplet1Run.start() END"); } @@ -271,12 +278,14 @@ public class JOGLNewtApplet1Run extends Applet { System.err.println("JOGLNewtApplet1Run.destroy() START"); } glWindow.setVisible(false); // hide 1st - if(!glStandalone) { + if( null != newtCanvasAWT ) { glWindow.reparentWindow(null); // get out of newtCanvasAWT this.remove(newtCanvasAWT); // remove newtCanvasAWT } base.destroy(); // destroy glWindow unrecoverable base=null; + glWindow=null; + newtCanvasAWT=null; if(DEBUG) { System.err.println("JOGLNewtApplet1Run.destroy() END"); } diff --git a/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtAppletBase.java b/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtAppletBase.java index c3ad51c96..25ddfad48 100644 --- a/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtAppletBase.java +++ b/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtAppletBase.java @@ -164,12 +164,14 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { // Closing action: back to parent! @Override public void windowDestroyNotify(WindowEvent e) { - if( WindowClosingMode.DO_NOTHING_ON_CLOSE == glWindow.getDefaultCloseOperation() ) { + if( isValid() && WindowClosingMode.DO_NOTHING_ON_CLOSE == glWindow.getDefaultCloseOperation() ) { if(null == glWindow.getParent()) { // we may be called directly by the native EDT new Thread(new Runnable() { public void run() { - glWindow.reparentWindow(awtParent); + if( glWindow.isNativeValid() ) { + glWindow.reparentWindow(awtParent); + } } }).start(); } @@ -284,11 +286,11 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { // *********************************************************************************** // *********************************************************************************** - public void keyPressed(KeyEvent e) { - } - public void keyReleased(KeyEvent e) { - } - public void keyTyped(KeyEvent e) { + @Override + public void keyPressed(KeyEvent e) { + if( !e.isPrintableKey() || e.isAutoRepeat() ) { + return; + } if(e.getKeyChar()=='d') { glWindow.setUndecorated(!glWindow.isUndecorated()); } if(e.getKeyChar()=='f') { @@ -304,5 +306,9 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { } } } + + @Override + public void keyReleased(KeyEvent e) { + } } diff --git a/src/newt/classes/com/jogamp/newt/event/KeyAdapter.java b/src/newt/classes/com/jogamp/newt/event/KeyAdapter.java index 93c8409b1..42ebea722 100644 --- a/src/newt/classes/com/jogamp/newt/event/KeyAdapter.java +++ b/src/newt/classes/com/jogamp/newt/event/KeyAdapter.java @@ -35,7 +35,5 @@ public abstract class KeyAdapter implements KeyListener } public void keyReleased(KeyEvent e) { } - public void keyTyped(KeyEvent e) { - } } diff --git a/src/newt/classes/com/jogamp/newt/event/KeyEvent.java b/src/newt/classes/com/jogamp/newt/event/KeyEvent.java index 6e63ad3a3..085f598dc 100644 --- a/src/newt/classes/com/jogamp/newt/event/KeyEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/KeyEvent.java @@ -43,9 +43,8 @@ import com.jogamp.common.util.IntBitfield; * <p> * <table border="0"> * <tr><th>#</th><th>Event Type</th> <th>Constraints</th> <th>Notes</th></tr> - * <tr><td>1</td><td>{@link #EVENT_KEY_PRESSED} </td><td> <i> excluding {@link #isAutoRepeat() auto-repeat} {@link #isModifierKey() modifier} keys</i></td><td></td></tr> - * <tr><td>2</td><td>{@link #EVENT_KEY_RELEASED} </td><td> <i> excluding {@link #isAutoRepeat() auto-repeat} {@link #isModifierKey() modifier} keys</i></td><td></td></tr> - * <tr><td>3</td><td>{@link #EVENT_KEY_TYPED} </td><td> <i>only for {@link #isPrintableKey() printable} and non {@link #isAutoRepeat() auto-repeat} keys</i></td><td><b>Deprecated</b>: Use {@link #EVENT_KEY_RELEASED} and apply constraints.</td></tr> + * <tr><td>1</td><td>{@link #EVENT_KEY_PRESSED} </td><td> <i> excluding {@link #isAutoRepeat() auto-repeat}-{@link #isModifierKey() modifier} keys</i></td><td></td></tr> + * <tr><td>2</td><td>{@link #EVENT_KEY_RELEASED} </td><td> <i> excluding {@link #isAutoRepeat() auto-repeat}-{@link #isModifierKey() modifier} keys</i></td><td></td></tr> * </table> * </p> * In case the native platform does not @@ -58,27 +57,24 @@ import com.jogamp.common.util.IntBitfield; * <p> * Auto-Repeat shall behave as follow: * <pre> - P = pressed, R = released, T = typed + P = pressed, R = released 0 = normal, 1 = auto-repeat - P(0), [ R(1), P(1), R(1), ..], R(0) T(0) + P(0), [ R(1), P(1), R(1), ..], R(0) * </pre> * The idea is if you mask out auto-repeat in your event listener - * or catch {@link #EVENT_KEY_TYPED typed} events only, - * you just get one long pressed P/R/T triple for {@link #isPrintableKey() printable} keys. - * {@link #isActionKey() Action} keys would produce one long pressed P/R tuple in case you mask out auto-repeat . + * you just get one long pressed P/R tuple for {@link #isPrintableKey() printable} and {@link #isActionKey() Action} keys. * </p> * <p> * {@link #isActionKey() Action} keys will produce {@link #EVENT_KEY_PRESSED pressed} * and {@link #EVENT_KEY_RELEASED released} events including {@link #isAutoRepeat() auto-repeat}. * </p> * <p> - * {@link #isPrintableKey() Printable} keys will produce {@link #EVENT_KEY_PRESSED pressed}, - * {@link #EVENT_KEY_RELEASED released} and {@link #EVENT_KEY_TYPED typed} events, the latter is excluded for {@link #isAutoRepeat() auto-repeat} events. + * {@link #isPrintableKey() Printable} keys will produce {@link #EVENT_KEY_PRESSED pressed} and {@link #EVENT_KEY_RELEASED released} events. * </p> * <p> - * {@link #isModifierKey() Modifier} keys will produce {@link #EVENT_KEY_PRESSED pressed} - * and {@link #EVENT_KEY_RELEASED released} events excluding {@link #isAutoRepeat() auto-repeat}. + * {@link #isModifierKey() Modifier} keys will produce {@link #EVENT_KEY_PRESSED pressed} and {@link #EVENT_KEY_RELEASED released} events + * excluding {@link #isAutoRepeat() auto-repeat}. * They will also influence subsequent event's {@link #getModifiers() modifier} bits while pressed. * </p> * @@ -217,7 +213,6 @@ public class KeyEvent extends InputEvent switch(type) { case EVENT_KEY_PRESSED: return "EVENT_KEY_PRESSED"; case EVENT_KEY_RELEASED: return "EVENT_KEY_RELEASED"; - case EVENT_KEY_TYPED: return "EVENT_KEY_TYPED"; default: return "unknown (" + type + ")"; } } @@ -317,13 +312,13 @@ public class KeyEvent extends InputEvent * @param isKeyChar true if <code>uniChar</code> is a key character, otherwise a virtual key code */ public static boolean isPrintableKey(final short uniChar, final boolean isKeyChar) { - if( VK_UNDEFINED == uniChar ) { - return false; + if ( VK_BACK_SPACE == uniChar || VK_TAB == uniChar || VK_ENTER == uniChar ) { + return true; } if( !isKeyChar ) { if( ( nonPrintableKeys[0].min <= uniChar && uniChar <= nonPrintableKeys[0].max ) || ( nonPrintableKeys[1].min <= uniChar && uniChar <= nonPrintableKeys[1].max ) || - ( nonPrintableKeys[2].min <= uniChar && uniChar <= nonPrintableKeys[2].max ) || + ( nonPrintableKeys[2].min <= uniChar && uniChar <= nonPrintableKeys[2].max ) || ( nonPrintableKeys[3].min <= uniChar && uniChar <= nonPrintableKeys[3].max ) ) { return false; } @@ -335,7 +330,7 @@ public class KeyEvent extends InputEvent return false; } } - return true; + return VK_UNDEFINED != uniChar; } /** @@ -361,15 +356,10 @@ public class KeyEvent extends InputEvent private static final byte F_ACTION_MASK = 1 << 1; private static final byte F_PRINTABLE_MASK = 1 << 2; - /** A key has been pressed, excluding {@link #isAutoRepeat() auto-repeat} {@link #isModifierKey() modifier} keys. */ + /** A key has been pressed, excluding {@link #isAutoRepeat() auto-repeat}-{@link #isModifierKey() modifier} keys. */ public static final short EVENT_KEY_PRESSED = 300; - /** A key has been released, excluding {@link #isAutoRepeat() auto-repeat} {@link #isModifierKey() modifier} keys. */ + /** A key has been released, excluding {@link #isAutoRepeat() auto-repeat}-{@link #isModifierKey() modifier} keys. */ public static final short EVENT_KEY_RELEASED= 301; - /** - * A {@link #isPrintableKey() printable} key has been typed (pressed and released), excluding {@link #isAutoRepeat() auto-repeat}. - * @deprecated Redundant, will be removed soon. Use {@link #EVENT_KEY_RELEASED} and exclude non {@link #isPrintableKey() printable} keys and {@link #isAutoRepeat() auto-repeat}. - */ - public static final short EVENT_KEY_TYPED = 302; /** * This value, {@code '\0'}, is used to indicate that the keyChar is unknown or not printable. @@ -391,9 +381,19 @@ public class KeyEvent extends InputEvent this.inclKeyChar = inclKeyChar; } }; - /** Non printable key ranges, currently fixed to an array of size 4. */ + /** + * Non printable key ranges, currently fixed to an array of size 4. + * <p> + * Not included, queried upfront: + * <ul> + * <li>{@link #VK_BACK_SPACE}</li> + * <li>{@link #VK_TAB}</li> + * <li>{@link #VK_ENTER}</li> + * </ul> + * </p> + */ public final static NonPrintableRange[] nonPrintableKeys = { - new NonPrintableRange( (short)0x0000, (short)0x001F, true ), // Unicode: Non printable controls: [0x00 - 0x1F] + new NonPrintableRange( (short)0x0000, (short)0x001F, true ), // Unicode: Non printable controls: [0x00 - 0x1F], see exclusion above new NonPrintableRange( (short)0x0061, (short)0x0078, false), // Small 'a' thru 'z' (0x61 - 0x7a) - Not used for keyCode / keySym - Re-used for Fn (collision) new NonPrintableRange( (short)0x008F, (short)0x009F, true ), // Unicode: Non printable controls: [0x7F - 0x9F], Numpad keys [0x7F - 0x8E] are printable! new NonPrintableRange( (short)0xE000, (short)0xF8FF, true ) // Unicode: Private 0xE000 - 0xF8FF (Marked Non-Printable) @@ -425,14 +425,14 @@ public class KeyEvent extends InputEvent static final short VK_FREE06 = (short) 0x06; static final short VK_FREE07 = (short) 0x07; - /** Constant for the BACK SPACE key "\b", matching ASCII. */ + /** Constant for the BACK SPACE key "\b", matching ASCII. Printable! */ public static final short VK_BACK_SPACE = (short) 0x08; - /** Constant for the HORIZ TAB key "\t", matching ASCII. */ + /** Constant for the HORIZ TAB key "\t", matching ASCII. Printable! */ public static final short VK_TAB = (short) 0x09; - /** Constant for the ENTER key, i.e. LINE FEED "\n", matching ASCII. */ - public static final short VK_ENTER = (short) 0x0A; + /** LINE_FEED "\n", matching ASCII, n/a on keyboard. */ + static final short VK_FREE0A = (short) 0x0A; /** Constant for the PAGE DOWN function key. ASCII: Vertical Tabulation. */ public static final short VK_PAGE_DOWN = (short) 0x0B; @@ -440,7 +440,9 @@ public class KeyEvent extends InputEvent /** Constant for the CLEAR key, i.e. FORM FEED, matching ASCII. */ public static final short VK_CLEAR = (short) 0x0C; - static final short VK_FREE0D = (short) 0x0D; + /** Constant for the ENTER key, i.e. CARRIAGE RETURN, matching ASCII. Printable! */ + public static final short VK_ENTER = (short) 0x0D; + static final short VK_FREE0E = (short) 0x0E; /** Constant for the CTRL function key. ASCII: shift-in. */ diff --git a/src/newt/classes/com/jogamp/newt/event/KeyListener.java b/src/newt/classes/com/jogamp/newt/event/KeyListener.java index 5bca733d3..b3927d81a 100644 --- a/src/newt/classes/com/jogamp/newt/event/KeyListener.java +++ b/src/newt/classes/com/jogamp/newt/event/KeyListener.java @@ -43,13 +43,19 @@ public interface KeyListener extends NEWTEventListener { /** A key has been {@link KeyEvent#EVENT_KEY_PRESSED pressed}, excluding {@link #isAutoRepeat() auto-repeat} {@link #isModifierKey() modifier} keys. See {@link KeyEvent}. */ public void keyPressed(KeyEvent e); - /** A key has been {@link KeyEvent#EVENT_KEY_RELEASED released}, excluding {@link #isAutoRepeat() auto-repeat} {@link #isModifierKey() modifier} keys. See {@link KeyEvent}. */ - public void keyReleased(KeyEvent e); /** - * A {@link #isPrintableKey() printable} key has been {@link KeyEvent#EVENT_KEY_TYPED typed} (pressed and released), excluding {@link #isAutoRepeat() auto-repeat}. See {@link KeyEvent}. - * @deprecated Redundant, will be removed soon. Use {@link #keyReleased(KeyEvent)} and exclude non {@link #isPrintableKey() printable} keys and {@link #isAutoRepeat() auto-repeat}. - */ - public void keyTyped(KeyEvent e) ; + * A key has been {@link KeyEvent#EVENT_KEY_RELEASED released}, excluding {@link #isAutoRepeat() auto-repeat} {@link #isModifierKey() modifier} keys. See {@link KeyEvent}. + * <p> + * To simulated the removed <code>keyTyped(KeyEvent e)</code> semantics, + * simply apply the following constraints upfront and bail out if not matched, i.e.: + * <pre> + if( !e.isPrintableKey() || e.isAutoRepeat() ) { + return; + } + * </pre> + * </p> + */ + public void keyReleased(KeyEvent e); } diff --git a/src/newt/classes/com/jogamp/newt/event/MouseEvent.java b/src/newt/classes/com/jogamp/newt/event/MouseEvent.java index 18c8285f7..93bbcc0b9 100644 --- a/src/newt/classes/com/jogamp/newt/event/MouseEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/MouseEvent.java @@ -252,33 +252,6 @@ public class MouseEvent extends InputEvent } /** - * <i>Usually</i> a wheel rotation of <b>> 0.0f is up</b>, - * and <b>< 0.0f is down</b>. - * <p> - * Usually a wheel rotations is considered a vertical scroll.<br/> - * If {@link #isShiftDown()}, a wheel rotations is - * considered a horizontal scroll, where <b>shift-up = left = > 0.0f</b>, - * and <b>shift-down = right = < 0.0f</b>. - * </p> - * <p> - * <i>However</i>, on some OS this might be flipped due to the OS <i>default</i> behavior. - * The latter is true for OS X 10.7 (Lion) for example. - * </p> - * <p> - * The events will be send usually in steps of one, ie. <i>-1.0f</i> and <i>1.0f</i>. - * Higher values may result due to fast scrolling. - * Fractional values may result due to slow scrolling with high resolution devices. - * </p> - * <p> - * The button number refers to the wheel number. - * </p> - * @deprecated Use {@link #getRotation()} - */ - public float getWheelRotation() { - return isShiftDown() ? rotationXYZ[0] : rotationXYZ[1] ; - } - - /** * Returns a 3-component float array filled with the values of the rotational axis * in the following order: horizontal-, vertical- and z-axis. * <p> diff --git a/src/newt/classes/com/jogamp/newt/event/TraceKeyAdapter.java b/src/newt/classes/com/jogamp/newt/event/TraceKeyAdapter.java index 98ba5a24d..629dc50d7 100644 --- a/src/newt/classes/com/jogamp/newt/event/TraceKeyAdapter.java +++ b/src/newt/classes/com/jogamp/newt/event/TraceKeyAdapter.java @@ -48,9 +48,5 @@ public class TraceKeyAdapter implements KeyListener { System.err.println(e); if(null!=downstream) { downstream.keyReleased(e); } } - public void keyTyped(KeyEvent e) { - System.err.println(e); - if(null!=downstream) { downstream.keyTyped(e); } - } } diff --git a/src/newt/classes/com/jogamp/newt/event/awt/AWTKeyAdapter.java b/src/newt/classes/com/jogamp/newt/event/awt/AWTKeyAdapter.java index 1edef347b..bef2e5d0f 100644 --- a/src/newt/classes/com/jogamp/newt/event/awt/AWTKeyAdapter.java +++ b/src/newt/classes/com/jogamp/newt/event/awt/AWTKeyAdapter.java @@ -71,12 +71,11 @@ public class AWTKeyAdapter extends AWTAdapter implements java.awt.event.KeyListe @Override public void keyReleased(java.awt.event.KeyEvent e) { - com.jogamp.newt.event.KeyEvent keyReleaseEvt = AWTNewtEventFactory.createKeyEvent(com.jogamp.newt.event.KeyEvent.EVENT_KEY_RELEASED, e, newtWindow); + final com.jogamp.newt.event.KeyEvent event = AWTNewtEventFactory.createKeyEvent(com.jogamp.newt.event.KeyEvent.EVENT_KEY_RELEASED, e, newtWindow); if(null!=newtListener) { - final com.jogamp.newt.event.KeyListener newtKeyListener = (com.jogamp.newt.event.KeyListener)newtListener; - newtKeyListener.keyReleased(keyReleaseEvt); + ((com.jogamp.newt.event.KeyListener)newtListener).keyReleased(event); } else { - enqueueEvent(false, keyReleaseEvt); + enqueueEvent(false, event); } } diff --git a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java index 1500d48e6..e85d67dea 100644 --- a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java +++ b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java @@ -34,6 +34,8 @@ package com.jogamp.newt.opengl; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.List; import javax.media.nativewindow.AbstractGraphicsConfiguration; @@ -47,6 +49,8 @@ import javax.media.nativewindow.util.InsetsImmutable; import javax.media.nativewindow.util.Point; import javax.media.opengl.FPSCounter; import javax.media.opengl.GL; +import javax.media.opengl.GL3; +import javax.media.opengl.GL4ES3; import javax.media.opengl.GLAnimatorControl; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCapabilities; @@ -54,6 +58,8 @@ import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawable; import javax.media.opengl.GLDrawableFactory; +import javax.media.opengl.GLES2; +import javax.media.opengl.GLES3; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; @@ -79,11 +85,16 @@ import com.jogamp.newt.event.WindowEvent; import com.jogamp.newt.event.WindowListener; import com.jogamp.newt.event.WindowUpdateEvent; import com.jogamp.opengl.JoglVersion; +import com.jogamp.opengl.GLStateKeeper; /** * An implementation of {@link GLAutoDrawable} and {@link Window} interface, * using a delegated {@link Window} instance, which may be an aggregation (lifecycle: created and destroyed). * <P> + * This implementation supports {@link GLStateKeeper GL state preservation}, + * hence {@link #isGLStatePreservationSupported()} returns <code>true</code>. + * </P> + * <P> * This implementation does not make the OpenGL context current<br> * before calling the various input EventListener callbacks, ie {@link com.jogamp.newt.event.MouseListener} etc.<br> * This design decision is made in favor of a more performant and simplified @@ -433,8 +444,8 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind protected class GLLifecycleHook implements WindowImpl.LifecycleHook { @Override - public void preserveGLStateAtDestroy() { - GLWindow.this.preserveGLStateAtDestroy(true); + public void preserveGLStateAtDestroy(boolean value) { + GLWindow.this.preserveGLStateAtDestroy(value); } @Override @@ -524,6 +535,29 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind savedAnimator.resume(); } } + + @SuppressWarnings("deprecation") + @Override + public void shutdownRenderingAction() { + final GLAnimatorControl anim = GLWindow.this.getAnimator(); + if ( null != anim && anim.isAnimating() ) { + final Thread animThread = anim.getThread(); + if( animThread == Thread.currentThread() ) { + anim.stop(); // on anim thread, non-blocking + } else { + AccessController.doPrivileged(new PrivilegedAction<Object>() { + public Object run() { + if( anim.isAnimating() && null != animThread ) { + try { + animThread.stop(); + } catch(Throwable t) { + } + } + return null; + } } ); + } + } + } } //---------------------------------------------------------------------- @@ -579,6 +613,12 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } } + /** + * {@inheritDoc} + * <p> + * GLWindow supports GL state preservation, hence returns <code>true</code>. + * </p> + */ @Override public final boolean isGLStatePreservationSupported() { return true; } @@ -794,14 +834,58 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind * A most simple JOGL AWT test entry */ public static void main(String args[]) { + final boolean forceES2; + final boolean forceES3; + final boolean forceGL3; + final boolean forceGL4ES3; + { + boolean _forceES2 = false; + boolean _forceES3 = false; + boolean _forceGL3 = false; + boolean _forceGL4ES3 = false; + if( null != args ) { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-es2")) { + _forceES2 = true; + } else if(args[i].equals("-es3")) { + _forceES3 = true; + } else if(args[i].equals("-gl3")) { + _forceGL3 = true; + } else if(args[i].equals("-gl4es3")) { + _forceGL4ES3 = true; + } + } + } + forceES2 = _forceES2; + forceES3 = _forceES3; + forceGL3 = _forceGL3; + forceGL4ES3 = _forceGL4ES3; + } + System.err.println("forceES2 "+forceES2); + System.err.println("forceES3 "+forceES3); + System.err.println("forceGL3 "+forceGL3); + System.err.println("forceGL4ES3 "+forceGL4ES3); + System.err.println(VersionUtil.getPlatformInfo()); System.err.println(GlueGenVersion.getInstance()); System.err.println(JoglVersion.getInstance()); System.err.println(JoglVersion.getDefaultOpenGLInfo(null, null, true).toString()); - final GLProfile glp = GLProfile.getDefault(); + final GLProfile glp; + if(forceGL4ES3) { + glp = GLProfile.get(GLProfile.GL4ES3); + } else if(forceGL3) { + glp = GLProfile.get(GLProfile.GL3); + } else if(forceES3) { + glp = GLProfile.get(GLProfile.GLES3); + } else if(forceES2) { + glp = GLProfile.get(GLProfile.GLES2); + } else { + glp = GLProfile.getDefault(); + } final GLCapabilitiesImmutable caps = new GLCapabilities( glp ); + System.err.println("Requesting: "+caps); GLWindow glWindow = GLWindow.create(caps); glWindow.setSize(128, 128); @@ -813,6 +897,23 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind System.err.println(JoglVersion.getGLInfo(gl, null)); System.err.println("Requested: "+drawable.getNativeSurface().getGraphicsConfiguration().getRequestedCapabilities()); System.err.println("Chosen : "+drawable.getChosenGLCapabilities()); + System.err.println("GL impl. class "+gl.getClass().getName()); + if( gl.isGL4ES3() ) { + GL4ES3 _gl = gl.getGL4ES3(); + System.err.println("GL4ES3 retrieved, impl. class "+_gl.getClass().getName()); + } + if( gl.isGL3() ) { + GL3 _gl = gl.getGL3(); + System.err.println("GL3 retrieved, impl. class "+_gl.getClass().getName()); + } + if( gl.isGLES3() ) { + GLES3 _gl = gl.getGLES3(); + System.err.println("GLES3 retrieved, impl. class "+_gl.getClass().getName()); + } + if( gl.isGLES2() ) { + GLES2 _gl = gl.getGLES2(); + System.err.println("GLES2 retrieved, impl. class "+_gl.getClass().getName()); + } } @Override diff --git a/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java b/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java index dbe7c0d98..47dfca0f3 100644 --- a/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java +++ b/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java @@ -345,7 +345,9 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { // set SWT EDT and start it { final Display newtDisplay = newtChild.getScreen().getDisplay(); - newtDisplay.setEDTUtil( new SWTEDTUtil(newtDisplay, getDisplay()) ); + final EDTUtil edtUtil = new SWTEDTUtil(newtDisplay, getDisplay()); + edtUtil.restart(); + newtDisplay.setEDTUtil( edtUtil ); } newtChild.setSize(w, h); diff --git a/src/newt/classes/com/jogamp/newt/util/EDTUtil.java b/src/newt/classes/com/jogamp/newt/util/EDTUtil.java index 0183da592..e86df2084 100644 --- a/src/newt/classes/com/jogamp/newt/util/EDTUtil.java +++ b/src/newt/classes/com/jogamp/newt/util/EDTUtil.java @@ -65,14 +65,19 @@ public interface EDTUtil { public void setPollPeriod(long ms); /** - * Create a new EDT. One should invoke <code>reset()</code><br> - * after <code>invokeStop(..)</code> in case another start via <code>invoke(..)</code> - * is expected. - * - * @see #invoke(boolean, java.lang.Runnable) - * @see #invokeStop(java.lang.Runnable) + * Starts or restarts the EDT. + * <p> + * If the EDT is running, it must be stopped first via {@link #invokeStop(boolean, Runnable)} + * and the caller should wait until it's stopped via {@link #waitUntilStopped()}. + * </p> + * + * @return true if EDT has been successfully restarted, otherwise false + * @throws IllegalStateException if EDT is running and not subject to be stopped, i.e. {@link #isRunning()} returns true + * + * @see #invokeStop(boolean, java.lang.Runnable) + * @see #waitUntilStopped() */ - public void reset(); + public boolean restart() throws IllegalStateException; /** * Returns true if the current thread is the event dispatch thread (EDT). @@ -107,44 +112,57 @@ public interface EDTUtil { public boolean isCurrentThreadEDTorNEDT(); /** - * @return True if EDT is running + * @return True if EDT is running and not subject to be stopped. */ public boolean isRunning(); /** * Append the final task to the EDT task queue, - * signals EDT to stop and wait until stopped.<br/> + * signals EDT to stop. + * <p> + * If <code>wait</code> is <code>true</code> methods + * blocks until EDT is stopped. + * </p> + * <p> * <code>task</code> maybe <code>null</code><br/> * Due to the nature of this method: * <ul> * <li>All previous queued tasks will be finished.</li> * <li>No new tasks are allowed, an Exception is thrown.</li> * <li>Can be issued from within EDT, ie from within an enqueued task.</li> - * <li>{@link #reset()} may follow immediately, ie creating a new EDT</li> + * <li>{@link #restart()} may follow immediately, ie creating a new EDT</li> * </ul> + * </p> + * @return true if <code>task</code> has been executed or queued for later execution, otherwise false */ - public void invokeStop(Runnable finalTask); + public boolean invokeStop(boolean wait, Runnable finalTask); /** - * Shall start the thread if not running, <code>task</code> maybe null for this purpose.<br> - * Append task to the EDT task queue.<br> - * Wait until execution is finished if <code>wait == true</code>.<br> + * Appends task to the EDT task queue if current thread is not EDT, + * otherwise execute task immediately. + * <p> + * Wait until execution is finished if <code>wait == true</code>. + * </p> * Can be issued from within EDT, ie from within an enqueued task.<br> - * - * @throws RuntimeException in case EDT is stopped and not {@link #reset()} + * @return true if <code>task</code> has been executed or queued for later execution, otherwise false */ - public void invoke(boolean wait, Runnable task); + public boolean invoke(boolean wait, Runnable task); /** * Wait until the EDT task queue is empty.<br> * The last task may still be in execution when this method returns. + * @return true if waited for idle, otherwise false, i.e. in case of current thread is EDT or NEDT */ - public void waitUntilIdle(); + public boolean waitUntilIdle(); /** * Wait until EDT task is stopped.<br> - * No <code>stop</code> action is performed, {@link #invokeStop(java.lang.Runnable)} should be used before. + * No <code>stop</code> action is performed, {@link #invokeStop(boolean, java.lang.Runnable)} should be used before. + * <p> + * If caller thread is EDT or NEDT, this call will not block. + * </p> + * @return true if stopped, otherwise false, i.e. in case of current thread is EDT or NEDT */ - public void waitUntilStopped(); + public boolean waitUntilStopped(); } diff --git a/src/newt/classes/com/jogamp/newt/util/MonitorModeUtil.java b/src/newt/classes/com/jogamp/newt/util/MonitorModeUtil.java index 16ffe754f..c30b427d6 100644 --- a/src/newt/classes/com/jogamp/newt/util/MonitorModeUtil.java +++ b/src/newt/classes/com/jogamp/newt/util/MonitorModeUtil.java @@ -31,7 +31,9 @@ package com.jogamp.newt.util; import com.jogamp.newt.MonitorMode; import java.util.ArrayList; +import java.util.Collections; import java.util.List; + import javax.media.nativewindow.util.DimensionImmutable; import javax.media.nativewindow.util.SurfaceSize; @@ -68,6 +70,15 @@ public class MonitorModeUtil { return null; } + /** Sort the given {@link MonitorMode} collection w/ {@link MonitorMode#compareTo(MonitorMode)} function. */ + public static void sort(List<MonitorMode> monitorModes, boolean ascendingOrder) { + if( ascendingOrder ) { + Collections.sort(monitorModes); + } else { + Collections.sort(monitorModes, MonitorMode.monitorModeComparatorInv); + } + } + /** * * @param monitorModes diff --git a/src/newt/classes/jogamp/newt/Debug.java b/src/newt/classes/jogamp/newt/Debug.java index 3c83da4d9..4b0a98216 100644 --- a/src/newt/classes/jogamp/newt/Debug.java +++ b/src/newt/classes/jogamp/newt/Debug.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -39,6 +40,9 @@ package jogamp.newt; +import java.security.AccessController; +import java.security.PrivilegedAction; + import com.jogamp.common.util.PropertyAccess; /** Helper routines for logging and debugging. */ @@ -49,7 +53,12 @@ public class Debug extends PropertyAccess { private static final boolean debugAll; static { - PropertyAccess.addTrustedPrefix("newt.", Debug.class); + AccessController.doPrivileged(new PrivilegedAction<Object>() { + public Object run() { + PropertyAccess.addTrustedPrefix("newt."); + return null; + } } ); + verbose = isPropertyDefined("newt.verbose", true); debugAll = isPropertyDefined("newt.debug", true); if (verbose) { @@ -60,27 +69,18 @@ public class Debug extends PropertyAccess { } } - public static final boolean isPropertyDefined(final String property, final boolean jnlpAlias) { - return PropertyAccess.isPropertyDefined(property, jnlpAlias, null); - } - - public static final int getIntProperty(final String property, final boolean jnlpAlias, int defaultValue) { - return PropertyAccess.getIntProperty(property, jnlpAlias, null, defaultValue); - } - - public static final boolean getBooleanProperty(final String property, final boolean jnlpAlias) { - return PropertyAccess.getBooleanProperty(property, jnlpAlias, null); - } - - public static boolean verbose() { + /** Ensures static init block has been issues, i.e. if calling through to {@link PropertyAccess#isPropertyDefined(String, boolean)}. */ + public static final void initSingleton() {} + + public static final boolean verbose() { return verbose; } - public static boolean debugAll() { + public static final boolean debugAll() { return debugAll; } - public static boolean debug(String subcomponent) { + public static final boolean debug(String subcomponent) { return debugAll() || isPropertyDefined("newt.debug." + subcomponent, true); } } diff --git a/src/newt/classes/jogamp/newt/DefaultEDTUtil.java b/src/newt/classes/jogamp/newt/DefaultEDTUtil.java index 651522799..a229a0512 100644 --- a/src/newt/classes/jogamp/newt/DefaultEDTUtil.java +++ b/src/newt/classes/jogamp/newt/DefaultEDTUtil.java @@ -53,7 +53,7 @@ public class DefaultEDTUtil implements EDTUtil { private final ThreadGroup threadGroup; private final String name; private final Runnable dispatchMessages; - private EventDispatchThread edt = null; + private NEDT edt = null; private int start_iter=0; private static long pollPeriod = EDTUtil.defaultEDTPollPeriod; @@ -61,7 +61,7 @@ public class DefaultEDTUtil implements EDTUtil { this.threadGroup = tg; this.name=Thread.currentThread().getName()+"-"+name+"-EDT-"; this.dispatchMessages=dispatchMessages; - this.edt = new EventDispatchThread(threadGroup, name); + this.edt = new NEDT(threadGroup, name); this.edt.setDaemon(true); // don't stop JVM from shutdown .. } @@ -76,31 +76,34 @@ public class DefaultEDTUtil implements EDTUtil { } @Override - public final void reset() { - synchronized(edtLock) { - waitUntilStopped(); + public final boolean restart() throws IllegalStateException { + synchronized(edtLock) { + if( edt.isRunning() ) { + throw new IllegalStateException("EDT still running and not subject to stop. Curr "+Thread.currentThread().getName()+", EDT "+edt.getName()+", isRunning "+edt.isRunning+", shouldStop "+edt.shouldStop); + } if(DEBUG) { if(edt.tasks.size()>0) { System.err.println(Thread.currentThread()+": Default-EDT reset, remaining tasks: "+edt.tasks.size()+" - "+edt); - // Thread.dumpStack(); } System.err.println(Thread.currentThread()+": Default-EDT reset - edt: "+edt); } - this.edt = new EventDispatchThread(threadGroup, name); - this.edt.setDaemon(true); // don't stop JVM from shutdown .. + if( edt.getState() != Thread.State.NEW ) { + edt = new NEDT(threadGroup, name); + edt.setDaemon(true); // don't stop JVM from shutdown .. + } + startImpl(); } + return invoke(true, nullTask); } private final void startImpl() { if(edt.isAlive()) { - throw new RuntimeException("Default-EDT Thread.isAlive(): true, isRunning: "+edt.isRunning()+", edt: "+edt+", tasks: "+edt.tasks.size()); + throw new RuntimeException("Default-EDT Thread.isAlive(): true, isRunning: "+edt.isRunning+", shouldStop "+edt.shouldStop+", edt: "+edt+", tasks: "+edt.tasks.size()); } start_iter++; edt.setName(name+start_iter); - edt.shouldStop = false; if(DEBUG) { System.err.println(Thread.currentThread()+": Default-EDT START - edt: "+edt); - // Thread.dumpStack(); } edt.start(); } @@ -126,13 +129,13 @@ public class DefaultEDTUtil implements EDTUtil { } @Override - public final void invokeStop(Runnable task) { - invokeImpl(true, task, true); + public final boolean invokeStop(boolean wait, Runnable task) { + return invokeImpl(wait, task, true); } @Override - public final void invoke(boolean wait, Runnable task) { - invokeImpl(wait, task, false); + public final boolean invoke(boolean wait, Runnable task) { + return invokeImpl(wait, task, false); } private static Runnable nullTask = new Runnable() { @@ -140,28 +143,26 @@ public class DefaultEDTUtil implements EDTUtil { public void run() { } }; - private void invokeImpl(boolean wait, Runnable task, boolean stop) { + private final boolean invokeImpl(boolean wait, Runnable task, boolean stop) { Throwable throwable = null; RunnableTask rTask = null; - Object rTaskLock = new Object(); + final Object rTaskLock = new Object(); synchronized(rTaskLock) { // lock the optional task execution synchronized(edtLock) { // lock the EDT status if( edt.shouldStop ) { // drop task .. + System.err.println(Thread.currentThread()+": Warning: Default-EDT about (1) to stop, won't enqueue new task: "+edt); if(DEBUG) { - System.err.println(Thread.currentThread()+": Warning: Default-EDT about (1) to stop, won't enqueue new task: "+edt); Thread.dumpStack(); } - return; + return false; } - // System.err.println(Thread.currentThread()+" XXX stop: "+stop+", tasks: "+edt.tasks.size()+", task: "+task); - // Thread.dumpStack(); if( isCurrentThreadEDT() ) { if(null != task) { task.run(); } wait = false; // running in same thread (EDT) -> no wait - if(stop) { + if( stop ) { edt.shouldStop = true; if( edt.tasks.size()>0 ) { System.err.println(Thread.currentThread()+": Warning: Default-EDT about (2) to stop, task executed. Remaining tasks: "+edt.tasks.size()+" - "+edt); @@ -171,19 +172,20 @@ public class DefaultEDTUtil implements EDTUtil { } } } else { - if( !edt.isRunning() ) { - if( !stop ) { - startImpl(); - } else { - // drop task and don't wait - task = null; - System.err.println(Thread.currentThread()+": Warning: Default-EDT is about (3) to stop and stopped already, dropping task. Remaining tasks: "+edt.tasks.size()+" - "+edt); + if( !edt.isRunning ) { + if( null != task ) { + if( stop ) { + System.err.println(Thread.currentThread()+": Warning: Default-EDT is about (3) to stop and stopped already, dropping task. Remaining tasks: "+edt.tasks.size()+" - "+edt); + } else { + System.err.println(Thread.currentThread()+": Warning: Default-EDT is not running, dropping task. NEDT "+edt); + } if(DEBUG) { Thread.dumpStack(); } } - } else if(stop && null == task) { - task = nullTask; + return false; + } else if( stop && null == task ) { + task = nullTask; // ensures execution triggering stop } if(null != task) { @@ -220,23 +222,26 @@ public class DefaultEDTUtil implements EDTUtil { throw new RuntimeException(throwable); } } - } - if(DEBUG && stop) { - System.err.println(Thread.currentThread()+": Default-EDT signal STOP X edt: "+edt); + if(DEBUG) { + if( stop) { + System.err.println(Thread.currentThread()+": Default-EDT signal STOP X edt: "+edt); + } + } + return true; } } @Override - final public void waitUntilIdle() { - final EventDispatchThread _edt; + final public boolean waitUntilIdle() { + final NEDT _edt; synchronized(edtLock) { _edt = edt; } - if(!_edt.isRunning() || _edt == Thread.currentThread()) { - return; + if(!_edt.isRunning || _edt == Thread.currentThread()) { + return false; } synchronized(_edt.tasks) { - while(_edt.isRunning() && _edt.tasks.size()>0) { + while(_edt.isRunning && _edt.tasks.size()>0) { try { _edt.tasks.notifyAll(); _edt.tasks.wait(); @@ -244,35 +249,39 @@ public class DefaultEDTUtil implements EDTUtil { e.printStackTrace(); } } + return true; } } @Override - final public void waitUntilStopped() { + final public boolean waitUntilStopped() { synchronized(edtLock) { - if(edt.isRunning() && edt != Thread.currentThread() ) { - while(edt.isRunning()) { + if(edt.isRunning && edt != Thread.currentThread() ) { + while( edt.isRunning ) { try { edtLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } + return true; + } else { + return false; } } } - class EventDispatchThread extends Thread { + class NEDT extends Thread { volatile boolean shouldStop = false; volatile boolean isRunning = false; ArrayList<RunnableTask> tasks = new ArrayList<RunnableTask>(); // one shot tasks - public EventDispatchThread(ThreadGroup tg, String name) { + public NEDT(ThreadGroup tg, String name) { super(tg, name); } final public boolean isRunning() { - return isRunning; + return isRunning && !shouldStop; } @Override @@ -282,11 +291,9 @@ public class DefaultEDTUtil implements EDTUtil { } private final void validateNoRecursiveLocksHold() { - if(Lock.DEBUG) { - if(LockDebugUtil.getRecursiveLockTrace().size()>0) { - LockDebugUtil.dumpRecursiveLockTrace(System.err); - throw new InternalError("XXX"); - } + if(LockDebugUtil.getRecursiveLockTrace().size()>0) { + LockDebugUtil.dumpRecursiveLockTrace(System.err); + throw new InternalError("XXX"); } } @@ -299,7 +306,9 @@ public class DefaultEDTUtil implements EDTUtil { if(DEBUG) { System.err.println(getName()+": Default-EDT run() START "+ getName()); } - validateNoRecursiveLocksHold(); + if(Lock.DEBUG) { + validateNoRecursiveLocksHold(); + } RuntimeException error = null; try { do { @@ -329,7 +338,9 @@ public class DefaultEDTUtil implements EDTUtil { } if(null!=task) { task.run(); - validateNoRecursiveLocksHold(); + if(Lock.DEBUG) { + validateNoRecursiveLocksHold(); + } if(!task.hasWaiter() && null != task.getThrowable()) { // at least dump stack-trace in case nobody waits for result System.err.println("DefaultEDT.run(): Catched exception occured on thread "+Thread.currentThread().getName()+": "+task.toString()); diff --git a/src/newt/classes/jogamp/newt/DisplayImpl.java b/src/newt/classes/jogamp/newt/DisplayImpl.java index d4842ba2f..8f792b23c 100644 --- a/src/newt/classes/jogamp/newt/DisplayImpl.java +++ b/src/newt/classes/jogamp/newt/DisplayImpl.java @@ -41,14 +41,29 @@ import com.jogamp.newt.event.NEWTEventConsumer; import jogamp.newt.event.NEWTEventTask; import com.jogamp.newt.util.EDTUtil; + import java.util.ArrayList; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.NativeWindowFactory; public abstract class DisplayImpl extends Display { private static int serialno = 1; + static { + NativeWindowFactory.addCustomShutdownHook(true /* head */, new Runnable() { + public void run() { + WindowImpl.shutdownAll(); + ScreenImpl.shutdownAll(); + DisplayImpl.shutdownAll(); + } + }); + } + + /** Ensure static init has been run. */ + /* pp */static void initSingleton() { } + private static Class<?> getDisplayClass(String type) throws ClassNotFoundException { @@ -62,12 +77,12 @@ public abstract class DisplayImpl extends Display { /** Make sure to reuse a Display with the same name */ public static Display create(String type, String name, final long handle, boolean reuse) { try { - Class<?> displayClass = getDisplayClass(type); - DisplayImpl display = (DisplayImpl) displayClass.newInstance(); + final Class<?> displayClass = getDisplayClass(type); + final DisplayImpl display = (DisplayImpl) displayClass.newInstance(); name = display.validateDisplayName(name, handle); synchronized(displayList) { if(reuse) { - Display display0 = Display.getLastDisplayOf(type, name, -1, true /* shared only */); + final Display display0 = Display.getLastDisplayOf(type, name, -1, true /* shared only */); if(null != display0) { if(DEBUG) { System.err.println("Display.create() REUSE: "+display0+" "+getThreadName()); @@ -82,9 +97,9 @@ public abstract class DisplayImpl extends Display { display.id = serialno++; display.fqname = getFQName(display.type, display.name, display.id); display.hashCode = display.fqname.hashCode(); - displayList.add(display); + display.setEDTUtil( display.edtUtil ); // device's default if EDT is used, or null + Display.addDisplay2List(display); } - display.setEDTUtil(display.edtUtil); // device's default if EDT is used, or null if(DEBUG) { System.err.println("Display.create() NEW: "+display+" "+getThreadName()); @@ -122,10 +137,10 @@ public abstract class DisplayImpl extends Display { } @Override - public synchronized final void createNative() + public synchronized final void createNative() throws NativeWindowException { - if(null==aDevice) { + if( null == aDevice ) { if(DEBUG) { System.err.println("Display.createNative() START ("+getThreadName()+", "+this+")"); } @@ -138,14 +153,14 @@ public abstract class DisplayImpl extends Display { } catch (Throwable t) { throw new NativeWindowException(t); } - if(null==aDevice) { + if( null == aDevice ) { throw new NativeWindowException("Display.createNative() failed to instanciate an AbstractGraphicsDevice"); } - if(DEBUG) { - System.err.println("Display.createNative() END ("+getThreadName()+", "+this+")"); - } synchronized(displayList) { displaysActive++; + if(DEBUG) { + System.err.println("Display.createNative() END ("+getThreadName()+", "+this+", active "+displaysActive+")"); + } } } } @@ -164,25 +179,23 @@ public abstract class DisplayImpl extends Display { } @Override - public EDTUtil setEDTUtil(EDTUtil newEDTUtil) { + public synchronized EDTUtil setEDTUtil(final EDTUtil usrEDTUtil) { final EDTUtil oldEDTUtil = edtUtil; - if(null == newEDTUtil) { - if(DEBUG) { - System.err.println("Display.setEDTUtil(default): "+oldEDTUtil+" -> "+newEDTUtil); + final EDTUtil newEDTUtil; + if( null != usrEDTUtil && usrEDTUtil == oldEDTUtil ) { + if( DEBUG ) { + System.err.println("Display.setEDTUtil: "+usrEDTUtil+" - keep!"); } - edtUtil = createEDTUtil(); - } else if( newEDTUtil != edtUtil ) { + newEDTUtil = oldEDTUtil; + } else { if(DEBUG) { - System.err.println("Display.setEDTUtil(custom): "+oldEDTUtil+" -> "+newEDTUtil); + final String msg = ( null == usrEDTUtil ) ? "default" : "custom"; + System.err.println("Display.setEDTUtil("+msg+"): "+oldEDTUtil+" -> "+usrEDTUtil); } - removeEDT( null ); - edtUtil = newEDTUtil; - } else if( DEBUG ) { - System.err.println("Display.setEDTUtil: "+newEDTUtil+" - keep!"); - } - if( !edtUtil.isRunning() ) { // start EDT if not running yet - edtUtil.invoke(true, null); + stopEDT( oldEDTUtil, null ); + newEDTUtil = ( null == usrEDTUtil ) ? createEDTUtil() : usrEDTUtil; } + edtUtil = newEDTUtil; return oldEDTUtil; } @@ -191,29 +204,61 @@ public abstract class DisplayImpl extends Display { return edtUtil; } - private void removeEDT(final Runnable task) { - if(null!=edtUtil) { - edtUtil.invokeStop(task); - // ready for restart .. + private static void stopEDT(final EDTUtil edtUtil, final Runnable task) { + if( null != edtUtil ) { + if( edtUtil.isRunning() ) { + final boolean res = edtUtil.invokeStop(true, task); + if( DEBUG ) { + if ( !res ) { + System.err.println("Warning: invokeStop() failed"); + Thread.dumpStack(); + } + } + } edtUtil.waitUntilStopped(); - edtUtil.reset(); - } else { + // ready for restart .. + } else if( null != task ) { task.run(); } } public void runOnEDTIfAvail(boolean wait, final Runnable task) { - if( null!=edtUtil && !edtUtil.isCurrentThreadEDT()) { - edtUtil.invoke(wait, task); + final EDTUtil _edtUtil = edtUtil; + if( null != _edtUtil && !_edtUtil.isCurrentThreadEDT() ) { + if( !_edtUtil.isRunning() ) { // start EDT if not running yet + synchronized( this ) { + if( !_edtUtil.isRunning() ) { // // volatile dbl-checked-locking OK + _edtUtil.restart(); + if( DEBUG ) { + System.err.println("Info: EDT started "+Thread.currentThread().getName()+", "+this); + Thread.dumpStack(); + } + } + } + } + if( !_edtUtil.invoke(wait, task) ) { + if( DEBUG ) { + System.err.println("Warning: invoke(wait "+wait+", ..) on EDT failed .. invoke on current thread "+Thread.currentThread().getName()); + Thread.dumpStack(); + } + task.run(); + } } else { task.run(); } } public boolean validateEDT() { - if(0==refCount && null==aDevice && null != edtUtil && edtUtil.isRunning()) { - removeEDT( null ); - return true; + if( 0==refCount && null == aDevice ) { + final EDTUtil _edtUtil = edtUtil; + if( null != _edtUtil && _edtUtil.isRunning() ) { + synchronized( this ) { + if( null != edtUtil && edtUtil.isRunning() ) { // // volatile dbl-checked-locking OK + stopEDT( edtUtil, null ); + return true; + } + } + } } return false; } @@ -224,29 +269,69 @@ public abstract class DisplayImpl extends Display { dumpDisplayList("Display.destroy("+getFQName()+") BEGIN"); } synchronized(displayList) { - displayList.remove(this); if(0 < displaysActive) { displaysActive--; } - } - if(DEBUG) { - System.err.println("Display.destroy(): "+this+" "+getThreadName()); - } - final AbstractGraphicsDevice f_aDevice = aDevice; + if(DEBUG) { + System.err.println("Display.destroy(): "+this+", active "+displaysActive+" "+getThreadName()); + } + } final DisplayImpl f_dpy = this; - removeEDT( new Runnable() { + final AbstractGraphicsDevice f_aDevice = aDevice; + aDevice = null; + refCount=0; + stopEDT( edtUtil, new Runnable() { // blocks! public void run() { if ( null != f_aDevice ) { - f_dpy.closeNativeImpl(); + f_dpy.closeNativeImpl(f_aDevice); } } } ); - aDevice = null; - refCount=0; if(DEBUG) { dumpDisplayList("Display.destroy("+getFQName()+") END"); } } + + /** May be utilized at a shutdown hook, impl. does not block. */ + /* pp */ static final void shutdownAll() { + final int dCount = displayList.size(); + if(DEBUG) { + dumpDisplayList("Display.shutdownAll "+dCount+" instances, on thread "+getThreadName()); + } + for(int i=0; i<dCount && displayList.size()>0; i++) { // be safe .. + final DisplayImpl d = (DisplayImpl) displayList.remove(0).get(); + if(DEBUG) { + System.err.println("Display.shutdownAll["+(i+1)+"/"+dCount+"]: "+d+", GCed "+(null==d)); + } + if( null != d ) { // GC'ed ? + if(0 < displaysActive) { + displaysActive--; + } + final EDTUtil edtUtil = d.getEDTUtil(); + final AbstractGraphicsDevice f_aDevice = d.aDevice; + d.aDevice = null; + d.refCount=0; + final Runnable closeNativeTask = new Runnable() { + public void run() { + if ( null != d.getGraphicsDevice() ) { + d.closeNativeImpl(f_aDevice); + } + } + }; + if(null != edtUtil) { + final long coopSleep = edtUtil.getPollPeriod() * 2; + if( edtUtil.isRunning() ) { + edtUtil.invokeStop(false, closeNativeTask); // don't block + } + try { + Thread.sleep( coopSleep < 50 ? coopSleep : 50 ); + } catch (InterruptedException e) { } + } else { + closeNativeTask.run(); + } + } + } + } public synchronized final int addReference() { if(DEBUG) { @@ -279,7 +364,7 @@ public abstract class DisplayImpl extends Display { } protected abstract void createNativeImpl(); - protected abstract void closeNativeImpl(); + protected abstract void closeNativeImpl(AbstractGraphicsDevice aDevice); @Override public final int getId() { @@ -347,15 +432,18 @@ public abstract class DisplayImpl extends Display { @Override public boolean isEDTRunning() { - if(null!=edtUtil) { - return edtUtil.isRunning(); + final EDTUtil _edtUtil = edtUtil; + if( null != _edtUtil ) { + return _edtUtil.isRunning(); } return false; } @Override public String toString() { - return "NEWT-Display["+getFQName()+", excl "+exclusive+", refCount "+refCount+", hasEDT "+(null!=edtUtil)+", edtRunning "+isEDTRunning()+", "+aDevice+"]"; + final EDTUtil _edtUtil = edtUtil; + final boolean _edtUtilRunning = ( null != _edtUtil ) ? _edtUtil.isRunning() : false; + return "NEWT-Display["+getFQName()+", excl "+exclusive+", refCount "+refCount+", hasEDT "+(null!=_edtUtil)+", edtRunning "+_edtUtilRunning+", "+aDevice+"]"; } /** Dispatch native Toolkit messageges */ @@ -448,17 +536,18 @@ public abstract class DisplayImpl extends Display { } public void enqueueEvent(boolean wait, NEWTEvent e) { - if(!isEDTRunning()) { + final EDTUtil _edtUtil = edtUtil; + if( !_edtUtil.isRunning() ) { // oops .. we are already dead if(DEBUG) { - Throwable t = new Throwable("Warning: EDT already stopped: wait:="+wait+", "+e); - t.printStackTrace(); + System.err.println("Warning: EDT already stopped: wait:="+wait+", "+e); + Thread.dumpStack(); } return; } // can't wait if we are on EDT or NEDT -> consume right away - if(wait && edtUtil.isCurrentThreadEDTorNEDT() ) { + if(wait && _edtUtil.isCurrentThreadEDTorNEDT() ) { dispatchMessage(e); return; } @@ -505,7 +594,7 @@ public abstract class DisplayImpl extends Display { return runWithLockedDevice(device, action); } - protected EDTUtil edtUtil = null; + protected volatile EDTUtil edtUtil = null; protected int id; protected String name; protected String type; diff --git a/src/newt/classes/jogamp/newt/MonitorDeviceImpl.java b/src/newt/classes/jogamp/newt/MonitorDeviceImpl.java index db31cc83f..43d558515 100644 --- a/src/newt/classes/jogamp/newt/MonitorDeviceImpl.java +++ b/src/newt/classes/jogamp/newt/MonitorDeviceImpl.java @@ -94,20 +94,20 @@ public class MonitorDeviceImpl extends MonitorDevice { } final long tStart; if(Screen.DEBUG) { - tStart = System.nanoTime(); + tStart = System.currentTimeMillis(); } else { tStart = 0; } sms.fireMonitorModeChangeNotify(this, mmU); if(Screen.DEBUG) { - System.err.println("Screen.setCurrentMode ("+(System.nanoTime()-tStart)/1e6+"ms): fireModeChangeNotify() "+mmU); + System.err.println("Screen.setCurrentMode ("+(System.currentTimeMillis()-tStart)+"ms): fireModeChangeNotify() "+mmU); } - + boolean success = screenImpl.setCurrentMonitorModeImpl(this, mmU); if(success) { if(Screen.DEBUG) { - System.err.println("Screen.setCurrentMode ("+(System.nanoTime()-tStart)/1e6+"ms): setCurrentModeImpl() "+mmU+", success(1): "+success); + System.err.println("Screen.setCurrentMode ("+(System.currentTimeMillis()-tStart)+"ms): setCurrentModeImpl() "+mmU+", success(1): "+success); } } else { // 2nd attempt validate! @@ -115,7 +115,7 @@ public class MonitorDeviceImpl extends MonitorDevice { success = queriedCurrent.hashCode() == mmU.hashCode() ; if(Screen.DEBUG) { System.err.println("Screen.setCurrentMode.2: queried "+queriedCurrent); - System.err.println("Screen.setCurrentMode ("+(System.nanoTime()-tStart)/1e6+"ms): setCurrentModeImpl() "+mmU+", success(2): "+success); + System.err.println("Screen.setCurrentMode ("+(System.currentTimeMillis()-tStart)+"ms): setCurrentModeImpl() "+mmU+", success(2): "+success); } } if( success ) { @@ -124,7 +124,7 @@ public class MonitorDeviceImpl extends MonitorDevice { } sms.fireMonitorModeChanged(this, mmU, success); if(Screen.DEBUG) { - System.err.println("Screen.setCurrentMode ("+(System.nanoTime()-tStart)/1e6+"ms): X.X "+this+", success: "+success); + System.err.println("Screen.setCurrentMode ("+(System.currentTimeMillis()-tStart)+"ms): X.X: success "+success+": "+this); } return success; } finally { diff --git a/src/newt/classes/jogamp/newt/ScreenImpl.java b/src/newt/classes/jogamp/newt/ScreenImpl.java index 7edf7b63a..5ffa2ebbf 100644 --- a/src/newt/classes/jogamp/newt/ScreenImpl.java +++ b/src/newt/classes/jogamp/newt/ScreenImpl.java @@ -34,8 +34,6 @@ package jogamp.newt; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -54,9 +52,15 @@ import com.jogamp.newt.NewtFactory; import com.jogamp.newt.Screen; import com.jogamp.newt.event.MonitorEvent; import com.jogamp.newt.event.MonitorModeListener; +import com.jogamp.newt.util.MonitorModeUtil; public abstract class ScreenImpl extends Screen implements MonitorModeListener { - protected static final boolean DEBUG_TEST_SCREENMODE_DISABLED = Debug.isPropertyDefined("newt.test.Screen.disableScreenMode", true); + protected static final boolean DEBUG_TEST_SCREENMODE_DISABLED; + + static { + Debug.initSingleton(); + DEBUG_TEST_SCREENMODE_DISABLED = Debug.isPropertyDefined("newt.test.Screen.disableScreenMode", true); + } public static final int default_sm_bpp = 32; public static final int default_sm_widthmm = 519; @@ -64,6 +68,13 @@ public abstract class ScreenImpl extends Screen implements MonitorModeListener { public static final int default_sm_rate = 60; public static final int default_sm_rotation = 0; + static { + DisplayImpl.initSingleton(); + } + + /** Ensure static init has been run. */ + /* pp */static void initSingleton() { } + protected DisplayImpl display; protected int screen_idx; protected String fqname; @@ -77,15 +88,6 @@ public abstract class ScreenImpl extends Screen implements MonitorModeListener { private long tCreated; // creationTime - static { - AccessController.doPrivileged(new PrivilegedAction<Object>() { - public Object run() { - registerShutdownHook(); - return null; - } - }); - } - private static Class<?> getScreenClass(String type) throws ClassNotFoundException { final Class<?> screenClass = NewtFactory.getCustomClass(type, "ScreenDriver"); @@ -128,7 +130,7 @@ public abstract class ScreenImpl extends Screen implements MonitorModeListener { screen.screen_idx = idx; screen.fqname = display.getFQName()+"-s"+idx; screen.hashCode = screen.fqname.hashCode(); - screenList.add(screen); + Screen.addScreen2List(screen); if(DEBUG) { System.err.println("Screen.create() NEW: "+screen+" "+Display.getThreadName()); } @@ -172,39 +174,39 @@ public abstract class ScreenImpl extends Screen implements MonitorModeListener { System.err.println("Screen.createNative() START ("+DisplayImpl.getThreadName()+", "+this+")"); } else { tCreated = 0; - } - + } display.addReference(); createNativeImpl(); if(null == aScreen) { throw new NativeWindowException("Screen.createNative() failed to instanciate an AbstractGraphicsScreen"); } - + initMonitorState(); - if(DEBUG) { - System.err.println("Screen.createNative() END ("+DisplayImpl.getThreadName()+", "+this+"), total "+ (System.nanoTime()-tCreated)/1e6 +"ms"); - } synchronized(screenList) { screensActive++; + if(DEBUG) { + System.err.println("Screen.createNative() END ("+DisplayImpl.getThreadName()+", "+this+"), active "+screensActive+", total "+ (System.nanoTime()-tCreated)/1e6 +"ms"); + } } + ScreenMonitorState.getScreenMonitorState(this.getFQName()).addListener(this); } - ScreenMonitorState sms = ScreenMonitorState.getScreenMonitorState(this.getFQName()); - sms.addListener(this); } @Override public synchronized final void destroy() { - releaseMonitorState(); - synchronized(screenList) { - screenList.remove(this); if(0 < screensActive) { screensActive--; } + if(DEBUG) { + System.err.println("Screen.destroy() ("+DisplayImpl.getThreadName()+"): active "+screensActive); + // Thread.dumpStack(); + } } if ( null != aScreen ) { + releaseMonitorState(); closeNativeImpl(); aScreen = null; } @@ -220,8 +222,7 @@ public abstract class ScreenImpl extends Screen implements MonitorModeListener { } if ( 0 == refCount ) { createNative(); - } - if(null == aScreen) { + } else if(null == aScreen) { throw new NativeWindowException("Screen.addReference() (refCount "+refCount+") null AbstractGraphicsScreen"); } return ++refCount; @@ -289,12 +290,12 @@ public abstract class ScreenImpl extends Screen implements MonitorModeListener { vOriginSize.setWidth(usrSize.getWidth()); vOriginSize.setHeight(usrSize.getHeight()); if(DEBUG) { - System.err.println("User virtual screen viewport "+vOriginSize); + System.err.println("Update user virtual screen viewport @ "+Thread.currentThread().getName()+": "+vOriginSize); } } else { calcVirtualScreenOriginAndSize(vOriginSize); if(DEBUG) { - System.err.println("Detected virtual screen viewport "+vOriginSize); + System.err.println("Updated virtual screen viewport @ "+Thread.currentThread().getName()+": "+vOriginSize); } } } @@ -401,7 +402,7 @@ public abstract class ScreenImpl extends Screen implements MonitorModeListener { @Override public void monitorModeChangeNotify(MonitorEvent me) { if(DEBUG) { - System.err.println("monitorModeChangeNotify: "+me); + System.err.println("monitorModeChangeNotify @ "+Thread.currentThread().getName()+": "+me); } for(int i=0; i<refMonitorModeListener.size(); i++) { ((MonitorModeListener)refMonitorModeListener.get(i)).monitorModeChangeNotify(me); @@ -414,7 +415,7 @@ public abstract class ScreenImpl extends Screen implements MonitorModeListener { final MonitorDeviceImpl monitor = (MonitorDeviceImpl) monitors.get(i); final Rectangle newViewport = getNativeMonitorDeviceViewportImpl(monitor); if( DEBUG ) { - System.err.println("Screen.updateMonitorViewport["+i+"]: "+monitor.getViewport()+" -> "+newViewport); + System.err.println("Screen.updateMonitorViewport["+i+"] @ "+Thread.currentThread().getName()+": "+monitor.getViewport()+" -> "+newViewport); } if( null != newViewport ) { monitor.setViewportValue(newViewport); @@ -429,7 +430,7 @@ public abstract class ScreenImpl extends Screen implements MonitorModeListener { updateVirtualScreenOriginAndSize(); } if(DEBUG) { - System.err.println("monitorModeChanged: success "+success+", "+me); + System.err.println("monitorModeChangeNotify @ "+Thread.currentThread().getName()+": success "+success+", "+me); } for(int i=0; i<refMonitorModeListener.size(); i++) { ((MonitorModeListener)refMonitorModeListener.get(i)).monitorModeChanged(me, success); @@ -540,6 +541,11 @@ public abstract class ScreenImpl extends Screen implements MonitorModeListener { final MonitorDevice monitor = getVirtualMonitorDevice(cache, 0, mode); cache.monitorDevices.getOrAdd(monitor); } + // Sort MonitorModes (all and per device) in descending order - default! + MonitorModeUtil.sort(cache.monitorModes.getData(), false ); // descending order + for(Iterator<MonitorDevice> iMonitor=cache.monitorDevices.iterator(); iMonitor.hasNext(); ) { + MonitorModeUtil.sort(iMonitor.next().getSupportedModes(), false ); // descending order + } if(DEBUG) { int i=0; for(Iterator<MonitorMode> iMode=cache.monitorModes.iterator(); iMode.hasNext(); i++) { @@ -554,7 +560,7 @@ public abstract class ScreenImpl extends Screen implements MonitorModeListener { System.err.println("["+i+"]["+j+"]: "+iMode.next()); } } - } + } sms = new ScreenMonitorState(cache.monitorDevices, cache.monitorModes); ScreenMonitorState.mapScreenMonitorState(this.getFQName(), sms); } @@ -609,7 +615,7 @@ public abstract class ScreenImpl extends Screen implements MonitorModeListener { return cache.monitorDevices.size(); } - private void releaseMonitorState() { + private final void releaseMonitorState() { ScreenMonitorState sms; ScreenMonitorState.lockScreenMonitorState(); try { @@ -660,23 +666,20 @@ public abstract class ScreenImpl extends Screen implements MonitorModeListener { ScreenMonitorState.unmapScreenMonitorStateUnlocked(getFQName()); } } - private static final void shutdownAll() { - for(int i=0; i < screenList.size(); i++) { - ((ScreenImpl)screenList.get(i)).shutdown(); - } - } - private static synchronized void registerShutdownHook() { - final Thread shutdownHook = new Thread(new Runnable() { - public void run() { - ScreenImpl.shutdownAll(); + /** pp */ static final void shutdownAll() { + final int sCount = screenList.size(); + if(DEBUG) { + System.err.println("Screen.shutdownAll "+sCount+" instances, on thread "+Display.getThreadName()); + } + for(int i=0; i<sCount && screenList.size()>0; i++) { // be safe .. + final ScreenImpl s = (ScreenImpl) screenList.remove(0).get(); + if(DEBUG) { + System.err.println("Screen.shutdownAll["+(i+1)+"/"+sCount+"]: "+s+", GCed "+(null==s)); } - }); - AccessController.doPrivileged(new PrivilegedAction<Object>() { - public Object run() { - Runtime.getRuntime().addShutdownHook(shutdownHook); - return null; + if( null != s ) { + s.shutdown(); } - }); + } } } diff --git a/src/newt/classes/jogamp/newt/WindowImpl.java b/src/newt/classes/jogamp/newt/WindowImpl.java index 805ad08ad..caa461e41 100644 --- a/src/newt/classes/jogamp/newt/WindowImpl.java +++ b/src/newt/classes/jogamp/newt/WindowImpl.java @@ -36,6 +36,7 @@ package jogamp.newt; import java.util.ArrayList; import java.util.List; +import java.lang.ref.WeakReference; import java.lang.reflect.Method; import com.jogamp.common.util.IntBitfield; @@ -80,7 +81,52 @@ import jogamp.nativewindow.SurfaceUpdatedHelper; public abstract class WindowImpl implements Window, NEWTEventConsumer { - public static final boolean DEBUG_TEST_REPARENT_INCOMPATIBLE = Debug.isPropertyDefined("newt.test.Window.reparent.incompatible", true); + public static final boolean DEBUG_TEST_REPARENT_INCOMPATIBLE; + + static { + Debug.initSingleton(); + DEBUG_TEST_REPARENT_INCOMPATIBLE = Debug.isPropertyDefined("newt.test.Window.reparent.incompatible", true); + + ScreenImpl.initSingleton(); + } + + protected static final ArrayList<WeakReference<WindowImpl>> windowList = new ArrayList<WeakReference<WindowImpl>>(); + + /** Maybe utilized at a shutdown hook, impl. does not block. */ + public static final void shutdownAll() { + final int wCount = windowList.size(); + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.shutdownAll "+wCount+" instances, on thread "+getThreadName()); + } + for(int i=0; i<wCount && windowList.size()>0; i++) { // be safe .. + final WindowImpl w = windowList.remove(0).get(); + if(DEBUG_IMPLEMENTATION) { + final long wh = null != w ? w.getWindowHandle() : 0; + System.err.println("Window.shutdownAll["+(i+1)+"/"+wCount+"]: "+toHexString(wh)+", GCed "+(null==w)); + } + if( null != w ) { + w.shutdown(); + } + } + } + private static void addWindow2List(WindowImpl window) { + synchronized(windowList) { + // GC before add + int i=0, gced=0; + while( i < windowList.size() ) { + if( null == windowList.get(i).get() ) { + gced++; + windowList.remove(i); + } else { + i++; + } + } + windowList.add(new WeakReference<WindowImpl>(window)); + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.addWindow2List: GCed "+gced+", size "+windowList.size()); + } + } + } /** Timeout of queued events (repaint and resize) */ static final long QUEUED_EVENT_TO = 1200; // ms @@ -108,6 +154,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private boolean fullscreen = false, brokenFocusChange = false; private List<MonitorDevice> fullscreenMonitors = null; private boolean fullscreenUseMainMonitor = true; + private boolean fullscreenUseSpanningMode = true; // spanning mode: fake full screen, only on certain platforms private boolean autoPosition = true; // default: true (allow WM to choose top-level position, if not set by user) private int nfs_width, nfs_height, nfs_x, nfs_y; // non fullscreen client-area size/pos w/o insets @@ -186,6 +233,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer window.screen = (ScreenImpl) screen; window.capsRequested = (CapabilitiesImmutable) caps.cloneMutable(); window.instantiationFinished(); + addWindow2List(window); return window; } catch (Throwable t) { t.printStackTrace(); @@ -207,12 +255,29 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer WindowImpl window = (WindowImpl) ReflectionUtil.createInstance( windowClass, cstrArgumentTypes, cstrArguments ) ; window.screen = (ScreenImpl) screen; window.capsRequested = (CapabilitiesImmutable) caps.cloneMutable(); + window.instantiationFinished(); + addWindow2List(window); return window; } catch (Throwable t) { throw new NativeWindowException(t); } } + /** Fast invalidation of instance w/o any blocking function call. */ + private final void shutdown() { + if(null!=lifecycleHook) { + lifecycleHook.shutdownRenderingAction(); + } + setWindowHandle(0); + visible = false; + fullscreen = false; + fullscreenMonitors = null; + fullscreenUseMainMonitor = true; + fullscreenUseSpanningMode = false; + hasFocus = false; + parentWindowHandle = 0; + } + protected final void setGraphicsConfiguration(AbstractGraphicsConfiguration cfg) { config = cfg; } @@ -233,9 +298,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer /** * Notifies the receiver to preserve resources (GL, ..) - * for the next destroy*() calls (only). + * for the next destroy*() calls (only), if supported and if <code>value</code> is <code>true</code>, otherwise clears preservation flag. + * @param value <code>true</code> to set the one-shot preservation if supported, otherwise clears it. */ - void preserveGLStateAtDestroy(); + void preserveGLStateAtDestroy(boolean value); /** * Invoked before Window destroy action, @@ -270,6 +336,14 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer * @see #pauseRenderingAction() */ void resumeRenderingAction(); + + /** + * Shutdown rendering action (thread) abnormally. + * <p> + * Should be called only at shutdown, if necessary. + * </p> + */ + void shutdownRenderingAction(); } private boolean createNative() { @@ -321,9 +395,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(isFullscreen()) { synchronized(fullScreenAction) { fullscreen = false; // trigger a state change - fullScreenAction.init(true, fullscreenUseMainMonitor, fullscreenMonitors); - fullscreenMonitors = null; // release references ASAP - fullscreenUseMainMonitor = true; + fullScreenAction.init(true); fullScreenAction.run(); } } else { @@ -511,6 +583,16 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer * @see #positionChanged(boolean,int, int) */ protected abstract boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags); + + /** + * Tests whether a single reconfigure flag is supported by implementation. + * <p> + * Default is all but {@link #FLAG_IS_FULLSCREEN_SPAN} + * </p> + */ + protected boolean isReconfigureFlagSupported(int changeFlags) { + return 0 == ( changeFlags & FLAG_IS_FULLSCREEN_SPAN ); + } protected int getReconfigureFlags(int changeFlags, boolean visible) { return changeFlags |= ( ( 0 != getParentWindowHandle() ) ? FLAG_HAS_PARENT : 0 ) | @@ -841,17 +923,19 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private class SetSizeAction implements Runnable { int width, height; + boolean disregardFS; - private SetSizeAction(int w, int h) { + private SetSizeAction(int w, int h, boolean disregardFS) { this.width = w; this.height = h; + this.disregardFS = disregardFS; } public final void run() { final RecursiveLock _lock = windowLock; _lock.lock(); try { - if ( !isFullscreen() && ( getWidth() != width || getHeight() != height ) ) { + if ( ( disregardFS || !isFullscreen() ) && ( getWidth() != width || getHeight() != height ) ) { if(DEBUG_IMPLEMENTATION) { System.err.println("Window setSize: START "+getWidth()+"x"+getHeight()+" -> "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible "+visible); } @@ -886,9 +970,12 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } + private void setFullscreenSize(int width, int height) { + runOnEDTIfAvail(true, new SetSizeAction(width, height, true)); + } @Override public void setSize(int width, int height) { - runOnEDTIfAvail(true, new SetSizeAction(width, height)); + runOnEDTIfAvail(true, new SetSizeAction(width, height, false)); } @Override public void setTopLevelSize(int width, int height) { @@ -964,6 +1051,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer fullscreen = false; fullscreenMonitors = null; fullscreenUseMainMonitor = true; + fullscreenUseSpanningMode = false; hasFocus = false; parentWindowHandle = 0; @@ -1000,8 +1088,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } protected void destroy(boolean preserveResources) { - if( preserveResources && null != WindowImpl.this.lifecycleHook ) { - WindowImpl.this.lifecycleHook.preserveGLStateAtDestroy(); + if( null != lifecycleHook ) { + lifecycleHook.preserveGLStateAtDestroy( preserveResources ); } destroy(); } @@ -1108,7 +1196,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } // Destroy this window and use parent's Screen. // It may be created properly when the parent is made visible. - destroy(false); + destroy( false ); setScreen( (ScreenImpl) newParentWindowNEWT.getScreen() ); operation = ReparentOperation.ACTION_NATIVE_CREATION_PENDING; } else if(newParentWindow != getParent()) { @@ -1796,25 +1884,13 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private class FullScreenAction implements Runnable { boolean fullscreen; - List<MonitorDevice> monitors; - boolean useMainMonitor; - private boolean init(boolean fullscreen, boolean useMainMonitor, List<MonitorDevice> monitors) { + private boolean init(boolean fullscreen) { if(isNativeValid()) { this.fullscreen = fullscreen; - if( isFullscreen() != fullscreen ) { - this.monitors = monitors; - this.useMainMonitor = useMainMonitor; - return true; - } else { - this.monitors = null; - this.useMainMonitor = true; - return false; - } + return isFullscreen() != fullscreen; } else { WindowImpl.this.fullscreen = fullscreen; // set current state for createNative(..) - WindowImpl.this.fullscreenMonitors = monitors; - WindowImpl.this.fullscreenUseMainMonitor = useMainMonitor; return false; } } @@ -1829,19 +1905,27 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer int x,y,w,h; - final RectangleImmutable viewport; + final RectangleImmutable sviewport = screen.getViewport(); + final RectangleImmutable viewport; final int fs_span_flag; if(fullscreen) { - if( null == monitors ) { - if( useMainMonitor ) { - monitors = new ArrayList<MonitorDevice>(); - monitors.add( getMainMonitor() ); + if( null == fullscreenMonitors ) { + if( fullscreenUseMainMonitor ) { + fullscreenMonitors = new ArrayList<MonitorDevice>(); + fullscreenMonitors.add( getMainMonitor() ); } else { - monitors = getScreen().getMonitorDevices(); + fullscreenMonitors = getScreen().getMonitorDevices(); } } - fs_span_flag = monitors.size() > 1 ? FLAG_IS_FULLSCREEN_SPAN : 0 ; - viewport = MonitorDevice.unionOfViewports(new Rectangle(), monitors); + viewport = MonitorDevice.unionOfViewports(new Rectangle(), fullscreenMonitors); + if( isReconfigureFlagSupported(FLAG_IS_FULLSCREEN_SPAN) && + ( fullscreenMonitors.size() > 1 || sviewport.compareTo(viewport) > 0 ) ) { + fullscreenUseSpanningMode = true; + fs_span_flag = FLAG_IS_FULLSCREEN_SPAN; + } else { + fullscreenUseSpanningMode = false; + fs_span_flag = 0; + } nfs_x = getX(); nfs_y = getY(); nfs_width = getWidth(); @@ -1851,6 +1935,9 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer w = viewport.getWidth(); h = viewport.getHeight(); } else { + fullscreenUseMainMonitor = true; + fullscreenUseSpanningMode = false; + fullscreenMonitors = null; fs_span_flag = 0; viewport = null; x = nfs_x; @@ -1872,16 +1959,15 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } } - monitors = null; // clear references ASAP - useMainMonitor = true; if(DEBUG_IMPLEMENTATION) { System.err.println("Window fs: "+fullscreen+" "+x+"/"+y+" "+w+"x"+h+", "+isUndecorated()+ - ", virtl-size: "+screen.getWidth()+"x"+screen.getHeight()+", monitorsViewport "+viewport); + ", virtl-screenSize: "+sviewport+", monitorsViewport "+viewport+ + ", spanning "+fullscreenUseSpanningMode+" @ "+Thread.currentThread().getName()); } - DisplayImpl display = (DisplayImpl) screen.getDisplay(); + final DisplayImpl display = (DisplayImpl) screen.getDisplay(); display.dispatchMessagesNative(); // status up2date - boolean wasVisible = isVisible(); + final boolean wasVisible = isVisible(); // Lock parentWindow only during reparenting (attempt) final NativeWindow parentWindowLocked; @@ -1909,8 +1995,6 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer WindowImpl.this.waitForVisible(true, false); display.dispatchMessagesNative(); // status up2date WindowImpl.this.waitForSize(w, h, false, TIMEOUT_NATIVEWINDOW); - display.dispatchMessagesNative(); // status up2date - if(DEBUG_IMPLEMENTATION) { System.err.println("Window fs done: " + WindowImpl.this); } @@ -1921,7 +2005,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener } } - private final FullScreenAction fullScreenAction = new FullScreenAction(); + private final FullScreenAction fullScreenAction = new FullScreenAction(); @Override public boolean setFullscreen(boolean fullscreen) { @@ -1935,7 +2019,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private boolean setFullscreenImpl(boolean fullscreen, boolean useMainMonitor, List<MonitorDevice> monitors) { synchronized(fullScreenAction) { - if( fullScreenAction.init(fullscreen, useMainMonitor, monitors) ) { + fullscreenMonitors = monitors; + fullscreenUseMainMonitor = useMainMonitor; + fullscreenUseSpanningMode = false; + if( fullScreenAction.init(fullscreen) ) { if(fullScreenAction.fsOn() && isOffscreenInstance(WindowImpl.this, parentWindow)) { // enable fullscreen on offscreen instance if(null != parentWindow) { @@ -1954,8 +2041,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer nfs_parent = null; } - if(isVisible()) { - requestFocus(true /* wait */, this.fullscreen /* skipFocusAction */, true /* force */); + if( fullscreen && isVisible() ) { // force focus on fullscreen + requestFocus(true /* wait */, true /* skipFocusAction */, true /* force */); } } return this.fullscreen; @@ -1964,20 +2051,34 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private class MonitorModeListenerImpl implements MonitorModeListener { boolean animatorPaused = false; + boolean hadFocus = false; + boolean fullscreenPaused = false; + List<MonitorDevice> _fullscreenMonitors = null; + boolean _fullscreenUseMainMonitor = true; public void monitorModeChangeNotify(MonitorEvent me) { + hadFocus = hasFocus(); if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.monitorModeChangeNotify: "+me); + System.err.println("Window.monitorModeChangeNotify: hadFocus "+hadFocus+", "+me+" @ "+Thread.currentThread().getName()); } if(null!=lifecycleHook) { animatorPaused = lifecycleHook.pauseRenderingAction(); } + if( fullscreen && isReconfigureFlagSupported(FLAG_IS_FULLSCREEN_SPAN) ) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.monitorModeChangeNotify: FS Pause"); + } + fullscreenPaused = true; + _fullscreenMonitors = fullscreenMonitors; + _fullscreenUseMainMonitor = fullscreenUseMainMonitor; + setFullscreenImpl(false, true, null); + } } public void monitorModeChanged(MonitorEvent me, boolean success) { if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.monitorModeChanged: "+me+", success: "+success); + System.err.println("Window.monitorModeChanged: hadFocus "+hadFocus+", "+me+", success: "+success+" @ "+Thread.currentThread().getName()); } if(success) { @@ -1985,7 +2086,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // Didn't pass above notify method. probably detected screen change after it happened. animatorPaused = lifecycleHook.pauseRenderingAction(); } - if( !fullscreen ) { + if( !fullscreen && !fullscreenPaused ) { // Simply move/resize window to fit in virtual screen if required final RectangleImmutable viewport = screen.getViewport(); if( viewport.getWidth() > 0 && viewport.getHeight() > 0 ) { // failsafe @@ -2001,13 +2102,35 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer setSize(viewport.getWidth(), viewport.getHeight()); } } + } else if( fullscreenPaused ){ + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.monitorModeChanged: FS Restore"); + } + setFullscreenImpl(true, _fullscreenUseMainMonitor, _fullscreenMonitors); + fullscreenPaused = false; + _fullscreenMonitors = null; + _fullscreenUseMainMonitor = true; + } else { + // If changed monitor is part of this fullscreen mode, reset size! (Bug 771) + final MonitorDevice md = me.getMonitor(); + if( fullscreenMonitors.contains(md) ) { + final RectangleImmutable viewport = MonitorDevice.unionOfViewports(new Rectangle(), fullscreenMonitors); + if(DEBUG_IMPLEMENTATION) { + final RectangleImmutable rect = new Rectangle(getX(), getY(), getWidth(), getHeight()); + System.err.println("Window.monitorModeChanged: FS Monitor Match: Fit window "+rect+" into new viewport union "+viewport+", provoked by "+md); + } + definePosition(viewport.getX(), viewport.getY()); // set pos for setVisible(..) or createNative(..) - reduce EDT roundtrip + setFullscreenSize(viewport.getWidth(), viewport.getHeight()); + } } } - if(animatorPaused) { lifecycleHook.resumeRenderingAction(); } sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener + if( hadFocus ) { + requestFocus(true); + } } } private final MonitorModeListenerImpl monitorModeListenerImpl = new MonitorModeListenerImpl(); @@ -2476,7 +2599,6 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer return keyListeners.toArray(new KeyListener[keyListeners.size()]); } - @SuppressWarnings("deprecation") private final boolean propagateKeyEvent(KeyEvent e, KeyListener l) { switch(e.getEventType()) { case KeyEvent.EVENT_KEY_PRESSED: @@ -2485,58 +2607,29 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer case KeyEvent.EVENT_KEY_RELEASED: l.keyReleased(e); break; - case KeyEvent.EVENT_KEY_TYPED: - l.keyTyped(e); - break; default: throw new NativeWindowException("Unexpected key event type " + e.getEventType()); } return e.isConsumed(); } - @SuppressWarnings("deprecation") protected void consumeKeyEvent(KeyEvent e) { - boolean consumedE = false, consumedTyped = false; - if( KeyEvent.EVENT_KEY_TYPED == e.getEventType() ) { - throw new InternalError("Deprecated KeyEvent.EVENT_KEY_TYPED is synthesized - don't send/enqueue it!"); - } - - // Synthesize deprecated event KeyEvent.EVENT_KEY_TYPED - final KeyEvent eTyped; - if( KeyEvent.EVENT_KEY_RELEASED == e.getEventType() && e.isPrintableKey() && !e.isAutoRepeat() ) { - eTyped = KeyEvent.create(KeyEvent.EVENT_KEY_TYPED, e.getSource(), e.getWhen(), e.getModifiers(), e.getKeyCode(), e.getKeySymbol(), e.getKeyChar()); - } else { - eTyped = null; - } - if(null != keyboardFocusHandler) { + boolean consumedE = false; + if( null != keyboardFocusHandler && !e.isAutoRepeat() ) { consumedE = propagateKeyEvent(e, keyboardFocusHandler); if(DEBUG_KEY_EVENT) { - System.err.println("consumeKeyEvent: "+e+", keyboardFocusHandler consumed: "+consumedE); - } - if( null != eTyped ) { - consumedTyped = propagateKeyEvent(eTyped, keyboardFocusHandler); - if(DEBUG_KEY_EVENT) { - System.err.println("consumeKeyEvent: "+eTyped+", keyboardFocusHandler consumed: "+consumedTyped); - } + if( consumedE ) { + System.err.println("consumeKeyEvent(kfh): "+e+", consumed: "+consumedE); + } } } - if(DEBUG_KEY_EVENT) { - if( !consumedE ) { - System.err.println("consumeKeyEvent: "+e); + if( !consumedE ) { + for(int i = 0; !consumedE && i < keyListeners.size(); i++ ) { + consumedE = propagateKeyEvent(e, keyListeners.get(i)); } - } - for(int i = 0; !consumedE && i < keyListeners.size(); i++ ) { - consumedE = propagateKeyEvent(e, keyListeners.get(i)); - } - if( null != eTyped ) { if(DEBUG_KEY_EVENT) { - if( !consumedTyped ) { - System.err.println("consumeKeyEvent: "+eTyped); - } + System.err.println("consumeKeyEvent(usr): "+e+", consumed: "+consumedE); } - for(int i = 0; !consumedTyped && i < keyListeners.size(); i++ ) { - consumedTyped = propagateKeyEvent(eTyped, keyListeners.get(i)); - } } } diff --git a/src/newt/classes/jogamp/newt/driver/android/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/android/DisplayDriver.java index a367462c4..a2877dba2 100644 --- a/src/newt/classes/jogamp/newt/driver/android/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/android/DisplayDriver.java @@ -55,7 +55,7 @@ public class DisplayDriver extends jogamp.newt.DisplayImpl { aDevice = EGLDisplayUtil.eglCreateEGLGraphicsDevice(EGL.EGL_DEFAULT_DISPLAY, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT); } - protected void closeNativeImpl() { + protected void closeNativeImpl(AbstractGraphicsDevice aDevice) { aDevice.close(); } diff --git a/src/newt/classes/jogamp/newt/driver/android/NewtVersionActivity.java b/src/newt/classes/jogamp/newt/driver/android/NewtVersionActivity.java index de524d54c..9f6210269 100644 --- a/src/newt/classes/jogamp/newt/driver/android/NewtVersionActivity.java +++ b/src/newt/classes/jogamp/newt/driver/android/NewtVersionActivity.java @@ -78,7 +78,7 @@ public class NewtVersionActivity extends NewtBaseActivity { glWindow.addGLEventListener(new GLEventListener() { public void init(GLAutoDrawable drawable) { GL gl = drawable.getGL(); - final StringBuffer sb = new StringBuffer(); + final StringBuilder sb = new StringBuilder(); sb.append(JoglVersion.getGLInfo(gl, null, true)).append(Platform.NEWLINE); sb.append("Requested: ").append(Platform.NEWLINE); sb.append(drawable.getNativeSurface().getGraphicsConfiguration().getRequestedCapabilities()).append(Platform.NEWLINE).append(Platform.NEWLINE); diff --git a/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java b/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java index fc9bbb848..80c72c008 100644 --- a/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java +++ b/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java @@ -45,7 +45,7 @@ public class AWTEDTUtil implements EDTUtil { private final ThreadGroup threadGroup; private final String name; private final Runnable dispatchMessages; - private NewtEventDispatchThread nedt = null; + private NEDT nedt = null; private int start_iter=0; private static long pollPeriod = EDTUtil.defaultEDTPollPeriod; @@ -53,7 +53,7 @@ public class AWTEDTUtil implements EDTUtil { this.threadGroup = tg; this.name=Thread.currentThread().getName()+"-"+name+"-EDT-"; this.dispatchMessages=dispatchMessages; - this.nedt = new NewtEventDispatchThread(threadGroup, name); + this.nedt = new NEDT(threadGroup, name); this.nedt.setDaemon(true); // don't stop JVM from shutdown .. } @@ -68,24 +68,29 @@ public class AWTEDTUtil implements EDTUtil { } @Override - final public void reset() { - synchronized(edtLock) { - waitUntilStopped(); + public final boolean restart() throws IllegalStateException { + synchronized(edtLock) { + if( nedt.isRunning() ) { + throw new IllegalStateException("EDT still running and not subject to stop. Curr "+Thread.currentThread().getName()+", NEDT "+nedt.getName()+", isRunning "+nedt.isRunning+", shouldStop "+nedt.shouldStop+", on AWT-EDT "+EventQueue.isDispatchThread()); + } if(DEBUG) { System.err.println(Thread.currentThread()+": AWT-EDT reset - edt: "+nedt); } - this.nedt = new NewtEventDispatchThread(threadGroup, name); - this.nedt.setDaemon(true); // don't stop JVM from shutdown .. + if( nedt.getState() != Thread.State.NEW ) { + nedt = new NEDT(threadGroup, name); + nedt.setDaemon(true); // don't stop JVM from shutdown .. + } + startImpl(); } + return invoke(true, nullTask); } private final void startImpl() { if(nedt.isAlive()) { - throw new RuntimeException("AWT-EDT Thread.isAlive(): true, isRunning: "+nedt.isRunning()+", edt: "+nedt); + throw new RuntimeException("AWT-EDT Thread.isAlive(): true, isRunning: "+nedt.isRunning+", shouldStop "+nedt.shouldStop+", edt: "+nedt); } start_iter++; nedt.setName(name+start_iter); - nedt.shouldStop = false; if(DEBUG) { System.err.println(Thread.currentThread()+": AWT-EDT START - edt: "+nedt); // Thread.dumpStack(); @@ -110,59 +115,76 @@ public class AWTEDTUtil implements EDTUtil { @Override final public boolean isRunning() { - return nedt.isRunning() ; // AWT is always running + return nedt.isRunning() ; } @Override - public final void invokeStop(Runnable task) { - invokeImpl(true, task, true); + public final boolean invokeStop(boolean wait, Runnable task) { + return invokeImpl(wait, task, true); } @Override - public final void invoke(boolean wait, Runnable task) { - invokeImpl(wait, task, false); + public final boolean invoke(boolean wait, Runnable task) { + return invokeImpl(wait, task, false); } - private void invokeImpl(boolean wait, Runnable task, boolean stop) { + private static Runnable nullTask = new Runnable() { + @Override + public void run() { } + }; + + private final boolean invokeImpl(boolean wait, Runnable task, boolean stop) { Throwable throwable = null; RunnableTask rTask = null; - Object rTaskLock = new Object(); + final Object rTaskLock = new Object(); synchronized(rTaskLock) { // lock the optional task execution synchronized(edtLock) { // lock the EDT status if( nedt.shouldStop ) { // drop task .. + System.err.println(Thread.currentThread()+": Warning: AWT-EDT about (1) to stop, won't enqueue new task: "+nedt); if(DEBUG) { - System.err.println(Thread.currentThread()+": Warning: AWT-EDT about (1) to stop, won't enqueue new task: "+nedt); Thread.dumpStack(); } - return; + return false; } - // System.err.println(Thread.currentThread()+" XXX stop: "+stop+", tasks: "+edt.tasks.size()+", task: "+task); - // Thread.dumpStack(); - if(stop) { - synchronized(nedt.sync) { + if( isCurrentThreadEDT() ) { + if(null != task) { + task.run(); + } + wait = false; // running in same thread (EDT) -> no wait + if(stop) { nedt.shouldStop = true; - nedt.sync.notifyAll(); // stop immediate if waiting (poll freq) } - if(DEBUG) { - System.err.println(Thread.currentThread()+": AWT-EDT signal STOP (on edt: "+isCurrentThreadEDT()+") - "+nedt); - // Thread.dumpStack(); + } else { + if( !nedt.isRunning ) { + if( null != task ) { + if( stop ) { + System.err.println(Thread.currentThread()+": Warning: AWT-EDT is about (3) to stop and stopped already, dropping task. NEDT "+nedt); + } else { + System.err.println(Thread.currentThread()+": Warning: AWT-EDT is not running, dropping task. NEDT "+nedt); + } + if(DEBUG) { + Thread.dumpStack(); + } + } + return false; + } else if( stop ) { + if(DEBUG) { + System.err.println(Thread.currentThread()+": AWT-EDT signal STOP (on edt: "+isCurrentThreadEDT()+") - "+nedt+", isRunning "+nedt.isRunning+", shouldStop "+nedt.shouldStop); + } + synchronized(nedt.sync) { + nedt.shouldStop = true; + nedt.sync.notifyAll(); // stop immediate if waiting (poll freq) + } + } + + if(null != task) { + rTask = new RunnableTask(task, + wait ? rTaskLock : null, + true /* always catch and report Exceptions, don't disturb EDT */, + wait ? null : System.err); + AWTEDTExecutor.singleton.invoke(false, rTask); } - } else if( !nedt.isRunning() ) { - // start if should not stop && not started yet - startImpl(); - } - if( null == task ) { - wait = false; - } else if( isCurrentThreadEDT() ) { - task.run(); - wait = false; // running in same thread (EDT) -> no wait - } else { - rTask = new RunnableTask(task, - wait ? rTaskLock : null, - true /* always catch and report Exceptions, don't disturb EDT */, - wait ? null : System.err); - AWTEDTExecutor.singleton.invoke(false, rTask); } } if( wait ) { @@ -181,51 +203,56 @@ public class AWTEDTUtil implements EDTUtil { throw new RuntimeException(throwable); } } + return true; } } @Override - final public void waitUntilIdle() { - final NewtEventDispatchThread _edt; + final public boolean waitUntilIdle() { + final NEDT _edt; synchronized(edtLock) { _edt = nedt; } - if(!_edt.isRunning() || _edt == Thread.currentThread() || EventQueue.isDispatchThread()) { - return; + if(!_edt.isRunning || _edt == Thread.currentThread() || EventQueue.isDispatchThread()) { + return false; } try { AWTEDTExecutor.singleton.invoke(true, new Runnable() { public void run() { } }); } catch (Exception e) { } + return true; } @Override - final public void waitUntilStopped() { + final public boolean waitUntilStopped() { synchronized(edtLock) { - if(nedt.isRunning() && nedt != Thread.currentThread() && !EventQueue.isDispatchThread()) { - while(nedt.isRunning()) { + if( nedt.isRunning && nedt != Thread.currentThread() && !EventQueue.isDispatchThread() ) { + while( nedt.isRunning ) { try { edtLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } + return true; + } else { + return false; } } } - class NewtEventDispatchThread extends Thread { + class NEDT extends Thread { volatile boolean shouldStop = false; volatile boolean isRunning = false; Object sync = new Object(); - public NewtEventDispatchThread(ThreadGroup tg, String name) { + public NEDT(ThreadGroup tg, String name) { super(tg, name); } final public boolean isRunning() { - return isRunning; + return isRunning && !shouldStop; } @Override @@ -278,10 +305,8 @@ public class AWTEDTUtil implements EDTUtil { System.err.println(getName()+": AWT-EDT run() END "+ getName()+", "+error); } synchronized(edtLock) { - isRunning = !shouldStop; - if(!isRunning) { - edtLock.notifyAll(); - } + isRunning = false; + edtLock.notifyAll(); } if(DEBUG) { System.err.println(getName()+": AWT-EDT run() EXIT "+ getName()+", exception: "+error); diff --git a/src/newt/classes/jogamp/newt/driver/awt/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/awt/DisplayDriver.java index 4139951aa..30449f9bc 100644 --- a/src/newt/classes/jogamp/newt/driver/awt/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/awt/DisplayDriver.java @@ -34,6 +34,8 @@ package jogamp.newt.driver.awt; +import javax.media.nativewindow.AbstractGraphicsDevice; + import com.jogamp.nativewindow.awt.AWTGraphicsDevice; import com.jogamp.newt.NewtFactory; import com.jogamp.newt.util.EDTUtil; @@ -65,7 +67,7 @@ public class DisplayDriver extends DisplayImpl { return def; } - protected void closeNativeImpl() { + protected void closeNativeImpl(AbstractGraphicsDevice aDevice) { aDevice.close(); } diff --git a/src/newt/classes/jogamp/newt/driver/bcm/egl/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/bcm/egl/DisplayDriver.java index cc55c336e..112be2b04 100644 --- a/src/newt/classes/jogamp/newt/driver/bcm/egl/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/bcm/egl/DisplayDriver.java @@ -69,7 +69,7 @@ public class DisplayDriver extends jogamp.newt.DisplayImpl { aDevice = new EGLGraphicsDevice(EGL.EGL_DEFAULT_DISPLAY, handle, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT, null); } - protected void closeNativeImpl() { + protected void closeNativeImpl(AbstractGraphicsDevice aDevice) { if (aDevice.getHandle() != EGL.EGL_NO_DISPLAY) { DestroyDisplay(aDevice.getHandle()); } diff --git a/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/DisplayDriver.java index 08c5c573c..4872a9071 100644 --- a/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/DisplayDriver.java @@ -64,7 +64,7 @@ public class DisplayDriver extends DisplayImpl { aDevice = EGLDisplayUtil.eglCreateEGLGraphicsDevice(EGL.EGL_DEFAULT_DISPLAY, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT); } - protected void closeNativeImpl() { + protected void closeNativeImpl(AbstractGraphicsDevice aDevice) { aDevice.close(); } diff --git a/src/newt/classes/jogamp/newt/driver/intel/gdl/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/intel/gdl/DisplayDriver.java index e370038d9..ee93eb932 100644 --- a/src/newt/classes/jogamp/newt/driver/intel/gdl/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/intel/gdl/DisplayDriver.java @@ -72,7 +72,7 @@ public class DisplayDriver extends jogamp.newt.DisplayImpl { aDevice = new DefaultGraphicsDevice(NativeWindowFactory.TYPE_DEFAULT, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT, displayHandle); } - protected void closeNativeImpl() { + protected void closeNativeImpl(AbstractGraphicsDevice aDevice) { if(0==displayHandle) { throw new NativeWindowException("displayHandle null; initCnt "+initCounter); } diff --git a/src/newt/classes/jogamp/newt/driver/kd/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/kd/DisplayDriver.java index 745be5dae..3cd72e971 100644 --- a/src/newt/classes/jogamp/newt/driver/kd/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/kd/DisplayDriver.java @@ -64,7 +64,7 @@ public class DisplayDriver extends DisplayImpl { aDevice = EGLDisplayUtil.eglCreateEGLGraphicsDevice(EGL.EGL_DEFAULT_DISPLAY, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT); } - protected void closeNativeImpl() { + protected void closeNativeImpl(AbstractGraphicsDevice aDevice) { aDevice.close(); } diff --git a/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java index b49c6b6e0..a99bc4f23 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java @@ -72,7 +72,7 @@ public class DisplayDriver extends DisplayImpl { aDevice = new MacOSXGraphicsDevice(AbstractGraphicsDevice.DEFAULT_UNIT); } - protected void closeNativeImpl() { + protected void closeNativeImpl(AbstractGraphicsDevice aDevice) { aDevice.close(); } diff --git a/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java index 6370782df..6aebf0410 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java @@ -423,8 +423,8 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl ", was: pressed "+isKeyPressed(keyCode)+", isModifierKeyCode "+isModifierKeyCode); } */ - // 1:1 Order: OSX and NEWT delivery order is PRESSED, RELEASED and TYPED - // Auto-Repeat: OSX delivers only PRESSED, inject auto-repeat RELEASE and TYPED keys _before_ PRESSED + // OSX delivery order is PRESSED (t0), RELEASED (t1) and TYPED (t2) -> NEWT order: PRESSED (t0) and RELEASED (t1) + // Auto-Repeat: OSX delivers only PRESSED, inject auto-repeat RELEASE key _before_ PRESSED switch(eventType) { case KeyEvent.EVENT_KEY_RELEASED: if( isKeyCodeTracked(keyCode) ) { diff --git a/src/newt/classes/jogamp/newt/driver/windows/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/windows/DisplayDriver.java index 1b9ec0f25..595d5e952 100644 --- a/src/newt/classes/jogamp/newt/driver/windows/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/windows/DisplayDriver.java @@ -76,7 +76,7 @@ public class DisplayDriver extends DisplayImpl { aDevice = new WindowsGraphicsDevice(AbstractGraphicsDevice.DEFAULT_UNIT); } - protected void closeNativeImpl() { + protected void closeNativeImpl(AbstractGraphicsDevice aDevice) { sharedClassFactory.releaseSharedClass(); aDevice.close(); } diff --git a/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java index ad7e1e8f3..393445db0 100644 --- a/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java @@ -79,6 +79,11 @@ public class WindowDriver extends WindowImpl { } hmon = MonitorFromWindow0(hWnd); + // Let's not trigger on HDC change, GLDrawableImpl.'s destroy/create is a nop here anyways. + // FIXME: Validate against EGL surface creation: ANGLE uses HWND -> fine! + return LOCK_SUCCESS; + + /** if( hdc_old == hdc ) { return LOCK_SUCCESS; } @@ -86,7 +91,7 @@ public class WindowDriver extends WindowImpl { System.err.println("WindowsWindow: surface change "+toHexString(hdc_old)+" -> "+toHexString(hdc)); // Thread.dumpStack(); } - return LOCK_SURFACE_CHANGED; + return LOCK_SURFACE_CHANGED; */ } @Override @@ -286,9 +291,9 @@ public class WindowDriver extends WindowImpl { public final void sendKeyEvent(short eventType, int modifiers, short keyCode, short keySym, char keyChar) { final boolean isModifierKey = KeyEvent.isModifierKey(keySym); // System.err.println("*** sendKeyEvent: event "+KeyEvent.getEventTypeString(eventType)+", keyCode "+toHexString(keyCode)+", keyChar <"+keyChar+">, mods "+toHexString(modifiers)+ - // ", isKeyCodeTracked "+isKeyCodeTracked(keyCode)+", was: pressed "+isKeyPressed(keyCode)+", repeat "+isKeyInAutoRepeat(keyCode)+", printableKey "+KeyEvent.isPrintableKey(keyCode)+" [modifierKey "+isModifierKey+"] - "+System.currentTimeMillis()); + // ", isKeyCodeTracked "+isKeyCodeTracked(keyCode)+", was: pressed "+isKeyPressed(keyCode)+", printableKey "+KeyEvent.isPrintableKey(keyCode, false)+" [modifierKey "+isModifierKey+"] - "+System.currentTimeMillis()); - // Reorder: WINDOWS delivery order is PRESSED (t0), TYPED (t0) and RELEASED (t1) -> NEWT order: PRESSED (t0), RELEASED (t1) and TYPED (t1) + // Reorder: WINDOWS delivery order is PRESSED (t0), TYPED (t0) and RELEASED (t1) -> NEWT order: PRESSED (t0) and RELEASED (t1) // Auto-Repeat: WINDOWS delivers only PRESSED (t0) and TYPED (t0). switch(eventType) { case KeyEvent.EVENT_KEY_RELEASED: @@ -307,8 +312,6 @@ public class WindowDriver extends WindowImpl { super.sendKeyEvent(KeyEvent.EVENT_KEY_PRESSED, modifiers, keyCode, keySym, keyChar); } break; - // case KeyEvent.EVENT_KEY_TYPED: - // break; } } diff --git a/src/newt/classes/jogamp/newt/driver/x11/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/x11/DisplayDriver.java index 4fe025ec4..d911483b0 100644 --- a/src/newt/classes/jogamp/newt/driver/x11/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/x11/DisplayDriver.java @@ -57,10 +57,8 @@ public class DisplayDriver extends DisplayImpl { } } - public static void initSingleton() { - // just exist to ensure static init has been run - } - + /** Ensure static init has been run. */ + /* pp */static void initSingleton() { } public DisplayDriver() { } @@ -84,13 +82,13 @@ public class DisplayDriver extends DisplayImpl { try { CompleteDisplay0(aDevice.getHandle()); } catch(RuntimeException e) { - closeNativeImpl(); + closeNativeImpl(aDevice); throw e; } } @Override - protected void closeNativeImpl() { + protected void closeNativeImpl(AbstractGraphicsDevice aDevice) { DisplayRelease0(aDevice.getHandle(), javaObjectAtom, windowDeleteAtom /*, kbdHandle */); // XKB disabled for now javaObjectAtom = 0; windowDeleteAtom = 0; diff --git a/src/newt/classes/jogamp/newt/driver/x11/RandR.java b/src/newt/classes/jogamp/newt/driver/x11/RandR.java index c569e5fd8..e39a6c63a 100644 --- a/src/newt/classes/jogamp/newt/driver/x11/RandR.java +++ b/src/newt/classes/jogamp/newt/driver/x11/RandR.java @@ -29,13 +29,21 @@ package jogamp.newt.driver.x11; import java.util.List; +import javax.media.nativewindow.util.RectangleImmutable; + import jogamp.newt.MonitorModeProps; +import com.jogamp.common.util.VersionNumber; import com.jogamp.newt.MonitorDevice; import com.jogamp.newt.MonitorMode; public interface RandR { + public static final VersionNumber version110 = new VersionNumber(1, 1, 0); + public static final VersionNumber version130 = new VersionNumber(1, 3, 0); + public static final VersionNumber version140 = new VersionNumber(1, 4, 0); + VersionNumber getVersion(); + void dumpInfo(final long dpy, final int screen_idx); /** @@ -72,5 +80,7 @@ public interface RandR { int[] getMonitorDeviceProps(final long dpy, final ScreenDriver screen, MonitorModeProps.Cache cache, final int crt_idx); int[] getMonitorDeviceViewport(final long dpy, final ScreenDriver screen, final int crt_idx); int[] getCurrentMonitorModeProps(final long dpy, final ScreenDriver screen, final int crt_idx); - boolean setCurrentMonitorMode(final long dpy, final ScreenDriver screen, MonitorDevice monitor, final MonitorMode mode); + boolean setCurrentMonitorMode(final long dpy, final ScreenDriver screen, MonitorDevice monitor, final MonitorMode mode); + + public void updateScreenViewport(final long dpy, final ScreenDriver screen, RectangleImmutable viewport); } diff --git a/src/newt/classes/jogamp/newt/driver/x11/RandR11.java b/src/newt/classes/jogamp/newt/driver/x11/RandR11.java index a938b4064..877607f72 100644 --- a/src/newt/classes/jogamp/newt/driver/x11/RandR11.java +++ b/src/newt/classes/jogamp/newt/driver/x11/RandR11.java @@ -27,6 +27,8 @@ */ package jogamp.newt.driver.x11; +import javax.media.nativewindow.util.RectangleImmutable; + import jogamp.newt.MonitorModeProps; import jogamp.newt.ScreenImpl; @@ -34,20 +36,17 @@ import com.jogamp.common.util.VersionNumber; import com.jogamp.newt.MonitorDevice; import com.jogamp.newt.MonitorMode; -public class RandR11 implements RandR { +class RandR11 implements RandR { private static final boolean DEBUG = ScreenDriver.DEBUG; - public static VersionNumber version = new VersionNumber(1, 1, 0); - - public static RandR11 createInstance(VersionNumber rAndRVersion) { - if( rAndRVersion.compareTo(version) >= 0 ) { - return new RandR11(); - } - return null; - } - private RandR11() { + RandR11() { } - + + @Override + public final VersionNumber getVersion() { + return version110; + } + @Override public void dumpInfo(final long dpy, final int screen_idx) { // NOP @@ -336,6 +335,11 @@ public class RandR11 implements RandR { } return done; } + + @Override + public final void updateScreenViewport(final long dpy, final ScreenDriver screen, RectangleImmutable viewport) { + // nop + } /** @return int[] { rot1, .. } */ private static native int[] getAvailableScreenRotations0(long display, int screen_index); diff --git a/src/newt/classes/jogamp/newt/driver/x11/RandR13.java b/src/newt/classes/jogamp/newt/driver/x11/RandR13.java index d10591381..ac83fc5f2 100644 --- a/src/newt/classes/jogamp/newt/driver/x11/RandR13.java +++ b/src/newt/classes/jogamp/newt/driver/x11/RandR13.java @@ -29,6 +29,8 @@ package jogamp.newt.driver.x11; import java.util.Iterator; +import javax.media.nativewindow.util.RectangleImmutable; + import jogamp.newt.MonitorModeProps; import com.jogamp.common.util.IntLongHashMap; @@ -43,18 +45,15 @@ import com.jogamp.newt.MonitorMode; * MonitorDevice.id == XRR monitor-idx (not id) * </pre> */ -public class RandR13 implements RandR { +class RandR13 implements RandR { private static final boolean DEBUG = ScreenDriver.DEBUG; - public static VersionNumber version = new VersionNumber(1, 3, 0); - - public static RandR13 createInstance(VersionNumber rAndRVersion) { - if( rAndRVersion.compareTo(version) >= 0 ) { - return new RandR13(); - } - return null; - } - private RandR13() { + RandR13() { + } + + @Override + public final VersionNumber getVersion() { + return version130; } @Override @@ -238,54 +237,20 @@ public class RandR13 implements RandR { } finally { releaseScreenResourceHandle(screenResources); } - /*** - * TODO: Would need a complete re-layout of crt positions, - * which is _not_ implicit by XRandR .. sadly. - * - if( res ) { - updateScreenViewport(dpy, screen, monitor); - } */ return res; } - /** See above .. - private final void updateScreenViewport(final long dpy, final ScreenDriver screen, MonitorDevice monitor) { + @Override + public final void updateScreenViewport(final long dpy, final ScreenDriver screen, final RectangleImmutable viewport) { final int screen_idx = screen.getIndex(); final long screenResources = getScreenResourceHandle(dpy, screen_idx); try { - RectangleImmutable newViewp = null; - final long monitorInfo = getMonitorInfoHandle(dpy, screen_idx, screenResources, monitor.getId()); - try { - final int[] vprops = getMonitorViewport0(monitorInfo); - if( null != vprops ) { - newViewp = new Rectangle(vprops[0], vprops[1], vprops[2], vprops[3]); - } - System.err.println("XXX setScreenViewport: newVp "+newViewp); - } finally { - releaseMonitorInfoHandle(monitorInfo); - } - if( null != newViewp ) { - final List<MonitorDevice> monitors = screen.getMonitorDevices(); - final ArrayList<RectangleImmutable> viewports = new ArrayList<RectangleImmutable>(); - for(int i=0; i<monitors.size(); i++) { - final MonitorDevice crt = monitors.get(i); - if( crt.getId() != monitor.getId() ) { - System.err.println("XXX setScreenViewport: add.pre["+i+"]: "+crt.getViewport()); - viewports.add( crt.getViewport() ) ; - } else { - System.err.println("XXX setScreenViewport: add.new["+i+"]: "+newViewp); - viewports.add( newViewp ); - } - } - final RectangleImmutable newScrnViewp = new Rectangle().union(viewports); - System.err.println("XXX setScreenViewport: "+screen.getViewport()+" -> "+newScrnViewp); - setScreenViewport0(dpy, screen_idx, screenResources, newScrnViewp.getX(), newScrnViewp.getY(), newScrnViewp.getWidth(), newScrnViewp.getHeight()); - } + setScreenViewport0(dpy, screen_idx, screenResources, viewport.getX(), viewport.getY(), viewport.getWidth(), viewport.getHeight()); } finally { dumpInfo0(dpy, screen_idx, screenResources); releaseScreenResourceHandle(screenResources); - } - } */ + } + } private static native long getScreenResources0(long display, int screen_index); private static native void freeScreenResources0(long screenResources); diff --git a/src/newt/classes/jogamp/newt/driver/x11/ScreenDriver.java b/src/newt/classes/jogamp/newt/driver/x11/ScreenDriver.java index b8b13939b..f37556dd7 100644 --- a/src/newt/classes/jogamp/newt/driver/x11/ScreenDriver.java +++ b/src/newt/classes/jogamp/newt/driver/x11/ScreenDriver.java @@ -38,6 +38,7 @@ import java.util.List; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.util.Rectangle; +import javax.media.nativewindow.util.RectangleImmutable; import jogamp.nativewindow.x11.X11Util; import jogamp.newt.Debug; @@ -54,12 +55,18 @@ import com.jogamp.newt.MonitorDevice; import com.jogamp.newt.MonitorMode; public class ScreenDriver extends ScreenImpl { - protected static final boolean DEBUG_TEST_RANDR13_DISABLED = Debug.isPropertyDefined("newt.test.Screen.disableRandR13", true); + protected static final boolean DEBUG_TEST_RANDR13_DISABLED; static { + Debug.initSingleton(); + DEBUG_TEST_RANDR13_DISABLED = Debug.isPropertyDefined("newt.test.Screen.disableRandR13", true); + DisplayDriver.initSingleton(); } + /** Ensure static init has been run. */ + /* pp */static void initSingleton() { } + public ScreenDriver() { } @@ -81,11 +88,12 @@ public class ScreenDriver extends ScreenImpl { randrVersion = new VersionNumber(v[0], v[1], 0); } { - final RandR13 rAndR13 = DEBUG_TEST_RANDR13_DISABLED ? null : RandR13.createInstance(randrVersion); - if( null != rAndR13 ) { - rAndR = rAndR13; + if( !DEBUG_TEST_RANDR13_DISABLED && randrVersion.compareTo(RandR.version130) >= 0 ) { + rAndR = new RandR13(); + } else if( randrVersion.compareTo(RandR.version110) >= 0 ) { + rAndR = new RandR11(); } else { - rAndR = RandR11.createInstance(randrVersion); + rAndR = null; } } if( DEBUG ) { @@ -183,7 +191,7 @@ public class ScreenDriver extends ScreenImpl { if( null == rAndR ) { return false; } final long t0 = System.currentTimeMillis(); - boolean done = runWithTempDisplayHandle( new DisplayImpl.DisplayRunnable<Boolean>() { + boolean done = runWithOptTempDisplayHandle( new DisplayImpl.DisplayRunnable<Boolean>() { public Boolean run(long dpy) { return Boolean.valueOf( rAndR.setCurrentMonitorMode(dpy, ScreenDriver.this, monitor, mode) ); } @@ -214,14 +222,31 @@ public class ScreenDriver extends ScreenImpl { @Override protected void calcVirtualScreenOriginAndSize(final Rectangle vOriginSize) { - runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { - public Object run(long dpy) { - vOriginSize.setX(0); - vOriginSize.setY(0); - vOriginSize.setWidth(getWidth0(dpy, screen_idx)); - vOriginSize.setHeight(getHeight0(dpy, screen_idx)); - return null; - } } ); + final RectangleImmutable ov = (RectangleImmutable) getViewport().cloneMutable(); + /** + if( null != rAndR && rAndR.getVersion().compareTo(RandR.version130) >= 0 && getMonitorDevices().size()>0 ) { + super.calcVirtualScreenOriginAndSize(vOriginSize); + if( DEBUG ) { + System.err.println("X11Screen.calcVirtualScreenOriginAndSize: UpdatingViewport "+ov+" -> "+vOriginSize); + } + runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { + public Object run(long dpy) { + rAndR.updateScreenViewport(dpy, ScreenDriver.this, vOriginSize); + return null; + } } ); + } else */ { + runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { + public Object run(long dpy) { + vOriginSize.setX(0); + vOriginSize.setY(0); + vOriginSize.setWidth(getWidth0(dpy, screen_idx)); + vOriginSize.setHeight(getHeight0(dpy, screen_idx)); + return null; + } } ); + if( DEBUG ) { + System.err.println("X11Screen.calcVirtualScreenOriginAndSize: Querying X11: "+ov+" -> "+vOriginSize); + } + } } //---------------------------------------------------------------------- @@ -245,6 +270,14 @@ public class ScreenDriver extends ScreenImpl { return res; } + private final <T> T runWithOptTempDisplayHandle(DisplayRunnable<T> action) { + if( null != rAndR && rAndR.getVersion().compareTo(RandR.version130) >= 0 ) { + return display.runWithLockedDisplayDevice(action); + } else { + return runWithTempDisplayHandle(action); + } + } + private static native long GetScreen0(long dpy, int scrn_idx); private static native int getWidth0(long display, int scrn_idx); diff --git a/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java index 69a09c1f9..4786ea04f 100644 --- a/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java @@ -59,12 +59,13 @@ public class WindowDriver extends WindowImpl { private static final int X11_WHEEL_TWO_DOWN_BUTTON = 7; static { - DisplayDriver.initSingleton(); + ScreenDriver.initSingleton(); } public WindowDriver() { } + @Override protected void createNativeImpl() { final ScreenDriver screen = (ScreenDriver) getScreen(); final DisplayDriver display = (DisplayDriver) screen.getDisplay(); @@ -109,6 +110,7 @@ public class WindowDriver extends WindowImpl { } } + @Override protected void closeNativeImpl() { if(0!=windowHandleClose && null!=getScreen() ) { DisplayDriver display = (DisplayDriver) getScreen().getDisplay(); @@ -133,7 +135,19 @@ public class WindowDriver extends WindowImpl { } } - protected boolean reconfigureWindowImpl(final int x, final int y, final int width, final int height, final int flags) { + /** + * <p> + * X11 Window supports {@link #FLAG_IS_FULLSCREEN_SPAN} + * </p> + * {@inheritDoc} + */ + @Override + protected boolean isReconfigureFlagSupported(int changeFlags) { + return true; // all flags! + } + + @Override + protected boolean reconfigureWindowImpl(final int x, final int y, final int width, final int height, int flags) { if(DEBUG_IMPLEMENTATION) { System.err.println("X11Window reconfig: "+x+"/"+y+" "+width+"x"+height+", "+ getReconfigureFlagsAsString(null, flags)); } @@ -148,18 +162,57 @@ public class WindowDriver extends WindowImpl { _x = x; _y = y; } + if( 0 != ( FLAG_CHANGE_FULLSCREEN & flags ) ) { + if( 0 != ( FLAG_IS_FULLSCREEN & flags) && 0 != ( FLAG_IS_FULLSCREEN_SPAN & flags) && 0 == ( FLAG_IS_ALWAYSONTOP & flags) ) { + tempFSAlwaysOnTop = true; + flags |= FLAG_IS_ALWAYSONTOP; + if(DEBUG_IMPLEMENTATION) { + System.err.println("X11Window reconfig.2: temporary "+getReconfigureFlagsAsString(null, flags)); + } + } else { + tempFSAlwaysOnTop = false; + } + } + final int fflags = flags; final DisplayDriver display = (DisplayDriver) getScreen().getDisplay(); runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { public Object run(long dpy) { reconfigureWindow0( dpy, getScreenIndex(), getParentWindowHandle(), getWindowHandle(), display.getWindowDeleteAtom(), - _x, _y, width, height, flags); + _x, _y, width, height, fflags); return null; } }); return true; } + volatile boolean tempFSAlwaysOnTop = false; + /** + * <p> + * Deal w/ tempAlwaysOnTop. + * </p> + * {@inheritDoc} + */ + @Override + protected void focusChanged(boolean defer, boolean focusGained) { + if( tempFSAlwaysOnTop && hasFocus() != focusGained && isNativeValid() ) { + final int flags = getReconfigureFlags(FLAG_CHANGE_ALWAYSONTOP, isVisible()) | ( focusGained ? FLAG_IS_ALWAYSONTOP : 0 ); + if(DEBUG_IMPLEMENTATION) { + System.err.println("X11Window reconfig.3 (focus): temporary "+getReconfigureFlagsAsString(null, flags)); + } + final DisplayDriver display = (DisplayDriver) getScreen().getDisplay(); + runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { + public Object run(long dpy) { + reconfigureWindow0( dpy, getScreenIndex(), + getParentWindowHandle(), getWindowHandle(), display.getWindowDeleteAtom(), + getX(), getY(), getWidth(), getHeight(), flags); + return null; + } + }); + } + super.focusChanged(defer, focusGained); + } + protected void reparentNotify(long newParentWindowHandle) { if(DEBUG_IMPLEMENTATION) { final long p0 = getParentWindowHandle(); @@ -167,6 +220,7 @@ public class WindowDriver extends WindowImpl { } } + @Override protected void requestFocusImpl(final boolean force) { runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { public Object run(long dpy) { @@ -214,6 +268,7 @@ public class WindowDriver extends WindowImpl { }); } + @Override protected Point getLocationOnScreenImpl(final int x, final int y) { return runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Point>() { public Point run(long dpy) { @@ -222,6 +277,7 @@ public class WindowDriver extends WindowImpl { } ); } + @Override protected void updateInsetsImpl(Insets insets) { // nop - using event driven insetsChange(..) } @@ -241,16 +297,17 @@ public class WindowDriver extends WindowImpl { } break; case MouseEvent.EVENT_MOUSE_RELEASED: + final boolean shiftPressed = 0 != ( modifiers & InputEvent.SHIFT_MASK ); switch(button) { case X11_WHEEL_ONE_UP_BUTTON: // vertical scroll up eventType = MouseEvent.EVENT_MOUSE_WHEEL_MOVED; button = 1; - rotationXYZ[1] = 1; + rotationXYZ[shiftPressed ? 0 : 1] = 1; break; case X11_WHEEL_ONE_DOWN_BUTTON: // vertical scroll down eventType = MouseEvent.EVENT_MOUSE_WHEEL_MOVED; button = 1; - rotationXYZ[1] = -1; + rotationXYZ[shiftPressed ? 0 : 1] = -1; break; case X11_WHEEL_TWO_UP_BUTTON: // horizontal scroll left eventType = MouseEvent.EVENT_MOUSE_WHEEL_MOVED; @@ -269,7 +326,8 @@ public class WindowDriver extends WindowImpl { } super.doMouseEvent(enqueue, wait, eventType, modifiers, x, y, button, rotationXYZ, rotationScale); } - + + /** Called by native TK */ protected final void sendKeyEvent(short eventType, int modifiers, short keyCode, short keySym, char keyChar0, String keyString) { // handleKeyEvent(true, false, eventType, modifiers, keyCode, keyChar); final boolean isModifierKey = KeyEvent.isModifierKey(keyCode); @@ -287,9 +345,6 @@ public class WindowDriver extends WindowImpl { case KeyEvent.EVENT_KEY_RELEASED: super.sendKeyEvent(KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, keySym, keyChar); break; - - // case KeyEvent.EVENT_KEY_TYPED: - // break; } } } diff --git a/src/newt/classes/jogamp/newt/swt/SWTEDTUtil.java b/src/newt/classes/jogamp/newt/swt/SWTEDTUtil.java index 2008b5ea4..d46562050 100644 --- a/src/newt/classes/jogamp/newt/swt/SWTEDTUtil.java +++ b/src/newt/classes/jogamp/newt/swt/SWTEDTUtil.java @@ -46,7 +46,7 @@ public class SWTEDTUtil implements EDTUtil { private final String name; private final Runnable dispatchMessages; private final org.eclipse.swt.widgets.Display swtDisplay; - private NewtEventDispatchThread nedt = null; + private NEDT nedt = null; private int start_iter=0; private static long pollPeriod = EDTUtil.defaultEDTPollPeriod; @@ -58,7 +58,7 @@ public class SWTEDTUtil implements EDTUtil { ((jogamp.newt.DisplayImpl) newtDisplay).dispatchMessages(); } }; this.swtDisplay = swtDisplay; - this.nedt = new NewtEventDispatchThread(threadGroup, name); + this.nedt = new NEDT(threadGroup, name); this.nedt.setDaemon(true); // don't stop JVM from shutdown .. } @@ -77,24 +77,39 @@ public class SWTEDTUtil implements EDTUtil { } @Override - public void reset() { - synchronized(edtLock) { - waitUntilStopped(); + public final boolean restart() throws IllegalStateException { + final boolean swtDisposed = swtDisplay.isDisposed(); + synchronized(edtLock) { + if( nedt.isRunning() ) { + final Thread curT = Thread.currentThread(); + final Thread swtT = !swtDisposed ? swtDisplay.getThread() : null; + final boolean onSWTEDT = swtT == curT; + throw new IllegalStateException("EDT still running and not subject to stop. Curr "+curT.getName()+", NEDT "+nedt.getName()+", isRunning "+nedt.isRunning+", shouldStop "+nedt.shouldStop+", SWT-EDT "+swtT.getName()+", on SWT-EDT "+onSWTEDT); + } if(DEBUG) { - System.err.println(Thread.currentThread()+": SWT-EDT reset - edt: "+nedt); + System.err.println(Thread.currentThread()+": SWT-EDT reset - edt: "+nedt+", swtDisposed (skipping) "+swtDisposed); + } + if( !swtDisposed ) { + if( nedt.getState() != Thread.State.NEW ) { + nedt = new NEDT(threadGroup, name); + nedt.setDaemon(true); // don't stop JVM from shutdown .. + } + startImpl(); } - this.nedt = new NewtEventDispatchThread(threadGroup, name); - this.nedt.setDaemon(true); // don't stop JVM from shutdown .. + } + if( !swtDisposed ) { + return invoke(true, nullTask); + } else { + return false; } } private final void startImpl() { if(nedt.isAlive()) { - throw new RuntimeException("SWT-EDT Thread.isAlive(): true, isRunning: "+nedt.isRunning()+", edt: "+nedt); + throw new RuntimeException("SWT-EDT Thread.isAlive(): true, isRunning: "+nedt.isRunning+", shouldStop "+nedt.shouldStop+", edt: "+nedt); } start_iter++; nedt.setName(name+start_iter); - nedt.shouldStop = false; if(DEBUG) { System.err.println(Thread.currentThread()+": SWT-EDT START - edt: "+nedt); // Thread.dumpStack(); @@ -104,7 +119,7 @@ public class SWTEDTUtil implements EDTUtil { @Override public boolean isCurrentThreadEDT() { - return swtDisplay.getThread() == Thread.currentThread(); + return !swtDisplay.isDisposed() && swtDisplay.getThread() == Thread.currentThread(); } @Override @@ -115,66 +130,94 @@ public class SWTEDTUtil implements EDTUtil { @Override public final boolean isCurrentThreadEDTorNEDT() { final Thread ct = Thread.currentThread(); - return ct == swtDisplay.getThread() || ct == nedt ; + return ( !swtDisplay.isDisposed() && ct == swtDisplay.getThread() ) || ct == nedt ; } @Override public boolean isRunning() { - return nedt.isRunning() ; // SWT is always running + return nedt.isRunning(); } @Override - public final void invokeStop(Runnable task) { - invokeImpl(true, task, true); + public final boolean invokeStop(boolean wait, Runnable task) { + return invokeImpl(wait, task, true); } @Override - public final void invoke(boolean wait, Runnable task) { - invokeImpl(wait, task, false); + public final boolean invoke(boolean wait, Runnable task) { + return invokeImpl(wait, task, false); } - private void invokeImpl(boolean wait, Runnable task, boolean stop) { + private static Runnable nullTask = new Runnable() { + @Override + public void run() { } + }; + + private final boolean invokeImpl(boolean wait, Runnable task, boolean stop) { Throwable throwable = null; RunnableTask rTask = null; - Object rTaskLock = new Object(); + final Object rTaskLock = new Object(); synchronized(rTaskLock) { // lock the optional task execution synchronized(edtLock) { // lock the EDT status if( nedt.shouldStop ) { // drop task .. if(DEBUG) { - System.err.println(Thread.currentThread()+": Warning: SWT-EDT about (1) to stop, won't enqueue new task: "+nedt); + System.err.println(Thread.currentThread()+": Warning: SWT-EDT about (1) to stop, won't enqueue new task: "+nedt+", isRunning "+nedt.isRunning+", shouldStop "+nedt.shouldStop); Thread.dumpStack(); } - return; + return false; + } + if( swtDisplay.isDisposed() ) { + stop = true; } - // System.err.println(Thread.currentThread()+" XXX stop: "+stop+", tasks: "+edt.tasks.size()+", task: "+task); - // Thread.dumpStack(); - if(stop) { - synchronized(nedt.sync) { + + if( isCurrentThreadEDT() ) { + if(null != task) { + task.run(); + } + wait = false; // running in same thread (EDT) -> no wait + if( stop ) { nedt.shouldStop = true; - nedt.sync.notifyAll(); // stop immediate if waiting (poll freq) } - if(DEBUG) { - System.err.println(Thread.currentThread()+": SWT-EDT signal STOP (on edt: "+isCurrentThreadEDT()+") - "+nedt); - // Thread.dumpStack(); + } else { + if( !nedt.isRunning && !swtDisplay.isDisposed() ) { + if( null != task ) { + if( stop ) { + System.err.println(Thread.currentThread()+": Warning: SWT-EDT is about (3) to stop and stopped already, dropping task. NEDT "+nedt); + } else { + System.err.println(Thread.currentThread()+": Warning: SWT-EDT is not running, dropping task. NEDT "+nedt); + } + if(DEBUG) { + Thread.dumpStack(); + } + } + return false; + } else if( stop ) { + if( nedt.isRunning ) { + if(DEBUG) { + System.err.println(Thread.currentThread()+": SWT-EDT signal STOP (on edt: "+isCurrentThreadEDT()+") - "+nedt+", isRunning "+nedt.isRunning+", shouldStop "+nedt.shouldStop); + } + synchronized(nedt.sync) { + nedt.shouldStop = true; + nedt.sync.notifyAll(); // stop immediate if waiting (poll freq) + } + } + if( swtDisplay.isDisposed() ) { + System.err.println(Thread.currentThread()+": Warning: SWT-EDT is about (3) to stop and stopped already, dropping task. "+nedt); + if(DEBUG) { + Thread.dumpStack(); + } + return false; + } + } + + if( null != task ) { + rTask = new RunnableTask(task, + wait ? rTaskLock : null, + true /* always catch and report Exceptions, don't disturb EDT */, + wait ? null : System.err); + swtDisplay.asyncExec(rTask); } - } else if( !nedt.isRunning() ) { - // start if should not stop && not started yet - startImpl(); - } - if( null == task ) { - wait = false; - } else if( isCurrentThreadEDT() ) { - task.run(); - wait = false; // running in same thread (EDT) -> no wait - } else if( swtDisplay.isDisposed() ) { - wait = false; // drop task, SWT disposed - } else { - rTask = new RunnableTask(task, - wait ? rTaskLock : null, - true /* always catch and report Exceptions, don't disturb EDT */, - wait ? null : System.err); - swtDisplay.asyncExec(rTask); } } if( wait ) { @@ -193,53 +236,60 @@ public class SWTEDTUtil implements EDTUtil { throw new RuntimeException(throwable); } } + return true; } } @Override - final public void waitUntilIdle() { - final NewtEventDispatchThread _nedt; + final public boolean waitUntilIdle() { + final NEDT _nedt; synchronized(edtLock) { _nedt = nedt; } final Thread ct = Thread.currentThread(); - if(!_nedt.isRunning() || _nedt == ct || swtDisplay.getThread() == ct) { - return; + if( !_nedt.isRunning || _nedt == ct || swtDisplay.isDisposed() || swtDisplay.getThread() == ct ) { + return false; } try { swtDisplay.syncExec(new Runnable() { public void run() { } }); } catch (Exception e) { } + return true; } @Override - final public void waitUntilStopped() { + final public boolean waitUntilStopped() { synchronized(edtLock) { - final Thread ct = Thread.currentThread(); - if(nedt.isRunning() && nedt != ct && swtDisplay.getThread() != ct) { - while(nedt.isRunning()) { + final Thread curT = Thread.currentThread(); + final Thread swtT = !swtDisplay.isDisposed() ? swtDisplay.getThread() : null; + final boolean onSWTEDT = swtT == curT; + if( nedt.isRunning && nedt != curT && !onSWTEDT ) { + while( nedt.isRunning ) { try { edtLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } + return true; + } else { + return false; } } } - class NewtEventDispatchThread extends Thread { + class NEDT extends Thread { volatile boolean shouldStop = false; volatile boolean isRunning = false; Object sync = new Object(); - public NewtEventDispatchThread(ThreadGroup tg, String name) { + public NEDT(ThreadGroup tg, String name) { super(tg, name); } final public boolean isRunning() { - return isRunning; + return isRunning && !shouldStop; } @Override @@ -296,10 +346,8 @@ public class SWTEDTUtil implements EDTUtil { System.err.println(getName()+": SWT-EDT run() END "+ getName()+", "+error); } synchronized(edtLock) { - isRunning = !shouldStop; - if(!isRunning) { - edtLock.notifyAll(); - } + isRunning = false; + edtLock.notifyAll(); } if(DEBUG) { System.err.println(getName()+": SWT-EDT run() EXIT "+ getName()+", exception: "+error); diff --git a/src/newt/classes/jogamp/newt/swt/event/SWTNewtEventFactory.java b/src/newt/classes/jogamp/newt/swt/event/SWTNewtEventFactory.java index 1e99d1ef7..3782a1186 100644 --- a/src/newt/classes/jogamp/newt/swt/event/SWTNewtEventFactory.java +++ b/src/newt/classes/jogamp/newt/swt/event/SWTNewtEventFactory.java @@ -56,7 +56,6 @@ public class SWTNewtEventFactory { case SWT.KeyDown: return com.jogamp.newt.event.KeyEvent.EVENT_KEY_PRESSED; case SWT.KeyUp: return com.jogamp.newt.event.KeyEvent.EVENT_KEY_RELEASED; - // case SWT.KeyXXX: return com.jogamp.newt.event.KeyEvent.EVENT_KEY_TYPED; } return (short)0; } |