diff options
Diffstat (limited to 'src/newt/classes')
40 files changed, 2913 insertions, 664 deletions
diff --git a/src/newt/classes/com/jogamp/newt/NewtFactory.java b/src/newt/classes/com/jogamp/newt/NewtFactory.java index 2ed2194d8..dd15eb3ea 100644 --- a/src/newt/classes/com/jogamp/newt/NewtFactory.java +++ b/src/newt/classes/com/jogamp/newt/NewtFactory.java @@ -59,6 +59,7 @@ public class NewtFactory { public static final String DRIVER_DEFAULT_ROOT_PACKAGE = "jogamp.newt.driver"; private static IOUtil.ClassResources defaultWindowIcons; + private static String sysPaths = "newt/data/jogamp-16x16.png newt/data/jogamp-32x32.png"; static { AccessController.doPrivileged(new PrivilegedAction<Object>() { @@ -67,12 +68,11 @@ public class NewtFactory { NativeWindowFactory.initSingleton(); // last resort .. { /** See API Doc in {@link Window} ! */ - final String[] paths = PropertyAccess.getProperty("newt.window.icons", true, "newt/data/jogamp-16x16.png newt/data/jogamp-32x32.png").split("\\s"); + final String[] paths = PropertyAccess.getProperty("newt.window.icons", true, sysPaths).split("[\\s,]"); if( paths.length < 2 ) { throw new IllegalArgumentException("Property 'newt.window.icons' did not specify at least two PNG icons, but "+Arrays.toString(paths)); } - final Class<?> clazz = NewtFactory.class; - defaultWindowIcons = new IOUtil.ClassResources(clazz, paths); + defaultWindowIcons = new IOUtil.ClassResources(paths, NewtFactory.class.getClassLoader(), null); } return null; } } ); @@ -81,7 +81,7 @@ public class NewtFactory { /** * Returns the application window icon resources to be used. * <p> - * Property <code>newt.window.icons</code> may define a list of PNG icons separated by a whitespace character. + * Property <code>newt.window.icons</code> may define a list of PNG icons separated by one whitespace or one comma character. * Shall reference at least two PNG icons, from lower (16x16) to higher (>= 32x32) resolution. * </p> * <p> diff --git a/src/newt/classes/com/jogamp/newt/Window.java b/src/newt/classes/com/jogamp/newt/Window.java index d46afd1cd..30b02cb61 100644 --- a/src/newt/classes/com/jogamp/newt/Window.java +++ b/src/newt/classes/com/jogamp/newt/Window.java @@ -83,16 +83,22 @@ import com.jogamp.nativewindow.util.SurfaceSize; * <a name="customwindowicons"><h5>Custom Window Icons</h5></a> * <p> * Custom window icons can be defined via system property <code>newt.window.icons</code>, - * which shall contain a space separated list of PNG icon locations from low- to high-resolution. + * which shall contain a list of PNG icon locations from low- to high-resolution, + * separated by one whitespace or one comma character. * The location must be resolvable via classpath, i.e. shall reference a location within the jar file. * Example (our default): * <pre> - * -Dnewt.window.icons="newt/data/jogamp-16x16.png newt/data/jogamp-32x32.png" - * -Djnlp.newt.window.icons="newt/data/jogamp-16x16.png newt/data/jogamp-32x32.png" + * -Dnewt.window.icons="newt/data/jogamp-16x16.png,newt/data/jogamp-32x32.png" + * -Djnlp.newt.window.icons="newt/data/jogamp-16x16.png,newt/data/jogamp-32x32.png" * </pre> * The property can also be set programmatically, which must happen before any NEWT classes are <i>touched</i>: * <pre> - * System.setProperty("newt.window.icons", "newt/data/jogamp-16x16.png newt/data/jogamp-32x32.png"); + * System.setProperty("newt.window.icons", "newt/data/jogamp-16x16.png, newt/data/jogamp-32x32.png"); + * </pre> + * To disable even Jogamp's own window icons in favor of system icons, + * simply set a non-existing location, e.g.: + * <pre> + * -Dnewt.window.icons="null,null" * </pre> * </p> * @@ -216,6 +222,10 @@ public interface Window extends NativeWindow, WindowClosingProtocol, ScalableSur public static final int STATE_BIT_MAXIMIZED_HORZ = 10; // reconfig-flag /** * Set if window is in <i>fullscreen mode</i>, otherwise cleared. + * <p> + * Usually fullscreen mode implies {@link #STATE_BIT_UNDECORATED}, + * however, an implementation is allowed to ignore this if unavailable. + * </p> * <p>Bit number {@value}.</p> * <p>Defaults to {@code false}.</p> * @see #getStateMask() @@ -223,9 +233,6 @@ public interface Window extends NativeWindow, WindowClosingProtocol, ScalableSur */ public static final int STATE_BIT_FULLSCREEN = 11; // reconfig-flag - // Hidden in WindowImpl: - // static final int STATE_BIT_FULLSCREEN_SPAN = 12; - /** * Set if the <i>pointer is visible</i> when inside the window, otherwise cleared. * <p>Bit number {@value}.</p> @@ -233,7 +240,7 @@ public interface Window extends NativeWindow, WindowClosingProtocol, ScalableSur * @see #getStateMask() * @since 2.3.2 */ - public static final int STATE_BIT_POINTERVISIBLE = 13; + public static final int STATE_BIT_POINTERVISIBLE = 12; /** * Set if the <i>pointer is confined</i> to the window, otherwise cleared. * <p>Bit number {@value}.</p> @@ -241,7 +248,7 @@ public interface Window extends NativeWindow, WindowClosingProtocol, ScalableSur * @see #getStateMask() * @since 2.3.2 */ - public static final int STATE_BIT_POINTERCONFINED = 14; + public static final int STATE_BIT_POINTERCONFINED = 13; /** * Bitmask for {@link #STATE_BIT_VISIBLE}, {@value}. @@ -330,6 +337,7 @@ public interface Window extends NativeWindow, WindowClosingProtocol, ScalableSur /** * Returns the current status mask of this instance. + * @see #getSupportedStateMask() * @see #STATE_MASK_VISIBLE * @see #STATE_MASK_AUTOPOSITION * @see #STATE_MASK_CHILDWIN @@ -354,6 +362,52 @@ public interface Window extends NativeWindow, WindowClosingProtocol, ScalableSur */ String getStateMaskString(); + /** + * Returns the supported {@link #getStateMask() state mask} of the implementation. + * <p> + * Implementation provides supported {@link #getStateMask() state mask} values at runtime + * <i>after</i> native window creation, i.e. first visibility. + * </p> + * <p> + * Please note that a window's size shall also be allowed to change, i.e. {@link #setSize(int, int)}. + * </p> + * <p> + * Default value is {@link #STATE_MASK_VISIBLE} | {@link #STATE_MASK_FOCUSED} | {@link #STATE_MASK_FULLSCREEN}, + * i.e. the <b>minimum requirement</b> for all implementations. + * </p> + * <p> + * Before native window creation {@link #getStatePublicBitmask()} is returned, + * i.e. it is assumed all features are supported. + * </p> + * <p> + * Semantic of the supported state-mask bits (after native creation, i.e. 1st visibility): + * <ul> + * <li>{@link #STATE_MASK_VISIBLE}: {@link #setVisible(boolean) Visibility} can be toggled. <b>Minimum requirement</b>.</li> + * <li>{@link #STATE_MASK_CHILDWIN}: {@link #reparentWindow(NativeWindow, int, int, int) Native window parenting} is supported.</li> + * <li>{@link #STATE_MASK_FOCUSED}: Window {@link #requestFocus() focus management} is supported. <b>Minimum requirement</b>.</li> + * <li>{@link #STATE_MASK_UNDECORATED}: {@link #setUndecorated(boolean) Window decoration} can be toggled.</li> + * <li>{@link #STATE_MASK_ALWAYSONTOP}: Window can be set {@link #setAlwaysOnTop(boolean) always-on-top}. </li> + * <li>{@link #STATE_MASK_ALWAYSONBOTTOM}: Window can be set {@link #setAlwaysOnBottom(boolean) always-on-bottom}. </li> + * <li>{@link #STATE_MASK_STICKY}: Window can be set {@link #setSticky(boolean) sticky}.</li> + * <li>{@link #STATE_MASK_RESIZABLE}: Window {@link #setResizable(boolean) resizability} can be toggled.</li> + * <li>{@link #STATE_MASK_MAXIMIZED_VERT}: Window can be {@link #setMaximized(boolean, boolean) maximized-vertically}. </li> + * <li>{@link #STATE_MASK_MAXIMIZED_HORZ}: Window can be {@link #setMaximized(boolean, boolean) maximized-horizontally}. </li> + * <li>{@link #STATE_MASK_FULLSCREEN}: Window {@link #setFullscreen(boolean) fullscreen} can be toggled. </li> + * <li>{@link #STATE_MASK_POINTERVISIBLE}: Window {@link #setPointerVisible(boolean) pointer visibility} can be toggled. </li> + * <li>{@link #STATE_MASK_POINTERCONFINED}: Window {@link #confinePointer(boolean) pointer can be confined}. </li> + * </ul> + * </p> + * @see #getStateMask() + * @since 2.3.2 + */ + int getSupportedStateMask(); + + /** + * Returns a string representation of the {@link #getSupportedStateMask() supported state mask}. + * @since 2.3.2 + */ + String getSupportedStateMaskString(); + // // Lifecycle // @@ -697,7 +751,6 @@ public interface Window extends NativeWindow, WindowClosingProtocol, ScalableSur boolean isSticky(); /** - * <p>Operation is ignored in {@link #isFullscreen() fullscreen mode}.</p> * <p>Operation is ignored if this instance {@link #isChildWindow() is a child window}.</p> * @see {@link #STATE_BIT_MAXIMIZED_HORZ} * @see {@link #STATE_BIT_MAXIMIZED_VERT} diff --git a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java index ae32fd164..a0083b4ea 100644 --- a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java +++ b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java @@ -49,7 +49,9 @@ import java.security.PrivilegedAction; import java.util.Set; import com.jogamp.nativewindow.CapabilitiesImmutable; +import com.jogamp.nativewindow.NativeSurface; import com.jogamp.nativewindow.NativeWindow; +import com.jogamp.nativewindow.NativeWindowHolder; import com.jogamp.nativewindow.OffscreenLayerOption; import com.jogamp.nativewindow.WindowClosingProtocol; import com.jogamp.opengl.GLAnimatorControl; @@ -100,7 +102,7 @@ import com.jogamp.opengl.util.TileRenderer; * the underlying JAWT mechanism to composite the image, if supported. */ @SuppressWarnings("serial") -public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProtocol, OffscreenLayerOption, AWTPrintLifecycle { +public class NewtCanvasAWT extends java.awt.Canvas implements NativeWindowHolder, WindowClosingProtocol, OffscreenLayerOption, AWTPrintLifecycle { public static final boolean DEBUG = Debug.debug("Window"); private final Object sync = new Object(); @@ -110,7 +112,7 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto private Window newtChild = null; private boolean newtChildAttached = false; private boolean isOnscreen = true; - private WindowClosingMode newtChildCloseOp; + private WindowClosingMode newtChildCloseOp = WindowClosingMode.DISPOSE_ON_CLOSE; private final AWTParentWindowAdapter awtWinAdapter; private final AWTAdapter awtMouseAdapter; private final AWTAdapter awtKeyAdapter; @@ -420,10 +422,22 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto return newtChild; } - /** @return this AWT Canvas NativeWindow representation, may be null in case {@link #removeNotify()} has been called, - * or {@link #addNotify()} hasn't been called yet.*/ + /** + * {@inheritDoc} + * @return this AWT Canvas {@link NativeWindow} representation, may be null in case {@link #removeNotify()} has been called, + * or {@link #addNotify()} hasn't been called yet. + */ + @Override public NativeWindow getNativeWindow() { return jawtWindow; } + /** + * {@inheritDoc} + * @return this AWT Canvas {@link NativeSurface} representation, may be null in case {@link #removeNotify()} has been called, + * or {@link #addNotify()} hasn't been called yet. + */ + @Override + public NativeSurface getNativeSurface() { return jawtWindow; } + @Override public WindowClosingMode getDefaultCloseOperation() { return awtWindowClosingProtocol.getDefaultCloseOperation(); 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 cd53294a1..6bf9f41a6 100644 --- a/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtApplet1Run.java +++ b/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtApplet1Run.java @@ -115,7 +115,7 @@ public class JOGLNewtApplet1Run extends Applet { String glEventListenerClazzName=null; String glProfileName=null; - int glSwapInterval=0; + int glSwapInterval=1; boolean glDebug=false; boolean glTrace=false; boolean glUndecorated=false; diff --git a/src/newt/classes/com/jogamp/newt/javafx/NewtCanvasJFX.java b/src/newt/classes/com/jogamp/newt/javafx/NewtCanvasJFX.java new file mode 100644 index 000000000..e04ed326d --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/javafx/NewtCanvasJFX.java @@ -0,0 +1,670 @@ +/** + * Copyright 2019 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 met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.newt.javafx; + +import com.jogamp.common.util.PropertyAccess; +import com.jogamp.nativewindow.AbstractGraphicsConfiguration; +import com.jogamp.nativewindow.AbstractGraphicsScreen; +import com.jogamp.nativewindow.Capabilities; +import com.jogamp.nativewindow.CapabilitiesImmutable; +import com.jogamp.nativewindow.GraphicsConfigurationFactory; +import com.jogamp.nativewindow.NativeSurface; +import com.jogamp.nativewindow.NativeWindow; +import com.jogamp.nativewindow.NativeWindowException; +import com.jogamp.nativewindow.NativeWindowFactory; +import com.jogamp.nativewindow.NativeWindowHolder; +import com.jogamp.nativewindow.SurfaceUpdatedListener; +import com.jogamp.nativewindow.WindowClosingProtocol; +import com.jogamp.nativewindow.WindowClosingProtocol.WindowClosingMode; +import com.jogamp.nativewindow.util.Insets; +import com.jogamp.nativewindow.util.InsetsImmutable; +import com.jogamp.nativewindow.util.Point; +import com.jogamp.nativewindow.util.Rectangle; +import com.jogamp.opengl.GLCapabilities; + +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.event.EventHandler; +import javafx.geometry.Bounds; +import javafx.scene.Scene; +import javafx.scene.canvas.Canvas; +import jogamp.newt.Debug; +import jogamp.newt.javafx.JFXEDTUtil; + +import com.jogamp.nativewindow.javafx.JFXAccessor; +import com.jogamp.newt.Display; +import com.jogamp.newt.Window; +import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.util.EDTUtil; + +/** + * A NEWT based JFX {@link Canvas} specialization allowing a NEWT child {@link Window} to be attached using native parenting. + * <p> + * {@link NewtCanvasJFX} allows utilizing custom {@link GLCapabilities} settings independent from the JavaFX's window + * as well as independent rendering from JavaFX's thread. + * </p> + * <p> + * {@link NewtCanvasJFX} allows native parenting operations before and after + * it's belonging Group's Scene has been attached to the JavaFX {@link javafx.stage.Window Window}'s actual native window, + * i.e. becoming fully realized and visible. + * </p> + * <p> + * Note that {@link JFXAccessor#runOnJFXThread(boolean, Runnable)} is still used to for certain + * mandatory JavaFX lifecycle operation on the JavaFX thread. + * </p> + */ +public class NewtCanvasJFX extends Canvas implements NativeWindowHolder, WindowClosingProtocol { + private static final boolean DEBUG = Debug.debug("Window"); + private static final boolean USE_JFX_EDT = PropertyAccess.getBooleanProperty("jogamp.newt.javafx.UseJFXEDT", true, true); + private volatile javafx.stage.Window parentWindow = null; + private volatile AbstractGraphicsScreen screen = null; + + private WindowClosingMode newtChildClosingMode = WindowClosingMode.DISPOSE_ON_CLOSE; + private WindowClosingMode closingMode = WindowClosingMode.DISPOSE_ON_CLOSE; + private final Rectangle clientArea = new Rectangle(); + + private volatile JFXNativeWindow nativeWindow = null; + private volatile Window newtChild = null; + private volatile boolean newtChildReady = false; // ready if JFXEDTUtil is set and newtChild parented + private volatile boolean postSetSize = false; // pending resize + private volatile boolean postSetPos = false; // pending pos + + private final EventHandler<javafx.stage.WindowEvent> windowClosingListener = new EventHandler<javafx.stage.WindowEvent>() { + public final void handle(final javafx.stage.WindowEvent e) { + if( DEBUG ) { + System.err.println("NewtCanvasJFX.Event.DISPOSE, "+e+", closeOp "+closingMode); + } + if( WindowClosingMode.DISPOSE_ON_CLOSE == closingMode ) { + NewtCanvasJFX.this.destroy(); + } else { + // avoid JavaFX closing operation + e.consume(); + } + } }; + private final EventHandler<javafx.stage.WindowEvent> windowShownListener = new EventHandler<javafx.stage.WindowEvent>() { + public final void handle(final javafx.stage.WindowEvent e) { + if( DEBUG ) { + System.err.println("NewtCanvasJFX.Event.SHOWN, "+e); + } + repaintAction(true); + } }; + + /** + * Instantiates a NewtCanvas with a NEWT child. + * + * <p> + * Note: The NEWT child {@link Display}'s {@link EDTUtil} is being set to an JFX conform implementation + * via {@link Display#setEDTUtil(EDTUtil)}. + * </p> + * @param child optional preassigned {@link #Window}, maybe null + */ + public NewtCanvasJFX(final Window child) { + super(); + + updateParentWindowAndScreen(); + + final ChangeListener<Number> sizeListener = new ChangeListener<Number>() { + @Override public void changed(final ObservableValue<? extends Number> observable, final Number oldValue, final Number newValue) { + if( DEBUG ) { + System.err.println("NewtCanvasJFX.Event.Size, "+oldValue.doubleValue()+" -> "+newValue.doubleValue()+", has "+getWidth()+"x"+getHeight()); + } + updateSizeCheck((int)getWidth(), (int)getHeight()); + repaintAction(isVisible()); + } }; + this.widthProperty().addListener(sizeListener); + this.heightProperty().addListener(sizeListener); + this.visibleProperty().addListener(new ChangeListener<Boolean>() { + @Override public void changed(final ObservableValue<? extends Boolean> observable, final Boolean oldValue, final Boolean newValue) { + if( DEBUG ) { + System.err.println("NewtCanvasJFX.Event.Visible, "+oldValue.booleanValue()+" -> "+newValue.booleanValue()+", has "+isVisible()); + } + repaintAction(newValue.booleanValue()); + } + }); + this.sceneProperty().addListener(new ChangeListener<Scene>() { + @Override public void changed(final ObservableValue<? extends Scene> observable, final Scene oldValue, final Scene newValue) { + if( DEBUG ) { + System.err.println("NewtCanvasJFX.Event.Scene, "+oldValue+" -> "+newValue+", has "+getScene()); + if(null != newValue) { + final javafx.stage.Window w = newValue.getWindow(); + System.err.println("NewtCanvasJFX.Event.Scene window "+w+" (showing "+(null!=w?w.isShowing():0)+")"); + } + } + if( updateParentWindowAndScreen() ) { + repaintAction(isVisible()); + } + } + }); + + if(null != child) { + setNEWTChild(child); + } + } + + private final void repaintAction(final boolean visible) { + if( visible && ( null != nativeWindow || validateNative(true /* completeReparent */) ) ) { + if( newtChildReady ) { + if( postSetSize ) { + newtChild.setSize(clientArea.getWidth(), clientArea.getHeight()); + postSetSize = false; + } + if( postSetPos ) { + newtChild.setPosition(clientArea.getX(), clientArea.getY()); + postSetPos = false; + } + newtChild.windowRepaint(0, 0, clientArea.getWidth(), clientArea.getHeight()); + } + } + } + + private final void updatePosSizeCheck() { + final Bounds b = localToScene(getBoundsInLocal()); + updatePosCheck((int)b.getMinX(), (int)b.getMinY()); + updateSizeCheck((int)getWidth(), (int)getHeight()); + } + private final void updatePosCheck(final int newX, final int newY) { + final boolean posChanged; + { + final Rectangle oClientArea = clientArea; + posChanged = newX != oClientArea.getX() || newY != oClientArea.getY(); + if( posChanged ) { + clientArea.setX(newX); + clientArea.setY(newY); + } + } + if(DEBUG) { + final long nsh = newtChildReady ? newtChild.getSurfaceHandle() : 0; + System.err.println("NewtCanvasJFX.updatePosCheck: posChanged "+posChanged+", ("+Thread.currentThread().getName()+"): newtChildReady "+newtChildReady+", "+clientArea.getX()+"/"+clientArea.getY()+" "+clientArea.getWidth()+"x"+clientArea.getHeight()+" - surfaceHandle 0x"+Long.toHexString(nsh)); + } + if( posChanged ) { + if( newtChildReady ) { + newtChild.setPosition(clientArea.getX(), clientArea.getY()); + } else { + postSetPos = true; + } + } + } + private final void updateSizeCheck(final int newWidth, final int newHeight) { + final boolean sizeChanged; + { + final Rectangle oClientArea = clientArea; + sizeChanged = newWidth != oClientArea.getWidth() || newHeight != oClientArea.getHeight(); + if( sizeChanged ) { + clientArea.setWidth(newWidth); + clientArea.setHeight(newHeight); + } + } + if(DEBUG) { + final long nsh = newtChildReady ? newtChild.getSurfaceHandle() : 0; + System.err.println("NewtCanvasJFX.updateSizeCheck: sizeChanged "+sizeChanged+", ("+Thread.currentThread().getName()+"): newtChildReady "+newtChildReady+", "+clientArea.getX()+"/"+clientArea.getY()+" "+clientArea.getWidth()+"x"+clientArea.getHeight()+" - surfaceHandle 0x"+Long.toHexString(nsh)); + } + if( sizeChanged ) { + if( newtChildReady ) { + newtChild.setSize(clientArea.getWidth(), clientArea.getHeight()); + } else { + postSetSize = true; + } + } + } + + private final ChangeListener<javafx.stage.Window> sceneWindowChangeListener = new ChangeListener<javafx.stage.Window>() { + @Override public void changed(final ObservableValue<? extends javafx.stage.Window> observable, final javafx.stage.Window oldValue, final javafx.stage.Window newValue) { + if( DEBUG ) { + System.err.println("NewtCanvasJFX.Event.Window, "+oldValue+" -> "+newValue); + } + if( updateParentWindowAndScreen() ) { + repaintAction(isVisible()); + } + } }; + + private boolean updateParentWindowAndScreen() { + final Scene s = this.getScene(); + if( null != s ) { + final javafx.stage.Window w = s.getWindow(); + if( DEBUG ) { + System.err.println("NewtCanvasJFX.updateParentWindowAndScreen: Scene "+s+", Window "+w+" (showing "+(null!=w?w.isShowing():0)+")"); + } + if( w != parentWindow ) { + destroyImpl(false); + } + parentWindow = w; + if( null != w ) { + screen = JFXAccessor.getScreen(JFXAccessor.getDevice(parentWindow), -1 /* default */); + parentWindow.addEventHandler(javafx.stage.WindowEvent.WINDOW_CLOSE_REQUEST, windowClosingListener); + parentWindow.addEventHandler(javafx.stage.WindowEvent.WINDOW_SHOWN, windowShownListener); + return true; + } else { + s.windowProperty().addListener(sceneWindowChangeListener); + } + } else { + if( DEBUG ) { + System.err.println("NewtCanvasJFX.updateParentWindowAndScreen: Null Scene"); + } + if( null != parentWindow ) { + destroyImpl(false); + } + } + return false; + } + + /** + * Destroys this resource: + * <ul> + * <li> Make the NEWT Child invisible </li> + * <li> Disconnects the NEWT Child from this Canvas NativeWindow, reparent to NULL </li> + * <li> Issues {@link Window#destroy()} on the NEWT Child</li> + * <li> Remove reference to the NEWT Child</li> + * </ul> + * JavaFX will issue this call when sending out the {@link javafx.stage.WindowEvent#WINDOW_CLOSE_REQUEST} automatically, + * if the user has not overridden the default {@link WindowClosingMode#DISPOSE_ON_CLOSE} to {@link WindowClosingMode#DO_NOTHING_ON_CLOSE} + * via {@link #setDefaultCloseOperation(com.jogamp.nativewindow.WindowClosingProtocol.WindowClosingMode)}. + * @see Window#destroy() + * @see #setDefaultCloseOperation(com.jogamp.nativewindow.WindowClosingProtocol.WindowClosingMode) + */ + public void destroy() { + destroyImpl(true); + } + private void destroyImpl(final boolean disposeNewtChild) { + if(DEBUG) { + System.err.println("NewtCanvasJFX.dispose: (has parent "+(null!=parentWindow)+", hasNative "+(null!=nativeWindow)+",\n\t"+newtChild); + } + if( null != newtChild ) { + if(DEBUG) { + System.err.println("NewtCanvasJFX.dispose.1: EDTUtil cur "+newtChild.getScreen().getDisplay().getEDTUtil()); + } + if( null != nativeWindow ) { + configureNewtChild(false); + newtChild.setVisible(false); + newtChild.reparentWindow(null, -1, -1, 0 /* hint */); + } + if( disposeNewtChild ) { + newtChild.destroy(); + newtChild = null; + } + } + if( null != parentWindow ) { + parentWindow.getScene().windowProperty().removeListener(sceneWindowChangeListener); + parentWindow.removeEventHandler(javafx.stage.WindowEvent.WINDOW_CLOSE_REQUEST, windowClosingListener); + parentWindow.removeEventHandler(javafx.stage.WindowEvent.WINDOW_SHOWN, windowShownListener); + parentWindow = null; + } + if( null != screen ) { + screen.getDevice().close(); + screen = null; + } + nativeWindow = null; + } + + private final boolean validateNative(final boolean completeReparent) { + if( null == parentWindow ) { + return false; + } + assert null == nativeWindow; + updatePosSizeCheck(); + if(0 >= clientArea.getWidth() || 0 >= clientArea.getHeight()) { + return false; + } + final long nativeWindowHandle = JFXAccessor.getWindowHandle(parentWindow); + if( 0 == nativeWindowHandle ) { + return false; + } + screen.getDevice().open(); + + /* Native handle for the control, used to associate with GLContext */ + final int visualID = JFXAccessor.getNativeVisualID(screen.getDevice(), nativeWindowHandle); + final boolean visualIDValid = NativeWindowFactory.isNativeVisualIDValidForProcessing(visualID); + if(DEBUG) { + System.err.println("NewtCanvasJFX.validateNative() windowHandle 0x"+Long.toHexString(nativeWindowHandle)+", visualID 0x"+Integer.toHexString(visualID)+", valid "+visualIDValid); + } + if( visualIDValid ) { + /* Get the nativewindow-Graphics Device associated with this control (which is determined by the parent Composite). + * Note: JFX is owner of the native handle, hence no closing operation will be a NOP. */ + final CapabilitiesImmutable caps = new Capabilities(); + final GraphicsConfigurationFactory factory = GraphicsConfigurationFactory.getFactory(screen.getDevice(), caps); + final AbstractGraphicsConfiguration config = factory.chooseGraphicsConfiguration( caps, caps, null, screen, visualID ); + if(DEBUG) { + System.err.println("NewtCanvasJFX.validateNative() factory: "+factory+", windowHandle 0x"+Long.toHexString(nativeWindowHandle)+", visualID 0x"+Integer.toHexString(visualID)+", chosen config: "+config); + // Thread.dumpStack(); + } + if (null == config) { + throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); + } + + nativeWindow = new JFXNativeWindow(config, nativeWindowHandle); + if( completeReparent ) { + reparentWindow( true ); + } + } + + return null != nativeWindow; + } + + /** + * Sets a new NEWT child, provoking reparenting. + * <p> + * A previously detached <code>newChild</code> will be released to top-level status + * and made invisible. + * </p> + * <p> + * Note: When switching NEWT child's, detaching the previous first via <code>setNEWTChild(null)</code> + * produced much cleaner visual results. + * </p> + * <p> + * Note: The NEWT child {@link Display}'s {@link EDTUtil} is being set to an JFX conform implementation + * via {@link Display#setEDTUtil(EDTUtil)}. + * </p> + * @return the previous attached newt child. + */ + public Window setNEWTChild(final Window newChild) { + final Window prevChild = newtChild; + if(DEBUG) { + System.err.println("NewtCanvasJFX.setNEWTChild.0: win "+newtWinHandleToHexString(prevChild)+" -> "+newtWinHandleToHexString(newChild)); + } + // remove old one + if(null != newtChild) { + reparentWindow( false ); + newtChild = null; + } + // add new one, reparent only if ready + newtChild = newChild; + if( null != newtChild && ( null != nativeWindow || validateNative(false /* completeReparent */) ) ) { + reparentWindow( true ); + } + return prevChild; + } + + private void reparentWindow(final boolean add) { + if( null == newtChild ) { + return; // nop + } + if(DEBUG) { + System.err.println("NewtCanvasJFX.reparentWindow.0: add="+add+", win "+newtWinHandleToHexString(newtChild)+", EDTUtil: cur "+newtChild.getScreen().getDisplay().getEDTUtil()); + } + + newtChild.setFocusAction(null); // no AWT focus traversal .. + if(add) { + assert null != nativeWindow && null != parentWindow; + updatePosSizeCheck(); + final int x = clientArea.getX(); + final int y = clientArea.getY(); + final int w = clientArea.getWidth(); + final int h = clientArea.getHeight(); + + if(USE_JFX_EDT) { + // setup JFX EDT and start it + final Display newtDisplay = newtChild.getScreen().getDisplay(); + final EDTUtil oldEDTUtil = newtDisplay.getEDTUtil(); + if( ! ( oldEDTUtil instanceof JFXEDTUtil ) ) { + final EDTUtil newEDTUtil = new JFXEDTUtil(newtDisplay); + if(DEBUG) { + System.err.println("NewtCanvasJFX.reparentWindow.1: replacing EDTUtil "+oldEDTUtil+" -> "+newEDTUtil); + } + newEDTUtil.start(); + newtDisplay.setEDTUtil( newEDTUtil ); + } + } + + newtChild.setSize(w, h); + newtChild.reparentWindow(nativeWindow, x, y, Window.REPARENT_HINT_BECOMES_VISIBLE); + newtChild.setPosition(x, y); + newtChild.setVisible(true); + configureNewtChild(true); + newtChild.sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout to listener + + // force this JFX Canvas to be focus-able, + // since it is completely covered by the newtChild (z-order). + // FIXME ??? super.requestFocus(); + } else { + configureNewtChild(false); + newtChild.setVisible(false); + newtChild.reparentWindow(null, -1, -1, 0 /* hints */); + } + if(DEBUG) { + System.err.println("NewtCanvasJFX.reparentWindow.X: add="+add+", win "+newtWinHandleToHexString(newtChild)+", EDTUtil: cur "+newtChild.getScreen().getDisplay().getEDTUtil()); + } + } + + private void configureNewtChild(final boolean attach) { + newtChildReady = attach; + if( null != newtChild ) { + newtChild.setKeyboardFocusHandler(null); + if(attach) { + newtChildClosingMode = newtChild.setDefaultCloseOperation(WindowClosingMode.DO_NOTHING_ON_CLOSE); + } else { + newtChild.setFocusAction(null); + newtChild.setDefaultCloseOperation(newtChildClosingMode); + } + } + } + + /** @return the current NEWT child */ + public Window getNEWTChild() { + return newtChild; + } + + /** + * {@inheritDoc} + * @return this JFX Canvas {@link NativeWindow} representation, may be null in case it has not been realized + */ + @Override + public NativeWindow getNativeWindow() { return nativeWindow; } + + /** + * {@inheritDoc} + * @return this JFX Canvas {@link NativeSurface} representation, may be null in case it has not been realized + */ + @Override + public NativeSurface getNativeSurface() { return nativeWindow; } + + @Override + public WindowClosingMode getDefaultCloseOperation() { + return closingMode; + } + + @Override + public WindowClosingMode setDefaultCloseOperation(final WindowClosingMode op) { + final WindowClosingMode old = closingMode; + closingMode = op; + return old; + } + + + boolean isParent() { + return null!=newtChild ; + } + + boolean isFullscreen() { + return null != newtChild && newtChild.isFullscreen(); + } + + private final void requestFocusNEWTChild() { + if( newtChildReady ) { + newtChild.setFocusAction(null); + newtChild.requestFocus(); + } + } + + @Override + public void requestFocus() { + NewtCanvasJFX.super.requestFocus(); + requestFocusNEWTChild(); + } + + private class JFXNativeWindow implements NativeWindow { + private final AbstractGraphicsConfiguration config; + private final long nativeWindowHandle; + private final InsetsImmutable insets; // only required to allow proper client position calculation on OSX + + public JFXNativeWindow(final AbstractGraphicsConfiguration config, final long nativeWindowHandle) { + this.config = config; + this.nativeWindowHandle = nativeWindowHandle; + this.insets = new Insets(0, 0, 0, 0); + } + + @Override + public int lockSurface() throws NativeWindowException, RuntimeException { + return NativeSurface.LOCK_SUCCESS; + } + + @Override + public void unlockSurface() { } + + @Override + public boolean isSurfaceLockedByOtherThread() { + return false; + } + + @Override + public Thread getSurfaceLockOwner() { + return null; + } + + @Override + public boolean surfaceSwap() { + return false; + } + + @Override + public void addSurfaceUpdatedListener(final SurfaceUpdatedListener l) { } + + @Override + public void addSurfaceUpdatedListener(final int index, final SurfaceUpdatedListener l) throws IndexOutOfBoundsException { + } + + @Override + public void removeSurfaceUpdatedListener(final SurfaceUpdatedListener l) { } + + @Override + public long getSurfaceHandle() { + return 0; + } + + @Override + public int getWidth() { + return getSurfaceWidth(); // FIXME: Use 'scale' or an actual window-width + } + + @Override + public int getHeight() { + return getSurfaceHeight(); // FIXME: Use 'scale' or an actual window-width + } + + @Override + public final int[] convertToWindowUnits(final int[] pixelUnitsAndResult) { + return pixelUnitsAndResult; // FIXME HiDPI: use 'pixelScale' + } + + @Override + public final int[] convertToPixelUnits(final int[] windowUnitsAndResult) { + return windowUnitsAndResult; // FIXME HiDPI: use 'pixelScale' + } + + @Override + public int getSurfaceWidth() { + return clientArea.getWidth(); + } + + @Override + public int getSurfaceHeight() { + return clientArea.getHeight(); + } + + @Override + public final NativeSurface getNativeSurface() { return this; } + + @Override + public AbstractGraphicsConfiguration getGraphicsConfiguration() { + return config; + } + + @Override + public long getDisplayHandle() { + return config.getScreen().getDevice().getHandle(); + } + + @Override + public int getScreenIndex() { + return config.getScreen().getIndex(); + } + + @Override + public void surfaceUpdated(final Object updater, final NativeSurface ns, final long when) { } + + @Override + public void destroy() { } + + @Override + public NativeWindow getParent() { + return null; + } + + @Override + public long getWindowHandle() { + return nativeWindowHandle; + } + + @Override + public InsetsImmutable getInsets() { + return insets; + } + + @Override + public int getX() { + return NewtCanvasJFX.this.clientArea.getX(); + } + + @Override + public int getY() { + return NewtCanvasJFX.this.clientArea.getY(); + } + + @Override + public Point getLocationOnScreen(final Point point) { + final Point los = NativeWindowFactory.getLocationOnScreen(this); // client window location on screen + if(null!=point) { + return point.translate(los); + } else { + return los; + } + } + + @Override + public boolean hasFocus() { + return isFocused(); + } + }; + + static String newtWinHandleToHexString(final Window w) { + return null != w ? toHexString(w.getWindowHandle()) : "nil"; + } + static String toHexString(final long l) { + return "0x"+Long.toHexString(l); + } +} + diff --git a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java index f15c87beb..569780311 100644 --- a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java +++ b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java @@ -246,6 +246,16 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } @Override + public final int getSupportedStateMask() { + return window.getSupportedStateMask(); + } + + @Override + public final String getSupportedStateMaskString() { + return window.getSupportedStateMaskString(); + } + + @Override public CapabilitiesChooser setCapabilitiesChooser(final CapabilitiesChooser chooser) { return window.setCapabilitiesChooser(chooser); } diff --git a/src/newt/classes/com/jogamp/newt/opengl/util/NEWTDemoListener.java b/src/newt/classes/com/jogamp/newt/opengl/util/NEWTDemoListener.java new file mode 100644 index 000000000..568b5d0bb --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/opengl/util/NEWTDemoListener.java @@ -0,0 +1,530 @@ +/** + * Copyright 2015 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 met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.newt.opengl.util; + +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.List; + +import com.jogamp.common.util.IOUtil; +import com.jogamp.nativewindow.CapabilitiesImmutable; +import com.jogamp.nativewindow.ScalableSurface; +import com.jogamp.newt.Window; +import com.jogamp.newt.Display; +import com.jogamp.newt.Display.PointerIcon; +import com.jogamp.newt.event.KeyEvent; +import com.jogamp.newt.event.KeyListener; +import com.jogamp.newt.event.MouseEvent; +import com.jogamp.newt.event.MouseListener; +import com.jogamp.newt.event.WindowAdapter; +import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.FPSCounter; +import com.jogamp.opengl.GL; +import com.jogamp.opengl.GLAnimatorControl; +import com.jogamp.opengl.GLAutoDrawable; +import com.jogamp.opengl.GLDrawable; +import com.jogamp.opengl.GLRunnable; +import com.jogamp.opengl.util.Gamma; +import com.jogamp.opengl.util.PNGPixelRect; + +import jogamp.newt.driver.PNGIcon; + +/** + * NEWT {@link GLWindow} Demo functionality + * <ul> + * <li>SPACE: Toggle animator {@link GLAnimatorControl#pause() pause}/{@link GLAnimatorControl#resume() resume}</li> + * <li>A: Toggle window {@link Window#setAlwaysOnTop(boolean) always on top}</li> + * <li>B: Toggle window {@link Window#setAlwaysOnBottom(boolean) always on bottom}</li> + * <li>C: Toggle different {@link Window#setPointerIcon(PointerIcon) pointer icons}</li> + * <li>D: Toggle window {@link Window#setUndecorated(boolean) decoration on/off}</li> + * <li>F: Toggle window {@link Window#setFullscreen(boolean) fullscreen on/off}</li> + * <li>Three-Finger Double-Tap: Toggle window {@link Window#setFullscreen(boolean) fullscreen on/off}</li> + * <li>G: Increase {@link Gamma#setDisplayGamma(GLDrawable, float, float, float) gamma} by 0.1, +SHIFT decrease gamma by 0.1</li> + * <li>I: Toggle {@link Window#setPointerVisible(boolean) pointer visbility}</li> + * <li>J: Toggle {@link Window#confinePointer(boolean) pointer jail (confine to window)}</li> + * <li>M: Toggle {@link Window#setMaximized(boolean, boolean) window maximized}: Y, +CTRL off, +SHIFT toggle X+Y, +ALT X</li> + * <li>P: Set window {@link Window#setPosition(int, int) position to 100/100}</li> + * <li>Q: Quit</li> + * <li>R: Toggle window {@link Window#setResizable(boolean) resizable}</li> + * <li>S: Toggle window {@link Window#setSticky(boolean) sticky}</li> + * <li>V: Toggle window {@link Window#setVisible(boolean) visibility} for 5s</li> + * <li>V: +CTRL: Rotate {@link GL#setSwapInterval(int) swap interval} -1, 0, 1</li> + * <li>W: {@link Window#warpPointer(int, int) Warp pointer} to center of window</li> + * <li>X: Toggle {@link ScalableSurface#setSurfaceScale(float[]) [{@link ScalableSurface#IDENTITY_PIXELSCALE}, {@link ScalableSurface#AUTOMAX_PIXELSCALE}]</li> + * </ul> + */ +public class NEWTDemoListener extends WindowAdapter implements KeyListener, MouseListener { + protected final GLWindow glWindow; + final PointerIcon[] pointerIcons; + int pointerIconIdx = 0; + float gamma = 1f; + float brightness = 0f; + float contrast = 1f; + boolean confinedFixedCenter = false; + + public NEWTDemoListener(final GLWindow glWin, final PointerIcon[] pointerIcons) { + this.glWindow = glWin; + if( null != pointerIcons ) { + this.pointerIcons = pointerIcons; + } else { + this.pointerIcons = createPointerIcons(glWindow.getScreen().getDisplay()); + } + } + public NEWTDemoListener(final GLWindow glWin) { + this(glWin, null); + } + + protected void printlnState(final String prelude) { + System.err.println(prelude+": "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getSurfaceWidth()+"x"+glWindow.getSurfaceHeight()+", f "+glWindow.isFullscreen()+", a "+glWindow.isAlwaysOnTop()+", "+glWindow.getInsets()+", state "+glWindow.getStateMaskString()); + } + protected void printlnState(final String prelude, final String post) { + System.err.println(prelude+": "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getSurfaceWidth()+"x"+glWindow.getSurfaceHeight()+", f "+glWindow.isFullscreen()+", a "+glWindow.isAlwaysOnTop()+", "+glWindow.getInsets()+", state "+glWindow.getStateMaskString()+", "+post); + } + + @Override + public void keyPressed(final KeyEvent e) { + if( e.isAutoRepeat() || e.isConsumed() ) { + return; + } + final int keySymbol = e.getKeySymbol(); + switch(keySymbol) { + case KeyEvent.VK_SPACE: + e.setConsumed(true); + glWindow.invokeOnCurrentThread(new Runnable() { + public void run() { + if(glWindow.getAnimator().isPaused()) { + glWindow.getAnimator().resume(); + } else { + glWindow.getAnimator().pause(); + } + } } ); + break; + case KeyEvent.VK_A: + e.setConsumed(true); + glWindow.invokeOnNewThread(null, false, new Runnable() { + public void run() { + printlnState("[set alwaysontop pre]"); + glWindow.setAlwaysOnTop(!glWindow.isAlwaysOnTop()); + printlnState("[set alwaysontop post]"); + } } ); + break; + case KeyEvent.VK_B: + e.setConsumed(true); + glWindow.invokeOnNewThread(null, false, new Runnable() { + public void run() { + printlnState("[set alwaysonbottom pre]"); + glWindow.setAlwaysOnBottom(!glWindow.isAlwaysOnBottom()); + printlnState("[set alwaysonbottom post]"); + } } ); + break; + case KeyEvent.VK_C: + e.setConsumed(true); + glWindow.invokeOnNewThread(null, false, new Runnable() { + public void run() { + if( null != pointerIcons ) { + printlnState("[set pointer-icon pre]"); + final PointerIcon currentPI = glWindow.getPointerIcon(); + final PointerIcon newPI; + if( pointerIconIdx >= pointerIcons.length ) { + newPI=null; + pointerIconIdx=0; + } else { + newPI=pointerIcons[pointerIconIdx++]; + } + glWindow.setPointerIcon( newPI ); + printlnState("[set pointer-icon post]", currentPI+" -> "+glWindow.getPointerIcon()); + } + } } ); + break; + case KeyEvent.VK_D: + e.setConsumed(true); + glWindow.invokeOnNewThread(null, false, new Runnable() { + public void run() { + printlnState("[set undecorated pre]"); + glWindow.setUndecorated(!glWindow.isUndecorated()); + printlnState("[set undecorated post]"); + } } ); + break; + case KeyEvent.VK_F: + e.setConsumed(true); + quitAdapterOff(); + glWindow.invokeOnNewThread(null, false, new Runnable() { + public void run() { + printlnState("[set fullscreen pre]"); + if( glWindow.isFullscreen() ) { + glWindow.setFullscreen( false ); + } else { + if( e.isAltDown() ) { + glWindow.setFullscreen( null ); + } else { + glWindow.setFullscreen( true ); + } + } + printlnState("[set fullscreen post]"); + quitAdapterOn(); + } } ); + break; + case KeyEvent.VK_G: + e.setConsumed(true); + glWindow.invokeOnNewThread(null, false, new Runnable() { + public void run() { + final float newGamma = gamma + ( e.isShiftDown() ? -0.1f : 0.1f ); + System.err.println("[set gamma]: "+gamma+" -> "+newGamma); + if( Gamma.setDisplayGamma(glWindow, newGamma, brightness, contrast) ) { + gamma = newGamma; + } + } } ); + break; + case KeyEvent.VK_I: + e.setConsumed(true); + glWindow.invokeOnNewThread(null, false, new Runnable() { + public void run() { + printlnState("[set pointer-visible pre]"); + glWindow.setPointerVisible(!glWindow.isPointerVisible()); + printlnState("[set pointer-visible post]"); + } } ); + break; + case KeyEvent.VK_J: + e.setConsumed(true); + glWindow.invokeOnNewThread(null, false, new Runnable() { + public void run() { + printlnState("[set pointer-confined pre]", "warp-center: "+e.isShiftDown()); + final boolean confine = !glWindow.isPointerConfined(); + glWindow.confinePointer(confine); + printlnState("[set pointer-confined post]", "warp-center: "+e.isShiftDown()); + if( e.isShiftDown() ) { + setConfinedFixedCenter(confine); + } else if( !confine ) { + setConfinedFixedCenter(false); + } + } } ); + break; + case KeyEvent.VK_M: + e.setConsumed(true); + glWindow.invokeOnNewThread(null, false, new Runnable() { + public void run() { + // none: max-v + // alt: max-h + // shift: max-hv + // ctrl: max-off + final boolean horz, vert; + if( e.isControlDown() ) { + horz = false; + vert = false; + } else if( e.isShiftDown() ) { + final boolean bothMax = glWindow.isMaximizedHorz() && glWindow.isMaximizedVert(); + horz = !bothMax; + vert = !bothMax; + } else if( !e.isAltDown() ) { + horz = glWindow.isMaximizedHorz(); + vert = !glWindow.isMaximizedVert(); + } else if( e.isAltDown() ) { + horz = !glWindow.isMaximizedHorz(); + vert = glWindow.isMaximizedVert(); + } else { + vert = glWindow.isMaximizedVert(); + horz = glWindow.isMaximizedHorz(); + } + printlnState("[set maximize pre]", "max[vert "+vert+", horz "+horz+"]"); + glWindow.setMaximized(horz, vert); + printlnState("[set maximize post]", "max[vert "+vert+", horz "+horz+"]"); + } } ); + break; + case KeyEvent.VK_P: + e.setConsumed(true); + glWindow.invokeOnNewThread(null, false, new Runnable() { + public void run() { + printlnState("[set position pre]"); + glWindow.setPosition(100, 100); + printlnState("[set position post]"); + } } ); + break; + case KeyEvent.VK_Q: + if( quitAdapterEnabled && 0 == e.getModifiers() ) { + System.err.println("QUIT Key "+Thread.currentThread()); + quitAdapterShouldQuit = true; + } + break; + case KeyEvent.VK_R: + e.setConsumed(true); + glWindow.invokeOnNewThread(null, false, new Runnable() { + public void run() { + printlnState("[set resizable pre]"); + glWindow.setResizable(!glWindow.isResizable()); + printlnState("[set resizable post]"); + } } ); + break; + case KeyEvent.VK_S: + e.setConsumed(true); + glWindow.invokeOnNewThread(null, false, new Runnable() { + public void run() { + printlnState("[set sticky pre]"); + glWindow.setSticky(!glWindow.isSticky()); + printlnState("[set sticky post]"); + } } ); + break; + case KeyEvent.VK_V: + e.setConsumed(true); + if( e.isControlDown() ) { + glWindow.invoke(false, new GLRunnable() { + @Override + public boolean run(final GLAutoDrawable drawable) { + final GL gl = drawable.getGL(); + final int _i = gl.getSwapInterval(); + final int i; + switch(_i) { + case 0: i = -1; break; + case -1: i = 1; break; + case 1: i = 0; break; + default: i = 1; break; + } + gl.setSwapInterval(i); + + final GLAnimatorControl a = drawable.getAnimator(); + if( null != a ) { + a.resetFPSCounter(); + } + if(drawable instanceof FPSCounter) { + ((FPSCounter)drawable).resetFPSCounter(); + } + System.err.println("Swap Interval: "+_i+" -> "+i+" -> "+gl.getSwapInterval()); + return true; + } + }); + } else { + glWindow.invokeOnNewThread(null, false, new Runnable() { + public void run() { + final boolean wasVisible = glWindow.isVisible(); + { + printlnState("[set visible pre]"); + glWindow.setVisible(!wasVisible); + printlnState("[set visible post]"); + } + if( wasVisible && !e.isShiftDown() ) { + try { + java.lang.Thread.sleep(5000); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + printlnState("[reset visible pre]"); + glWindow.setVisible(true); + printlnState("[reset visible post]"); + } + } } ); + } + break; + case KeyEvent.VK_W: + e.setConsumed(true); + glWindow.invokeOnNewThread(null, false, new Runnable() { + public void run() { + printlnState("[set pointer-pos pre]"); + glWindow.warpPointer(glWindow.getSurfaceWidth()/2, glWindow.getSurfaceHeight()/2); + printlnState("[set pointer-pos post]"); + } } ); + break; + case KeyEvent.VK_X: + e.setConsumed(true); + final float[] hadSurfacePixelScale = glWindow.getCurrentSurfaceScale(new float[2]); + final float[] reqSurfacePixelScale; + if( hadSurfacePixelScale[0] == ScalableSurface.IDENTITY_PIXELSCALE ) { + reqSurfacePixelScale = new float[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; + } else { + reqSurfacePixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + } + System.err.println("[set PixelScale pre]: had "+hadSurfacePixelScale[0]+"x"+hadSurfacePixelScale[1]+" -> req "+reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]); + glWindow.setSurfaceScale(reqSurfacePixelScale); + final float[] valReqSurfacePixelScale = glWindow.getRequestedSurfaceScale(new float[2]); + final float[] hasSurfacePixelScale1 = glWindow.getCurrentSurfaceScale(new float[2]); + System.err.println("[set PixelScale post]: "+hadSurfacePixelScale[0]+"x"+hadSurfacePixelScale[1]+" (had) -> "+ + reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]+" (req) -> "+ + valReqSurfacePixelScale[0]+"x"+valReqSurfacePixelScale[1]+" (val) -> "+ + hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)"); + setTitle(); + } + } + @Override + public void keyReleased(final KeyEvent e) { } + + public void setConfinedFixedCenter(final boolean v) { + confinedFixedCenter = v; + } + @Override + public void mouseMoved(final MouseEvent e) { + if( e.isConfined() ) { + mouseCenterWarp(e); + } + } + @Override + public void mouseDragged(final MouseEvent e) { + if( e.isConfined() ) { + mouseCenterWarp(e); + } + } + @Override + public void mouseClicked(final MouseEvent e) { + if(e.getClickCount() == 2 && e.getPointerCount() == 3) { + glWindow.setFullscreen(!glWindow.isFullscreen()); + System.err.println("setFullscreen: "+glWindow.isFullscreen()); + } + } + private void mouseCenterWarp(final MouseEvent e) { + if(e.isConfined() && confinedFixedCenter ) { + final int x=glWindow.getSurfaceWidth()/2; + final int y=glWindow.getSurfaceHeight()/2; + glWindow.warpPointer(x, y); + } + } + @Override + public void mouseEntered(final MouseEvent e) {} + @Override + public void mouseExited(final MouseEvent e) {} + @Override + public void mousePressed(final MouseEvent e) {} + @Override + public void mouseReleased(final MouseEvent e) {} + @Override + public void mouseWheelMoved(final MouseEvent e) {} + + ///////////////////////////////////////////////////////////// + + private boolean quitAdapterShouldQuit = false; + private boolean quitAdapterEnabled = false; + private boolean quitAdapterEnabled2 = true; + + protected void quitAdapterOff() { + quitAdapterEnabled2 = false; + } + protected void quitAdapterOn() { + clearQuitAdapter(); + quitAdapterEnabled2 = true; + } + public void quitAdapterEnable(final boolean v) { quitAdapterEnabled = v; } + public void clearQuitAdapter() { quitAdapterShouldQuit = false; } + public boolean shouldQuit() { return quitAdapterShouldQuit; } + public void doQuit() { quitAdapterShouldQuit=true; } + + public void windowDestroyNotify(final WindowEvent e) { + if( quitAdapterEnabled && quitAdapterEnabled2 ) { + System.err.println("QUIT Window "+Thread.currentThread()); + quitAdapterShouldQuit = true; + } + } + + ///////////////////////////////////////////////////////////// + + public void setTitle() { + setTitle(glWindow); + } + public static void setTitle(final GLWindow win) { + final CapabilitiesImmutable chosenCaps = win.getChosenCapabilities(); + final CapabilitiesImmutable reqCaps = win.getRequestedCapabilities(); + final CapabilitiesImmutable caps = null != chosenCaps ? chosenCaps : reqCaps; + final String capsA = caps.isBackgroundOpaque() ? "opaque" : "transl"; + final float[] sDPI = win.getPixelsPerMM(new float[2]); + sDPI[0] *= 25.4f; + sDPI[1] *= 25.4f; + final float[] minSurfacePixelScale = win.getMinimumSurfaceScale(new float[2]); + final float[] maxSurfacePixelScale = win.getMaximumSurfaceScale(new float[2]); + final float[] reqSurfacePixelScale = win.getRequestedSurfaceScale(new float[2]); + final float[] hasSurfacePixelScale = win.getCurrentSurfaceScale(new float[2]); + win.setTitle("GLWindow["+capsA+"], win: "+win.getBounds()+", pix: "+win.getSurfaceWidth()+"x"+win.getSurfaceHeight()+", sDPI "+sDPI[0]+" x "+sDPI[1]+ + ", scale[min "+minSurfacePixelScale[0]+"x"+minSurfacePixelScale[1]+", max "+ + maxSurfacePixelScale[0]+"x"+maxSurfacePixelScale[1]+", req "+ + reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]+" -> has "+ + hasSurfacePixelScale[0]+"x"+hasSurfacePixelScale[1]+"]"); + } + + public static PointerIcon[] createPointerIcons(final Display disp) { + final List<PointerIcon> pointerIcons = new ArrayList<PointerIcon>(); + { + disp.createNative(); + { + PointerIcon _pointerIcon = null; + final IOUtil.ClassResources res = new IOUtil.ClassResources(new String[] { "newt/data/cross-grey-alpha-16x16.png" }, disp.getClass().getClassLoader(), null); + try { + _pointerIcon = disp.createPointerIcon(res, 8, 8); + pointerIcons.add(_pointerIcon); + System.err.printf("Create PointerIcon #%02d: %s%n", pointerIcons.size(), _pointerIcon.toString()); + } catch (final Exception e) { + System.err.println(e.getMessage()); + } + } + { + PointerIcon _pointerIcon = null; + final IOUtil.ClassResources res = new IOUtil.ClassResources(new String[] { "newt/data/pointer-grey-alpha-16x24.png" }, disp.getClass().getClassLoader(), null); + try { + _pointerIcon = disp.createPointerIcon(res, 0, 0); + pointerIcons.add(_pointerIcon); + System.err.printf("Create PointerIcon #%02d: %s%n", pointerIcons.size(), _pointerIcon.toString()); + } catch (final Exception e) { + System.err.println(e.getMessage()); + } + } + { + PointerIcon _pointerIcon = null; + final IOUtil.ClassResources res = new IOUtil.ClassResources(new String[] { "arrow-red-alpha-64x64.png" }, disp.getClass().getClassLoader(), null); + try { + _pointerIcon = disp.createPointerIcon(res, 0, 0); + pointerIcons.add(_pointerIcon); + System.err.printf("Create PointerIcon #%02d: %s%n", pointerIcons.size(), _pointerIcon.toString()); + } catch (final Exception e) { + System.err.println(e.getMessage()); + } + } + { + PointerIcon _pointerIcon = null; + final IOUtil.ClassResources res = new IOUtil.ClassResources(new String[] { "arrow-blue-alpha-64x64.png" }, disp.getClass().getClassLoader(), null); + try { + _pointerIcon = disp.createPointerIcon(res, 0, 0); + pointerIcons.add(_pointerIcon); + System.err.printf("Create PointerIcon #%02d: %s%n", pointerIcons.size(), _pointerIcon.toString()); + } catch (final Exception e) { + System.err.println(e.getMessage()); + } + } + if( PNGIcon.isAvailable() ) { + PointerIcon _pointerIcon = null; + final IOUtil.ClassResources res = new IOUtil.ClassResources(new String[] { "jogamp-pointer-64x64.png" }, disp.getClass().getClassLoader(), null); + try { + final URLConnection urlConn = res.resolve(0); + if( null != urlConn ) { + final PNGPixelRect image = PNGPixelRect.read(urlConn.getInputStream(), null, false /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + System.err.printf("Create PointerIcon #%02d: %s%n", pointerIcons.size()+1, image.toString()); + _pointerIcon = disp.createPointerIcon(image, 32, 0); + pointerIcons.add(_pointerIcon); + System.err.printf("Create PointerIcon #%02d: %s%n", pointerIcons.size(), _pointerIcon.toString()); + } + } catch (final Exception e) { + System.err.println(e.getMessage()); + } + } + } + return pointerIcons.toArray(new PointerIcon[pointerIcons.size()]); + } +}
\ No newline at end of file diff --git a/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java b/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java index 186ffb162..bd5c43d6c 100644 --- a/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java +++ b/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java @@ -38,6 +38,7 @@ import com.jogamp.nativewindow.NativeSurface; import com.jogamp.nativewindow.NativeWindow; import com.jogamp.nativewindow.NativeWindowException; import com.jogamp.nativewindow.NativeWindowFactory; +import com.jogamp.nativewindow.NativeWindowHolder; import com.jogamp.nativewindow.SurfaceUpdatedListener; import com.jogamp.nativewindow.WindowClosingProtocol; import com.jogamp.nativewindow.util.Insets; @@ -52,6 +53,7 @@ import jogamp.newt.Debug; import jogamp.newt.swt.SWTEDTUtil; import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; @@ -70,13 +72,14 @@ import com.jogamp.newt.util.EDTUtil; * Implementation allows use of custom {@link GLCapabilities}. * </p> */ -public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { +public class NewtCanvasSWT extends Canvas implements NativeWindowHolder, WindowClosingProtocol { private static final boolean DEBUG = Debug.debug("Window"); private final AbstractGraphicsScreen screen; - private WindowClosingMode newtChildCloseOp = WindowClosingMode.DISPOSE_ON_CLOSE; - private volatile Rectangle clientArea; + private WindowClosingMode newtChildClosingMode = WindowClosingMode.DISPOSE_ON_CLOSE; + private final WindowClosingMode closingMode = WindowClosingMode.DISPOSE_ON_CLOSE; + private volatile Rectangle clientAreaPixels, clientAreaWindow; private volatile SWTNativeWindow nativeWindow; private volatile Window newtChild = null; @@ -126,7 +129,8 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { SWTAccessor.setRealized(this, true); - clientArea = getClientArea(); + clientAreaPixels = SWTAccessor.getClientAreaInPixels(this); + clientAreaWindow = getClientArea(); final AbstractGraphicsDevice device = SWTAccessor.getDevice(this); screen = SWTAccessor.getScreen(device, -1 /* default */); @@ -136,6 +140,10 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { setNEWTChild(child); } + // Bug 1362 fix or workaround: Seems SWT/GTK3 at least performs lazy initialization + // Minimal action required: setBackground of the parent canvas before reparenting! + setBackground(new Color(parent.getDisplay(), 255, 255, 255)); + final Listener listener = new Listener () { @Override public void handleEvent (final Event event) { @@ -147,14 +155,14 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { if( null != nativeWindow || validateNative() ) { if( newtChildReady ) { if( postSetSize ) { - newtChild.setSize(clientArea.width, clientArea.height); + newtChild.setSize(clientAreaPixels.width, clientAreaPixels.height); postSetSize = false; } if( postSetPos ) { - newtChild.setPosition(clientArea.x, clientArea.y); + newtChild.setPosition(clientAreaPixels.x, clientAreaPixels.y); postSetPos = false; } - newtChild.windowRepaint(0, 0, clientArea.width, clientArea.height); + newtChild.windowRepaint(0, 0, clientAreaPixels.width, clientAreaPixels.height); } } break; @@ -168,7 +176,7 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { if( DEBUG ) { System.err.println("NewtCanvasSWT.Event.RESIZE, "+event); } - updateSizeCheck(); + updatePosSizeCheck(false); break; case SWT.Dispose: if( DEBUG ) { @@ -197,14 +205,14 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { } if( SWTAccessor.isOSX ) { // Force newtChild to update its size and position (OSX only) - updatePosSizeCheck(x, y, width, height, true /* updatePos */); + updatePosSizeCheck(true /* updatePos */); } } /** assumes nativeWindow == null ! */ protected final boolean validateNative() { - updateSizeCheck(); - final Rectangle nClientArea = clientArea; + updatePosSizeCheck(false); + final Rectangle nClientArea = clientAreaPixels; if(0 >= nClientArea.width || 0 >= nClientArea.height) { return false; } @@ -242,46 +250,34 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { return null != nativeWindow; } - protected final void updateSizeCheck() { - final Rectangle nClientArea = getClientArea(); - if( null != nClientArea ) { - updatePosSizeCheck(nClientArea.x, nClientArea.y, nClientArea.width, nClientArea.height, false /* updatePos */); - } - } - protected final void updatePosSizeCheck() { - final Rectangle nClientArea = getClientArea(); - if( null != nClientArea ) { - updatePosSizeCheck(nClientArea.x, nClientArea.y, nClientArea.width, nClientArea.height, true /* updatePos */); - } - } - protected final void updatePosSizeCheck(final int newX, final int newY, final int newWidth, final int newHeight, final boolean updatePos) { + protected final void updatePosSizeCheck(final boolean updatePos) { final boolean sizeChanged, posChanged; - final Rectangle nClientArea; + Rectangle nClientAreaPixels = SWTAccessor.getClientAreaInPixels(this); { - final Rectangle oClientArea = clientArea; - sizeChanged = newWidth != oClientArea.width || newHeight != oClientArea.height; - posChanged = newX != oClientArea.x || newY != oClientArea.y; + final Rectangle oClientAreaPixels = clientAreaPixels; + sizeChanged = nClientAreaPixels.width != oClientAreaPixels.width || nClientAreaPixels.height != oClientAreaPixels.height; + posChanged = nClientAreaPixels.x != oClientAreaPixels.x || nClientAreaPixels.y != oClientAreaPixels.y; if( sizeChanged || posChanged ) { - nClientArea = new Rectangle(updatePos ? newX : oClientArea.x, updatePos ? newY : oClientArea.y, newWidth, newHeight); - clientArea = nClientArea; + clientAreaPixels = nClientAreaPixels; + clientAreaWindow = getClientArea(); } else { - nClientArea = clientArea; + nClientAreaPixels = clientAreaPixels; } } if(DEBUG) { final long nsh = newtChildReady ? newtChild.getSurfaceHandle() : 0; - System.err.println("NewtCanvasSWT.updatePosSizeCheck: sizeChanged "+sizeChanged+", posChanged "+posChanged+", updatePos "+updatePos+", ("+Thread.currentThread().getName()+"): newtChildReady "+newtChildReady+", "+nClientArea.x+"/"+nClientArea.y+" "+nClientArea.width+"x"+nClientArea.height+" - surfaceHandle 0x"+Long.toHexString(nsh)); + System.err.println("NewtCanvasSWT.updatePosSizeCheck: sizeChanged "+sizeChanged+", posChanged "+posChanged+", updatePos "+updatePos+", ("+Thread.currentThread().getName()+"): newtChildReady "+newtChildReady+", "+nClientAreaPixels.x+"/"+nClientAreaPixels.y+" "+nClientAreaPixels.width+"x"+nClientAreaPixels.height+" - surfaceHandle 0x"+Long.toHexString(nsh)); } if( sizeChanged ) { if( newtChildReady ) { - newtChild.setSize(nClientArea.width, nClientArea.height); + newtChild.setSize(nClientAreaPixels.width, nClientAreaPixels.height); } else { postSetSize = true; } } if( updatePos && posChanged ) { if( newtChildReady ) { - newtChild.setPosition(nClientArea.x, nClientArea.y); + newtChild.setPosition(nClientAreaPixels.x, nClientAreaPixels.y); } else { postSetPos = true; } @@ -330,17 +326,28 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { return new Point(parentLoc[0].x, parentLoc[0].y); } - /** @return this SWT Canvas NativeWindow representation, may be null in case it has not been realized. */ + /** + * {@inheritDoc} + * @return this SWT Canvas {@link NativeWindow} representation, may be null in case it has not been realized + */ + @Override public NativeWindow getNativeWindow() { return nativeWindow; } + /** + * {@inheritDoc} + * @return this SWT Canvas {@link NativeSurface} representation, may be null in case it has not been realized + */ + @Override + public NativeSurface getNativeSurface() { return nativeWindow; } + @Override public WindowClosingMode getDefaultCloseOperation() { - return newtChildCloseOp; // TODO: implement ?! + return closingMode; } @Override public WindowClosingMode setDefaultCloseOperation(final WindowClosingMode op) { - return newtChildCloseOp = op; // TODO: implement ?! + return closingMode; // TODO: implement! } @@ -401,10 +408,10 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { if( null != newtChild ) { newtChild.setKeyboardFocusHandler(null); if(attach) { - newtChildCloseOp = newtChild.setDefaultCloseOperation(WindowClosingMode.DO_NOTHING_ON_CLOSE); + newtChildClosingMode = newtChild.setDefaultCloseOperation(WindowClosingMode.DO_NOTHING_ON_CLOSE); } else { newtChild.setFocusAction(null); - newtChild.setDefaultCloseOperation(newtChildCloseOp); + newtChild.setDefaultCloseOperation(newtChildClosingMode); } } } @@ -419,9 +426,9 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { newtChild.setFocusAction(null); // no AWT focus traversal .. if(add) { - updateSizeCheck(); - final int w = clientArea.width; - final int h = clientArea.height; + updatePosSizeCheck(false); + final int w = clientAreaPixels.width; + final int h = clientAreaPixels.height; // set SWT EDT and start it { @@ -539,12 +546,12 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { @Override public int getSurfaceWidth() { - return clientArea.width; + return clientAreaPixels.width; } @Override public int getSurfaceHeight() { - return clientArea.height; + return clientAreaPixels.height; } @Override diff --git a/src/newt/classes/com/jogamp/newt/util/MainThread.java b/src/newt/classes/com/jogamp/newt/util/MainThread.java index 8bf4bc20f..05df63794 100644 --- a/src/newt/classes/com/jogamp/newt/util/MainThread.java +++ b/src/newt/classes/com/jogamp/newt/util/MainThread.java @@ -45,6 +45,7 @@ import java.util.List; import com.jogamp.nativewindow.NativeWindowFactory; import com.jogamp.common.os.Platform; +import com.jogamp.common.util.InterruptSource; import com.jogamp.common.util.PropertyAccess; import com.jogamp.common.util.ReflectionUtil; @@ -171,15 +172,14 @@ public class MainThread { return res; } - static class UserApp extends Thread { + static class UserApp extends InterruptSource.Thread { private final String mainClassNameShort; private final String mainClassName; private final String[] mainClassArgs; private final Method mainClassMain; - private List<Thread> nonDaemonThreadsAtStart; + private List<java.lang.Thread> nonDaemonThreadsAtStart; public UserApp(final String mainClassName, final String[] mainClassArgs) throws SecurityException, NoSuchMethodException, ClassNotFoundException { - super(); this.mainClassName=mainClassName; this.mainClassArgs=mainClassArgs; @@ -200,10 +200,10 @@ public class MainThread { @Override public void run() { nonDaemonThreadsAtStart = getNonDaemonThreads(); - if(DEBUG) System.err.println("MainAction.run(): "+Thread.currentThread().getName()+" start, nonDaemonThreadsAtStart "+nonDaemonThreadsAtStart); + if(DEBUG) System.err.println("MainAction.run(): "+java.lang.Thread.currentThread().getName()+" start, nonDaemonThreadsAtStart "+nonDaemonThreadsAtStart); // start user app .. try { - if(DEBUG) System.err.println("MainAction.run(): "+Thread.currentThread().getName()+" invoke "+mainClassName); + if(DEBUG) System.err.println("MainAction.run(): "+java.lang.Thread.currentThread().getName()+" invoke "+mainClassName); mainClassMain.invoke(null, new Object[] { mainClassArgs } ); } catch (final InvocationTargetException ite) { ite.getTargetException().printStackTrace(); @@ -219,30 +219,30 @@ public class MainThread { while( 0 < ( ndtr = getNonDaemonThreadCount(nonDaemonThreadsAtStart) ) ) { if(DEBUG) System.err.println("MainAction.run(): post user app, non daemon threads alive: "+ndtr); try { - Thread.sleep(1000); + java.lang.Thread.sleep(1000); } catch (final InterruptedException e) { e.printStackTrace(); } } - if(DEBUG) System.err.println("MainAction.run(): "+Thread.currentThread().getName()+" user app fin: "+ndtr); + if(DEBUG) System.err.println("MainAction.run(): "+java.lang.Thread.currentThread().getName()+" user app fin: "+ndtr); } if ( useMainThread ) { if(isMacOSX) { try { if(DEBUG) { - System.err.println("MainAction.main(): "+Thread.currentThread()+" MainAction fin - stopNSApp.0"); + System.err.println("MainAction.main(): "+java.lang.Thread.currentThread()+" MainAction fin - stopNSApp.0"); } ReflectionUtil.callStaticMethod(MACOSXDisplayClassName, "stopNSApplication", null, null, MainThread.class.getClassLoader()); if(DEBUG) { - System.err.println("MainAction.main(): "+Thread.currentThread()+" MainAction fin - stopNSApp.X"); + System.err.println("MainAction.main(): "+java.lang.Thread.currentThread()+" MainAction fin - stopNSApp.X"); } } catch (final Exception e) { e.printStackTrace(); } } else { - if(DEBUG) System.err.println("MainAction.run(): "+Thread.currentThread().getName()+" MainAction fin - System.exit(0)"); + if(DEBUG) System.err.println("MainAction.run(): "+java.lang.Thread.currentThread().getName()+" MainAction fin - System.exit(0)"); System.exit(0); } } diff --git a/src/newt/classes/com/jogamp/newt/util/applet/JOGLNewtAppletBase.java b/src/newt/classes/com/jogamp/newt/util/applet/JOGLNewtAppletBase.java index cc159e6ed..c30576ff4 100644 --- a/src/newt/classes/com/jogamp/newt/util/applet/JOGLNewtAppletBase.java +++ b/src/newt/classes/com/jogamp/newt/util/applet/JOGLNewtAppletBase.java @@ -42,10 +42,8 @@ import com.jogamp.opengl.GLPipelineFactory; import jogamp.newt.Debug; -import com.jogamp.common.util.IOUtil; -import com.jogamp.newt.Display; +import com.jogamp.common.util.InterruptSource; import com.jogamp.newt.Window; -import com.jogamp.newt.Display.PointerIcon; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.KeyListener; import com.jogamp.newt.event.MouseListener; @@ -53,13 +51,18 @@ import com.jogamp.newt.event.WindowAdapter; import com.jogamp.newt.event.WindowEvent; import com.jogamp.newt.event.WindowListener; import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.newt.opengl.util.NEWTDemoListener; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.AnimatorBase; -/** Shows how to deploy an applet using JOGL. This demo must be - referenced from a web page via an <applet> tag. */ - +/** + * Shows how to deploy an applet using JOGL. + * This demo must be referenced from a web page via an <applet> tag. + * <p> + * The demo code uses {@link NEWTDemoListener} functionality. + * </p> + */ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { public static final boolean DEBUG = Debug.debug("Applet"); @@ -69,7 +72,6 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { boolean glClosable; boolean glDebug; boolean glTrace; - PointerIcon pointerIconTest = null; GLEventListener glEventListener = null; GLWindow glWindow = null; @@ -193,8 +195,13 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { glWindow.addKeyListener((KeyListener)glEventListener); } + glWindow.addWindowListener(reparentHomeListener); + if(!noDefaultKeyListener) { glWindow.addKeyListener(this); + final NEWTDemoListener newtDemoListener = new NEWTDemoListener(glWindow); + glWindow.addKeyListener(newtDemoListener); + glWindow.addMouseListener(newtDemoListener); } glWindow.setUpdateFPSFrames(FPSCounter.DEFAULT_FRAMES_PER_INTERVAL, System.err); @@ -220,7 +227,7 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { null == glWindow.getParent() && null != parentWin && 0 != parentWin.getWindowHandle() ) { // we may be called directly by the native EDT - new Thread(new Runnable() { + new InterruptSource.Thread(null, new Runnable() { @Override public void run() { if( glWindow.isNativeValid() && null != parentWin && 0 != parentWin.getWindowHandle() ) { @@ -235,24 +242,13 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { if(isValid) { glWindow.setVisible(true); glWindow.sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); - if( null == pointerIconTest ) { - final IOUtil.ClassResources res = new IOUtil.ClassResources(glWindow.getClass(), new String[] { "newt/data/cross-grey-alpha-16x16.png" } ); - final Display disp = glWindow.getScreen().getDisplay(); - try { - pointerIconTest = disp.createPointerIcon(res, 8, 8); - } catch (final Exception e) { - e.printStackTrace(); - } - } glAnimator.start(); parentWin = glWindow.getParent(); - glWindow.addWindowListener(reparentHomeListener); } } public void stop() { if(null!=glAnimator) { - glWindow.removeWindowListener(reparentHomeListener); glAnimator.stop(); glWindow.setVisible(false); } @@ -291,10 +287,7 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { _gl = _gl.getContext().setGL( GLPipelineFactory.create("com.jogamp.opengl.Trace", null, _gl, new Object[] { System.err } ) ); } catch (final Exception e) {e.printStackTrace();} } - - if(glSwapInterval>=0) { - _gl.setSwapInterval(glSwapInterval); - } + _gl.setSwapInterval(glSwapInterval); } @Override public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { @@ -315,27 +308,14 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { if( !e.isPrintableKey() || e.isAutoRepeat() ) { return; } - if(e.getKeyChar()=='d') { - new Thread() { - public void run() { - glWindow.setUndecorated(!glWindow.isUndecorated()); - } }.start(); - } if(e.getKeyChar()=='f') { - new Thread() { - public void run() { - glWindow.setFullscreen(!glWindow.isFullscreen()); - } }.start(); - } else if(e.getKeyChar()=='a') { - new Thread() { - public void run() { - glWindow.setAlwaysOnTop(!glWindow.isAlwaysOnTop()); - } }.start(); - } else if(e.getKeyChar()=='r' && null!=parentWin) { - new Thread() { + + if(e.getKeyChar()=='r' && 0==e.getModifiers() && null!=parentWin) { + e.setConsumed(true); + glWindow.invokeOnNewThread(null, false, new Runnable() { public void run() { if(null == glWindow.getParent()) { - glWindow.reparentWindow(parentWin, -1, -1, 0 /* hints */); - } else { + glWindow.reparentWindow(parentWin, -1, -1, 0 /* hints */); + } else { final InsetsImmutable insets = glWindow.getInsets(); final int x, y; if ( 0 >= insets.getTopHeight() ) { @@ -349,43 +329,13 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { glWindow.reparentWindow(null, x, y, 0 /* hints */); glWindow.setDefaultCloseOperation( glClosable ? WindowClosingMode.DISPOSE_ON_CLOSE : WindowClosingMode.DO_NOTHING_ON_CLOSE ); } - } }.start(); - } else if(e.getKeyChar()=='c') { - new Thread() { - public void run() { - System.err.println("[set pointer-icon pre]"); - final PointerIcon currentPI = glWindow.getPointerIcon(); - glWindow.setPointerIcon( currentPI == pointerIconTest ? null : pointerIconTest); - System.err.println("[set pointer-icon post] "+currentPI+" -> "+glWindow.getPointerIcon()); - } }.start(); - } else if(e.getKeyChar()=='i') { - new Thread() { - public void run() { - System.err.println("[set mouse visible pre]: "+glWindow.isPointerVisible()); - glWindow.setPointerVisible(!glWindow.isPointerVisible()); - System.err.println("[set mouse visible post]: "+glWindow.isPointerVisible()); - } }.start(); - } else if(e.getKeyChar()=='j') { - new Thread() { - public void run() { - final Thread t = glWindow.setExclusiveContextThread(null); - System.err.println("[set mouse confined pre]: "+glWindow.isPointerConfined()); - glWindow.confinePointer(!glWindow.isPointerConfined()); - System.err.println("[set mouse confined post]: "+glWindow.isPointerConfined()); - glWindow.setExclusiveContextThread(t); - } }.start(); - } else if(e.getKeyChar()=='w') { - new Thread() { - public void run() { - System.err.println("[set mouse pos pre]"); - glWindow.warpPointer(glWindow.getSurfaceWidth()/2, glWindow.getSurfaceHeight()/2); - System.err.println("[set mouse pos post]"); - } }.start(); + } } ); } } @Override public void keyReleased(final KeyEvent e) { } + } diff --git a/src/newt/classes/com/jogamp/newt/util/applet3/JOGLNewtApplet3Run.java b/src/newt/classes/com/jogamp/newt/util/applet3/JOGLNewtApplet3Run.java index 9b9b7d532..fce43d71f 100644 --- a/src/newt/classes/com/jogamp/newt/util/applet3/JOGLNewtApplet3Run.java +++ b/src/newt/classes/com/jogamp/newt/util/applet3/JOGLNewtApplet3Run.java @@ -243,7 +243,7 @@ public class JOGLNewtApplet3Run implements Applet3 { } this.ctx = ctx; String glEventListenerClazzName=null; - int glSwapInterval=0; + int glSwapInterval=1; boolean glDebug=false; boolean glTrace=false; boolean glNoDefaultKeyListener = false; diff --git a/src/newt/classes/jogamp/newt/DefaultEDTUtil.java b/src/newt/classes/jogamp/newt/DefaultEDTUtil.java index bf46ce609..ff713410b 100644 --- a/src/newt/classes/jogamp/newt/DefaultEDTUtil.java +++ b/src/newt/classes/jogamp/newt/DefaultEDTUtil.java @@ -44,6 +44,8 @@ import com.jogamp.nativewindow.NativeWindowException; import jogamp.common.util.locks.LockDebugUtil; import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.util.InterruptSource; +import com.jogamp.common.util.InterruptedRuntimeException; import com.jogamp.common.util.RunnableTask; import com.jogamp.common.util.locks.Lock; import com.jogamp.newt.util.EDTUtil; @@ -68,7 +70,7 @@ public class DefaultEDTUtil implements EDTUtil { this.threadGroup = tg; this.name=Thread.currentThread().getName()+"-"+name+"-EDT-"; this.dispatchMessages=dispatchMessages; - this.edt = new NEDT(threadGroup, name); + this.edt = new NEDT(threadGroup, this.name); this.edt.setDaemon(true); // don't stop JVM from shutdown .. } @@ -169,8 +171,7 @@ public class DefaultEDTUtil implements EDTUtil { }; private final boolean invokeImpl(boolean wait, Runnable task, final boolean stop, final boolean provokeError) { - Throwable throwable = null; - RunnableTask rTask = null; + final RunnableTask rTask; final Object rTaskLock = new Object(); synchronized(rTaskLock) { // lock the optional task execution synchronized(edtLock) { // lock the EDT status @@ -187,6 +188,7 @@ public class DefaultEDTUtil implements EDTUtil { task.run(); } wait = false; // running in same thread (EDT) -> no wait + rTask = null; if( stop ) { edt.shouldStop = true; if( edt.tasks.size()>0 ) { @@ -230,18 +232,19 @@ public class DefaultEDTUtil implements EDTUtil { } } else { wait = false; + rTask = null; } } } if( wait ) { try { - rTaskLock.wait(); // free lock, allow execution of rTask + while( rTask.isInQueue() ) { + rTaskLock.wait(); // free lock, allow execution of rTask + } } catch (final InterruptedException ie) { - throwable = ie; - } - if(null==throwable) { - throwable = rTask.getThrowable(); + throw new InterruptedRuntimeException(ie); } + final Throwable throwable = rTask.getThrowable(); if(null!=throwable) { if(throwable instanceof NativeWindowException) { throw (NativeWindowException)throwable; @@ -268,13 +271,13 @@ public class DefaultEDTUtil implements EDTUtil { return false; } synchronized(_edt.tasks) { - while(_edt.isRunning && _edt.tasks.size()>0) { - try { + try { + while(_edt.isRunning && _edt.tasks.size()>0) { _edt.tasks.notifyAll(); _edt.tasks.wait(); - } catch (final InterruptedException e) { - e.printStackTrace(); } + } catch (final InterruptedException e) { + throw new InterruptedRuntimeException(e); } return true; } @@ -284,12 +287,12 @@ public class DefaultEDTUtil implements EDTUtil { final public boolean waitUntilStopped() { synchronized(edtLock) { if(edt.isRunning && edt != Thread.currentThread() ) { - while( edt.isRunning ) { - try { + try { + while( edt.isRunning ) { edtLock.wait(); - } catch (final InterruptedException e) { - e.printStackTrace(); } + } catch (final InterruptedException e) { + throw new InterruptedRuntimeException(e); } return true; } else { @@ -298,13 +301,13 @@ public class DefaultEDTUtil implements EDTUtil { } } - class NEDT extends Thread { + class NEDT extends InterruptSource.Thread { volatile boolean shouldStop = false; volatile boolean isRunning = false; final ArrayList<RunnableTask> tasks = new ArrayList<RunnableTask>(); // one shot tasks public NEDT(final ThreadGroup tg, final String name) { - super(tg, name); + super(tg, null, name); } final public boolean isRunning() { @@ -347,11 +350,11 @@ public class DefaultEDTUtil implements EDTUtil { RunnableTask task = null; synchronized(tasks) { // wait for tasks - if(!shouldStop && tasks.size()==0) { + if( !shouldStop && tasks.size()==0 ) { try { tasks.wait(pollPeriod); } catch (final InterruptedException e) { - e.printStackTrace(); + throw new InterruptedRuntimeException(e); } } // execute one task, if available @@ -375,7 +378,7 @@ public class DefaultEDTUtil implements EDTUtil { } if(!task.hasWaiter() && null != task.getThrowable()) { // at least dump stack-trace in case nobody waits for result - System.err.println("DefaultEDT.run(): Caught exception occured on thread "+Thread.currentThread().getName()+": "+task.toString()); + System.err.println("DefaultEDT.run(): Caught exception occured on thread "+java.lang.Thread.currentThread().getName()+": "+task.toString()); task.getThrowable().printStackTrace(); } } diff --git a/src/newt/classes/jogamp/newt/DisplayImpl.java b/src/newt/classes/jogamp/newt/DisplayImpl.java index cc1f3d8e0..61a39c133 100644 --- a/src/newt/classes/jogamp/newt/DisplayImpl.java +++ b/src/newt/classes/jogamp/newt/DisplayImpl.java @@ -37,6 +37,7 @@ package jogamp.newt; import com.jogamp.common.ExceptionUtils; import com.jogamp.common.nio.Buffers; import com.jogamp.common.util.IOUtil; +import com.jogamp.common.util.InterruptedRuntimeException; import com.jogamp.common.util.ReflectionUtil; import com.jogamp.newt.Display; import com.jogamp.newt.NewtFactory; @@ -117,6 +118,8 @@ public abstract class DisplayImpl extends Display { return null; } final PointerIconImpl[] res = { null }; + final Exception[] ex = { null }; + final String exStr = "Could not resolve "+pngResource.resourcePaths[0]; runOnEDTIfAvail(true, new Runnable() { public void run() { try { @@ -125,7 +128,7 @@ public abstract class DisplayImpl extends Display { } final URLConnection urlConn = pngResource.resolve(0); if( null == urlConn ) { - throw new IOException("Could not resolve "+pngResource.resourcePaths[0]); + throw new IOException(exStr); } final PNGPixelRect image = PNGPixelRect.read(urlConn.getInputStream(), getNativePointerIconPixelFormat(), @@ -137,20 +140,32 @@ public abstract class DisplayImpl extends Display { if( DEBUG_POINTER_ICON ) { System.err.println("createPointerIconPNG.0: "+image+", handle: "+toHexString(handle)+", hot "+hotspot); } - if( 0 != handle ) { - res[0] = new PointerIconImpl(DisplayImpl.this, image, hotspot, handle); - if( DEBUG_POINTER_ICON ) { - System.err.println("createPointerIconPNG.0: "+res[0]); - } + if( 0 == handle ) { + throw new IOException(exStr); + } + res[0] = new PointerIconImpl(DisplayImpl.this, image, hotspot, handle); + if( DEBUG_POINTER_ICON ) { + System.err.println("createPointerIconPNG.0: "+res[0]); } } catch (final Exception e) { - e.printStackTrace(); + ex[0] = e; } } } ); - if( null != res[0] ) { - synchronized(pointerIconList) { - pointerIconList.add(res[0]); + if( null != ex[0] ) { + final Exception e = ex[0]; + if( e instanceof IllegalArgumentException) { + throw new IllegalArgumentException(e); } + if( e instanceof IllegalStateException) { + throw new IllegalStateException(e); + } + throw new IOException(e); + } + if( null == res[0] ) { + throw new IOException(exStr); + } + synchronized(pointerIconList) { + pointerIconList.add(res[0]); } return res[0]; } @@ -697,8 +712,9 @@ public abstract class DisplayImpl extends Display { } else { throw re; } + } finally { + eventTask.notifyCaller(); } - eventTask.notifyCaller(); } @Override @@ -725,7 +741,10 @@ public abstract class DisplayImpl extends Display { } if( null != _events ) { for (int i=0; i < _events.size(); i++) { - dispatchMessage(_events.get(i)); + final NEWTEventTask e = _events.get(i); + if( !e.isDispatched() ) { + dispatchMessage(e); + } } } } @@ -759,11 +778,12 @@ public abstract class DisplayImpl extends Display { haveEvents = true; eventsLock.notifyAll(); } - if( wait ) { + while( wait && !eTask.isDispatched() ) { try { lock.wait(); } catch (final InterruptedException ie) { - throw new RuntimeException(ie); + eTask.setDispatched(); // Cancels NEWTEvent .. + throw new InterruptedRuntimeException(ie); } if( null != eTask.getException() ) { throw eTask.getException(); diff --git a/src/newt/classes/jogamp/newt/NEWTJNILibLoader.java b/src/newt/classes/jogamp/newt/NEWTJNILibLoader.java index 793fbf9b0..84e2167ee 100644 --- a/src/newt/classes/jogamp/newt/NEWTJNILibLoader.java +++ b/src/newt/classes/jogamp/newt/NEWTJNILibLoader.java @@ -53,7 +53,7 @@ public class NEWTJNILibLoader extends JNILibLoaderBase { public Boolean run() { Platform.initSingleton(); final String libName = "newt"; - if(TempJarCache.isInitialized() && null == TempJarCache.findLibrary(libName)) { + if( TempJarCache.isInitialized(true) && null == TempJarCache.findLibrary(libName) ) { JNILibLoaderBase.addNativeJarLibsJoglCfg(new Class<?>[] { jogamp.nativewindow.Debug.class, jogamp.newt.Debug.class }); } return Boolean.valueOf(loadLibrary(libName, false, NEWTJNILibLoader.class.getClassLoader())); diff --git a/src/newt/classes/jogamp/newt/OffscreenWindow.java b/src/newt/classes/jogamp/newt/OffscreenWindow.java index 819435331..f4b8ecd42 100644 --- a/src/newt/classes/jogamp/newt/OffscreenWindow.java +++ b/src/newt/classes/jogamp/newt/OffscreenWindow.java @@ -42,7 +42,6 @@ import com.jogamp.nativewindow.GraphicsConfigurationFactory; import com.jogamp.nativewindow.MutableSurface; import com.jogamp.nativewindow.NativeWindowException; import com.jogamp.nativewindow.VisualIDHolder; -import com.jogamp.nativewindow.util.Insets; import com.jogamp.nativewindow.util.Point; import com.jogamp.newt.MonitorDevice; @@ -116,6 +115,10 @@ public class OffscreenWindow extends WindowImpl implements MutableSurface { return false; // nop } + @Override + protected final int getSupportedReconfigMaskImpl() { + return minimumReconfigStateMask; + } @Override protected boolean reconfigureWindowImpl(final int x, final int y, final int width, final int height, final int flags) { diff --git a/src/newt/classes/jogamp/newt/WindowImpl.java b/src/newt/classes/jogamp/newt/WindowImpl.java index dd1680b0f..5f03189ac 100644 --- a/src/newt/classes/jogamp/newt/WindowImpl.java +++ b/src/newt/classes/jogamp/newt/WindowImpl.java @@ -96,11 +96,12 @@ import com.jogamp.newt.event.WindowUpdateEvent; public abstract class WindowImpl implements Window, NEWTEventConsumer { public static final boolean DEBUG_TEST_REPARENT_INCOMPATIBLE; + private static final boolean DEBUG_FREEZE_AT_VISIBILITY_FAILURE; static { Debug.initSingleton(); DEBUG_TEST_REPARENT_INCOMPATIBLE = PropertyAccess.isPropertyDefined("newt.test.Window.reparent.incompatible", true); - + DEBUG_FREEZE_AT_VISIBILITY_FAILURE = PropertyAccess.isPropertyDefined("newt.debug.Window.visibility.failure.freeze", true); ScreenImpl.initSingleton(); } @@ -148,7 +149,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private static final PointerType[] constMousePointerTypes = new PointerType[] { PointerType.Mouse }; // - // Volatile: Multithread Mutable Access + // Volatile: Multithreaded Mutable Access // private volatile long windowHandle = 0; // lifecycle critical private volatile int pixWidth = 128, pixHeight = 128; // client-area size w/o insets in pixel units, default: may be overwritten by user @@ -181,6 +182,25 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private LifecycleHook lifecycleHook = null; // + // Quirks + // + + /** + * Bug 1249 and Bug 1250: Visibility issues on X11 + * <ul> + * <li>setVisible(false) IconicState not listening to _NET_WM_STATE_HIDDEN</li> + * <li>setVisible(true) not restoring from _NET_WM_STATE_HIDDEN</li> + * </ul> + * <p> + * If {@code true} fall back to traditional visibility state, + * i.e. {@code fast=true}. + * </p> + */ + protected static final int QUIRK_BIT_VISIBILITY = 0; + /** Quirk mask */ + protected static final Bitfield quirks = Bitfield.Factory.synchronize(Bitfield.Factory.create(32)); + + // // State Mask // @@ -190,37 +210,46 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer * @see #getStateMask() * @since 2.3.2 */ - protected static final int STATE_BIT_COUNT_ALL_PUBLIC = 15; + protected static final int STATE_BIT_COUNT_ALL_PUBLIC = STATE_BIT_POINTERCONFINED + 1; /** Bitmask for {@link #STATE_BIT_COUNT_ALL_PUBLIC} */ protected static final int STATE_MASK_ALL_PUBLIC = ( 1 << STATE_BIT_COUNT_ALL_PUBLIC ) - 1; // - // Additional private state-mask bits and mask values + // Additional private reconfigure state-mask bits and mask values // - /** - * <p>Bit number {@value}.</p> - * <p>Defaults to {@code false}.</p> - * @see #getStateMask() - * @since 2.3.2 - */ - /* pp */ static final int STATE_BIT_FULLSCREEN_SPAN = 12; - /* pp */ static final int PSTATE_BIT_MINMAXSIZE_SET = 27; - /* pp */ static final int PSTATE_BIT_FOCUS_CHANGE_BROKEN = 28; - /* pp */ static final int PSTATE_BIT_FULLSCREEN_MAINMONITOR = 29; // true - /* pp */ static final int PSTATE_BIT_FULLSCREEN_NFS_ALWAYSONTOP = 30; // non fullscreen alwaysOnTop setting - /* pp */ static final int PSTATE_BIT_FULLSCREEN_NFS_RESIZABLE = 31; // non fullscreen resizable setting + protected static final int STATE_BIT_FULLSCREEN_SPAN = STATE_BIT_COUNT_ALL_PUBLIC; + + protected static final int STATE_BIT_COUNT_ALL_RECONFIG = STATE_BIT_FULLSCREEN_SPAN + 1; + /** Bitmask for {@link #STATE_BIT_COUNT_ALL_RECONFIG} */ + protected static final int STATE_MASK_ALL_RECONFIG = ( 1 << STATE_BIT_COUNT_ALL_RECONFIG ) - 1; + + protected static final int STATE_MASK_ALL_PUBLIC_SUPPORTED = STATE_MASK_ALL_PUBLIC & ~STATE_MASK_AUTOPOSITION; + + // + // Additional private non-reconfigure state-mask bits and mask values + // + + /* pp */ static final int PSTATE_BIT_FOCUS_CHANGE_BROKEN = 30; + /* pp */ static final int PSTATE_BIT_FULLSCREEN_MAINMONITOR = 31; // true /** Bitmask for {@link #STATE_BIT_FULLSCREEN_SPAN}, {@value}. */ /* pp */ static final int STATE_MASK_FULLSCREEN_SPAN = 1 << STATE_BIT_FULLSCREEN_SPAN; + /* pp */ static final int PSTATE_MASK_FOCUS_CHANGE_BROKEN = 1 << PSTATE_BIT_FOCUS_CHANGE_BROKEN; /* pp */ static final int PSTATE_MASK_FULLSCREEN_MAINMONITOR = 1 << PSTATE_BIT_FULLSCREEN_MAINMONITOR; - /* pp */ static final int PSTATE_MASK_FULLSCREEN_NFS_ALWAYSONTOP = 1 << PSTATE_BIT_FULLSCREEN_NFS_ALWAYSONTOP; - /* pp */ static final int PSTATE_MASK_FULLSCREEN_NFS_RESIZABLE = 1 << PSTATE_BIT_FULLSCREEN_NFS_RESIZABLE; + + /** + * Mask covering all preserved non-fullscreen (NFS) states + * while in fullscreen mode. + */ + private static final int STATE_MASK_FULLSCREEN_NFS = STATE_MASK_ALWAYSONTOP | + STATE_MASK_RESIZABLE | + STATE_MASK_MAXIMIZED_VERT | + STATE_MASK_MAXIMIZED_HORZ; /** * Reconfig mask for createNativeImpl(..) taking out from {@link #getStateMask()}: * <ul> - * <li>{@link #STATE_MASK_VISIBLE}</li> * <li>{@link #STATE_MASK_FULLSCREEN}</li> * <li>{@link #STATE_MASK_POINTERVISIBLE}</li> * <li>{@link #STATE_MASK_POINTERCONFINED}</li> @@ -228,13 +257,11 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer * Above taken out states are achieved from caller createNative() 'manually'. * @since 2.3.2 */ - protected final int STATE_MASK_CREATENATIVE = STATE_MASK_UNDECORATED | - STATE_MASK_ALWAYSONTOP | - STATE_MASK_ALWAYSONBOTTOM | - STATE_MASK_STICKY | - STATE_MASK_RESIZABLE | - STATE_MASK_MAXIMIZED_VERT | - STATE_MASK_MAXIMIZED_HORZ; + protected static final int STATE_MASK_CREATENATIVE = STATE_MASK_ALL_PUBLIC & + ~( STATE_MASK_FULLSCREEN | + STATE_MASK_POINTERVISIBLE | + STATE_MASK_POINTERCONFINED + ); // // Additional private state-mask mask values for reconfiguration only // (keep in sync w/ src/newt/native/Window.h) @@ -251,17 +278,28 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer protected static final int CHANGE_MASK_MAXIMIZED_HORZ = 1 << 22; protected static final int CHANGE_MASK_FULLSCREEN = 1 << 21; + /** Regular state mask */ /* pp */ final Bitfield stateMask = Bitfield.Factory.synchronize(Bitfield.Factory.create(32)); + /** Non fullscreen state mask */ + private final Bitfield stateMaskNFS = Bitfield.Factory.synchronize(Bitfield.Factory.create(32)); + + /** Default is all but {@link #STATE_MASK_FULLSCREEN_SPAN}. */ + protected int supportedReconfigStateMask = 0; + /** See {@link #getSupportedStateMask()}, i.e. {@link #STATE_MASK_VISIBLE} | {@link #STATE_MASK_FOCUSED} | {@link STATE_MASK_FULLSCREEN}. */ + protected static final int minimumReconfigStateMask = STATE_MASK_VISIBLE | STATE_MASK_FOCUSED | STATE_MASK_FULLSCREEN; + /* pp */ final void resetStateMask() { stateMask.clearField(false); - stateMask.set(STATE_BIT_AUTOPOSITION); - stateMask.put(STATE_BIT_CHILDWIN, null != parentWindow); - stateMask.set(STATE_BIT_RESIZABLE); - stateMask.set(STATE_BIT_POINTERVISIBLE); - stateMask.set(PSTATE_BIT_FULLSCREEN_NFS_RESIZABLE); - stateMask.set(PSTATE_BIT_FULLSCREEN_MAINMONITOR); + stateMask.put32(0, 32, + STATE_MASK_AUTOPOSITION | + ( null != parentWindow ? STATE_MASK_CHILDWIN : 0 ) | + STATE_MASK_RESIZABLE | + STATE_MASK_POINTERVISIBLE | + PSTATE_MASK_FULLSCREEN_MAINMONITOR); + stateMaskNFS.clearField(false); normPosSizeStored[0] = false; normPosSizeStored[1] = false; + supportedReconfigStateMask = STATE_MASK_ALL_RECONFIG; } @Override @@ -277,16 +315,29 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer public final int getStateMask() { return stateMask.get32(0, STATE_BIT_COUNT_ALL_PUBLIC); } + @Override public final String getStateMaskString() { return appendStateBits(new StringBuilder(), stateMask.get32(0, STATE_BIT_COUNT_ALL_PUBLIC), false).toString(); } + @Override + public final int getSupportedStateMask() { + return supportedReconfigStateMask & STATE_MASK_ALL_PUBLIC_SUPPORTED; + } + + @Override + public final String getSupportedStateMaskString() { + return appendStateBits(new StringBuilder(), getSupportedStateMask(), true).toString(); + } + protected static StringBuilder appendStateBits(final StringBuilder sb, final int mask, final boolean showChangeFlags) { sb.append("["); - if( showChangeFlags && 0 != ( CHANGE_MASK_VISIBILITY & mask) ) { - sb.append("*"); + if( showChangeFlags ) { + if( 0 != ( CHANGE_MASK_VISIBILITY & mask) ) { + sb.append("*"); + } if( 0 != ( CHANGE_MASK_VISIBILITY_FAST & mask) ) { sb.append("*"); } @@ -296,9 +347,11 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer sb.append((0 != ( STATE_MASK_AUTOPOSITION & mask))?"autopos, ":""); - if( showChangeFlags && 0 != ( CHANGE_MASK_PARENTING & mask) ) { - sb.append("*"); - sb.append((0 != ( STATE_MASK_CHILDWIN & mask))?"child":"top"); + if( showChangeFlags ) { + if( 0 != ( CHANGE_MASK_PARENTING & mask) ) { + sb.append("*"); + } + sb.append((0 != ( STATE_MASK_CHILDWIN & mask))?"child":"toplevel"); sb.append(", "); } else if( 0 != ( STATE_MASK_CHILDWIN & mask) ) { sb.append("child"); @@ -307,8 +360,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer sb.append((0 != ( STATE_MASK_FOCUSED & mask))?"focused, ":""); - if( showChangeFlags && 0 != ( CHANGE_MASK_DECORATION & mask) ) { - sb.append("*"); + if( showChangeFlags ) { + if( 0 != ( CHANGE_MASK_DECORATION & mask) ) { + sb.append("*"); + } sb.append((0 != ( STATE_MASK_UNDECORATED & mask))?"undecor":"decor"); sb.append(", "); } else if( 0 != ( STATE_MASK_UNDECORATED & mask) ) { @@ -316,8 +371,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer sb.append(", "); } - if( showChangeFlags && 0 != ( CHANGE_MASK_ALWAYSONTOP & mask) ) { - sb.append("*"); + if( showChangeFlags ) { + if( 0 != ( CHANGE_MASK_ALWAYSONTOP & mask) ) { + sb.append("*"); + } sb.append((0 != ( STATE_MASK_ALWAYSONTOP & mask))?"aontop":"!aontop"); sb.append(", "); } else if( 0 != ( STATE_MASK_ALWAYSONTOP & mask) ) { @@ -325,8 +382,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer sb.append(", "); } - if( showChangeFlags && 0 != ( CHANGE_MASK_ALWAYSONBOTTOM & mask) ) { - sb.append("*"); + if( showChangeFlags ) { + if( 0 != ( CHANGE_MASK_ALWAYSONBOTTOM & mask) ) { + sb.append("*"); + } sb.append((0 != ( STATE_MASK_ALWAYSONBOTTOM & mask))?"aonbottom":"!aonbottom"); sb.append(", "); } else if( 0 != ( STATE_MASK_ALWAYSONBOTTOM & mask) ) { @@ -334,8 +393,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer sb.append(", "); } - if( showChangeFlags && 0 != ( CHANGE_MASK_STICKY & mask) ) { - sb.append("*"); + if( showChangeFlags ) { + if( 0 != ( CHANGE_MASK_STICKY & mask) ) { + sb.append("*"); + } sb.append((0 != ( STATE_MASK_STICKY & mask))?"sticky":"unsticky"); sb.append(", "); } else if( 0 != ( STATE_MASK_STICKY & mask) ) { @@ -343,8 +404,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer sb.append(", "); } - if( showChangeFlags && 0 != ( CHANGE_MASK_RESIZABLE & mask) ) { - sb.append("*"); + if( showChangeFlags ) { + if( 0 != ( CHANGE_MASK_RESIZABLE & mask) ) { + sb.append("*"); + } sb.append((0 != ( STATE_MASK_RESIZABLE & mask))?"resizable":"unresizable"); sb.append(", "); } else if( 0 == ( STATE_MASK_RESIZABLE & mask) ) { @@ -352,7 +415,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer sb.append(", "); } - if( showChangeFlags && 0 != ( ( CHANGE_MASK_MAXIMIZED_HORZ | CHANGE_MASK_MAXIMIZED_VERT ) & mask) ) { + if( showChangeFlags ) { sb.append("max["); if( 0 != ( CHANGE_MASK_MAXIMIZED_HORZ & mask) ) { sb.append("*"); @@ -381,28 +444,47 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer sb.append("], "); } - if( showChangeFlags && 0 != ( CHANGE_MASK_FULLSCREEN & mask) ) { - sb.append("*"); - sb.append((0 != ( STATE_MASK_FULLSCREEN & mask))?"fullscreen":"window"); - sb.append((0 != ( STATE_MASK_FULLSCREEN_SPAN & mask))?"[span]":"[]"); - sb.append(", "); + if( showChangeFlags ) { + if( 0 != ( CHANGE_MASK_FULLSCREEN & mask) ) { + sb.append("*"); + } + sb.append("fullscreen["); + sb.append(0 != ( STATE_MASK_FULLSCREEN & mask)); + sb.append((0 != ( STATE_MASK_FULLSCREEN_SPAN & mask))?", span":""); + sb.append("], "); } else if( 0 != ( STATE_MASK_FULLSCREEN & mask) ) { sb.append("fullscreen"); sb.append(", "); } - if( 0 == ( STATE_MASK_POINTERVISIBLE & mask) || - 0 != ( STATE_MASK_POINTERCONFINED & mask) ) - { - sb.append("pointer["); - if( 0 == ( STATE_MASK_POINTERVISIBLE & mask) ) { - sb.append("invisible"); + if( showChangeFlags ) { + sb.append("pointer["); + if( 0 == ( STATE_MASK_POINTERVISIBLE & mask) ) { + sb.append("invisible"); + } else { + sb.append("visible"); + } sb.append(", "); + if( 0 != ( STATE_MASK_POINTERCONFINED & mask) ) { + sb.append("confined"); + } else { + sb.append("free"); + } + sb.append("]"); + } else { + if( 0 == ( STATE_MASK_POINTERVISIBLE & mask) || + 0 != ( STATE_MASK_POINTERCONFINED & mask) ) + { + sb.append("pointer["); + if( 0 == ( STATE_MASK_POINTERVISIBLE & mask) ) { + sb.append("invisible"); + sb.append(", "); + } + if( 0 != ( STATE_MASK_POINTERCONFINED & mask) ) { + sb.append("confined"); + } + sb.append("]"); } - if( 0 != ( STATE_MASK_POINTERCONFINED & mask) ) { - sb.append("confined"); - } - sb.append("]"); } sb.append("]"); return sb; @@ -667,15 +749,37 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } final long t0 = System.currentTimeMillis(); createNativeImpl(); + supportedReconfigStateMask = getSupportedReconfigMaskImpl() & STATE_MASK_ALL_RECONFIG; + if( DEBUG_IMPLEMENTATION) { + final boolean minimumOK = minimumReconfigStateMask == ( minimumReconfigStateMask & supportedReconfigStateMask ); + System.err.println("Supported Reconfig (minimum-ok "+minimumOK+"): "+appendStateBits(new StringBuilder(), supportedReconfigStateMask, true).toString()); + } screen.addMonitorModeListener(monitorModeListenerImpl); setTitleImpl(title); setPointerIconIntern(pointerIcon); - setPointerVisibleIntern(stateMask.get(STATE_BIT_POINTERVISIBLE)); - confinePointerImpl(stateMask.get(STATE_BIT_POINTERCONFINED)); + if( !stateMask.get(STATE_BIT_POINTERVISIBLE) ) { + // non default action + if( isReconfigureMaskSupported(STATE_MASK_POINTERVISIBLE) ) { + setPointerVisibleIntern(stateMask.get(STATE_BIT_POINTERVISIBLE)); + } else { + stateMask.set(STATE_BIT_POINTERVISIBLE); + } + } + if( stateMask.get(STATE_BIT_POINTERCONFINED) ) { + // non default action + if( isReconfigureMaskSupported(STATE_MASK_POINTERCONFINED) ) { + confinePointerImpl(true); + } else { + stateMask.clear(STATE_BIT_POINTERCONFINED); + } + } setKeyboardVisible(keyboardVisible); final long remainingV = waitForVisible(true, false); if( 0 <= remainingV ) { - if(isFullscreen()) { + if( stateMask.get(STATE_BIT_FULLSCREEN) && !isReconfigureMaskSupported(STATE_MASK_FULLSCREEN) ) { + stateMask.clear(STATE_BIT_FULLSCREEN); + } + if( stateMask.get(STATE_BIT_FULLSCREEN) ) { synchronized(fullScreenAction) { stateMask.clear(STATE_BIT_FULLSCREEN); // trigger a state change fullScreenAction.init(true); @@ -844,6 +948,17 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer protected abstract void requestFocusImpl(boolean force); /** + * Returns the reconfigure state-mask supported by the implementation. + * <p> + * Default value is {@link #STATE_MASK_VISIBLE} | {@link #STATE_MASK_FOCUSED}, + * i.e. the <b>minimum requirement</b> for all implementations. + * </p> + * @see #getSupportedStateMask() + * @see #reconfigureWindowImpl(int, int, int, int, int) + */ + protected abstract int getSupportedReconfigMaskImpl(); + + /** * The native implementation should invoke the referenced java state callbacks * to notify this Java object of state changes. * @@ -858,27 +973,26 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer * @param height client-area size in window units, or <=0 if unchanged * @param flags bitfield of change and status flags * + * @see #getSupportedReconfigMaskImpl() * @see #sizeChanged(int,int) * @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 #STATE_MASK_FULLSCREEN_SPAN} - * </p> + * Tests whether the given reconfigure state-mask is supported by implementation. */ - protected boolean isReconfigureMaskSupported(final int changeFlags) { - return 0 == ( changeFlags & STATE_MASK_FULLSCREEN_SPAN ); + protected final boolean isReconfigureMaskSupported(final int reconfigMask) { + return reconfigMask == ( reconfigMask & supportedReconfigStateMask ); } protected int getReconfigureMask(final int changeFlags, final boolean visible) { - final int smask = stateMask.get32(0, STATE_BIT_COUNT_ALL_PUBLIC); + final int smask = stateMask.get32(0, STATE_BIT_COUNT_ALL_RECONFIG); return changeFlags - | ( smask & ~STATE_MASK_VISIBLE ) + | ( smask & ~(STATE_MASK_VISIBLE | STATE_MASK_UNDECORATED | STATE_MASK_CHILDWIN) ) | ( visible ? STATE_MASK_VISIBLE : 0 ) | ( isUndecorated(smask) ? STATE_MASK_UNDECORATED : 0 ) + | ( 0 != getParentWindowHandle() ? STATE_MASK_CHILDWIN : 0 ) ; } protected static String getReconfigStateMaskString(final int flags) { @@ -1111,9 +1225,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } reconfigureWindowImpl(x, y, width, height, mask); } + final void setVisibleActionImpl(final boolean visible) { boolean nativeWindowCreated = false; - boolean madeVisible = false; + int madeVisible = -1; final RecursiveLock _lock = windowLock; _lock.lock(); @@ -1131,16 +1246,31 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(!isNativeValid() && visible) { if( 0<getWidth()*getHeight() ) { nativeWindowCreated = createNative(); - madeVisible = nativeWindowCreated; + madeVisible = nativeWindowCreated ? 1 : -1; } // always flag visible, allowing a retry .. stateMask.set(STATE_BIT_VISIBLE); } else if(stateMask.get(STATE_BIT_VISIBLE) != visible) { if(isNativeValid()) { // Skip WM if child-window! - setVisibleImpl(visible /* visible */, isChildWindow() /* fast */, getX(), getY(), getWidth(), getHeight()); - WindowImpl.this.waitForVisible(visible, false); - madeVisible = visible; + final boolean hasVisibilityQuirk = quirks.get(QUIRK_BIT_VISIBILITY); + setVisibleImpl(visible /* visible */, hasVisibilityQuirk || isChildWindow() /* fast */, + getX(), getY(), getWidth(), getHeight()); + if( 0 > WindowImpl.this.waitForVisible(visible, false) ) { + if( !hasVisibilityQuirk ) { + quirks.set(QUIRK_BIT_VISIBILITY); + if( DEBUG_IMPLEMENTATION ) { + System.err.println("Setting VISIBILITY QUIRK, due to setVisible("+visible+") failure"); + } + setVisibleImpl(visible /* visible */, true /* fast */, + getX(), getY(), getWidth(), getHeight()); + if( 0 <= WindowImpl.this.waitForVisible(visible, false) ) { + madeVisible = visible ? 1 : 0; + } // else: still not working .. bail out + } // else: no other remedy known .. bail out + } else { + madeVisible = visible ? 1 : 0; + } } else { stateMask.set(STATE_BIT_VISIBLE); } @@ -1172,7 +1302,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } _lock.unlock(); } - if( nativeWindowCreated || madeVisible ) { + if( nativeWindowCreated || 1==madeVisible ) { sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener } } @@ -1191,6 +1321,9 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer @Override public final void setVisible(final boolean wait, final boolean visible) { + if( !isReconfigureMaskSupported(STATE_MASK_VISIBLE) && isNativeValid() ) { + return; + } if(DEBUG_IMPLEMENTATION) { System.err.println("Window setVisible: START ("+getThreadName()+") "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()+", windowHandle "+toHexString(windowHandle)+", state "+getStateMaskString()+" -> visible "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+(null!=parentWindow)); } @@ -1780,6 +1913,9 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer @Override public final ReparentOperation reparentWindow(final NativeWindow newParent, final int x, final int y, final int hints) { + if( !isReconfigureMaskSupported(STATE_MASK_CHILDWIN) && isNativeValid() ) { + return ReparentOperation.ACTION_INVALID; + } final ReparentAction reparentAction = new ReparentAction(newParent, x, y, hints); runOnEDTIfAvail(true, reparentAction); return reparentAction.getOp(); @@ -1841,6 +1977,15 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer @Override public final void setUndecorated(final boolean value) { + if( isNativeValid() ) { + if( !isReconfigureMaskSupported(STATE_MASK_UNDECORATED) ) { + return; + } + if( isFullscreen() ) { + stateMaskNFS.put(STATE_MASK_UNDECORATED, value); + return; + } + } runOnEDTIfAvail(true, new DecorationAction(value)); } @Override @@ -1864,7 +2009,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer _lock.lock(); try { if( stateMask.put(STATE_BIT_ALWAYSONTOP, alwaysOnTop) != alwaysOnTop ) { - if( isNativeValid() ) { + if( isNativeValid() && !isFullscreen() ) { // Mirror pos/size so native change notification can get overwritten final int x = getX(); final int y = getY(); @@ -1888,17 +2033,22 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if( isChildWindow() ) { return; // ignore for child windows } - if( isFullscreen() ) { - if( value && isAlwaysOnBottom() ) { - setAlwaysOnBottom(false); + if( isNativeValid() ) { + if( !isReconfigureMaskSupported(STATE_MASK_ALWAYSONTOP) ) { + return; } - stateMask.put(PSTATE_BIT_FULLSCREEN_NFS_ALWAYSONTOP, value); - } else { - if( value && isAlwaysOnBottom() ) { - setAlwaysOnBottom(false); + if( isFullscreen() ) { + if( value && isAlwaysOnBottom() ) { + setAlwaysOnBottom(false); + } + stateMaskNFS.put(STATE_BIT_ALWAYSONTOP, value); + return; } - runOnEDTIfAvail(true, new AlwaysOnTopAction(value)); } + if( value && isAlwaysOnBottom() ) { + setAlwaysOnBottom(false); + } + runOnEDTIfAvail(true, new AlwaysOnTopAction(value)); } @Override public final boolean isAlwaysOnTop() { @@ -1942,6 +2092,9 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if( isChildWindow() ) { return; // ignore for child windows } + if( !isReconfigureMaskSupported(STATE_MASK_ALWAYSONBOTTOM) && isNativeValid() ) { + return; + } if( value && isAlwaysOnTop() ) { setAlwaysOnTop(false); } @@ -1989,11 +2142,16 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if( isChildWindow() ) { return; // ignore for child windows } - if( isFullscreen() ) { - stateMask.put(PSTATE_BIT_FULLSCREEN_NFS_RESIZABLE, value); - } else { - runOnEDTIfAvail(true, new ResizableAction(value)); + if( isNativeValid() ) { + if( !isReconfigureMaskSupported(STATE_MASK_RESIZABLE) ) { + return; + } + if( isFullscreen() ) { + stateMaskNFS.put(STATE_BIT_RESIZABLE, value); + return; + } } + runOnEDTIfAvail(true, new ResizableAction(value)); } @Override public final boolean isResizable() { @@ -2037,6 +2195,9 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if( isChildWindow() ) { return; // ignore for child windows } + if( !isReconfigureMaskSupported(STATE_MASK_STICKY) && isNativeValid() ) { + return; + } runOnEDTIfAvail(true, new StickyAction(value)); } @Override @@ -2066,6 +2227,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } if( 0 != cmask ) { if( isNativeValid() ) { + final boolean focused = hasFocus(); // Mirror pos/size so native change notification can get overwritten final int x = getX(); final int y = getY(); @@ -2077,6 +2239,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer reconfigureWindowImpl(x, y, width, height, getReconfigureMask(cmask, isVisible())); display.dispatchMessagesNative(); // status up2date + + if(focused) { + requestFocusInt( 0 == parentWindowHandle /* skipFocusAction if top-level */); + } } } } finally { @@ -2086,11 +2252,24 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } @Override - public final void setMaximized(final boolean horz, final boolean vert) { - if( isChildWindow() || isFullscreen() ) { + public final void setMaximized(boolean horz, boolean vert) { + if( isNativeValid() ) { + if( horz && !isReconfigureMaskSupported(STATE_MASK_MAXIMIZED_HORZ) ) { + horz = false; + } + if( vert && !isReconfigureMaskSupported(STATE_MASK_MAXIMIZED_VERT) ) { + vert = false; + } + } + if( isChildWindow() ) { return; // ignore for child windows } - runOnEDTIfAvail(true, new MaximizeAction(horz, vert)); + if( isFullscreen() ) { + stateMaskNFS.put(STATE_BIT_MAXIMIZED_HORZ, horz); + stateMaskNFS.put(STATE_BIT_MAXIMIZED_VERT, vert); + } else { + runOnEDTIfAvail(true, new MaximizeAction(horz, vert)); + } } @Override public final boolean isMaximizedVert() { @@ -2101,13 +2280,22 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer return stateMask.get(STATE_BIT_MAXIMIZED_HORZ); } /** Triggered by implementation's WM events to update maximized window state. */ - protected void maximizedChanged(final boolean newMaxHorz, final boolean newMaxVert) { - final String stateMask0 = DEBUG_IMPLEMENTATION ? getStateMaskString() : null; - final boolean changedHorz = stateMask.put(STATE_BIT_MAXIMIZED_HORZ, newMaxHorz) != newMaxHorz; - final boolean changedVert = stateMask.put(STATE_BIT_MAXIMIZED_VERT, newMaxVert) != newMaxVert; - if ( DEBUG_IMPLEMENTATION ) { + protected final void maximizedChanged(final boolean newMaxHorz, final boolean newMaxVert) { + if( !isFullscreen() ) { + final String stateMask0 = DEBUG_IMPLEMENTATION ? getStateMaskString() : null; + final boolean changedHorz = stateMask.put(STATE_BIT_MAXIMIZED_HORZ, newMaxHorz) != newMaxHorz; + final boolean changedVert = stateMask.put(STATE_BIT_MAXIMIZED_VERT, newMaxVert) != newMaxVert; + if ( DEBUG_IMPLEMENTATION ) { + if( changedHorz || changedVert ) { + System.err.println("Window.maximizedChanged.accepted: "+stateMask0+" -> "+getStateMaskString()); + } + } + } else if( DEBUG_IMPLEMENTATION ) { + final String stateMask0 = DEBUG_IMPLEMENTATION ? getStateMaskString() : null; + final boolean changedHorz = stateMask.get(STATE_BIT_MAXIMIZED_HORZ) != newMaxHorz; + final boolean changedVert = stateMask.get(STATE_BIT_MAXIMIZED_VERT) != newMaxVert; if( changedHorz || changedVert ) { - System.err.println("Window.maximizedChanged: "+stateMask0+" -> "+getStateMaskString()); + System.err.println("Window.maximizedChanged.ignored: "+stateMask0+" -> max["+(newMaxHorz?"":"!")+"h, "+(newMaxVert?"":"!")+"v]"); } } } @@ -2200,6 +2388,9 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } @Override public final void setPointerVisible(final boolean pointerVisible) { + if( !isReconfigureMaskSupported(STATE_MASK_POINTERVISIBLE) && isNativeValid() ) { + return; + } if(stateMask.get(STATE_BIT_POINTERVISIBLE) != pointerVisible) { boolean setVal = 0 == getWindowHandle(); if(!setVal) { @@ -2310,6 +2501,9 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } @Override public final void confinePointer(final boolean confine) { + if( !isReconfigureMaskSupported(STATE_MASK_POINTERCONFINED) && isNativeValid() ) { + return; + } if(stateMask.get(STATE_BIT_POINTERCONFINED) != confine) { boolean setVal = 0 == getWindowHandle(); if(!setVal) { @@ -2634,6 +2828,9 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer /** Internally forcing request focus on current thread */ private void requestFocusInt(final boolean skipFocusAction) { if( skipFocusAction || !focusAction() ) { + if( !isReconfigureMaskSupported(STATE_MASK_FOCUSED) ) { + return; + } if(DEBUG_IMPLEMENTATION) { System.err.println("Window.RequestFocusInt: forcing - ("+getThreadName()+"): skipFocusAction "+ skipFocusAction+", state "+getStateMaskString()+" -> focus true - windowHandle "+ @@ -2725,8 +2922,12 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private boolean init(final boolean fullscreen) { if(isNativeValid()) { - this._fullscreen = fullscreen; - return isFullscreen() != fullscreen; + if( !isReconfigureMaskSupported(STATE_MASK_FULLSCREEN) ) { + return false; + } else { + this._fullscreen = fullscreen; + return isFullscreen() != fullscreen; + } } else { stateMask.put(STATE_BIT_FULLSCREEN, fullscreen); // set current state for createNative(..) return false; @@ -2745,11 +2946,12 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer final int oldWidth = getWidth(); final int oldHeight = getHeight(); - int x,y,w,h; + final int x,y,w,h; final RectangleImmutable sviewport = screen.getViewportInWindowUnits(); // window units final RectangleImmutable viewport; // window units final boolean alwaysOnTopChange, resizableChange; + if(_fullscreen) { if( null == fullscreenMonitors ) { if( stateMask.get(PSTATE_BIT_FULLSCREEN_MAINMONITOR) ) { @@ -2774,32 +2976,28 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer nfs_y = oldY; nfs_width = oldWidth; nfs_height = oldHeight; - stateMask.copy(STATE_BIT_ALWAYSONTOP, PSTATE_BIT_FULLSCREEN_NFS_ALWAYSONTOP); - stateMask.copy(STATE_BIT_RESIZABLE, PSTATE_BIT_FULLSCREEN_NFS_RESIZABLE); + stateMaskNFS.put32(0, 32, stateMask.get32(0, 32) & STATE_MASK_FULLSCREEN_NFS); x = viewport.getX(); y = viewport.getY(); w = viewport.getWidth(); h = viewport.getHeight(); stateMask.clear(STATE_BIT_ALWAYSONTOP); // special aontop handling for fullscreen stateMask.set(STATE_BIT_RESIZABLE); // allow fullscreen to resize to max - alwaysOnTopChange = stateMask.get(PSTATE_BIT_FULLSCREEN_NFS_ALWAYSONTOP); - resizableChange = !stateMask.get(PSTATE_BIT_FULLSCREEN_NFS_RESIZABLE); + alwaysOnTopChange = stateMaskNFS.get(STATE_BIT_ALWAYSONTOP); + resizableChange = !stateMaskNFS.get(STATE_BIT_RESIZABLE); } else { + int _x,_y,_w,_h; stateMask.set(PSTATE_BIT_FULLSCREEN_MAINMONITOR); fullscreenMonitors = null; stateMask.clear(STATE_BIT_FULLSCREEN_SPAN); viewport = null; - x = nfs_x; - y = nfs_y; - w = nfs_width; - h = nfs_height; - alwaysOnTopChange = stateMask.get(PSTATE_BIT_FULLSCREEN_NFS_ALWAYSONTOP) != stateMask.get(STATE_BIT_ALWAYSONTOP); - // alwaysOnBottomChange = stateMask.get(PSTATE_BIT_FULLSCREEN_NFS_ALWAYSONBOTTOM) != stateMask.get(STATE_BIT_ALWAYSONBOTTOM); - resizableChange = stateMask.get(PSTATE_BIT_FULLSCREEN_NFS_RESIZABLE) != stateMask.get(STATE_BIT_RESIZABLE); - stateMask.copy(PSTATE_BIT_FULLSCREEN_NFS_ALWAYSONTOP, STATE_BIT_ALWAYSONTOP); - stateMask.copy(PSTATE_BIT_FULLSCREEN_NFS_RESIZABLE, STATE_BIT_RESIZABLE); - stateMask.clear(PSTATE_BIT_FULLSCREEN_NFS_ALWAYSONTOP); - stateMask.set(PSTATE_BIT_FULLSCREEN_NFS_RESIZABLE); + _x = nfs_x; + _y = nfs_y; + _w = nfs_width; + _h = nfs_height; + alwaysOnTopChange = stateMaskNFS.get(STATE_BIT_ALWAYSONTOP) != stateMask.get(STATE_BIT_ALWAYSONTOP); + resizableChange = stateMaskNFS.get(STATE_BIT_RESIZABLE) != stateMask.get(STATE_BIT_RESIZABLE); + stateMask.put32(0, 32, stateMaskNFS.get32(0, 32) | ( stateMask.get32(0, 32) & ~STATE_MASK_FULLSCREEN_NFS ) ); if(null!=parentWindow) { // reset position to 0/0 within parent space @@ -2807,12 +3005,21 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer y = 0; // refit if size is bigger than parent - if( w > parentWindow.getWidth() ) { + if( _w > parentWindow.getWidth() ) { w = parentWindow.getWidth(); + } else { + w = _w; } - if( h > parentWindow.getHeight() ) { + if( _h > parentWindow.getHeight() ) { h = parentWindow.getHeight(); + } else { + h = _h; } + } else { + x = _x; + y = _y; + w = _w; + h = _h; } } @@ -2851,7 +3058,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer final int changeMask; try { { - // Enter fullscreen - Disable alwaysOnTop/alwaysOnBottom/resizableChange + // Enter fullscreen - Disable alwaysOnTop/resizableChange int cm = 0; if( alwaysOnTopChange ) { cm = CHANGE_MASK_ALWAYSONTOP; @@ -2862,11 +3069,13 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer changeMask = cm; } if( _fullscreen && 0 != changeMask ) { - // Enter fullscreen - Disable alwaysOnTop/alwaysOnBottom/resizableChange + // Enter fullscreen - Disable alwaysOnTop/resizableChange reconfigureWindowImpl(oldX, oldY, oldWidth, oldHeight, getReconfigureMask(changeMask, isVisible())); } stateMask.put(STATE_BIT_FULLSCREEN, _fullscreen); + // Note CHANGE_MASK_PARENTING: STATE_MASK_CHILDWIN is refined in getReconfigureMask() + // Note CHANGE_MASK_DECORATION: STATE_MASK_UNDECORATED is refined in getReconfigureMask() reconfigureWindowImpl(x, y, w, h, getReconfigureMask( ( ( null != parentWindowLocked ) ? CHANGE_MASK_PARENTING : 0 ) | CHANGE_MASK_FULLSCREEN | CHANGE_MASK_DECORATION, isVisible()) ); @@ -3066,6 +3275,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener if(animatorPaused) { lifecycleHook.resumeRenderingAction(); + animatorPaused = false; } if( hadFocus ) { requestFocus(true); @@ -3180,7 +3390,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // public final void sendMouseEvent(final short eventType, final int modifiers, - final int x, final int y, final short button, final float rotation) { + final int x, final int y, final short button, final float rotation) { doMouseEvent(false, false, eventType, modifiers, x, y, button, MouseEvent.getRotationXYZ(rotation, modifiers), 1f); } @@ -3335,7 +3545,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer final int pCount = pTypes.length; if( 0 > pActionIdx || pActionIdx >= pCount) { - throw new IllegalArgumentException("actionIdx out of bounds [0.."+(pCount-1)+"]"); + throw new IllegalArgumentException("actionIdx "+pActionIdx+" out of bounds [0.."+(pCount-1)+"]"); } if( 0 < pActionIdx ) { // swap values to make idx 0 the triggering pointer @@ -4247,14 +4457,31 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } if( visible != _visible ) { final String msg = "Visibility not reached as requested within "+timeOut+"ms : requested "+visible+", is "+_visible; - if(failFast) { + if(DEBUG_FREEZE_AT_VISIBILITY_FAILURE) { + System.err.println("XXXX: "+msg); + System.err.println("XXXX: FREEZE"); + try { + while(true) { + Thread.sleep(100); + display.dispatchMessagesNative(); // status up2date + } + } catch (final InterruptedException e) { + ExceptionUtils.dumpThrowable("", e); + Thread.currentThread().interrupt(); // keep state + } throw new NativeWindowException(msg); - } else if (DEBUG_IMPLEMENTATION) { - System.err.println(msg); - ExceptionUtils.dumpStack(System.err); + } else { + if(failFast) { + throw new NativeWindowException(msg); + } else { + if (DEBUG_IMPLEMENTATION) { + System.err.println(msg); + ExceptionUtils.dumpStack(System.err); + } + return -1; + } } - return -1; - } else if( 0 < remaining ){ + } else if( 0 < remaining ) { return remaining; } else { return 0; @@ -4384,18 +4611,27 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer /** * Triggered by implementation's WM events to update the insets. * + * @param defer + * @param left insets, -1 ignored + * @param right insets, -1 ignored + * @param top insets, -1 ignored + * @param bottom insets, -1 ignored + * * @see #getInsets() * @see #updateInsetsImpl(Insets) */ protected void insetsChanged(final boolean defer, final int left, final int right, final int top, final int bottom) { if ( left >= 0 && right >= 0 && top >= 0 && bottom >= 0 ) { + final boolean changed = left != insets.getLeftWidth() || right != insets.getRightWidth() || + top != insets.getTopHeight() || bottom != insets.getBottomHeight(); + if( blockInsetsChange || isUndecorated() ) { if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.insetsChanged (defer: "+defer+"): Skip insets change "+insets+" -> "+new Insets(left, right, top, bottom)+" (blocked "+blockInsetsChange+", undecoration "+isUndecorated()+")"); + if( changed ) { + System.err.println("Window.insetsChanged (defer: "+defer+"): Skip insets change "+insets+" -> "+new Insets(left, right, top, bottom)+" (blocked "+blockInsetsChange+", undecoration "+isUndecorated()+")"); + } } - } else if ( (left != insets.getLeftWidth() || right != insets.getRightWidth() || - top != insets.getTopHeight() || bottom != insets.getBottomHeight() ) - ) { + } else if ( changed ) { if(DEBUG_IMPLEMENTATION) { System.err.println("Window.insetsChanged (defer: "+defer+"): Changed "+insets+" -> "+new Insets(left, right, top, bottom)); } @@ -4485,24 +4721,157 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // Accumulated actions // - /** Triggered by implementation's WM events to update the client-area position, size and maximized flags. */ - protected void sizePosMaxInsetsChanged(final boolean defer, - final int newX, final int newY, - final int newWidth, final int newHeight, - final boolean newMaxHorz, final boolean newMaxVert, - final int left, final int right, final int top, final int bottom, - final boolean force) { - sizeChanged(defer, newWidth, newHeight, force); - positionChanged(defer, newX, newY); - maximizedChanged(newMaxHorz, newMaxVert); - insetsChanged(defer, left, right, top, bottom); - } /** Triggered by implementation. */ protected final void sendMouseEventRequestFocus(final short eventType, final int modifiers, - final int x, final int y, final short button, final float rotation) { + final int x, final int y, final short button, final float rotation) { sendMouseEvent(eventType, modifiers, x, y, button, rotation); requestFocus(false /* wait */); } + /** + * Triggered by implementation's WM events to update the visibility state and send- or enqueue one mouse event + * + * @param defer + * @param visibleChange -1 ignored, 0 invisible, > 0 visible + * @param entranceChange -1 ignored, 0 exit, > 0 enter + * @param eventType 0 ignored, > 0 [send|enqueue]MouseEvent + * @param modifiers + * @param x + * @param y + * @param button + * @param rotation + */ + protected final void visibleChangedSendMouseEvent(final boolean defer, final int visibleChange, + final short eventType, final int modifiers, + final int x, final int y, final short button, final float rotation) { + if( 0 <= visibleChange ) { // ignore visible < 0 + visibleChanged(defer, 0 < visibleChange); + } + if( 0 < eventType ) { + if( defer ) { + enqueueMouseEvent(false /* wait */, eventType, modifiers, x, y, button, rotation); + } else { + sendMouseEvent(eventType, modifiers, x, y, button, rotation); + } + } + } + /** + * Triggered by implementation's WM events to update the content + * @param defer if true sent event later, otherwise wait until processed. + * @param visibleChange -1 ignored, 0 invisible, > 0 visible + * @param x dirty-region y-pos in pixel units + * @param y dirty-region x-pos in pixel units + * @param width dirty-region width in pixel units + * @param height dirty-region height in pixel units + */ + protected final void visibleChangedWindowRepaint(final boolean defer, final int visibleChange, + final int x, final int y, final int width, final int height) { + if( 0 <= visibleChange ) { // ignore visible < 0 + visibleChanged(defer, 0 < visibleChange); + } + windowRepaint(defer, x, y, width, height); + } + /** + * Triggered by implementation's WM events to update the focus and visibility state + * + * @param defer + * @param focusChange -1 ignored, 0 unfocused, > 0 focused + * @param visibleChange -1 ignored, 0 invisible, > 0 visible + */ + protected final void focusVisibleChanged(final boolean defer, + final int focusChange, + final int visibleChange) { + if( 0 <= focusChange ) { // ignore focus < 0 + focusChanged(defer, 0 < focusChange); + } + if( 0 <= visibleChange ) { // ignore visible < 0 + visibleChanged(defer, 0 < visibleChange); + } + } + /** + * Triggered by implementation's WM events to update the client-area position, size, insets and maximized flags. + * + * @param defer + * @param left insets, -1 ignored + * @param right insets, -1 ignored + * @param top insets, -1 ignored + * @param bottom insets, -1 ignored + * @param visibleChange -1 ignored, 0 invisible, > 0 visible + */ + protected final void insetsVisibleChanged(final boolean defer, + final int left, final int right, final int top, final int bottom, + final int visibleChange) { + insetsChanged(defer, left, right, top, bottom); + if( 0 <= visibleChange ) { // ignore visible < 0 + visibleChanged(defer, 0 < visibleChange); + } + } + /** + * Triggered by implementation's WM events to update the client-area position, size, insets and maximized flags. + * + * @param defer + * @param newX + * @param newY + * @param newWidth + * @param newHeight + * @param left insets, -1 ignored + * @param right insets, -1 ignored + * @param top insets, -1 ignored + * @param bottom insets, -1 ignored + * @param focusChange -1 ignored, 0 unfocused, > 0 focused + * @param visibleChange -1 ignored, 0 invisible, > 0 visible + * @param force + */ + protected final void sizePosInsetsFocusVisibleChanged(final boolean defer, + final int newX, final int newY, + final int newWidth, final int newHeight, + final int left, final int right, final int top, final int bottom, + final int focusChange, + final int visibleChange, + final boolean force) { + sizeChanged(defer, newWidth, newHeight, force); + positionChanged(defer, newX, newY); + insetsChanged(defer, left, right, top, bottom); + if( 0 <= focusChange ) { // ignore focus < 0 + focusChanged(defer, 0 < focusChange); + } + if( 0 <= visibleChange ) { // ignore visible < 0 + visibleChanged(defer, 0 < visibleChange); + } + } + /** + * Triggered by implementation's WM events to update the client-area position, size, insets and maximized flags. + * + * @param defer + * @param newX + * @param newY + * @param newWidth + * @param newHeight + * @param maxHorzChange -1 ignored, 0 !maximized, > 0 maximized + * @param maxVertChange -1 ignored, 0 !maximized, > 0 maximized + * @param left insets, -1 ignored + * @param right insets, -1 ignored + * @param top insets, -1 ignored + * @param bottom insets, -1 ignored + * @param visibleChange -1 ignored, 0 invisible, > 0 visible + * @param force + */ + protected final void sizePosMaxInsetsVisibleChanged(final boolean defer, + final int newX, final int newY, + final int newWidth, final int newHeight, + final int maxHorzChange, final int maxVertChange, + final int left, final int right, final int top, final int bottom, + final int visibleChange, + final boolean force) { + sizeChanged(defer, newWidth, newHeight, force); + positionChanged(defer, newX, newY); + if( 0 <= maxHorzChange && 0 <= maxVertChange ) { + maximizedChanged(0 < maxHorzChange, 0 < maxVertChange); + } + insetsChanged(defer, left, right, top, bottom); + if( 0 <= visibleChange ) { // ignore visible < 0 + visibleChanged(defer, 0 < visibleChange); + } + } // // Reflection helper .. diff --git a/src/newt/classes/jogamp/newt/awt/NewtFactoryAWT.java b/src/newt/classes/jogamp/newt/awt/NewtFactoryAWT.java index cc71fb559..4e9273e83 100644 --- a/src/newt/classes/jogamp/newt/awt/NewtFactoryAWT.java +++ b/src/newt/classes/jogamp/newt/awt/NewtFactoryAWT.java @@ -55,41 +55,6 @@ public class NewtFactoryAWT extends NewtFactory { public static final boolean DEBUG_IMPLEMENTATION = Debug.debug("Window"); /** - * @deprecated Use {@link #getNativeWindow(java.awt.Component, AWTGraphicsConfiguration)} - * - * Wraps an AWT component into a {@link com.jogamp.nativewindow.NativeWindow} utilizing the {@link com.jogamp.nativewindow.NativeWindowFactory},<br> - * using a configuration agnostic dummy {@link com.jogamp.nativewindow.DefaultGraphicsConfiguration}.<br> - * <p> - * The actual wrapping implementation is {@link com.jogamp.nativewindow.awt.JAWTWindow}.<br></p> - * <p> - * Purpose of this wrapping is to access the AWT window handle,<br> - * not to actually render into it.<br> - * Hence the dummy configuration only.</p> - * - * @param awtCompObject must be of type java.awt.Component - */ - public static JAWTWindow getNativeWindow(final Object awtCompObject, final CapabilitiesImmutable capsRequested) { - if(null==awtCompObject) { - throw new NativeWindowException("Null AWT Component"); - } - if( ! (awtCompObject instanceof java.awt.Component) ) { - throw new NativeWindowException("AWT Component not a java.awt.Component"); - } - return getNativeWindow( (java.awt.Component) awtCompObject, capsRequested ); - } - - /** - * @deprecated Use {@link #getNativeWindow(java.awt.Component, AWTGraphicsConfiguration)} - * @param awtComp - * @param capsRequested - * @return - */ - public static JAWTWindow getNativeWindow(final java.awt.Component awtComp, final CapabilitiesImmutable capsRequested) { - final AWTGraphicsConfiguration awtConfig = AWTGraphicsConfiguration.create(awtComp, null, capsRequested); - return getNativeWindow(awtComp, awtConfig); - } - - /** * Wraps an AWT component into a {@link com.jogamp.nativewindow.NativeWindow} utilizing the {@link com.jogamp.nativewindow.NativeWindowFactory},<br> * using the given {@link AWTGraphicsConfiguration}. * <p> diff --git a/src/newt/classes/jogamp/newt/driver/android/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/android/WindowDriver.java index 959e75df8..af5d08da0 100644 --- a/src/newt/classes/jogamp/newt/driver/android/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/android/WindowDriver.java @@ -38,7 +38,6 @@ import com.jogamp.nativewindow.CapabilitiesImmutable; import com.jogamp.nativewindow.DefaultGraphicsScreen; import com.jogamp.nativewindow.NativeWindowException; import com.jogamp.nativewindow.VisualIDHolder; -import com.jogamp.nativewindow.util.Insets; import com.jogamp.nativewindow.util.Point; import com.jogamp.nativewindow.util.RectangleImmutable; import com.jogamp.opengl.GLCapabilitiesChooser; @@ -455,6 +454,11 @@ public class WindowDriver extends jogamp.newt.WindowImpl implements Callback2 { } @Override + protected final int getSupportedReconfigMaskImpl() { + return minimumReconfigStateMask; + } + + @Override protected final boolean reconfigureWindowImpl(final int x, final int y, final int width, final int height, final int flags) { boolean res = true; diff --git a/src/newt/classes/jogamp/newt/driver/awt/AWTCanvas.java b/src/newt/classes/jogamp/newt/driver/awt/AWTCanvas.java index 40e3bf85a..bdf78386a 100644 --- a/src/newt/classes/jogamp/newt/driver/awt/AWTCanvas.java +++ b/src/newt/classes/jogamp/newt/driver/awt/AWTCanvas.java @@ -60,13 +60,14 @@ import com.jogamp.newt.Window; @SuppressWarnings("serial") public class AWTCanvas extends Canvas { - private GraphicsDevice device; - private GraphicsConfiguration chosen; - private AWTGraphicsConfiguration awtConfig; - private volatile JAWTWindow jawtWindow=null; // the JAWTWindow presentation of this AWT Canvas, bound to the 'drawable' lifecycle - private CapabilitiesChooser chooser=null; + private final WindowDriver driver; private final CapabilitiesImmutable capabilities; + private final CapabilitiesChooser chooser; private final UpstreamScalable upstreamScale; + private GraphicsConfiguration chosen; + private volatile GraphicsDevice device; + private volatile AWTGraphicsConfiguration awtConfig; + private volatile JAWTWindow jawtWindow=null; // the JAWTWindow presentation of this AWT Canvas, bound to the 'drawable' lifecycle public static interface UpstreamScalable { float[] getReqPixelScale(); @@ -75,12 +76,15 @@ public class AWTCanvas extends Canvas { private boolean displayConfigChanged=false; - public AWTCanvas(final CapabilitiesImmutable capabilities, final CapabilitiesChooser chooser, final UpstreamScalable upstreamScale) { + public AWTCanvas(final WindowDriver driver, final CapabilitiesImmutable capabilities, final CapabilitiesChooser chooser, final UpstreamScalable upstreamScale) { super(); - if(null==capabilities) { throw new NativeWindowException("Capabilities null"); } + if(null==driver) { + throw new NativeWindowException("driver null"); + } + this.driver = driver; this.capabilities=capabilities; this.chooser=chooser; this.upstreamScale = upstreamScale; @@ -117,6 +121,9 @@ public class AWTCanvas extends Canvas { @Override public void addNotify() { + // before native peer is valid: X11 + disableBackgroundErase(); + /** * 'super.addNotify()' determines the GraphicsConfiguration, * while calling this class's overriden 'getGraphicsConfiguration()' method @@ -134,8 +141,7 @@ public class AWTCanvas extends Canvas { } chosen = awtConfig.getAWTGraphicsConfiguration(); - // before native peer is valid: X11 - disableBackgroundErase(); + setAWTGraphicsConfiguration(awtConfig); // issues getGraphicsConfiguration() and creates the native peer super.addNotify(); @@ -146,16 +152,20 @@ public class AWTCanvas extends Canvas { { jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig); // trigger initialization cycle - jawtWindow.setSurfaceScale(upstreamScale.getReqPixelScale() ); jawtWindow.lockSurface(); - upstreamScale.setHasPixelScale(jawtWindow.getCurrentSurfaceScale(new float[2])); - jawtWindow.unlockSurface(); + try { + jawtWindow.setSurfaceScale(upstreamScale.getReqPixelScale() ); + upstreamScale.setHasPixelScale(jawtWindow.getCurrentSurfaceScale(new float[2])); + } finally { + jawtWindow.unlockSurface(); + } } final GraphicsConfiguration gc = super.getGraphicsConfiguration(); if(null!=gc) { device = gc.getDevice(); } + driver.localCreate(); if(Window.DEBUG_IMPLEMENTATION) { System.err.println(getThreadName()+": AWTCanvas.addNotify.X"); } @@ -170,16 +180,28 @@ public class AWTCanvas extends Canvas { return null != jawtWindow ? jawtWindow.isOffscreenLayerSurfaceEnabled() : false; } + private void setAWTGraphicsConfiguration(final AWTGraphicsConfiguration config) { + // Cache awtConfig + awtConfig = config; + if( null != jawtWindow ) { + // Notify JAWTWindow .. + jawtWindow.setAWTGraphicsConfiguration(config); + } + } + @Override public void removeNotify() { + if(Window.DEBUG_IMPLEMENTATION) { + System.err.println(getThreadName()+": AWTCanvas.removeNotify.0: Created Config: "+awtConfig); + } try { - dispose(); + driver.localDestroy(); } finally { super.removeNotify(); } } - private void dispose() { + void dispose() { if( null != jawtWindow ) { jawtWindow.destroy(); if(Window.DEBUG_IMPLEMENTATION) { @@ -198,6 +220,7 @@ public class AWTCanvas extends Canvas { System.err.println(getThreadName()+": AWTCanvas.dispose(): closed GraphicsDevice: "+adeviceMsg+", result: "+closed); } } + awtConfig = null; } private String getThreadName() { return Thread.currentThread().getName(); } @@ -255,9 +278,9 @@ public class AWTCanvas extends Canvas { * block, both devices should have the same visual list, and the * same configuration should be selected here. */ - final AWTGraphicsConfiguration config = chooseGraphicsConfiguration( + final AWTGraphicsConfiguration newConfig = chooseGraphicsConfiguration( awtConfig.getChosenCapabilities(), awtConfig.getRequestedCapabilities(), chooser, gc.getDevice()); - final GraphicsConfiguration compatible = (null!=config)?config.getAWTGraphicsConfiguration():null; + final GraphicsConfiguration compatible = (null!=newConfig)?newConfig.getAWTGraphicsConfiguration():null; if(Window.DEBUG_IMPLEMENTATION) { final Exception e = new Exception("Info: Call Stack: "+Thread.currentThread().getName()); e.printStackTrace(); @@ -265,8 +288,8 @@ public class AWTCanvas extends Canvas { System.err.println("Created Config (n): THIS GC "+gc); System.err.println("Created Config (n): Choosen GC "+compatible); System.err.println("Created Config (n): HAVE CF "+awtConfig); - System.err.println("Created Config (n): Choosen CF "+config); - System.err.println("Created Config (n): EQUALS CAPS "+config.getChosenCapabilities().equals(awtConfig.getChosenCapabilities())); + System.err.println("Created Config (n): Choosen CF "+newConfig); + System.err.println("Created Config (n): EQUALS CAPS "+newConfig.getChosenCapabilities().equals(awtConfig.getChosenCapabilities())); } if (compatible != null) { @@ -275,10 +298,10 @@ public class AWTCanvas extends Canvas { * any outside callers of this method. */ chosen = compatible; - if( !config.getChosenCapabilities().equals(awtConfig.getChosenCapabilities())) { + if( !newConfig.getChosenCapabilities().equals(awtConfig.getChosenCapabilities())) { displayConfigChanged=true; } - awtConfig = config; + setAWTGraphicsConfiguration(newConfig); } } diff --git a/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java b/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java index 9d3121635..3d9073769 100644 --- a/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java +++ b/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java @@ -33,6 +33,8 @@ import java.awt.EventQueue; import com.jogamp.nativewindow.NativeWindowException; import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.util.InterruptSource; +import com.jogamp.common.util.InterruptedRuntimeException; import com.jogamp.common.util.RunnableTask; import com.jogamp.common.util.awt.AWTEDTExecutor; import com.jogamp.newt.util.EDTUtil; @@ -54,7 +56,7 @@ public class AWTEDTUtil implements EDTUtil { this.threadGroup = tg; this.name=Thread.currentThread().getName()+"-"+name+"-EDT-"; this.dispatchMessages=dispatchMessages; - this.nedt = new NEDT(threadGroup, name); + this.nedt = new NEDT(threadGroup, this.name); this.nedt.setDaemon(true); // don't stop JVM from shutdown .. } @@ -132,8 +134,7 @@ public class AWTEDTUtil implements EDTUtil { } private final boolean invokeImpl(boolean wait, final Runnable task, final boolean stop) { - Throwable throwable = null; - RunnableTask rTask = null; + final RunnableTask rTask; final Object rTaskLock = new Object(); synchronized(rTaskLock) { // lock the optional task execution synchronized(edtLock) { // lock the EDT status @@ -150,6 +151,7 @@ public class AWTEDTUtil implements EDTUtil { task.run(); } wait = false; // running in same thread (EDT) -> no wait + rTask = null; if(stop) { nedt.shouldStop = true; } @@ -182,18 +184,21 @@ public class AWTEDTUtil implements EDTUtil { true /* always catch and report Exceptions, don't disturb EDT */, wait ? null : System.err); AWTEDTExecutor.singleton.invoke(false, rTask); + } else { + wait = false; + rTask = null; } } } if( wait ) { try { - rTaskLock.wait(); // free lock, allow execution of rTask + while( rTask.isInQueue() ) { + rTaskLock.wait(); // free lock, allow execution of rTask + } } catch (final InterruptedException ie) { - throwable = ie; - } - if(null==throwable) { - throwable = rTask.getThrowable(); + throw new InterruptedRuntimeException(ie); } + final Throwable throwable = rTask.getThrowable(); if(null!=throwable) { if(throwable instanceof NativeWindowException) { throw (NativeWindowException)throwable; @@ -227,12 +232,12 @@ public class AWTEDTUtil implements EDTUtil { final public boolean waitUntilStopped() { synchronized(edtLock) { if( nedt.isRunning && nedt != Thread.currentThread() && !EventQueue.isDispatchThread() ) { - while( nedt.isRunning ) { - try { + try { + while( nedt.isRunning ) { edtLock.wait(); - } catch (final InterruptedException e) { - e.printStackTrace(); } + } catch (final InterruptedException e) { + throw new InterruptedRuntimeException(e); } return true; } else { @@ -241,13 +246,13 @@ public class AWTEDTUtil implements EDTUtil { } } - class NEDT extends Thread { + class NEDT extends InterruptSource.Thread { volatile boolean shouldStop = false; volatile boolean isRunning = false; Object sync = new Object(); public NEDT(final ThreadGroup tg, final String name) { - super(tg, name); + super(tg, null, name); } final public boolean isRunning() { @@ -286,7 +291,7 @@ public class AWTEDTUtil implements EDTUtil { try { sync.wait(pollPeriod); } catch (final InterruptedException e) { - e.printStackTrace(); + throw new InterruptedRuntimeException(e); } } } diff --git a/src/newt/classes/jogamp/newt/driver/awt/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/awt/WindowDriver.java index 222ac8ae9..aa93dd9aa 100644 --- a/src/newt/classes/jogamp/newt/driver/awt/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/awt/WindowDriver.java @@ -74,6 +74,8 @@ public class WindowDriver extends WindowImpl { public WindowDriver(final Container container) { super(); + this.withinLocalDispose = false; + this.addWindowListener(0, new NEWTWindowListener()); this.awtContainer = container; if(container instanceof Frame) { awtFrame = (Frame) container; @@ -85,6 +87,7 @@ public class WindowDriver extends WindowImpl { /** same instance as container, just for impl. convenience */ private Frame awtFrame = null; private AWTCanvas awtCanvas; + private volatile boolean withinLocalDispose; @Override protected void requestFocusImpl(final boolean reparented) { @@ -112,64 +115,98 @@ public class WindowDriver extends WindowImpl { @Override protected void createNativeImpl() { - if(0!=getParentWindowHandle()) { - throw new RuntimeException("Window parenting not supported in AWT, use AWTWindow(Frame) cstr for wrapping instead"); - } - - if(null==awtContainer) { - awtFrame = new Frame(); - awtContainer = awtFrame; - owningFrame=true; + if( withinLocalDispose ) { + setupHandleAndGC(); + definePosition(getX(), getY()); // clear AUTOPOS + visibleChanged(false, true); + withinLocalDispose = false; } else { - owningFrame=false; - defineSize(awtContainer.getWidth(), awtContainer.getHeight()); - definePosition(awtContainer.getX(), awtContainer.getY()); - } - if(null!=awtFrame) { - awtFrame.setTitle(getTitle()); - } - awtContainer.setLayout(new BorderLayout()); + if(0!=getParentWindowHandle()) { + throw new RuntimeException("Window parenting not supported in AWT, use AWTWindow(Frame) cstr for wrapping instead"); + } - if( null == awtCanvas ) { - awtCanvas = new AWTCanvas(capsRequested, WindowDriver.this.capabilitiesChooser, upstreamScalable); + if(null==awtContainer) { + awtFrame = new Frame(); + awtContainer = awtFrame; + owningFrame=true; + } else { + owningFrame=false; + defineSize(awtContainer.getWidth(), awtContainer.getHeight()); + definePosition(awtContainer.getX(), awtContainer.getY()); + } + if(null!=awtFrame) { + awtFrame.setTitle(getTitle()); + } + awtContainer.setLayout(new BorderLayout()); + + if( null == awtCanvas ) { + awtCanvas = new AWTCanvas(this, capsRequested, WindowDriver.this.capabilitiesChooser, upstreamScalable); - // canvas.addComponentListener(listener); - awtContainer.add(awtCanvas, BorderLayout.CENTER); + // canvas.addComponentListener(listener); + awtContainer.add(awtCanvas, BorderLayout.CENTER); - // via EDT .. - new AWTMouseAdapter(this).addTo(awtCanvas); // fwd all AWT Mouse events to here - new AWTKeyAdapter(this).addTo(awtCanvas); // fwd all AWT Key events to here + // via EDT .. + new AWTMouseAdapter(this).addTo(awtCanvas); // fwd all AWT Mouse events to here + new AWTKeyAdapter(this).addTo(awtCanvas); // fwd all AWT Key events to here - // direct w/o EDT - new AWTWindowAdapter(new LocalWindowListener(), this).addTo(awtCanvas); // fwd all AWT Window events to here + // direct w/o EDT + new AWTWindowAdapter(new AWTWindowListener(), this).addTo(awtCanvas); // fwd all AWT Window events to here + } else { + awtContainer.add(awtCanvas, BorderLayout.CENTER); + } + reconfigureWindowImpl(getX(), getY(), getWidth(), getHeight(), getReconfigureMask(CHANGE_MASK_VISIBILITY | CHANGE_MASK_DECORATION, true)); + // throws exception if failed .. + // AWTCanvas -> localCreate -> setupHandleAndGC(); } + } - reconfigureWindowImpl(getX(), getY(), getWidth(), getHeight(), getReconfigureMask(CHANGE_MASK_VISIBILITY | CHANGE_MASK_DECORATION, true)); - // throws exception if failed .. + private void setupHandleAndGC() { + // reconfigureWindowImpl(getX(), getY(), getWidth(), getHeight(), getReconfigureMask(CHANGE_MASK_VISIBILITY | CHANGE_MASK_DECORATION, true)); + if( null != awtCanvas ) { + final NativeWindow nw = awtCanvas.getNativeWindow(); + if( null != nw ) { + setGraphicsConfiguration( awtCanvas.getAWTGraphicsConfiguration() ); + setWindowHandle( nw.getWindowHandle() ); + } + } + } - final NativeWindow nw = awtCanvas.getNativeWindow(); - if( null != nw ) { - setGraphicsConfiguration( awtCanvas.getAWTGraphicsConfiguration() ); - setWindowHandle( nw.getWindowHandle() ); + void localCreate() { + if( withinLocalDispose ) { + setVisible(true); + } else { + setupHandleAndGC(); } } + void localDestroy() { + this.withinLocalDispose = true; + super.destroy(); + } + @Override protected void closeNativeImpl() { setWindowHandle(0); - if(null!=awtContainer) { - awtContainer.setVisible(false); - awtContainer.remove(awtCanvas); - awtContainer.setEnabled(false); - awtCanvas.setEnabled(false); - } - if(owningFrame && null!=awtFrame) { - awtFrame.dispose(); - owningFrame=false; + if( this.withinLocalDispose ) { + if(null!=awtCanvas) { + awtCanvas.dispose(); + } + } else { + if(null!=awtContainer) { + awtContainer.setVisible(false); + awtContainer.remove(awtCanvas); + awtContainer.setEnabled(false); + awtCanvas.setEnabled(false); + awtCanvas.dispose(); + } + if(owningFrame && null!=awtFrame) { + awtFrame.dispose(); + owningFrame=false; + } + awtCanvas = null; + awtFrame = null; + awtContainer = null; } - awtCanvas = null; - awtFrame = null; - awtContainer = null; } @Override @@ -225,6 +262,11 @@ public class WindowDriver extends WindowImpl { } @Override + protected final int getSupportedReconfigMaskImpl() { + return minimumReconfigStateMask; + } + + @Override protected boolean reconfigureWindowImpl(final int x, final int y, final int width, final int height, final int flags) { if(DEBUG_IMPLEMENTATION) { System.err.println("AWTWindow reconfig: "+x+"/"+y+" "+width+"x"+height+", "+ @@ -261,6 +303,7 @@ public class WindowDriver extends WindowImpl { if( awtContainer.getX() != x || awtContainer.getY() != y ) { awtContainer.setLocation(x, y); } + definePosition(x, y); if( 0 != ( CHANGE_MASK_VISIBILITY & flags) ) { if( 0 != ( STATE_MASK_VISIBLE & flags ) ) { @@ -275,6 +318,9 @@ public class WindowDriver extends WindowImpl { } visibleChanged(false, 0 != ( STATE_MASK_VISIBLE & flags)); } + if( isVisible() ) { + windowRepaint(false, 0, 0, getSurfaceWidth(), getSurfaceHeight()); + } return true; } @@ -291,7 +337,7 @@ public class WindowDriver extends WindowImpl { return ( null != awtCanvas ) ? awtCanvas.getNativeWindow() : null; } - class LocalWindowListener implements com.jogamp.newt.event.WindowListener { + class AWTWindowListener implements com.jogamp.newt.event.WindowListener { @Override public void windowMoved(final com.jogamp.newt.event.WindowEvent e) { if(null!=awtContainer) { @@ -334,4 +380,28 @@ public class WindowDriver extends WindowImpl { } } } + class NEWTWindowListener implements com.jogamp.newt.event.WindowListener { + @Override + public void windowMoved(final com.jogamp.newt.event.WindowEvent e) { } + @Override + public void windowResized(final com.jogamp.newt.event.WindowEvent e) { } + @Override + public void windowDestroyNotify(final WindowEvent e) { + if( withinLocalDispose ) { + e.setConsumed(true); + } + } + @Override + public void windowDestroyed(final WindowEvent e) { + if( withinLocalDispose ) { + e.setConsumed(true); + } + } + @Override + public void windowGainedFocus(final WindowEvent e) { } + @Override + public void windowLostFocus(final WindowEvent e) { } + @Override + public void windowRepaint(final WindowUpdateEvent e) { } + } } diff --git a/src/newt/classes/jogamp/newt/driver/bcm/egl/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/bcm/egl/WindowDriver.java index f95d23364..d19618bd1 100644 --- a/src/newt/classes/jogamp/newt/driver/bcm/egl/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/bcm/egl/WindowDriver.java @@ -38,7 +38,6 @@ import com.jogamp.nativewindow.AbstractGraphicsConfiguration; import com.jogamp.nativewindow.GraphicsConfigurationFactory; import com.jogamp.nativewindow.NativeWindowException; import com.jogamp.nativewindow.VisualIDHolder; -import com.jogamp.nativewindow.util.Insets; import com.jogamp.nativewindow.util.Point; import com.jogamp.opengl.GLCapabilitiesImmutable; @@ -94,6 +93,11 @@ public class WindowDriver extends jogamp.newt.WindowImpl { } @Override + protected final int getSupportedReconfigMaskImpl() { + return minimumReconfigStateMask; + } + + @Override protected boolean reconfigureWindowImpl(final int x, final int y, final int width, final int height, final int flags) { if(0!=getWindowHandle()) { if(0 != ( CHANGE_MASK_FULLSCREEN & flags)) { 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 d111e850e..d4af1b972 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 @@ -67,10 +67,12 @@ public class DisplayDriver extends DisplayImpl { PNGPixelRect image = null; if( DisplayImpl.isPNGUtilAvailable() ) { - final IOUtil.ClassResources res = new IOUtil.ClassResources(DisplayDriver.class, new String[] { "newt/data/pointer-grey-alpha-16x24.png" } ); + final IOUtil.ClassResources res = new IOUtil.ClassResources(new String[] { "newt/data/pointer-grey-alpha-16x24.png" }, DisplayDriver.class.getClassLoader(), null); try { final URLConnection urlConn = res.resolve(0); - image = PNGPixelRect.read(urlConn.getInputStream(), PixelFormat.BGRA8888, false /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + if( null != urlConn ) { + image = PNGPixelRect.read(urlConn.getInputStream(), PixelFormat.BGRA8888, false /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } } catch (final Exception e) { e.printStackTrace(); } @@ -96,7 +98,11 @@ public class DisplayDriver extends DisplayImpl { aDevice = EGLDisplayUtil.eglCreateEGLGraphicsDevice(EGL.EGL_DEFAULT_DISPLAY, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT); aDevice.open(); - defaultPointerIcon = (PointerIconImpl) createPointerIcon(defaultPointerIconImage, 0, 0); + if( null != defaultPointerIconImage ) { + defaultPointerIcon = (PointerIconImpl) createPointerIcon(defaultPointerIconImage, 0, 0); + } else { + defaultPointerIcon = null; + } if( DEBUG_POINTER_ICON ) { System.err.println("Display.PointerIcon.createDefault: "+defaultPointerIcon); } diff --git a/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/ScreenDriver.java b/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/ScreenDriver.java index f236edd6b..b4af4045c 100644 --- a/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/ScreenDriver.java +++ b/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/ScreenDriver.java @@ -84,11 +84,11 @@ public class ScreenDriver extends ScreenImpl { props[i++] = 0; // rotated viewport x pixel-units props[i++] = 0; // rotated viewport y pixel-units props[i++] = cachedWidth; // rotated viewport width pixel-units - props[i++] = cachedWidth; // rotated viewport height pixel-units + props[i++] = cachedHeight; // rotated viewport height pixel-units props[i++] = 0; // rotated viewport x window-units props[i++] = 0; // rotated viewport y window-units props[i++] = cachedWidth; // rotated viewport width window-units - props[i++] = cachedWidth; // rotated viewport height window-units + props[i++] = cachedHeight; // rotated viewport height window-units MonitorModeProps.streamInMonitorDevice(cache, this, currentMode, null, cache.monitorModes, props, 0, null); } diff --git a/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/WindowDriver.java index 16b0f2ee9..b0a4ee34a 100644 --- a/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/WindowDriver.java @@ -36,7 +36,6 @@ import com.jogamp.nativewindow.GraphicsConfigurationFactory; import com.jogamp.nativewindow.NativeWindowException; import com.jogamp.nativewindow.VisualIDHolder; -import com.jogamp.nativewindow.util.Insets; import com.jogamp.nativewindow.util.Point; import com.jogamp.nativewindow.util.Rectangle; import com.jogamp.nativewindow.util.RectangleImmutable; @@ -249,6 +248,20 @@ public class WindowDriver extends WindowImpl { } @Override + protected final int getSupportedReconfigMaskImpl() { + return minimumReconfigStateMask | + // STATE_MASK_UNDECORATED | + // STATE_MASK_ALWAYSONTOP | + // STATE_MASK_ALWAYSONBOTTOM | + // STATE_MASK_STICKY | + // STATE_MASK_RESIZABLE | + // STATE_MASK_MAXIMIZED_VERT | + // STATE_MASK_MAXIMIZED_HORZ | + STATE_MASK_POINTERVISIBLE | + STATE_MASK_POINTERCONFINED; + } + + @Override protected boolean reconfigureWindowImpl(final int x, final int y, final int width, final int height, final int flags) { final RectangleImmutable rect = clampRect((ScreenDriver) getScreen(), new Rectangle(x, y, width, height), false); // reconfigure0 will issue position/size changed events if required diff --git a/src/newt/classes/jogamp/newt/driver/intel/gdl/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/intel/gdl/WindowDriver.java index ceda0997b..b01928449 100644 --- a/src/newt/classes/jogamp/newt/driver/intel/gdl/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/intel/gdl/WindowDriver.java @@ -35,7 +35,6 @@ package jogamp.newt.driver.intel.gdl; import com.jogamp.nativewindow.*; -import com.jogamp.nativewindow.util.Insets; import com.jogamp.nativewindow.util.Point; public class WindowDriver extends jogamp.newt.WindowImpl { @@ -86,6 +85,11 @@ public class WindowDriver extends jogamp.newt.WindowImpl { } @Override + protected final int getSupportedReconfigMaskImpl() { + return minimumReconfigStateMask; + } + + @Override protected boolean reconfigureWindowImpl(int x, int y, int width, int height, final int flags) { final ScreenDriver screen = (ScreenDriver) getScreen(); diff --git a/src/newt/classes/jogamp/newt/driver/kd/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/kd/WindowDriver.java index c18eff217..ce5d208db 100644 --- a/src/newt/classes/jogamp/newt/driver/kd/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/kd/WindowDriver.java @@ -39,7 +39,6 @@ import com.jogamp.nativewindow.GraphicsConfigurationFactory; import com.jogamp.nativewindow.NativeWindowException; import com.jogamp.nativewindow.VisualIDHolder; import com.jogamp.nativewindow.VisualIDHolder.VIDType; -import com.jogamp.nativewindow.util.Insets; import com.jogamp.nativewindow.util.Point; import com.jogamp.opengl.GLCapabilitiesImmutable; @@ -96,6 +95,11 @@ public class WindowDriver extends WindowImpl { protected void requestFocusImpl(final boolean reparented) { } @Override + protected final int getSupportedReconfigMaskImpl() { + return minimumReconfigStateMask; + } + + @Override protected boolean reconfigureWindowImpl(final int x, final int y, int width, int height, final int flags) { if( 0 != ( CHANGE_MASK_VISIBILITY & flags) ) { setVisible0(eglWindowHandle, 0 != ( STATE_MASK_VISIBLE & flags)); diff --git a/src/newt/classes/jogamp/newt/driver/linux/LinuxEventDeviceTracker.java b/src/newt/classes/jogamp/newt/driver/linux/LinuxEventDeviceTracker.java index bc0bfaa16..c3b7bff36 100644 --- a/src/newt/classes/jogamp/newt/driver/linux/LinuxEventDeviceTracker.java +++ b/src/newt/classes/jogamp/newt/driver/linux/LinuxEventDeviceTracker.java @@ -43,6 +43,7 @@ import jogamp.newt.WindowImpl; import jogamp.newt.driver.KeyTracker; import com.jogamp.common.nio.StructAccessor; +import com.jogamp.common.util.InterruptSource; import com.jogamp.newt.Window; import com.jogamp.newt.event.InputEvent; import com.jogamp.newt.event.WindowEvent; @@ -63,7 +64,7 @@ public class LinuxEventDeviceTracker implements WindowListener, KeyTracker { static { ledt = new LinuxEventDeviceTracker(); - final Thread t = new Thread(ledt.eventDeviceManager, "NEWT-LinuxEventDeviceManager"); + final Thread t = new InterruptSource.Thread(null, ledt.eventDeviceManager, "NEWT-LinuxEventDeviceManager"); t.setDaemon(true); t.start(); } @@ -153,7 +154,7 @@ public class LinuxEventDeviceTracker implements WindowListener, KeyTracker { if(number<32&&number>=0) { if(eventDevicePollers[number]==null){ eventDevicePollers[number] = new EventDevicePoller(number); - final Thread t = new Thread(eventDevicePollers[number], "NEWT-LinuxEventDeviceTracker-event"+number); + final Thread t = new InterruptSource.Thread(null, eventDevicePollers[number], "NEWT-LinuxEventDeviceTracker-event"+number); t.setDaemon(true); t.start(); } else if(eventDevicePollers[number].stop) { diff --git a/src/newt/classes/jogamp/newt/driver/linux/LinuxMouseTracker.java b/src/newt/classes/jogamp/newt/driver/linux/LinuxMouseTracker.java index f40728da0..53bb9c3a5 100644 --- a/src/newt/classes/jogamp/newt/driver/linux/LinuxMouseTracker.java +++ b/src/newt/classes/jogamp/newt/driver/linux/LinuxMouseTracker.java @@ -37,6 +37,7 @@ import java.io.InputStream; import jogamp.newt.WindowImpl; import jogamp.newt.driver.MouseTracker; +import com.jogamp.common.util.InterruptSource; import com.jogamp.newt.Screen; import com.jogamp.newt.Window; import com.jogamp.newt.event.MouseEvent; @@ -55,7 +56,7 @@ public class LinuxMouseTracker implements WindowListener, MouseTracker { static { lmt = new LinuxMouseTracker(); - final Thread t = new Thread(lmt.mouseDevicePoller, "NEWT-LinuxMouseTracker"); + final Thread t = new InterruptSource.Thread(null, lmt.mouseDevicePoller, "NEWT-LinuxMouseTracker"); t.setDaemon(true); t.start(); } diff --git a/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java index c8146b85d..8ff37872b 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java @@ -70,7 +70,9 @@ public class DisplayDriver extends DisplayImpl { // NOTE: MUST BE DIRECT BUFFER, since NSBitmapImageRep uses buffer directly! final IOUtil.ClassResources iconRes = NewtFactory.getWindowIcons(); final URLConnection urlConn = iconRes.resolve(iconRes.resourceCount()-1); - image = PNGPixelRect.read(urlConn.getInputStream(), PixelFormat.RGBA8888, true /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + if( null != urlConn ) { + image = PNGPixelRect.read(urlConn.getInputStream(), PixelFormat.RGBA8888, true /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } } catch (final Exception e) { e.printStackTrace(); } diff --git a/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java index 0ab400fa3..e6ae7719c 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java @@ -34,6 +34,7 @@ package jogamp.newt.driver.macosx; +import com.jogamp.common.util.InterruptSource; import com.jogamp.nativewindow.AbstractGraphicsConfiguration; import com.jogamp.nativewindow.GraphicsConfigurationFactory; import com.jogamp.nativewindow.NativeWindow; @@ -41,7 +42,6 @@ import com.jogamp.nativewindow.NativeWindowException; import com.jogamp.nativewindow.MutableSurface; import com.jogamp.nativewindow.ScalableSurface; import com.jogamp.nativewindow.VisualIDHolder; -import com.jogamp.nativewindow.util.Insets; import com.jogamp.nativewindow.util.Point; import com.jogamp.nativewindow.util.PointImmutable; @@ -119,11 +119,10 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl /** Called from native code */ protected void updatePixelScale(final boolean defer, final float newPixelScaleRaw, final float maxPixelScaleRaw) { - final long handle = getWindowHandle(); if( DEBUG_IMPLEMENTATION ) { - System.err.println("WindowDriver.updatePixelScale.3: "+hasPixelScale[0]+" (has) -> "+newPixelScaleRaw+" (new), "+maxPixelScaleRaw+" (max), drop "+(0==handle)); + System.err.println("WindowDriver.updatePixelScale.3: "+hasPixelScale[0]+" (has) -> "+newPixelScaleRaw+" (new), "+maxPixelScaleRaw+" (max), drop "+!isNativeValid()); } - if( 0 != handle ) { + if( isNativeValid() ) { updatePixelScale(true /* sendEvent*/, defer, true /*offthread */, newPixelScaleRaw, maxPixelScaleRaw); } } @@ -197,7 +196,7 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl } setGraphicsConfiguration(cfg); reconfigureWindowImpl(getX(), getY(), getWidth(), getHeight(), getReconfigureMask(CHANGE_MASK_VISIBILITY, true)); - if (0 == getWindowHandle()) { + if ( !isNativeValid() ) { throw new NativeWindowException("Error creating window"); } } @@ -212,6 +211,7 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl surfaceHandle = 0; sscSurfaceHandle = 0; isOffscreenInstance = false; + resizeAnimatorPaused = false; if (0 != handle) { OSXUtil.RunOnMainThread(false, true /* kickNSApp */, new Runnable() { @Override @@ -269,7 +269,7 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl System.err.println("MacWindow.setSurfaceHandle(): 0x"+Long.toHexString(surfaceHandle)); } sscSurfaceHandle = surfaceHandle; - if (isNativeValid()) { + if ( isNativeValid() ) { if (0 != sscSurfaceHandle) { OSXUtil.RunOnMainThread(false, false, new Runnable() { @Override @@ -341,15 +341,20 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl if( 0 != handle && !isOffscreenInstance ) { final NativeWindow parent = getParent(); final boolean useParent = useParent(parent); - final int pX=parent.getX(), pY=parent.getY(); - final Point p0S = getLocationOnScreenImpl(x, y, parent, useParent); + final Point p0S; + if( useParent ) { + p0S = getLocationOnScreenByParent(x, y, parent); + } else { + p0S = (Point) getLocationOnScreen0(handle, x, y); + } if(DEBUG_IMPLEMENTATION) { + final int pX=parent.getX(), pY=parent.getY(); System.err.println("MacWindow: updatePosition() parent["+useParent+" "+pX+"/"+pY+"] "+x+"/"+y+" -> "+x+"/"+y+" rel-client-pos, "+p0S+" screen-client-pos"); } OSXUtil.RunOnMainThread(false, false, new Runnable() { @Override public void run() { - setWindowClientTopLeftPoint0(handle, p0S.getX(), p0S.getY(), isVisible()); + setWindowClientTopLeftPoint0(getWindowHandle(), p0S.getX(), p0S.getY(), isVisible()); } } ); // no native event (fullscreen, some reparenting) positionChanged(true, x, y); @@ -357,39 +362,20 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl } @Override - protected void sizeChanged(final boolean defer, final int newWidth, final int newHeight, final boolean force) { - final long handle = getWindowHandle(); - if( 0 != handle && !isOffscreenInstance ) { - final NativeWindow parent = getParent(); - final boolean useParent = useParent(parent); - if( useParent && ( getWidth() != newWidth || getHeight() != newHeight ) ) { - final int x=getX(), y=getY(); - final Point p0S = getLocationOnScreenImpl(x, y, parent, useParent); - if(DEBUG_IMPLEMENTATION) { - System.err.println("MacWindow: sizeChanged() parent["+useParent+" "+x+"/"+y+"] "+getX()+"/"+getY()+" "+newWidth+"x"+newHeight+" -> "+p0S+" screen-client-pos"); - } - OSXUtil.RunOnMainThread(false, false, new Runnable() { - @Override - public void run() { - setWindowClientTopLeftPoint0(getWindowHandle(), p0S.getX(), p0S.getY(), isVisible()); - } } ); - } - } - superSizeChangedOffThread(defer, newWidth, newHeight, force); - } - private void superSizeChangedOffThread(final boolean defer, final int newWidth, final int newHeight, final boolean force) { - if( defer ) { - new Thread() { - public void run() { - WindowDriver.super.sizeChanged(false /* defer */, newWidth, newHeight, force); - } }.start(); - } else { - WindowDriver.super.sizeChanged(false /* defer */, newWidth, newHeight, force); - } + protected final int getSupportedReconfigMaskImpl() { + return minimumReconfigStateMask | + STATE_MASK_CHILDWIN | + STATE_MASK_UNDECORATED | + STATE_MASK_ALWAYSONTOP | + STATE_MASK_ALWAYSONBOTTOM | + STATE_MASK_STICKY | + STATE_MASK_RESIZABLE | + STATE_MASK_MAXIMIZED_VERT | + STATE_MASK_MAXIMIZED_HORZ | + STATE_MASK_POINTERVISIBLE | + STATE_MASK_POINTERCONFINED; } - private final int[] normPosSize = { 0, 0, 0, 0 }; - @Override protected boolean reconfigureWindowImpl(int _x, int _y, int _width, int _height, final int flags) { final boolean _isOffscreenInstance = isOffscreenInstance(this, this.getParent()); @@ -400,9 +386,8 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl pClientLevelOnSreen = new Point(0, 0); } else { final NativeWindow parent = getParent(); - final boolean useParent = useParent(parent); - if( useParent ) { - pClientLevelOnSreen = getLocationOnScreenImpl(_x, _y, parent, useParent); + if( useParent(parent) ) { + pClientLevelOnSreen = getLocationOnScreenByParent(_x, _y, parent); } else { if( 0 != ( ( CHANGE_MASK_MAXIMIZED_HORZ | CHANGE_MASK_MAXIMIZED_VERT ) & flags ) ) { final int[] posSize = { _x, _y, _width, _height }; @@ -424,7 +409,7 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl final AbstractGraphicsConfiguration cWinCfg = this.getGraphicsConfiguration(); final NativeWindow pWin = getParent(); final AbstractGraphicsConfiguration pWinCfg = null != pWin ? pWin.getGraphicsConfiguration() : null; - System.err.println("MacWindow reconfig.0: "+x+"/"+y+" -> clientPos "+pClientLevelOnSreen+" - "+width+"x"+height+ + System.err.println("MacWindow reconfig.0: "+x+"/"+y+" -> clientPosOnScreen "+pClientLevelOnSreen+" - "+width+"x"+height+ ", "+getReconfigStateMaskString(flags)+ ",\n\t parent type "+(null != pWin ? pWin.getClass().getName() : null)+ ",\n\t this-chosenCaps "+(null != cWinCfg ? cWinCfg.getChosenCapabilities() : null)+ @@ -449,38 +434,47 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl visibleChanged(true, false); } } - if( ( 0 == getWindowHandle() && 0 != ( STATE_MASK_VISIBLE & flags) ) || + final long oldWindowHandle = getWindowHandle(); + if( ( 0 == oldWindowHandle && 0 != ( STATE_MASK_VISIBLE & flags) ) || 0 != ( CHANGE_MASK_PARENTING & flags) || 0 != ( CHANGE_MASK_DECORATION & flags) || + 0 != ( CHANGE_MASK_ALWAYSONTOP & flags) || + 0 != ( CHANGE_MASK_ALWAYSONBOTTOM & flags) || 0 != ( CHANGE_MASK_RESIZABLE & flags) || 0 != ( CHANGE_MASK_FULLSCREEN & flags) ) { if(isOffscreenInstance) { - createWindow(true, 0 != getWindowHandle(), pClientLevelOnSreen, 64, 64, flags); + createWindow(true, 0 != oldWindowHandle, pClientLevelOnSreen, 64, 64, flags); } else { - createWindow(false, 0 != getWindowHandle(), pClientLevelOnSreen, width, height, flags); + createWindow(false, 0 != oldWindowHandle, pClientLevelOnSreen, width, height, flags); } // no native event (fullscreen, some reparenting) - positionChanged(false, x, y); updatePixelScaleByWindowHandle(false /* sendEvent */); - super.sizeChanged(false, width, height, true); + if( isOffscreenInstance) { + super.sizeChanged(false, width, height, true); + positionChanged(false, x, y); + } else { + updateSizePosInsets0(getWindowHandle(), false); + } visibleChanged(false, 0 != ( STATE_MASK_VISIBLE & flags)); if( hasFocus ) { requestFocusImpl(true); } - } else { + } else if( 0 != oldWindowHandle ) { if( width>0 && height>0 ) { if( !isOffscreenInstance ) { OSXUtil.RunOnMainThread(true, false, new Runnable() { @Override public void run() { - setWindowClientTopLeftPointAndSize0(getWindowHandle(), + setWindowClientTopLeftPointAndSize0(oldWindowHandle, pClientLevelOnSreen.getX(), pClientLevelOnSreen.getY(), width, height, 0 != ( STATE_MASK_VISIBLE & flags)); } } ); - } // else offscreen size is realized via recreation - // no native event (fullscreen, some reparenting) - positionChanged(true, x, y); - super.sizeChanged(true, width, height, false); + updateSizePosInsets0(oldWindowHandle, false); + } else { // else offscreen size is realized via recreation + // no native event (fullscreen, some reparenting) + super.sizeChanged(false, width, height, false); + positionChanged(false, x, y); + } } if( 0 != ( CHANGE_MASK_VISIBILITY & flags) && 0 != ( STATE_MASK_VISIBLE & flags) ) @@ -496,13 +490,11 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl visibleChanged(true, true); } } - if( !isOffscreenInstance ) { - setAlwaysOnTop0(getWindowHandle(), 0 != ( STATE_MASK_ALWAYSONTOP & flags)); - setAlwaysOnBottom0(getWindowHandle(), 0 != ( STATE_MASK_ALWAYSONBOTTOM & flags)); - } + } else { + throw new InternalError("Null windowHandle but no re-creation triggered, check visibility: "+getStateMaskString()); } if(DEBUG_IMPLEMENTATION) { - System.err.println("MaxWindow reconfig.X: "+getLocationOnScreenImpl(0, 0)+" "+getWidth()+"x"+getHeight()+", insets "+getInsets()+", "+getStateMaskString()); + System.err.println("MacWindow reconfig.X: "+getLocationOnScreenImpl(0, 0)+" "+getWidth()+"x"+getHeight()+", insets "+getInsets()+", "+getStateMaskString()); } return true; } @@ -510,41 +502,50 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl @Override protected Point getLocationOnScreenImpl(final int x, final int y) { final NativeWindow parent = getParent(); - final boolean useParent = useParent(parent); - return getLocationOnScreenImpl(x, y, parent, useParent); - } - - private Point getLocationOnScreenImpl(final int x, final int y, final NativeWindow parent, final boolean useParent) { - if( !useParent && !isOffscreenInstance && 0 != surfaceHandle) { - return OSXUtil.GetLocationOnScreen(surfaceHandle, x, y); + if( useParent(parent) ) { + return getLocationOnScreenByParent(x, y, parent); + } else { + final long windowHandle = getWindowHandle(); + if( !isOffscreenInstance && 0 != windowHandle ) { + return (Point) getLocationOnScreen0(windowHandle, x, y); + } else { + return new Point(x, y); + } } + } - final Point p = new Point(x, y); - if( useParent ) { - p.translate( parent.getLocationOnScreen(null) ); - } - return p; + private Point getLocationOnScreenByParent(final int x, final int y, final NativeWindow parent) { + return new Point(x, y).translate( parent.getLocationOnScreen(null) ); } /** Callback for native screen position change event of the client area. */ protected void screenPositionChanged(final boolean defer, final int newX, final int newY) { // passed coordinates are in screen position of the client area - if(getWindowHandle()!=0) { + if( isNativeValid() ) { final NativeWindow parent = getParent(); - if( null == parent || isOffscreenInstance ) { + if( !useParent(parent) || isOffscreenInstance ) { if(DEBUG_IMPLEMENTATION) { System.err.println("MacWindow.positionChanged.0 (Screen Pos - TOP): ("+getThreadName()+"): (defer: "+defer+") "+getX()+"/"+getY()+" -> "+newX+"/"+newY); } positionChanged(defer, newX, newY); } else { - // screen position -> rel child window position - final Point absPos = new Point(newX, newY); - final Point parentOnScreen = parent.getLocationOnScreen(null); - absPos.translate( parentOnScreen.scale(-1, -1) ); - if(DEBUG_IMPLEMENTATION) { - System.err.println("MacWindow.positionChanged.1 (Screen Pos - CHILD): ("+getThreadName()+"): (defer: "+defer+") "+getX()+"/"+getY()+" -> absPos "+newX+"/"+newY+", parentOnScreen "+parentOnScreen+" -> "+absPos); + final Runnable action = new Runnable() { + public void run() { + // screen position -> rel child window position + final Point absPos = new Point(newX, newY); + final Point parentOnScreen = parent.getLocationOnScreen(null); + absPos.translate( parentOnScreen.scale(-1, -1) ); + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow.positionChanged.1 (Screen Pos - CHILD): ("+getThreadName()+"): (defer: "+defer+") "+getX()+"/"+getY()+" -> absPos "+newX+"/"+newY+", parentOnScreen "+parentOnScreen+" -> "+absPos); + } + positionChanged(false, absPos.getX(), absPos.getY()); + } }; + if( defer ) { + new InterruptSource.Thread(null, action).start(); + } else { + action.run(); } - positionChanged(defer, absPos.getX(), absPos.getY()); + } } else if(DEBUG_IMPLEMENTATION) { System.err.println("MacWindow.positionChanged.2 (Screen Pos - IGN): ("+getThreadName()+"): (defer: "+defer+") "+getX()+"/"+getY()+" -> "+newX+"/"+newY); @@ -552,6 +553,65 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl } @Override + protected void sizeChanged(final boolean defer, final int newWidth, final int newHeight, final boolean force) { + if(force || getWidth() != newWidth || getHeight() != newHeight) { + if( isNativeValid() && !isOffscreenInstance ) { + final NativeWindow parent = getParent(); + final boolean useParent = useParent(parent); + if( useParent ) { + final int x=getX(), y=getY(); + final Point p0S = getLocationOnScreenByParent(x, y, parent); + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow: sizeChanged() parent["+useParent+" "+x+"/"+y+"] "+getX()+"/"+getY()+" "+newWidth+"x"+newHeight+" -> "+p0S+" screen-client-pos"); + } + OSXUtil.RunOnMainThread(false, false, new Runnable() { + @Override + public void run() { + setWindowClientTopLeftPoint0(getWindowHandle(), p0S.getX(), p0S.getY(), isVisible()); + } } ); + } + } + superSizeChangedOffThread(defer, newWidth, newHeight, force); + } + } + private void superSizeChangedOffThread(final boolean defer, final int newWidth, final int newHeight, final boolean force) { + if( defer ) { + new InterruptSource.Thread() { + public void run() { + WindowDriver.super.sizeChanged(false /* defer */, newWidth, newHeight, force); + } }.start(); + } else { + WindowDriver.super.sizeChanged(false /* defer */, newWidth, newHeight, force); + } + } + + // + // Accumulated actions + // + + /** Triggered by implementation's WM events to update the client-area position, size and insets. */ + protected void sizeScreenPosInsetsChanged(final boolean defer, + final int newX, final int newY, + final int newWidth, final int newHeight, + final int left, final int right, final int top, final int bottom, + final boolean force, + final boolean withinLiveResize) { + final LifecycleHook lh = getLifecycleHook(); + if( withinLiveResize && !resizeAnimatorPaused && null!=lh ) { + resizeAnimatorPaused = lh.pauseRenderingAction(); + } + sizeChanged(defer, newWidth, newHeight, force); + screenPositionChanged(defer, newX, newY); + insetsChanged(defer, left, right, top, bottom); + if( !withinLiveResize && resizeAnimatorPaused ) { + resizeAnimatorPaused = false; + if( null!=lh ) { + lh.resumeRenderingAction(); + } + } + } + + @Override protected void setPointerIconImpl(final PointerIconImpl pi) { if( !isOffscreenInstance ) { final long piHandle = null != pi ? pi.validatedHandle() : 0; @@ -655,10 +715,7 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl protected int getDisplayID() { if( !isOffscreenInstance ) { - final long whandle = getWindowHandle(); - if(0 != whandle) { - return getDisplayID0(whandle); - } + return getDisplayID0(getWindowHandle()); } return 0; } @@ -718,32 +775,35 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl } windowStyle = ws; } - final long newWin = createWindow0( pS.getX(), pS.getY(), width, height, - 0 != ( STATE_MASK_FULLSCREEN & flags), - windowStyle, - NSBackingStoreBuffered, surfaceHandle); - if ( newWin == 0 ) { - throw new NativeWindowException("Could not create native window "+Thread.currentThread().getName()+" "+this); - } - setWindowHandle( newWin ); - - final boolean isOpaque = getGraphicsConfiguration().getChosenCapabilities().isBackgroundOpaque() && !offscreenInstance; // Blocking initialization on main-thread! + final long[] newWin = { 0 }; OSXUtil.RunOnMainThread(true, false /* kickNSApp */, new Runnable() { @Override public void run() { - initWindow0( parentWinHandle, newWin, pS.getX(), pS.getY(), width, height, reqPixelScale[0] /* HiDPI uniformPixelScale */, - isOpaque, - !offscreenInstance && 0 != ( STATE_MASK_VISIBLE & flags), - surfaceHandle); - if( offscreenInstance ) { - orderOut0(0!=parentWinHandle ? parentWinHandle : newWin); - } else { - setTitle0(newWin, getTitle()); - setAlwaysOnTop0(getWindowHandle(), 0 != ( STATE_MASK_ALWAYSONTOP & flags)); - setAlwaysOnBottom0(getWindowHandle(), 0 != ( STATE_MASK_ALWAYSONBOTTOM & flags)); + newWin[0] = createWindow0( pS.getX(), pS.getY(), width, height, + 0 != ( STATE_MASK_FULLSCREEN & flags), + windowStyle, + NSBackingStoreBuffered, surfaceHandle); + if ( newWin[0] != 0 ) { + final boolean isOpaque = getGraphicsConfiguration().getChosenCapabilities().isBackgroundOpaque() && !offscreenInstance; + initWindow0( parentWinHandle, newWin[0], pS.getX(), pS.getY(), width, height, reqPixelScale[0] /* HiDPI uniformPixelScale */, + isOpaque, + !offscreenInstance && 0 != ( STATE_MASK_ALWAYSONTOP & flags), + !offscreenInstance && 0 != ( STATE_MASK_ALWAYSONBOTTOM & flags), + !offscreenInstance && 0 != ( STATE_MASK_VISIBLE & flags), + surfaceHandle); + if( offscreenInstance ) { + orderOut0(0!=parentWinHandle ? parentWinHandle : newWin[0]); + } else { + setTitle0(newWin[0], getTitle()); + } } } }); + + if ( newWin[0] == 0 ) { + throw new NativeWindowException("Could not create native window "+Thread.currentThread().getName()+" "+this); + } + setWindowHandle( newWin[0] ); } catch (final Exception ie) { ie.printStackTrace(); } @@ -754,7 +814,8 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl private native long createWindow0(int x, int y, int w, int h, boolean fullscreen, int windowStyle, int backingStoreType, long view); /** Must be called on Main-Thread */ private native void initWindow0(long parentWindow, long window, int x, int y, int w, int h, float reqPixelScale, - boolean opaque, boolean visible, long view); + boolean opaque, boolean atop, boolean abottom, boolean visible, long view); + private native int getDisplayID0(long window); private native void setPixelScale0(long window, long view, float reqPixelScale); private native boolean lockSurface0(long window, long view); @@ -778,10 +839,8 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl private native void setWindowClientTopLeftPointAndSize0(long window, int x, int y, int w, int h, boolean display); /** Must be called on Main-Thread */ private native void setWindowClientTopLeftPoint0(long window, int x, int y, boolean display); - /** Must be called on Main-Thread */ - private native void setAlwaysOnTop0(long window, boolean atop); - /** Must be called on Main-Thread */ - private native void setAlwaysOnBottom0(long window, boolean abottom); + /** Triggers {@link #sizeScreenPosInsetsChanged(boolean, int, int, int, int, int, int, int, int, boolean)} */ + private native void updateSizePosInsets0(long window, boolean defer); private static native Object getLocationOnScreen0(long windowHandle, int src_x, int src_y); private static native void setPointerIcon0(long windowHandle, long handle); private static native void setPointerVisible0(long windowHandle, boolean hasFocus, boolean visible); @@ -803,5 +862,5 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl private volatile long surfaceHandle = 0; private long sscSurfaceHandle = 0; private boolean isOffscreenInstance = false; - + private boolean resizeAnimatorPaused = false; } diff --git a/src/newt/classes/jogamp/newt/driver/opengl/JoglUtilPNGIcon.java b/src/newt/classes/jogamp/newt/driver/opengl/JoglUtilPNGIcon.java index f9f1f13ad..f7d6b9f25 100644 --- a/src/newt/classes/jogamp/newt/driver/opengl/JoglUtilPNGIcon.java +++ b/src/newt/classes/jogamp/newt/driver/opengl/JoglUtilPNGIcon.java @@ -46,9 +46,17 @@ public class JoglUtilPNGIcon { data_size[0] = 0; for(int i=0; i<resources.resourceCount(); i++) { final URLConnection urlConn = resources.resolve(i); - final PNGPixelRect image = PNGPixelRect.read(urlConn.getInputStream(), PixelFormat.BGRA8888, false /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); - data_size[0] += 2 + image.getSize().getWidth() * image.getSize().getHeight(); - images[i] = image; + if( null != urlConn ) { + final PNGPixelRect image = PNGPixelRect.read(urlConn.getInputStream(), PixelFormat.BGRA8888, false /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + data_size[0] += 2 + image.getSize().getWidth() * image.getSize().getHeight(); + images[i] = image; + } else { + images[i] = null; + } + } + if( 0 == data_size[0] ) { + // no image, abort + return null; } final boolean is64Bit = Platform.is64Bit(); elem_bytesize[0] = is64Bit ? Buffers.SIZEOF_LONG : Buffers.SIZEOF_INT; @@ -56,29 +64,31 @@ public class JoglUtilPNGIcon { for(int i=0; i<images.length; i++) { final PNGPixelRect image1 = images[i]; - final int width = image1.getSize().getWidth(); - final int height = image1.getSize().getHeight(); - if( is64Bit ) { - buffer.putLong(width); - buffer.putLong(height); - } else { - buffer.putInt(width); - buffer.putInt(height); - } - final ByteBuffer bb = image1.getPixels(); - final int stride = image1.getStride(); - for(int y=0; y<height; y++) { - int bbOff = y * stride; - for(int x=0; x<width; x++) { - long pixel; - pixel = ( 0xffL & bb.get(bbOff++) ); // B - pixel |= ( 0xffL & bb.get(bbOff++) ) << 8; // G - pixel |= ( 0xffL & bb.get(bbOff++) ) << 16; // R - pixel |= ( 0xffL & bb.get(bbOff++) ) << 24; // A - if( is64Bit ) { - buffer.putLong(pixel); - } else { - buffer.putInt((int)pixel); + if( null != image1 ) { + final int width = image1.getSize().getWidth(); + final int height = image1.getSize().getHeight(); + if( is64Bit ) { + buffer.putLong(width); + buffer.putLong(height); + } else { + buffer.putInt(width); + buffer.putInt(height); + } + final ByteBuffer bb = image1.getPixels(); + final int stride = image1.getStride(); + for(int y=0; y<height; y++) { + int bbOff = y * stride; + for(int x=0; x<width; x++) { + long pixel; + pixel = ( 0xffL & bb.get(bbOff++) ); // B + pixel |= ( 0xffL & bb.get(bbOff++) ) << 8; // G + pixel |= ( 0xffL & bb.get(bbOff++) ) << 16; // R + pixel |= ( 0xffL & bb.get(bbOff++) ) << 24; // A + if( is64Bit ) { + buffer.putLong(pixel); + } else { + buffer.putInt((int)pixel); + } } } } diff --git a/src/newt/classes/jogamp/newt/driver/windows/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/windows/DisplayDriver.java index c44aa39f4..0bd7c5b2a 100644 --- a/src/newt/classes/jogamp/newt/driver/windows/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/windows/DisplayDriver.java @@ -68,19 +68,23 @@ public class DisplayDriver extends DisplayImpl { final IOUtil.ClassResources iconRes = NewtFactory.getWindowIcons(); { final URLConnection urlConn = iconRes.resolve(0); - final PNGPixelRect image = PNGPixelRect.read(urlConn.getInputStream(), PixelFormat.BGRA8888, false /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); - _defaultIconHandle[0] = DisplayDriver.createBGRA8888Icon0(image.getPixels(), image.getSize().getWidth(), image.getSize().getHeight(), false, 0, 0); + if( null != urlConn ) { + final PNGPixelRect image = PNGPixelRect.read(urlConn.getInputStream(), PixelFormat.BGRA8888, false /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + _defaultIconHandle[0] = DisplayDriver.createBGRA8888Icon0(image.getPixels(), image.getSize().getWidth(), image.getSize().getHeight(), false, 0, 0); + } } { final URLConnection urlConn = iconRes.resolve(iconRes.resourceCount()-1); - final PNGPixelRect image = PNGPixelRect.read(urlConn.getInputStream(), PixelFormat.BGRA8888, false /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); - _defaultIconHandle[1] = DisplayDriver.createBGRA8888Icon0(image.getPixels(), image.getSize().getWidth(), image.getSize().getHeight(), false, 0, 0); + if( null != urlConn ) { + final PNGPixelRect image = PNGPixelRect.read(urlConn.getInputStream(), PixelFormat.BGRA8888, false /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + _defaultIconHandle[1] = DisplayDriver.createBGRA8888Icon0(image.getPixels(), image.getSize().getWidth(), image.getSize().getHeight(), false, 0, 0); + } } } catch (final Exception e) { e.printStackTrace(); } } - defaultIconHandles = _defaultIconHandle; + defaultIconHandles = _defaultIconHandle; // null is a valid value for an icon handle } sharedClassFactory = new RegisteredClassFactory(newtClassBaseName, WindowDriver.getNewtWndProc0(), false /* useDummyDispatchThread */, defaultIconHandles[0], defaultIconHandles[1]); diff --git a/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java index 751ce854a..af5dad3ac 100644 --- a/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java @@ -157,6 +157,10 @@ public class WindowDriver extends WindowImpl { if ( 0 == _windowHandle ) { throw new NativeWindowException("Error creating window"); } + if( !cfg.getChosenCapabilities().isBackgroundOpaque() ) { + GDIUtil.DwmSetupTranslucency(_windowHandle, true); + } + InitWindow0(_windowHandle, flags); setWindowHandle(_windowHandle); windowHandleClose = _windowHandle; @@ -200,6 +204,21 @@ public class WindowDriver extends WindowImpl { } @Override + protected final int getSupportedReconfigMaskImpl() { + return minimumReconfigStateMask | + STATE_MASK_CHILDWIN | + STATE_MASK_UNDECORATED | + STATE_MASK_ALWAYSONTOP | + STATE_MASK_ALWAYSONBOTTOM | + // STATE_MASK_STICKY | + STATE_MASK_RESIZABLE | + STATE_MASK_MAXIMIZED_VERT | + STATE_MASK_MAXIMIZED_HORZ | + STATE_MASK_POINTERVISIBLE | + STATE_MASK_POINTERCONFINED; + } + + @Override protected boolean reconfigureWindowImpl(int x, int y, int width, int height, final int flags) { if(DEBUG_IMPLEMENTATION) { System.err.println("WindowsWindow reconfig.0: "+x+"/"+y+" "+width+"x"+height+ @@ -220,7 +239,16 @@ public class WindowDriver extends WindowImpl { width = posSize[2]; height = posSize[3]; } + + final boolean changeDecoration = 0 != ( CHANGE_MASK_DECORATION & flags); + final boolean isTranslucent = !getChosenCapabilities().isBackgroundOpaque(); + if( changeDecoration && isTranslucent ) { + GDIUtil.DwmSetupTranslucency(getWindowHandle(), false); + } reconfigureWindow0( getParentWindowHandle(), getWindowHandle(), x, y, width, height, flags); + if( changeDecoration && isTranslucent ) { + GDIUtil.DwmSetupTranslucency(getWindowHandle(), true); + } if( 0 != ( CHANGE_MASK_VISIBILITY & flags) ) { visibleChanged(false, 0 != ( STATE_MASK_VISIBLE & flags)); @@ -381,6 +409,7 @@ public class WindowDriver extends WindowImpl { private native long CreateWindow0(long hInstance, String wndClassName, String wndName, int winMajor, int winMinor, long parentWindowHandle, int x, int y, int width, int height, int flags); + private native void InitWindow0(long windowHandle, int flags); private native long MonitorFromWindow0(long windowHandle); private native void reconfigureWindow0(long parentWindowHandle, long windowHandle, int x, int y, int width, int height, int flags); diff --git a/src/newt/classes/jogamp/newt/driver/x11/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/x11/DisplayDriver.java index 81ccdbfcd..9d89ba085 100644 --- a/src/newt/classes/jogamp/newt/driver/x11/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/x11/DisplayDriver.java @@ -110,7 +110,7 @@ public class DisplayDriver extends DisplayImpl { final long handle = _aDevice.getHandle(); if(0 != handle) { DispatchMessages0(handle, javaObjectAtom, windowDeleteAtom /*, kbdHandle */, // XKB disabled for now - randr_event_base, randr_error_base); + randr_event_base, randr_error_base, xi_opcode); } } finally { _aDevice.unlock(); @@ -122,6 +122,7 @@ public class DisplayDriver extends DisplayImpl { // protected long getKbdHandle() { return kbdHandle; } // XKB disabled for now protected int getRandREventBase() { return randr_event_base; } protected int getRandRErrorBase() { return randr_error_base; } + protected int getXiOpcode() { return xi_opcode; } /** Returns <code>null</code> if !{@link #isNativeValid()}, otherwise the Boolean value of {@link X11GraphicsDevice#isXineramaEnabled()}. */ protected Boolean isXineramaEnabled() { return isNativeValid() ? Boolean.valueOf(((X11GraphicsDevice)aDevice).isXineramaEnabled()) : null; } @@ -145,12 +146,13 @@ public class DisplayDriver extends DisplayImpl { private native void CompleteDisplay0(long handle); private void displayCompleted(final long javaObjectAtom, final long windowDeleteAtom /*, long kbdHandle */, - final int randr_event_base, final int randr_error_base) { + final int randr_event_base, final int randr_error_base, final int xi_opcode) { this.javaObjectAtom=javaObjectAtom; this.windowDeleteAtom=windowDeleteAtom; // this.kbdHandle = kbdHandle; // XKB disabled for now this.randr_event_base = randr_event_base; this.randr_error_base = randr_error_base; + this.xi_opcode = xi_opcode; } private void sendRRScreenChangeNotify(final long event) { if( null != rAndR ) { @@ -163,7 +165,7 @@ public class DisplayDriver extends DisplayImpl { private native void DisplayRelease0(long handle, long javaObjectAtom, long windowDeleteAtom /*, long kbdHandle */); // XKB disabled for now private native void DispatchMessages0(long display, long javaObjectAtom, long windowDeleteAtom /* , long kbdHandle */, // XKB disabled for now - final int randr_event_base, final int randr_error_base); + final int randr_event_base, final int randr_error_base, final int xi_opcode); private static long createPointerIcon(final long display, final Buffer pixels, final int width, final int height, final int hotX, final int hotY) { final boolean pixels_is_direct = Buffers.isDirect(pixels); @@ -185,7 +187,7 @@ public class DisplayDriver extends DisplayImpl { /** X11 Keyboard handle used on EDT */ // private long kbdHandle; // XKB disabled for now - private int randr_event_base, randr_error_base; + private int randr_event_base, randr_error_base, xi_opcode; private RandR rAndR; } diff --git a/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java index 5b6ea45d5..afd10f54b 100644 --- a/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java @@ -46,7 +46,6 @@ import jogamp.newt.driver.PNGIcon; import com.jogamp.nativewindow.*; import com.jogamp.nativewindow.VisualIDHolder.VIDType; -import com.jogamp.nativewindow.util.Insets; import com.jogamp.nativewindow.util.InsetsImmutable; import com.jogamp.nativewindow.util.Point; @@ -128,13 +127,13 @@ public class WindowDriver extends WindowImpl { try { final long[] handles = CreateWindow(getParentWindowHandle(), edtDevice.getHandle(), screen.getIndex(), visualID, - display.getJavaObjectAtom(), display.getWindowDeleteAtom(), + display.getJavaObjectAtom(), display.getWindowDeleteAtom(), display.getXiOpcode(), getX(), getY(), getWidth(), getHeight(), flags, defaultIconDataSize, defaultIconData, DEBUG_IMPLEMENTATION); if (null == handles || 2 != handles.length || 0 == handles[0] || 0 == handles[1] ) { throw new NativeWindowException("Error creating window"); } - if(DEBUG_IMPLEMENTATION) { // FIXME + if(DEBUG_IMPLEMENTATION) { System.err.println("X11Window.createNativeImpl() handles "+toHexString(handles[0])+", "+toHexString(handles[1])); } setWindowHandle(handles[0]); @@ -152,7 +151,7 @@ public class WindowDriver extends WindowImpl { edtDevice.lock(); try { CloseWindow0(edtDevice.getHandle(), javaWindowHandle /* , display.getKbdHandle() */, // XKB disabled for now - display.getRandREventBase(), display.getRandRErrorBase()); + display.getRandREventBase(), display.getRandRErrorBase(), display.getXiOpcode()); } catch (final Throwable t) { if(DEBUG_IMPLEMENTATION) { final Exception e = new Exception("Warning: closeNativeImpl failed - "+Thread.currentThread().getName(), t); @@ -169,15 +168,9 @@ public class WindowDriver extends WindowImpl { } } - /** - * <p> - * X11 Window supports {@link #FLAG_IS_FULLSCREEN_SPAN} - * </p> - * {@inheritDoc} - */ @Override - protected boolean isReconfigureMaskSupported(final int changeFlags) { - return true; // all flags! + protected final int getSupportedReconfigMaskImpl() { + return ( minimumReconfigStateMask | GetSupportedReconfigMask0(javaWindowHandle) ) & STATE_MASK_ALL_RECONFIG; } @Override @@ -413,6 +406,19 @@ public class WindowDriver extends WindowImpl { } } + public final void sendTouchScreenEvent(final short eventType, final int modifiers, + final int pActionIdx, final int[] pNames, + final int[] pX, final int[] pY, final float[] pPressure, final float maxPressure) { + final int pCount = pNames.length; + final MouseEvent.PointerType[] pTypes = new MouseEvent.PointerType[pCount]; + for(int i=0; i<pCount; i++) { + pTypes[i] = MouseEvent.PointerType.TouchScreen; + } + doPointerEvent(false /*enqueue*/, false /*wait*/, + pTypes, eventType, modifiers, pActionIdx, true /*normalPNames*/, pNames, + pX, pY, pPressure, maxPressure, new float[] { 0f, 0f, 0f} /*rotationXYZ*/, 1f/*rotationScale*/); + } + @Override public final void sendKeyEvent(final short eventType, final int modifiers, final short keyCode, final short keySym, final char keyChar) { throw new InternalError("XXX: Adapt Java Code to Native Code Changes"); @@ -435,7 +441,7 @@ public class WindowDriver extends WindowImpl { protected static native boolean initIDs0(); private long[] CreateWindow(final long parentWindowHandle, final long display, final int screen_index, - final int visualID, final long javaObjectAtom, final long windowDeleteAtom, + final int visualID, final long javaObjectAtom, final long windowDeleteAtom, final int xi_opcode, final int x, final int y, final int width, final int height, final int flags, final int pixelDataSize, final Buffer pixels, final boolean verbose) { // NOTE: MUST BE DIRECT BUFFER, since _NET_WM_ICON Atom uses buffer directly! @@ -443,22 +449,22 @@ public class WindowDriver extends WindowImpl { throw new IllegalArgumentException("data buffer is not direct "+pixels); } return CreateWindow0(parentWindowHandle, display, screen_index, - visualID, javaObjectAtom, windowDeleteAtom, + visualID, javaObjectAtom, windowDeleteAtom, xi_opcode, x, y, width, height, flags, pixelDataSize, pixels, Buffers.getDirectBufferByteOffset(pixels), true /* pixels_is_direct */, verbose); } /** returns long[2] { X11-window-handle, JavaWindow-handle } */ private native long[] CreateWindow0(long parentWindowHandle, long display, int screen_index, - int visualID, long javaObjectAtom, long windowDeleteAtom, + int visualID, long javaObjectAtom, long windowDeleteAtom, int xi_opcode, int x, int y, int width, int height, int flags, int pixelDataSize, Object pixels, int pixels_byte_offset, boolean pixels_is_direct, boolean verbose); - private native long GetNativeWindowHandle0(long javaWindowHandle); + private static native int GetSupportedReconfigMask0(long javaWindowHandle); private native void CloseWindow0(long display, long javaWindowHandle /*, long kbdHandle*/, // XKB disabled for now - final int randr_event_base, final int randr_error_base); - private native void reconfigureWindow0(long display, int screen_index, long parentWindowHandle, long javaWindowHandle, - int x, int y, int width, int height, int flags); - private native void requestFocus0(long display, long javaWindowHandle, boolean force); + final int randr_event_base, final int randr_error_base, final int xi_opcode); + private static native void reconfigureWindow0(long display, int screen_index, long parentWindowHandle, long javaWindowHandle, + int x, int y, int width, int height, int flags); + private static native void requestFocus0(long display, long javaWindowHandle, boolean force); private static native void setTitle0(long display, long javaWindowHandle, String title); diff --git a/src/newt/classes/jogamp/newt/driver/x11/X11UnderlayTracker.java b/src/newt/classes/jogamp/newt/driver/x11/X11UnderlayTracker.java index 6e64e7025..d7e184a3c 100644 --- a/src/newt/classes/jogamp/newt/driver/x11/X11UnderlayTracker.java +++ b/src/newt/classes/jogamp/newt/driver/x11/X11UnderlayTracker.java @@ -38,6 +38,7 @@ import com.jogamp.common.util.ReflectionUtil; import com.jogamp.nativewindow.Capabilities; import com.jogamp.nativewindow.GraphicsConfigurationFactory; import com.jogamp.nativewindow.NativeWindowFactory; +import com.jogamp.nativewindow.util.Point; import com.jogamp.newt.Display; import com.jogamp.newt.NewtFactory; import com.jogamp.newt.Screen; @@ -135,19 +136,59 @@ public class X11UnderlayTracker implements WindowListener, KeyListener, MouseLis if (underlayWindowMap.containsKey(s)) { WindowImpl underlayWindow = (WindowImpl)s; WindowImpl overlayWindow = underlayWindowMap.get(s); - if(overlayWindow.getX()!=underlayWindow.getX() || - overlayWindow.getY()!=underlayWindow.getY()) { - overlayWindow.setPosition(underlayWindow.getX(), underlayWindow.getY()); + Point overlayOnScreen = new Point(); + Point underlayOnScreen = new Point(); + overlayWindow.getLocationOnScreen(overlayOnScreen); + underlayWindow.getLocationOnScreen(underlayOnScreen); + /* + * Apply an offset when the dimensions of over- and + * underlay don't match + */ + int dx = (overlayWindow.getScreen().getWidth() - underlayWindow.getScreen().getWidth()) / 2; + int dy = (overlayWindow.getScreen().getHeight() - underlayWindow.getScreen().getHeight()) / 2; + underlayOnScreen.translate(dx, dy); + if(overlayOnScreen.getX()!=underlayOnScreen.getX() || + overlayOnScreen.getY()!=underlayOnScreen.getY()) { + overlayWindow.setPosition(underlayOnScreen.getX(), underlayOnScreen.getY()); } } else if (overlayWindowMap.containsKey(s)) { WindowImpl overlayWindow = (WindowImpl)s; WindowImpl underlayWindow = overlayWindowMap.get(s); - if(overlayWindow.getX()!=underlayWindow.getX() || - overlayWindow.getY()!=underlayWindow.getY()) { - //FIXME: Pressing Maximize on the underlay X11 - //with this line enabled locks-up the NEWT EDT while using the BCM.VC.IV - //underlayWindow.setPosition(overlayWindow.getX(), overlayWindow.getY()); + // FIXME: Pressing Maximize on the underlay X11 + // with these lines enabled locks-up the NEWT EDT + /* + Point overlayOnScreen = new Point(); + Point underlayOnScreen = new Point(); + overlayWindow.getLocationOnScreen(overlayOnScreen); + underlayWindow.getLocationOnScreen(underlayOnScreen); + if(overlayOnScreen.getX()!=underlayOnScreen.getX() || + overlayOnScreen.getY()!=underlayOnScreen.getY()) { + underlayWindow.setPosition(overlayOnScreen.getX(), overlayOnScreen.getY()); } + */ + /* it locks up like this + Caused by: java.lang.RuntimeException: Waited 5000ms for: <5ccc078, 45700941>[count 1, qsz 0, owner <main-Display-.x11_:0-1-EDT-1>] - <main-Display-.x11_:0-2-EDT-1> + at jogamp.common.util.locks.RecursiveLockImpl01Unfairish.lock(RecursiveLockImpl01Unfairish.java:198) + at jogamp.nativewindow.ResourceToolkitLock.lock(ResourceToolkitLock.java:56) + at com.jogamp.nativewindow.DefaultGraphicsDevice.lock(DefaultGraphicsDevice.java:126) + at jogamp.newt.DisplayImpl.runWithLockedDevice(DisplayImpl.java:780) + at jogamp.newt.DisplayImpl.runWithLockedDisplayDevice(DisplayImpl.java:793) + at jogamp.newt.driver.x11.WindowDriver.runWithLockedDisplayDevice(WindowDriver.java:425) + at jogamp.newt.driver.x11.WindowDriver.getLocationOnScreenImpl(WindowDriver.java:334) + at jogamp.newt.WindowImpl.getLocationOnScreen(WindowImpl.java:1113) + at jogamp.newt.driver.x11.X11UnderlayTracker.windowMoved(X11UnderlayTracker.java:153) + at jogamp.newt.WindowImpl.consumeWindowEvent(WindowImpl.java:4243) + at jogamp.newt.WindowImpl.sendWindowEvent(WindowImpl.java:4174) + at jogamp.newt.WindowImpl.positionChanged(WindowImpl.java:4403) + at jogamp.newt.WindowImpl.sizePosMaxInsetsChanged(WindowImpl.java:4567) + at jogamp.newt.driver.x11.DisplayDriver.DispatchMessages0(Native Method) + at jogamp.newt.driver.x11.DisplayDriver.dispatchMessagesNative(DisplayDriver.java:112) + at jogamp.newt.WindowImpl.waitForPosition(WindowImpl.java:4438) + at jogamp.newt.WindowImpl.access$2200(WindowImpl.java:96) + at jogamp.newt.WindowImpl$SetPositionAction.run(WindowImpl.java:2765) + at com.jogamp.common.util.RunnableTask.run(RunnableTask.java:150) + at jogamp.newt.DefaultEDTUtil$NEDT.run(DefaultEDTUtil.java:372) + */ } } @@ -282,7 +323,7 @@ public class X11UnderlayTracker implements WindowListener, KeyListener, MouseLis if (underlayWindowMap.containsKey(s)) { WindowImpl overlayWindow = underlayWindowMap.get(s); overlayWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_CLICKED, 0, - e.getX(), e.getY(), (short) 0, 0); + e.getX(), e.getY(), e.getButton(), 0); } } @@ -293,7 +334,7 @@ public class X11UnderlayTracker implements WindowListener, KeyListener, MouseLis if (underlayWindowMap.containsKey(s)) { WindowImpl overlayWindow = underlayWindowMap.get(s); overlayWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_ENTERED, 0, - e.getX(), e.getY(), (short) 0, 0); + e.getX(), e.getY(), e.getButton(), 0); } } @@ -304,7 +345,7 @@ public class X11UnderlayTracker implements WindowListener, KeyListener, MouseLis if (underlayWindowMap.containsKey(s)) { WindowImpl overlayWindow = underlayWindowMap.get(s); overlayWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_EXITED, 0, - e.getX(), e.getY(), (short) 0, 0); + e.getX(), e.getY(), e.getButton(), 0); } } @@ -315,7 +356,7 @@ public class X11UnderlayTracker implements WindowListener, KeyListener, MouseLis if (underlayWindowMap.containsKey(s)) { WindowImpl overlayWindow = underlayWindowMap.get(s); overlayWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_PRESSED, 0, - e.getX(), e.getY(), (short) 0, 0); + e.getX(), e.getY(), e.getButton(), 0); } } @@ -326,7 +367,7 @@ public class X11UnderlayTracker implements WindowListener, KeyListener, MouseLis if (underlayWindowMap.containsKey(s)) { WindowImpl overlayWindow = underlayWindowMap.get(s); overlayWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_RELEASED, 0, - e.getX(), e.getY(), (short) 0, 0); + e.getX(), e.getY(), e.getButton(), 0); } } @@ -337,7 +378,7 @@ public class X11UnderlayTracker implements WindowListener, KeyListener, MouseLis if (underlayWindowMap.containsKey(s)) { WindowImpl overlayWindow = underlayWindowMap.get(s); overlayWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_MOVED, 0, - e.getX(), e.getY(), (short) 0, 0); + e.getX(), e.getY(), e.getButton(), 0); } } @@ -348,7 +389,7 @@ public class X11UnderlayTracker implements WindowListener, KeyListener, MouseLis if (underlayWindowMap.containsKey(s)) { WindowImpl overlayWindow = underlayWindowMap.get(s); overlayWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_DRAGGED, 0, - e.getX(), e.getY(), (short) 0, 0); + e.getX(), e.getY(), e.getButton(), 0); } } @@ -359,27 +400,23 @@ public class X11UnderlayTracker implements WindowListener, KeyListener, MouseLis if (underlayWindowMap.containsKey(s)) { WindowImpl overlayWindow = underlayWindowMap.get(s); overlayWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_WHEEL_MOVED, 0, - e.getX(), e.getY(), (short) 0, 0); + e.getX(), e.getY(), e.getButton(), 0); } } @Override public void keyPressed(KeyEvent e) { if (focusedWindow != null) { - // e.setConsumed(false); - // focusedWindow.consumeEvent(e); focusedWindow.sendKeyEvent(e.getEventType(), e.getModifiers(), - e.getKeyCode(), e.getKeyCode(), (char) e.getKeySymbol()); + e.getKeyCode(), e.getKeySymbol(), e.getKeyChar()); } } @Override public void keyReleased(KeyEvent e) { if (focusedWindow != null) { - // e.setConsumed(false); - // focusedWindow.consumeEvent(e); focusedWindow.sendKeyEvent(e.getEventType(), e.getModifiers(), - e.getKeyCode(), e.getKeyCode(), (char) e.getKeySymbol()); + e.getKeyCode(), e.getKeySymbol(), e.getKeyChar()); } } diff --git a/src/newt/classes/jogamp/newt/event/NEWTEventTask.java b/src/newt/classes/jogamp/newt/event/NEWTEventTask.java index 2bdab2796..260a1beb4 100644 --- a/src/newt/classes/jogamp/newt/event/NEWTEventTask.java +++ b/src/newt/classes/jogamp/newt/event/NEWTEventTask.java @@ -38,19 +38,27 @@ public class NEWTEventTask { private final NEWTEvent event; private final Object notifyObject; private RuntimeException exception; + private volatile boolean dispatched; public NEWTEventTask(final NEWTEvent event, final Object notifyObject) { this.event = event ; this.notifyObject = notifyObject ; this.exception = null; + this.dispatched = false; } public final NEWTEvent get() { return event; } public final void setException(final RuntimeException e) { exception = e; } public final RuntimeException getException() { return exception; } public final boolean isCallerWaiting() { return null != notifyObject; } + public final boolean isDispatched() { return dispatched; } + public final void setDispatched() { dispatched = true; } + /** + * Notifies caller after {@link #setDispatched()}. + */ public void notifyCaller() { + setDispatched(); if(null != notifyObject) { synchronized (notifyObject) { notifyObject.notifyAll(); diff --git a/src/newt/classes/jogamp/newt/javafx/JFXEDTUtil.java b/src/newt/classes/jogamp/newt/javafx/JFXEDTUtil.java new file mode 100644 index 000000000..4ee15b43d --- /dev/null +++ b/src/newt/classes/jogamp/newt/javafx/JFXEDTUtil.java @@ -0,0 +1,358 @@ +/** + * Copyright 2019 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 met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.newt.javafx; + +import com.jogamp.nativewindow.NativeWindowException; +import com.jogamp.nativewindow.javafx.JFXAccessor; + +import jogamp.newt.Debug; + +import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.util.InterruptSource; +import com.jogamp.common.util.InterruptedRuntimeException; +import com.jogamp.common.util.RunnableTask; +import com.jogamp.newt.util.EDTUtil; + +import javafx.application.Platform; + +/** + * Simple {@link EDTUtil} implementation utilizing the JFX UI thread + * of the given {@link Display}. + */ +public class JFXEDTUtil implements EDTUtil { + public static final boolean DEBUG = Debug.debug("EDT"); + + private final Object edtLock = new Object(); // locking the EDT start/stop state + private final ThreadGroup threadGroup; + private final String name; + private final Runnable dispatchMessages; + private NEDT nedt = null; + private int start_iter=0; + private static long pollPeriod = EDTUtil.defaultEDTPollPeriod; + + public JFXEDTUtil(final com.jogamp.newt.Display newtDisplay) { + this.threadGroup = Thread.currentThread().getThreadGroup(); + this.name=Thread.currentThread().getName()+"-JFXDisplay-"+newtDisplay.getFQName()+"-EDT-"; + this.dispatchMessages = new Runnable() { + @Override + public void run() { + ((jogamp.newt.DisplayImpl) newtDisplay).dispatchMessages(); + } }; + this.nedt = new NEDT(threadGroup, name); + this.nedt.setDaemon(true); // don't stop JVM from shutdown .. + } + + @Override + public long getPollPeriod() { + return pollPeriod; + } + + @Override + public void setPollPeriod(final long ms) { + pollPeriod = ms; // writing to static field is intended + } + + @Override + public final void start() throws IllegalStateException { + synchronized(edtLock) { + if( nedt.isRunning() ) { + final Thread curT = Thread.currentThread(); + final boolean onJFXEDT = JFXAccessor.isJFXThread(); + throw new IllegalStateException("EDT still running and not subject to stop. Curr "+curT.getName()+ + ", NEDT "+nedt.getName()+", isRunning "+nedt.isRunning+", shouldStop "+nedt.shouldStop+", JFX-EDT "+JFXAccessor.getJFXThreadName()+", on JFX-EDT "+onJFXEDT); + } + if(DEBUG) { + System.err.println(Thread.currentThread()+": JFX-EDT reset - edt: "+nedt); + } + if( !JFXAccessor.hasJFXThreadStopped() ) { + if( nedt.getState() != Thread.State.NEW ) { + nedt = new NEDT(threadGroup, name); + nedt.setDaemon(true); // don't stop JVM from shutdown .. + } + startImpl(); + } + } + if( !JFXAccessor.hasJFXThreadStopped() ) { + if( !nedt.isRunning() ) { + throw new RuntimeException("EDT could not be started: "+nedt); + } + } else { + // FIXME: Throw exception ? + } + } + + private final void startImpl() { + if(nedt.isAlive()) { + throw new RuntimeException("JFX-EDT Thread.isAlive(): true, isRunning: "+nedt.isRunning+", shouldStop "+nedt.shouldStop+", edt: "+nedt); + } + start_iter++; + nedt.setName(name+start_iter); + if(DEBUG) { + System.err.println(Thread.currentThread()+": JFX-EDT START - edt: "+nedt+", jfxThread "+JFXAccessor.getJFXThreadName()); + // Thread.dumpStack(); + } + nedt.start(); + } + + @Override + public boolean isCurrentThreadEDT() { + return JFXAccessor.isJFXThread(); + } + + @Override + public final boolean isCurrentThreadNEDT() { + return nedt == Thread.currentThread(); + } + + @Override + public final boolean isCurrentThreadEDTorNEDT() { + return JFXAccessor.isJFXThread() || Thread.currentThread() == nedt ; + } + + @Override + public boolean isRunning() { + return nedt.isRunning(); + } + + @Override + public final boolean invokeStop(final boolean wait, final Runnable task) { + return invokeImpl(wait, task, true); + } + + @Override + public final boolean invoke(final boolean wait, final Runnable task) { + return invokeImpl(wait, task, false); + } + + private static Runnable nullTask = new Runnable() { + @Override + public void run() { } + }; + + private final boolean invokeImpl(boolean wait, final Runnable task, boolean stop) { + final RunnableTask rTask; + 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: JFX-EDT about (1) to stop, won't enqueue new task: "+nedt+", isRunning "+nedt.isRunning+", shouldStop "+nedt.shouldStop); + ExceptionUtils.dumpStack(System.err); + } + return false; + } + final boolean hasJFXThreadStopped = JFXAccessor.hasJFXThreadStopped(); + + if( hasJFXThreadStopped ) { + stop = true; + } + + if( isCurrentThreadEDT() ) { + if(null != task) { + task.run(); + } + wait = false; // running in same thread (EDT) -> no wait + rTask = null; + if( stop ) { + nedt.shouldStop = true; + } + } else { + if( !nedt.isRunning && !hasJFXThreadStopped ) { + if( null != task ) { + if( stop ) { + System.err.println(Thread.currentThread()+": Warning: JFX-EDT is about (3) to stop and stopped already, dropping task. NEDT "+nedt); + } else { + System.err.println(Thread.currentThread()+": Warning: JFX-EDT is not running, dropping task. NEDT "+nedt); + } + if(DEBUG) { + ExceptionUtils.dumpStack(System.err); + } + } + return false; + } else if( stop ) { + if( nedt.isRunning ) { + if(DEBUG) { + System.err.println(Thread.currentThread()+": JFX-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( JFXAccessor.hasJFXThreadStopped() ) { + System.err.println(Thread.currentThread()+": Warning: JFX-EDT is about (3) to stop and stopped already, dropping task. "+nedt); + if(DEBUG) { + ExceptionUtils.dumpStack(System.err); + } + 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); + Platform.runLater(rTask); + } else { + wait = false; + rTask = null; + } + } + } + if( wait ) { + try { + while( rTask.isInQueue() ) { + rTaskLock.wait(); // free lock, allow execution of rTask + } + } catch (final InterruptedException ie) { + throw new InterruptedRuntimeException(ie); + } + final Throwable throwable = rTask.getThrowable(); + if(null!=throwable) { + if(throwable instanceof NativeWindowException) { + throw (NativeWindowException)throwable; + } + throw new RuntimeException(throwable); + } + } + return true; + } + } + + @Override + final public boolean waitUntilIdle() { + final NEDT _nedt; + synchronized(edtLock) { + _nedt = nedt; + } + if( !_nedt.isRunning || Thread.currentThread() == _nedt || JFXAccessor.hasJFXThreadStopped() || JFXAccessor.isJFXThread() ) { + return false; + } + JFXAccessor.runOnJFXThread(true, nullTask); + return true; + } + + @Override + final public boolean waitUntilStopped() { + synchronized(edtLock) { + final Thread curT = Thread.currentThread(); + final boolean onJFXEDT = JFXAccessor.isJFXThread(); + if( nedt.isRunning && nedt != curT && !onJFXEDT ) { + try { + while( nedt.isRunning ) { + edtLock.wait(); + } + } catch (final InterruptedException e) { + throw new InterruptedRuntimeException(e); + } + return true; + } else { + return false; + } + } + } + + class NEDT extends InterruptSource.Thread { + volatile boolean shouldStop = false; + volatile boolean isRunning = false; + Object sync = new Object(); + + public NEDT(final ThreadGroup tg, final String name) { + super(tg, null, name); + } + + final public boolean isRunning() { + return isRunning && !shouldStop; + } + + @Override + final public void start() throws IllegalThreadStateException { + isRunning = true; + super.start(); + } + + /** + * Utilizing locking only on tasks and its execution, + * not for event dispatching. + */ + @Override + final public void run() { + if(DEBUG) { + System.err.println(getName()+": JFX-EDT run() START "+ getName()); + } + RuntimeException error = null; + try { + do { + // event dispatch + if(!shouldStop) { + // EDT invoke thread is JFX-EDT, + // hence dispatching is required to run on JFX-EDT as well. + // Otherwise a deadlock may happen due to dispatched event's + // triggering a locking action. + JFXAccessor.runOnJFXThread(true, dispatchMessages); + } + // wait + synchronized(sync) { + if(!shouldStop) { + try { + sync.wait(pollPeriod); + } catch (final InterruptedException e) { + throw new InterruptedRuntimeException(e); + } + } + } + } while(!shouldStop) ; + } catch (final Throwable t) { + // handle errors .. + shouldStop = true; + if(t instanceof RuntimeException) { + error = (RuntimeException) t; + } else { + error = new RuntimeException("Within JFX-EDT", t); + } + } finally { + if(DEBUG) { + System.err.println(getName()+": JFX-EDT run() END "+ getName()+", "+error); + } + synchronized(edtLock) { + isRunning = false; + edtLock.notifyAll(); + } + if(DEBUG) { + System.err.println(getName()+": JFX-EDT run() EXIT "+ getName()+", exception: "+error); + } + if(null!=error) { + throw error; + } + } // finally + } // run() + } // EventDispatchThread + +} diff --git a/src/newt/classes/jogamp/newt/swt/SWTEDTUtil.java b/src/newt/classes/jogamp/newt/swt/SWTEDTUtil.java index 9039b6083..9d2b41bbc 100644 --- a/src/newt/classes/jogamp/newt/swt/SWTEDTUtil.java +++ b/src/newt/classes/jogamp/newt/swt/SWTEDTUtil.java @@ -32,6 +32,8 @@ import com.jogamp.nativewindow.NativeWindowException; import jogamp.newt.Debug; import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.util.InterruptSource; +import com.jogamp.common.util.InterruptedRuntimeException; import com.jogamp.common.util.RunnableTask; import com.jogamp.newt.util.EDTUtil; @@ -162,8 +164,7 @@ public class SWTEDTUtil implements EDTUtil { } private final boolean invokeImpl(boolean wait, final Runnable task, boolean stop) { - Throwable throwable = null; - RunnableTask rTask = null; + final RunnableTask rTask; final Object rTaskLock = new Object(); synchronized(rTaskLock) { // lock the optional task execution synchronized(edtLock) { // lock the EDT status @@ -184,6 +185,7 @@ public class SWTEDTUtil implements EDTUtil { task.run(); } wait = false; // running in same thread (EDT) -> no wait + rTask = null; if( stop ) { nedt.shouldStop = true; } @@ -225,18 +227,21 @@ public class SWTEDTUtil implements EDTUtil { true /* always catch and report Exceptions, don't disturb EDT */, wait ? null : System.err); swtDisplay.asyncExec(rTask); + } else { + wait = false; + rTask = null; } } } if( wait ) { try { - rTaskLock.wait(); // free lock, allow execution of rTask + while( rTask.isInQueue() ) { + rTaskLock.wait(); // free lock, allow execution of rTask + } } catch (final InterruptedException ie) { - throwable = ie; - } - if(null==throwable) { - throwable = rTask.getThrowable(); + throw new InterruptedRuntimeException(ie); } + final Throwable throwable = rTask.getThrowable(); if(null!=throwable) { if(throwable instanceof NativeWindowException) { throw (NativeWindowException)throwable; @@ -274,12 +279,12 @@ public class SWTEDTUtil implements EDTUtil { final Thread swtT = !swtDisplay.isDisposed() ? swtDisplay.getThread() : null; final boolean onSWTEDT = swtT == curT; if( nedt.isRunning && nedt != curT && !onSWTEDT ) { - while( nedt.isRunning ) { - try { + try { + while( nedt.isRunning ) { edtLock.wait(); - } catch (final InterruptedException e) { - e.printStackTrace(); } + } catch (final InterruptedException e) { + throw new InterruptedRuntimeException(e); } return true; } else { @@ -288,13 +293,13 @@ public class SWTEDTUtil implements EDTUtil { } } - class NEDT extends Thread { + class NEDT extends InterruptSource.Thread { volatile boolean shouldStop = false; volatile boolean isRunning = false; Object sync = new Object(); public NEDT(final ThreadGroup tg, final String name) { - super(tg, name); + super(tg, null, name); } final public boolean isRunning() { @@ -337,7 +342,7 @@ public class SWTEDTUtil implements EDTUtil { try { sync.wait(pollPeriod); } catch (final InterruptedException e) { - e.printStackTrace(); + throw new InterruptedRuntimeException(e); } } } |