summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--make/build-common.xml2
-rw-r--r--make/build-nativewindow.xml2
-rw-r--r--make/build-newt.xml19
-rw-r--r--make/scripts/tests.sh18
-rw-r--r--src/nativewindow/classes/com/jogamp/nativewindow/NativeWindowFactory.java3
-rw-r--r--src/nativewindow/classes/com/jogamp/nativewindow/javafx/JFXAccessor.java293
-rw-r--r--src/nativewindow/classes/com/jogamp/nativewindow/util/Point.java12
-rw-r--r--src/newt/classes/com/jogamp/newt/javafx/NewtCanvasJFX.java637
-rw-r--r--src/newt/classes/jogamp/newt/javafx/JFXEDTUtil.java358
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/javafx/PureJFXApp01.java54
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/javafx/TestNewtCanvasJFXGLn.java462
11 files changed, 1855 insertions, 5 deletions
diff --git a/make/build-common.xml b/make/build-common.xml
index 3abebc949..0205e1e99 100644
--- a/make/build-common.xml
+++ b/make/build-common.xml
@@ -391,6 +391,7 @@
<property name="newt-ogl.jar" value="${build.newt}/newt-ogl.jar" />
<property name="newt-awt.jar" value="${build.newt}/newt-awt.jar" />
<property name="newt-swt.jar" value="${build.newt}/newt-swt.jar" />
+ <property name="newt-javafx.jar" value="${build.newt}/newt-javafx.jar" />
<property name="newt-event.jar" value="${build.newt}/newt-event.jar" /> <!-- using NEWT events w/o NEWT -->
<property name="newt-driver-linux.jar" value="${build.newt}/newt-driver-linux.jar" />
<property name="newt-driver-x11.jar" value="${build.newt}/newt-driver-x11.jar" />
@@ -406,6 +407,7 @@
<pathelement location="${newt-ogl.jar}" />
<pathelement location="${newt-awt.jar}" />
<pathelement location="${newt-swt.jar}" />
+ <pathelement location="${newt-javafx.jar}" />
<pathelement location="${newt-driver-linux.jar}" />
<pathelement location="${newt-driver-x11.jar}" />
<pathelement location="${newt-driver-win.jar}" />
diff --git a/make/build-nativewindow.xml b/make/build-nativewindow.xml
index 216ae461f..b4d7858a7 100644
--- a/make/build-nativewindow.xml
+++ b/make/build-nativewindow.xml
@@ -67,7 +67,7 @@
<!-- partitioning -->
<property name="java.part.core"
- value="com/jogamp/nativewindow/* com/jogamp/nativewindow/util/* com/jogamp/nativewindow/egl/* com/jogamp/nativewindow/swt/** jogamp/nativewindow/*"/>
+ value="com/jogamp/nativewindow/* com/jogamp/nativewindow/util/* com/jogamp/nativewindow/egl/* com/jogamp/nativewindow/swt/** com/jogamp/nativewindow/javafx/** jogamp/nativewindow/*"/>
<property name="java.part.awt"
value="com/jogamp/nativewindow/awt/* jogamp/nativewindow/jawt/** jogamp/nativewindow/**/awt/**"/>
diff --git a/make/build-newt.xml b/make/build-newt.xml
index ec7a8a789..b19aa8fc4 100644
--- a/make/build-newt.xml
+++ b/make/build-newt.xml
@@ -114,6 +114,9 @@
<property name="java.part.swt"
value="com/jogamp/newt/swt/** jogamp/newt/swt/**"/>
+ <property name="java.part.javafx"
+ value="com/jogamp/newt/javafx/** jogamp/newt/javafx/**"/>
+
<property name="java.part.applet3"
value="com/jogamp/newt/util/applet3/**"/>
@@ -156,6 +159,11 @@
<isset property="setup.noSWT"/>
</condition>
+ <condition property="java.excludes.javafx"
+ value="${java.part.javafx}">
+ <isset property="setup.noJFX"/>
+ </condition>
+
<condition property="java.excludes.applet3"
value="${java.part.applet3}">
<not>
@@ -167,7 +175,7 @@
<isset property="setup.noOpenGL"/>
</condition>
- <property name="java.excludes.all" value="${java.excludes.awt}, ${java.excludes.swt}, ${java.excludes.applet3}, ${java.excludes.opengl}" />
+ <property name="java.excludes.all" value="${java.excludes.awt}, ${java.excludes.swt}, ${java.excludes.javafx}, ${java.excludes.applet3}, ${java.excludes.opengl}" />
<echo message="java.excludes.all: ${java.excludes.all}" />
</target>
@@ -806,6 +814,13 @@
</jar>
</target>
+ <target name="build-jars-javafx" depends="setup-manifestfile" unless="setup.noJFX">
+ <jar manifest="${build.newt}/manifest.mf" destfile="${newt-javafx.jar}" filesonly="true">
+ <fileset dir="${classes}"
+ includes="${java.part.javafx}"/>
+ </jar>
+ </target>
+
<target name="build-jars-opengl" depends="setup-manifestfile" unless="setup.noOpenGL">
<jar manifest="${build.newt}/manifest.mf" destfile="${newt-ogl.jar}" filesonly="true">
<fileset dir="${classes}"
@@ -848,7 +863,7 @@
</jar>
</target>
- <target name="build-jars-javase" depends="setup-manifestfile,build-jars-opengl,build-jars-awt,build-jars-swt,build-jars-driver">
+ <target name="build-jars-javase" depends="setup-manifestfile,build-jars-opengl,build-jars-awt,build-jars-swt,build-jars-javafx,build-jars-driver">
<jar manifest="${build.newt}/manifest.mf" destfile="${newt.jar}" filesonly="true">
<fileset dir="${classes}"
includes="${java.part.core}"/>
diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh
index a5ac7c844..ffaaa7554 100644
--- a/make/scripts/tests.sh
+++ b/make/scripts/tests.sh
@@ -126,6 +126,7 @@ function jrun() {
#D_ARGS="-Dnativewindow.debug.X11Util.ATI_HAS_NO_XCLOSEDISPLAY_BUG"
#D_ARGS="-Dnativewindow.debug.X11Util.ATI_HAS_NO_MULTITHREADING_BUG"
+ D_ARGS="-Dnativewindow.debug.JFX -Dnewt.debug.Window"
#D_ARGS="-Djogl.disable.opengldesktop"
#D_ARGS="-Djogl.disable.opengles"
#D_ARGS="-Djogl.disable.openglcore"
@@ -398,6 +399,11 @@ function testnoawt() {
jrun 0 0 $* 2>&1 | tee -a java-run.log
}
+function testjfx() {
+ MOSX_MT=0
+ jrun 1 0 $* 2>&1 | tee -a java-run.log
+}
+
function testawt() {
MOSX_MT=0
jrun 1 0 $* 2>&1 | tee -a java-run.log
@@ -441,7 +447,7 @@ function testawtswt() {
#
# HiDPI
#
-testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $*
+#testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2SimpleNEWT $*
#testawt com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2GLJPanelAWT $*
#testawt com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2AWT $*
@@ -836,6 +842,12 @@ testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $*
#testnoawt com.jogamp.opengl.test.junit.newt.parenting.TestTranslucentChildWindowBug632NEWT $*
#
+# JavaFX (testjfx)
+#
+#testjfx com.jogamp.opengl.test.junit.jogl.javafx.JFXStageGLChild01 $*
+testjfx com.jogamp.opengl.test.junit.jogl.javafx.TestNewtCanvasJFXGLn $*
+
+#
# Misc Utils
#
#testnoawt com.jogamp.opengl.test.junit.jogl.util.TestImmModeSinkES1NEWT $*
@@ -928,6 +940,10 @@ testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $*
#testnoawt com.jogamp.opengl.test.junit.newt.mm.TestScreenMode02aNEWT $*
#testnoawt com.jogamp.opengl.test.junit.newt.mm.TestScreenMode02bNEWT $*
+# caused a freeze/crash on OSX
+#testawt com.jogamp.opengl.test.junit.jogl.awt.TestGLCanvasAWTActionDeadlock02AWT $*
+#testawt com.jogamp.opengl.test.junit.jogl.perf.TestPerf001GLJPanelInit02AWT $*
+
# NEW
$spath/count-edt-start.sh java-run.log
diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/NativeWindowFactory.java b/src/nativewindow/classes/com/jogamp/nativewindow/NativeWindowFactory.java
index ebd498401..323bc8c86 100644
--- a/src/nativewindow/classes/com/jogamp/nativewindow/NativeWindowFactory.java
+++ b/src/nativewindow/classes/com/jogamp/nativewindow/NativeWindowFactory.java
@@ -42,6 +42,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import com.jogamp.nativewindow.util.Point;
import com.jogamp.nativewindow.util.PointImmutable;
import jogamp.common.os.PlatformPropsImpl;
@@ -733,7 +734,7 @@ public abstract class NativeWindowFactory {
* FIXME: Bug 973 Needs service provider interface (SPI) for TK dependent implementation
* </p>
*/
- public static PointImmutable getLocationOnScreen(final NativeWindow nw) {
+ public static Point getLocationOnScreen(final NativeWindow nw) {
final String nwt = NativeWindowFactory.getNativeWindowType(true);
if( NativeWindowFactory.TYPE_X11 == nwt ) {
return X11Lib.GetRelativeLocation(nw.getDisplayHandle(), nw.getScreenIndex(), nw.getWindowHandle(), 0, 0, 0);
diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/javafx/JFXAccessor.java b/src/nativewindow/classes/com/jogamp/nativewindow/javafx/JFXAccessor.java
new file mode 100644
index 000000000..12cd9bdc0
--- /dev/null
+++ b/src/nativewindow/classes/com/jogamp/nativewindow/javafx/JFXAccessor.java
@@ -0,0 +1,293 @@
+/**
+ * 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.nativewindow.javafx;
+
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import com.jogamp.nativewindow.AbstractGraphicsDevice;
+import com.jogamp.nativewindow.AbstractGraphicsScreen;
+import com.jogamp.nativewindow.NativeWindowException;
+import com.jogamp.nativewindow.NativeWindowFactory;
+import com.jogamp.nativewindow.VisualIDHolder;
+import com.jogamp.nativewindow.macosx.MacOSXGraphicsDevice;
+import com.jogamp.nativewindow.windows.WindowsGraphicsDevice;
+import com.jogamp.nativewindow.x11.X11GraphicsDevice;
+import com.jogamp.common.ExceptionUtils;
+import com.jogamp.common.util.InterruptedRuntimeException;
+import com.jogamp.common.util.ReflectionUtil;
+import com.jogamp.common.util.RunnableTask;
+import com.sun.javafx.tk.TKStage;
+
+import javafx.application.Platform;
+import javafx.stage.Window;
+import jogamp.nativewindow.Debug;
+import jogamp.nativewindow.x11.X11Lib;
+import jogamp.nativewindow.x11.X11Util;
+
+public class JFXAccessor {
+ private static final boolean DEBUG;
+
+ private static final boolean jfxAvailable;
+ private static final Method fxUserThreadGetter;
+ private static final Method tkStageGetter;
+ private static final Method glassWindowGetter;
+ private static final Method nativeWindowGetter;
+
+ private static final String nwt;
+ private static final boolean isOSX;
+ private static final boolean isWindows;
+ private static final boolean isX11;
+
+ static {
+ final boolean[] _DEBUG = new boolean[] { true };
+
+ final Method[] res = AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
+ @Override
+ public Method[] run() {
+ NativeWindowFactory.initSingleton(); // last resort ..
+ final Method[] res = new Method[] { null, null, null, null };
+ try {
+ int i=0;
+ _DEBUG[0] = Debug.debug("JFX");
+ /**
+ * com.sun.javafx.tk.Toolkit
+ */
+ final Class<?> jfxToolkitClz = ReflectionUtil.getClass("com.sun.javafx.tk.Toolkit", false, JFXAccessor.class.getClassLoader());
+ res[i] = jfxToolkitClz.getDeclaredMethod("getFxUserThread");
+ res[i++].setAccessible(true);
+
+ /***
+ * class javafx.stage.Window
+ * class javafx.stage.Stage extends javafx.stage.Window
+ * class com.sun.javafx.tk.quantum.WindowStage extends com.sun.javafx.tk.quantum.GlassStage implements com.sun.javafx.tk.TKStage
+ * abstract com.sun.glass.ui.Window
+ *
+ * javafx.stage.Window: com.sun.javafx.tk.TKStage [impl_]getPeer()
+ * com.sun.javafx.tk.quantum.WindowStage: final com.sun.glass.ui.Window getPlatformWindow()
+ * com.sun.glass.ui.Window: public long getNativeWindow()
+ */
+ final Class<?> jfxStageWindowClz = ReflectionUtil.getClass("javafx.stage.Window", false, JFXAccessor.class.getClassLoader());
+ // final Class<?> jfxTkTKStageClz = ReflectionUtil.getClass("com.sun.javafx.tk.TKStage", false, JFXAccessor.class.getClassLoader());
+ final Class<?> jfxTkQuWindowStageClz = ReflectionUtil.getClass("com.sun.javafx.tk.quantum.WindowStage", false, JFXAccessor.class.getClassLoader());
+ final Class<?> jfxGlassUiWindowClz = ReflectionUtil.getClass("com.sun.glass.ui.Window", false, JFXAccessor.class.getClassLoader());
+
+ try {
+ // jfx 9, 11, 12, ..
+ res[i] = jfxStageWindowClz.getDeclaredMethod("getPeer");
+ } catch (final NoSuchMethodException ex) {
+ // jfx 8
+ res[i] = jfxStageWindowClz.getDeclaredMethod("impl_getPeer");
+ }
+ res[i++].setAccessible(true);
+
+ res[i] = jfxTkQuWindowStageClz.getDeclaredMethod("getPlatformWindow");
+ res[i++].setAccessible(true);
+ res[i] = jfxGlassUiWindowClz.getDeclaredMethod("getNativeWindow");
+ res[i++].setAccessible(true);
+ } catch (final Throwable t) {
+ if(_DEBUG[0]) {
+ ExceptionUtils.dumpThrowable("jfx-init", t);
+ }
+ }
+ return res;
+ }
+ });
+ {
+ int i=0;
+ fxUserThreadGetter = res[i++];
+ tkStageGetter = res[i++];
+ glassWindowGetter = res[i++];
+ nativeWindowGetter = res[i++];
+ }
+ jfxAvailable = null != fxUserThreadGetter && null != tkStageGetter && null != glassWindowGetter && null != nativeWindowGetter;
+
+ nwt = NativeWindowFactory.getNativeWindowType(false);
+ isOSX = NativeWindowFactory.TYPE_MACOSX == nwt;
+ isWindows = NativeWindowFactory.TYPE_WINDOWS == nwt;
+ isX11 = NativeWindowFactory.TYPE_X11 == nwt;
+
+ DEBUG = _DEBUG[0];
+ if(DEBUG) {
+ System.err.println(Thread.currentThread().getName()+" - Info: JFXAccessor.<init> available "+jfxAvailable+", nwt "+nwt+"( x11 "+isX11+", win "+isWindows+", osx "+isOSX+")");
+ }
+ }
+
+ //
+ // Common any toolkit
+ //
+
+ public static boolean isJFXAvailable() { return jfxAvailable; }
+
+ /**
+ * Runs given {@code task} on the JFX Thread if it has not stopped and if caller is not already on the JFX Thread,
+ * otherwise execute given {@code task} on the current thread.
+ * @param wait
+ * @param task
+ * @see #isJFXThreadOrHasJFXThreadStopped()
+ */
+ public static void runOnJFXThread(final boolean wait, final Runnable task) {
+ final Object rTaskLock = new Object();
+ synchronized(rTaskLock) { // lock the task execution
+ if( isJFXThreadOrHasJFXThreadStopped() ) {
+ task.run();
+ } else if( !wait ) {
+ Platform.runLater(task);
+ } else {
+ final RunnableTask rTask = new RunnableTask(task,
+ wait ? rTaskLock : null,
+ true /* always catch and report Exceptions, don't disturb EDT */,
+ wait ? null : System.err);
+ Platform.runLater(rTask);
+ 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);
+ }
+ }
+ }
+ }
+ }
+
+ public static Thread getJFXThread() throws NativeWindowException {
+ try {
+ return (Thread) fxUserThreadGetter.invoke(null);
+ } catch (final Throwable e) {
+ throw new NativeWindowException("Error getting JFX-Thread", e);
+ }
+ }
+ public static String getJFXThreadName() {
+ final Thread t = getJFXThread();
+ return null != t ? t.getName() : null;
+ }
+ /**
+ * @return true if the JFX Thread has stopped
+ */
+ public static boolean hasJFXThreadStopped() {
+ final Thread t = getJFXThread();
+ return null == t || !t.isAlive();
+ }
+ /**
+ * @return true if caller is on the JFX Thread
+ */
+ public static boolean isJFXThread() {
+ final Thread t = getJFXThread();
+ return Thread.currentThread() == t;
+ }
+ /**
+ * @return true if the JFX Thread has stopped or if caller is on the JFX Thread
+ */
+ public static boolean isJFXThreadOrHasJFXThreadStopped() {
+ final Thread t = getJFXThread();
+ return null == t || !t.isAlive() || Thread.currentThread() == t;
+ }
+
+ /**
+ * @param swtControl the SWT Control to retrieve the native device handle from
+ * @return the AbstractGraphicsDevice w/ the native device handle
+ * @throws NativeWindowException if the widget handle is null
+ * @throws UnsupportedOperationException if the windowing system is not supported
+ */
+ public static AbstractGraphicsDevice getDevice(final Window stageWindow) throws NativeWindowException, UnsupportedOperationException {
+ if( isX11 ) {
+ // Decoupled X11 Device/Screen allowing X11 display lock-free off-thread rendering
+ final String connection = null;
+ final long x11DeviceHandle = X11Util.openDisplay(connection);
+ if( 0 == x11DeviceHandle ) {
+ throw new RuntimeException("Error creating display: "+connection);
+ }
+ return new X11GraphicsDevice(x11DeviceHandle, AbstractGraphicsDevice.DEFAULT_UNIT, true /* owner */);
+ }
+ if( isWindows ) {
+ return new WindowsGraphicsDevice(AbstractGraphicsDevice.DEFAULT_UNIT);
+ }
+ if( isOSX ) {
+ return new MacOSXGraphicsDevice(AbstractGraphicsDevice.DEFAULT_UNIT);
+ }
+ throw new UnsupportedOperationException("n/a for this windowing system: "+nwt);
+ }
+
+ /**
+ * @param device
+ * @param screen -1 is default screen of the given device, e.g. maybe 0 or determined by native API. >= 0 is specific screen
+ * @return
+ */
+ public static AbstractGraphicsScreen getScreen(final AbstractGraphicsDevice device, final int screen) {
+ return NativeWindowFactory.createScreen(device, screen);
+ }
+
+ public static int getNativeVisualID(final AbstractGraphicsDevice device, final long windowHandle) {
+ if( isX11 ) {
+ return X11Lib.GetVisualIDFromWindow(device.getHandle(), windowHandle);
+ }
+ if( isWindows || isOSX ) {
+ return VisualIDHolder.VID_UNDEFINED;
+ }
+ throw new UnsupportedOperationException("n/a for this windowing system: "+nwt);
+ }
+
+ /**
+ * @param swtControl the SWT Control to retrieve the native widget-handle from
+ * @return the native widget-handle
+ * @throws NativeWindowException if the widget handle is null
+ */
+ public static long getWindowHandle(final Window stageWindow) throws NativeWindowException {
+ final long h[] = { 0 };
+ runOnJFXThread(true, new Runnable() {
+ public void run() {
+ try {
+ final TKStage tkStage = (TKStage) tkStageGetter.invoke(stageWindow);
+ if( null != tkStage ) {
+ final Object platformWindow = glassWindowGetter.invoke(tkStage);
+ if( null != platformWindow ) {
+ final Object nativeHandle = nativeWindowGetter.invoke(platformWindow);
+ h[0] = ((Long) nativeHandle).longValue();
+ } else if(DEBUG) {
+ System.err.println(Thread.currentThread().getName()+" - Info: JFXAccessor null GlassWindow");
+ }
+ } else if(DEBUG) {
+ System.err.println(Thread.currentThread().getName()+" - Info: JFXAccessor null TKStage");
+ }
+ } catch (final Throwable e) {
+ throw new NativeWindowException("Error getting Window handle", e);
+ }
+ } });
+ return h[0];
+ }
+}
diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/util/Point.java b/src/nativewindow/classes/com/jogamp/nativewindow/util/Point.java
index fc5465bbf..aa511b625 100644
--- a/src/nativewindow/classes/com/jogamp/nativewindow/util/Point.java
+++ b/src/nativewindow/classes/com/jogamp/nativewindow/util/Point.java
@@ -120,6 +120,18 @@ public class Point implements Cloneable, PointImmutable {
/**
* Translate this instance's x- and y-components,
+ * i.e. add the values of the given delta point to them.
+ * @param pd delta point
+ * @return this instance for scaling
+ */
+ public final Point translate(final PointImmutable pd) {
+ x += pd.getX() ;
+ y += pd.getY() ;
+ return this;
+ }
+
+ /**
+ * Translate this instance's x- and y-components,
* i.e. add the given deltas to them.
* @param dx delta for x
* @param dy delta for y
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..1fe3fa9ba
--- /dev/null
+++ b/src/newt/classes/com/jogamp/newt/javafx/NewtCanvasJFX.java
@@ -0,0 +1,637 @@
+/**
+ * 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.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.SurfaceUpdatedListener;
+import com.jogamp.nativewindow.WindowClosingProtocol;
+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;
+
+/**
+ * JFX {@link Canvas} containing a NEWT {@link Window} using native parenting.
+ * <p>
+ * Implementation allows use of custom {@link GLCapabilities}.
+ * </p>
+ */
+public class NewtCanvasJFX extends Canvas implements WindowClosingProtocol {
+ private static final boolean DEBUG = Debug.debug("Window");
+
+ private volatile javafx.stage.Window parentWindow = null;
+ private volatile AbstractGraphicsScreen screen = null;
+
+ private WindowClosingMode newtChildCloseOp = 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);
+ }
+ NewtCanvasJFX.this.dispose();
+ } };
+ 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();
+
+ this.widthProperty().addListener(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.Width, "+oldValue.doubleValue()+" -> "+newValue.doubleValue()+", has "+getWidth());
+ }
+ updateSizeCheck(newValue.intValue(), (int)getHeight());
+ repaintAction(isVisible());
+ }
+ });
+ this.heightProperty().addListener(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.Height, "+oldValue.doubleValue()+" -> "+newValue.doubleValue()+", has "+getHeight());
+ }
+ updateSizeCheck((int)getWidth(), newValue.intValue());
+ repaintAction(isVisible());
+ }
+ });
+ 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 ) {
+ disposeImpl(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 ) {
+ disposeImpl(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 <code>destroy()</code> on the NEWT Child</li>
+ * <li> Remove reference to the NEWT Child</li>
+ * </ul>
+ * @see Window#destroy()
+ */
+ // FIXME JFX similar @Override
+ public void dispose() {
+ disposeImpl(true);
+ }
+ private void disposeImpl(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();
+
+ // set JFX EDT and start it
+ if(false) {
+ final Display newtDisplay = newtChild.getScreen().getDisplay();
+ final EDTUtil edtUtil = new JFXEDTUtil(newtDisplay);
+ edtUtil.start();
+ newtDisplay.setEDTUtil( edtUtil );
+ }
+
+ 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) {
+ newtChildCloseOp = newtChild.setDefaultCloseOperation(WindowClosingMode.DO_NOTHING_ON_CLOSE);
+ } else {
+ newtChild.setFocusAction(null);
+ newtChild.setDefaultCloseOperation(newtChildCloseOp);
+ }
+ }
+ }
+
+ /** @return the current NEWT child */
+ public Window getNEWTChild() {
+ return newtChild;
+ }
+
+ /** @return this JFX Canvas NativeWindow representation, may be null in case it has not been realized. */
+ public NativeWindow getNativeWindow() { return nativeWindow; }
+
+ @Override
+ public WindowClosingMode getDefaultCloseOperation() {
+ return newtChildCloseOp; // TODO: implement ?!
+ }
+
+ @Override
+ public WindowClosingMode setDefaultCloseOperation(final WindowClosingMode op) {
+ return newtChildCloseOp = op; // TODO: implement ?!
+ }
+
+
+ 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 0;
+ }
+
+ @Override
+ public int getY() {
+ return 0;
+ }
+
+ @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/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/test/com/jogamp/opengl/test/junit/jogl/javafx/PureJFXApp01.java b/src/test/com/jogamp/opengl/test/junit/jogl/javafx/PureJFXApp01.java
new file mode 100644
index 000000000..d2a3b8073
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/javafx/PureJFXApp01.java
@@ -0,0 +1,54 @@
+/**
+ * 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.opengl.test.junit.jogl.javafx;
+
+import javafx.application.Application;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+import javafx.scene.text.Font;
+import javafx.scene.text.Text;
+import javafx.stage.Stage;
+
+public class PureJFXApp01 extends Application {
+
+ @Override public void start(Stage stage) {
+ Text text = new Text(10, 40, "Pure JFX App 01");
+ text.setFont(new Font(40));
+ Scene scene = new Scene(new Group(text));
+
+ stage.setTitle("JavaFX Stage");
+ stage.setScene(scene);
+ stage.sizeToScene();
+ stage.show();
+ }
+
+ public static void main(String[] args) {
+ Application.launch(args);
+ }
+}
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/javafx/TestNewtCanvasJFXGLn.java b/src/test/com/jogamp/opengl/test/junit/jogl/javafx/TestNewtCanvasJFXGLn.java
new file mode 100644
index 000000000..8ec5a87d1
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/javafx/TestNewtCanvasJFXGLn.java
@@ -0,0 +1,462 @@
+/**
+ * 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.opengl.test.junit.jogl.javafx;
+
+import com.jogamp.opengl.GLAutoDrawable;
+import com.jogamp.opengl.GLCapabilities;
+import com.jogamp.opengl.GLCapabilitiesImmutable;
+import com.jogamp.opengl.GLEventListener;
+import com.jogamp.opengl.GLProfile;
+
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.junit.FixMethodOrder;
+import org.junit.runners.MethodSorters;
+
+import com.jogamp.common.util.RunnableTask;
+import com.jogamp.nativewindow.javafx.JFXAccessor;
+import com.jogamp.nativewindow.swt.SWTAccessor;
+import com.jogamp.newt.NewtFactory;
+import com.jogamp.newt.Screen;
+import com.jogamp.newt.javafx.NewtCanvasJFX;
+import com.jogamp.newt.opengl.GLWindow;
+import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2;
+import com.jogamp.opengl.test.junit.jogl.demos.es2.MultisampleDemoES2;
+import com.jogamp.opengl.test.junit.util.AWTRobotUtil;
+import com.jogamp.opengl.test.junit.util.MiscUtils;
+import com.jogamp.opengl.test.junit.util.UITestCase;
+import com.jogamp.opengl.util.Animator;
+import com.jogamp.opengl.util.GLReadBufferUtil;
+import com.jogamp.opengl.util.texture.TextureIO;
+
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+import javafx.scene.canvas.Canvas;
+import javafx.scene.canvas.GraphicsContext;
+import javafx.scene.paint.Color;
+import javafx.scene.text.Font;
+import javafx.scene.text.Text;
+import javafx.stage.Stage;
+
+/**
+ * Tests that a basic SWT app can open without crashing under different GL profiles
+ * _and_ custom GLCapabilities.
+ * <p>
+ * Uses JOGL's NewtCanvasSWT, which allows to be a native container of a NEWT Window.<br/>
+ * This method allows utilizing custom GLCapability settings,
+ * independent from the already instantiated SWT visual.
+ * </p>
+ * <p>
+ * Note that {@link SWTAccessor#invoke(boolean, Runnable)} is still used to comply w/
+ * SWT running on Mac OSX, i.e. to enforce UI action on the main thread.
+ * </p>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class TestNewtCanvasJFXGLn extends UITestCase {
+
+ static int duration = 5000; // 250;
+
+ static final int iwidth = 640;
+ static final int iheight = 480;
+
+ com.jogamp.newt.Display jfxNewtDisplay = null;
+
+ public static class JFXApp extends Application {
+ static Stage stage;
+
+ final static Object sync = new Object();
+ static volatile boolean isLaunched = false;
+
+ public JFXApp() {
+ }
+
+ @Override public void init() throws Exception {
+ // pre JFX thread
+ System.err.println("JFX init ...: "+Thread.currentThread());
+ }
+
+ @Override public void start(final Stage stage) {
+ System.err.println("JFX start.0 ...: "+Thread.currentThread());
+ synchronized(sync) {
+ try {
+ // on JFX thread
+ final Scene scene = new Scene(new Group(), defWidth, defHeight);
+ stage.setTitle(TestNewtCanvasJFXGLn.class.getSimpleName());
+ stage.setScene(scene);
+ stage.sizeToScene();
+ {
+ final long h = JFXAccessor.getWindowHandle(stage);
+ System.err.println("t1 - Native window: 0x"+Long.toHexString(h));
+ }
+ stage.show();
+ {
+ final long h = JFXAccessor.getWindowHandle(stage);
+ System.err.println("t2 - Native window: 0x"+Long.toHexString(h));
+ }
+ JFXApp.stage = stage;
+ } finally {
+ isLaunched = true;
+ sync.notifyAll();
+ }
+ }
+ System.err.println("JFX start.X ...: "+Thread.currentThread());
+ }
+ @Override public void stop() throws Exception {
+ System.err.println("JFX stop ...: "+Thread.currentThread());
+ }
+ public static void startup() throws InterruptedException {
+ System.out.println( "GLProfile " + GLProfile.glAvailabilityToString() );
+ System.err.println("JFX Available: "+JFXAccessor.isJFXAvailable());
+ if( JFXAccessor.isJFXAvailable() ) {
+ Platform.setImplicitExit(false); // FIXME: Default for all NEWT cases?
+ synchronized(sync) {
+ final Thread ct = Thread.currentThread();
+ RunnableTask.invokeOnNewThread(ct.getThreadGroup(), ct.getName()+"JFXLauncher", false,
+ new Runnable() {
+ public void run() {
+ Application.launch(JFXApp.class);
+ }
+ });
+ while(!isLaunched) {
+ sync.wait();
+ }
+ }
+ System.err.println("JFX launched ...");
+ }
+ }
+ public static void shutdown() {
+ JFXAccessor.runOnJFXThread(true, new Runnable() {
+ public void run() {
+ if( null != stage ) {
+ stage.close();
+ }
+ } });
+ }
+ }
+
+ @BeforeClass
+ public static void startup() throws InterruptedException {
+ JFXApp.startup();
+ }
+
+ @AfterClass
+ public static void shutdown() {
+ JFXApp.shutdown();
+ Platform.exit();
+ }
+
+ @Before
+ public void init() {
+ jfxNewtDisplay = NewtFactory.createDisplay(null, false); // no-reuse
+ }
+
+ @After
+ public void release() {
+ jfxNewtDisplay = null;
+ }
+
+ class WaitAction implements Runnable {
+ private final long sleepMS;
+
+ WaitAction(final long sleepMS) {
+ this.sleepMS = sleepMS;
+ }
+ public void run() {
+ // blocks on linux .. display.sleep();
+ try {
+ Thread.sleep(sleepMS);
+ } catch (final InterruptedException e) { }
+ }
+ }
+ final WaitAction awtRobotWaitAction = new WaitAction(AWTRobotUtil.TIME_SLICE);
+ final WaitAction generalWaitAction = new WaitAction(10);
+
+ static final int defWidth = 800, defHeight = 600;
+
+ static void populateScene(final Scene scene, final boolean postAttach,
+ final GLWindow glWindow,
+ final int width, final int height, final boolean useBorder,
+ final NewtCanvasJFX[] res) {
+ final javafx.stage.Window w = scene.getWindow();
+ final boolean isShowing = null != w && w.isShowing();
+ final Group g = new Group();
+
+ final int cx, cy, cw, ch, bw, bh;
+ if( useBorder ) {
+ bw = width/5; bh = height/5;
+ cx = bw; cy = bh; cw = width-bw-bw; ch = height-bh-bh;
+ } else {
+ bw = 0; bh = 0;
+ cx = 0; cy = 0; cw = width; ch = height;
+ }
+ System.err.println("Scene "+width+"x"+height+", isShowing "+isShowing+", postAttach "+postAttach);
+ System.err.println("Scene.canvas "+cx+"/"+cy+" "+cw+"x"+ch);
+ System.err.println("Scene.border "+bw+"x"+bh);
+
+ if( !postAttach ) {
+ if(isShowing) {
+ JFXAccessor.runOnJFXThread(true, new Runnable() {
+ @Override
+ public void run() {
+ scene.setRoot(g);
+ }});
+ } else {
+ scene.setRoot(g);
+ }
+ }
+
+ final Canvas canvas0;
+ if( null == res ) {
+ canvas0 = new Canvas();
+ } else {
+ res[0] = new NewtCanvasJFX( glWindow );
+ canvas0 = res[0];
+ }
+ canvas0.setWidth(cw);
+ canvas0.setHeight(ch);
+ if( null == res ) {
+ final GraphicsContext gc = canvas0.getGraphicsContext2D();
+ gc.setFill(Color.BLUE);
+ gc.fillRect(0, 0, cw, ch);
+ }
+ canvas0.relocate(cx, cy);
+
+ final Text text0 = new Text(0, 0, "left");
+ {
+ text0.setFont(new Font(40));
+ text0.relocate(0, height/2);
+ }
+ final Text text1 = new Text(0, 0, "above");
+ {
+ text1.setFont(new Font(40));
+ text1.relocate(width/2, bh-40);
+ }
+ final Text text2 = new Text(0, 0, "right");
+ {
+ text2.setFont(new Font(40));
+ text2.relocate(width-bw, height/2);
+ }
+ final Text text3 = new Text(0, 0, "below");
+ {
+ text3.setFont(new Font(40));
+ text3.relocate(width/2, height-bh);
+ }
+ final Runnable attach2Group = new Runnable() {
+ @Override
+ public void run() {
+ g.getChildren().add(text0);
+ g.getChildren().add(text1);
+ g.getChildren().add(canvas0);
+ g.getChildren().add(text2);
+ g.getChildren().add(text3);
+ } };
+ if( !postAttach && isShowing ) {
+ JFXAccessor.runOnJFXThread(true, attach2Group);
+ } else {
+ attach2Group.run();
+ }
+ if( postAttach ) {
+ if(isShowing) {
+ JFXAccessor.runOnJFXThread(true, new Runnable() {
+ @Override
+ public void run() {
+ scene.setRoot(g);
+ }});
+ } else {
+ scene.setRoot(g);
+ }
+ }
+ }
+
+ protected void runTestAGL( final GLCapabilitiesImmutable caps, final GLEventListener demo,
+ final boolean postAttachNewtCanvas, final boolean postAttachGLWindow,
+ final boolean useAnimator ) throws InterruptedException {
+ if( !JFXAccessor.isJFXAvailable() ) {
+ System.err.println("JFX not available");
+ return;
+ }
+ final GLReadBufferUtil screenshot = new GLReadBufferUtil(false, false);
+ final GLWindow glWindow1;
+ if( null == demo ) {
+ glWindow1 = null;
+ } else {
+ final Screen screen = NewtFactory.createScreen(jfxNewtDisplay, 0);
+ glWindow1 = GLWindow.create(screen, caps);
+ Assert.assertNotNull(glWindow1);
+ Assert.assertEquals(false, glWindow1.isVisible());
+ Assert.assertEquals(false, glWindow1.isNativeValid());
+ Assert.assertNull(glWindow1.getParent());
+ glWindow1.addGLEventListener(demo);
+ glWindow1.addGLEventListener(new GLEventListener() {
+ int displayCount = 0;
+ public void init(final GLAutoDrawable drawable) { }
+ public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { }
+ public void display(final GLAutoDrawable drawable) {
+ if(displayCount < 3) {
+ snapshot(displayCount++, null, drawable.getGL(), screenshot, TextureIO.PNG, null);
+ }
+ }
+ public void dispose(final GLAutoDrawable drawable) { }
+ });
+ }
+
+ final NewtCanvasJFX[] glCanvas = null==demo? null : new NewtCanvasJFX[]{null};
+
+ final Scene scene = new Scene(new Group(), defWidth, defHeight);
+ if(!postAttachNewtCanvas) {
+ System.err.println("Stage set.A0");
+ JFXAccessor.runOnJFXThread(true, new Runnable() {
+ public void run() {
+ System.err.println("Stage set.A1");
+ JFXApp.stage.setScene(scene);
+ JFXApp.stage.sizeToScene();
+ System.err.println("Stage set.AX");
+ } });
+ }
+ populateScene( scene, postAttachNewtCanvas, postAttachGLWindow?null:glWindow1, defWidth, defHeight, true, glCanvas);
+ if(postAttachNewtCanvas) {
+ System.err.println("Stage set.B0");
+ JFXAccessor.runOnJFXThread(true, new Runnable() {
+ public void run() {
+ System.err.println("Stage set.B1");
+ JFXApp.stage.setScene(scene);
+ JFXApp.stage.sizeToScene();
+ System.err.println("Stage set.BX");
+ } });
+ }
+
+ if(postAttachGLWindow && null != demo) {
+ glCanvas[0].setNEWTChild(glWindow1);
+ }
+
+ if( null != glWindow1 ) {
+ Assert.assertTrue("GLWindow didn't become visible natively!", AWTRobotUtil.waitForRealized(glWindow1, awtRobotWaitAction, true));
+ System.err.println("GLWindow LOS.0: "+glWindow1.getLocationOnScreen(null));
+ }
+ if( null != demo ) {
+ System.err.println("NewtCanvasSWT LOS.0: "+glCanvas[0].getNativeWindow().getLocationOnScreen(null));
+ }
+
+ Animator anim;
+ if(useAnimator && null != demo) {
+ anim = new Animator(glWindow1);
+ anim.start();
+ } else {
+ anim = null;
+ }
+
+ final long lStartTime = System.currentTimeMillis();
+ final long lEndTime = lStartTime + duration;
+ try {
+ while( (System.currentTimeMillis() < lEndTime) ) {
+ generalWaitAction.run();
+ }
+ } catch( final Throwable throwable ) {
+ throwable.printStackTrace();
+ Assume.assumeNoException( throwable );
+ }
+ if(null != anim) {
+ anim.stop();
+ }
+
+ JFXAccessor.runOnJFXThread(true, new Runnable() {
+ public void run() {
+ populateScene( JFXApp.stage.getScene(), false, null, defWidth, defHeight, true, null);
+ JFXApp.stage.sizeToScene();
+ } });
+ }
+
+ @Test
+ public void test00() throws InterruptedException {
+ runTestAGL( null, null,
+ false /* postAttachNewtCanvas */, false /* postAttach */, false /* animator */);
+ }
+
+ @Test
+ public void test11_preAttachNewtGL_NoAnim() throws InterruptedException {
+ runTestAGL( new GLCapabilities(GLProfile.getGL2ES2()), new GearsES2(),
+ false /* postAttachNewtCanvas */, false /* postAttachGLWindow */, false /* animator */);
+ }
+
+ @Test
+ public void test12_postAttachNewt_NoAnim() throws InterruptedException {
+ runTestAGL( new GLCapabilities(GLProfile.getGL2ES2()), new GearsES2(),
+ true /* postAttachNewtCanvas */, false /* postAttachGLWindow */, false /* animator */);
+ }
+
+ @Test
+ public void test13_postAttachGL_NoAnim() throws InterruptedException {
+ runTestAGL( new GLCapabilities(GLProfile.getGL2ES2()), new GearsES2(),
+ false /* postAttachNewtCanvas */, true /* postAttachGLWindow */, false /* animator */);
+ }
+
+ @Test
+ public void test14_postAttachNewtGL_NoAnim() throws InterruptedException {
+ runTestAGL( new GLCapabilities(GLProfile.getGL2ES2()), new GearsES2(),
+ true /* postAttachNewtCanvas */, true /* postAttachGLWindow */, false /* animator */);
+ }
+
+ @Test
+ public void test21_preAttachNewtGL_DoAnim() throws InterruptedException {
+ runTestAGL( new GLCapabilities(GLProfile.getGL2ES2()), new GearsES2(),
+ false /* postAttachNewtCanvas */, false /* postAttachGLWindow */, true /* animator */);
+ }
+
+ @Test
+ public void test22_postAttachNewt_DoAnim() throws InterruptedException {
+ runTestAGL( new GLCapabilities(GLProfile.getGL2ES2()), new GearsES2(),
+ true /* postAttachNewtCanvas */, false /* postAttachGLWindow */, true /* animator */);
+ }
+
+ @Test
+ public void test30_MultisampleAndAlpha() throws InterruptedException {
+ final GLCapabilities caps = new GLCapabilities(GLProfile.getGL2ES2());
+ caps.setSampleBuffers(true);
+ caps.setNumSamples(2);
+ runTestAGL( caps, new MultisampleDemoES2(true),
+ false /* postAttachNewtCanvas */, false /* postAttachGLWindow */, false /* animator */);
+ }
+
+ public static void main(final String args[]) {
+ for(int i=0; i<args.length; i++) {
+ if(args[i].equals("-time")) {
+ duration = MiscUtils.atoi(args[++i], duration);
+ }
+ }
+ System.out.println("durationPerTest: "+duration);
+ org.junit.runner.JUnitCore.main(TestNewtCanvasJFXGLn.class.getName());
+ }
+}