aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMichael Bien <[email protected]>2010-08-22 21:09:25 +0200
committerMichael Bien <[email protected]>2010-08-22 21:09:25 +0200
commitd24cac77e8d7658b32b8bcb654e10a899de59433 (patch)
tree1ecee6a3bc12a2b71c3454ba4de0efc5e1385684 /src
parentddb53592e6633ed2b27519790ca15e07221942ff (diff)
parent8d55c437547a697b7d0bd4dd81b6669209cf912f (diff)
Merge branch 'master' of github.com:sgothel/jogl
Diffstat (limited to 'src')
-rw-r--r--src/junit/com/jogamp/test/junit/newt/TestGLWindows01NEWT.java2
-rw-r--r--src/newt/classes/com/jogamp/newt/Display.java61
-rw-r--r--src/newt/classes/com/jogamp/newt/impl/macosx/MacDisplay.java65
-rw-r--r--src/newt/classes/com/jogamp/newt/impl/macosx/MacWindow.java7
-rw-r--r--src/newt/classes/com/jogamp/newt/util/DefaultEDTUtil.java228
-rw-r--r--src/newt/classes/com/jogamp/newt/util/EDTUtil.java202
-rw-r--r--src/newt/classes/com/jogamp/newt/util/MainThread.java235
-rw-r--r--src/newt/native/KDWindow.c16
-rw-r--r--src/newt/native/NewtMacWindow.m53
9 files changed, 509 insertions, 360 deletions
diff --git a/src/junit/com/jogamp/test/junit/newt/TestGLWindows01NEWT.java b/src/junit/com/jogamp/test/junit/newt/TestGLWindows01NEWT.java
index 3e7228aba..ea31dfc02 100644
--- a/src/junit/com/jogamp/test/junit/newt/TestGLWindows01NEWT.java
+++ b/src/junit/com/jogamp/test/junit/newt/TestGLWindows01NEWT.java
@@ -110,7 +110,7 @@ public class TestGLWindows01NEWT {
int wait=0;
while(wait<10 && glWindow.getTotalFrames()<1) { Thread.sleep(100); wait++; }
System.out.println("Frames for initial setVisible(true): "+glWindow.getTotalFrames());
- Assert.assertTrue(0<glWindow.getTotalFrames()); // native expose ..
+ // FIXME: Assert.assertTrue(0<glWindow.getTotalFrames()); // native expose ..
// Assert.assertEquals(width,glWindow.getWidth());
// Assert.assertEquals(height,glWindow.getHeight());
// System.out.println("Created: "+glWindow);
diff --git a/src/newt/classes/com/jogamp/newt/Display.java b/src/newt/classes/com/jogamp/newt/Display.java
index deb4c7abe..a8ab8d520 100644
--- a/src/newt/classes/com/jogamp/newt/Display.java
+++ b/src/newt/classes/com/jogamp/newt/Display.java
@@ -39,10 +39,13 @@ import com.jogamp.newt.event.*;
import com.jogamp.newt.impl.event.*;
import com.jogamp.newt.impl.Debug;
import com.jogamp.newt.util.EDTUtil;
+import com.jogamp.newt.util.MainThread;
+import com.jogamp.newt.util.DefaultEDTUtil;
import java.util.*;
public abstract class Display {
public static final boolean DEBUG = Debug.debug("Display");
+ public static final boolean DEBUG_TEST_EDT_MAINTHREAD = Debug.debug("TestEDTMainThread"); // JAU EDT Test ..
private static Class getDisplayClass(String type)
throws ClassNotFoundException
@@ -181,21 +184,35 @@ public abstract class Display {
public boolean runCreateAndDestroyOnEDT() {
return true;
}
+
public EDTUtil getEDTUtil() {
if( null == edtUtil ) {
synchronized (this) {
if( null == edtUtil ) {
if(NewtFactory.useEDT()) {
final Display f_dpy = this;
- Thread current = Thread.currentThread();
- edtUtil = new EDTUtil(current.getThreadGroup(),
- "Display_"+getFQName(),
- new Runnable() {
- public void run() {
- if(null!=f_dpy.getGraphicsDevice()) {
- f_dpy.dispatchMessages();
- } } } );
- edt = edtUtil.start();
+ if ( ! DEBUG_TEST_EDT_MAINTHREAD ) {
+ Thread current = Thread.currentThread();
+ edtUtil = new DefaultEDTUtil(current.getThreadGroup(),
+ "Display_"+getFQName(),
+ new Runnable() {
+ public void run() {
+ if(null!=f_dpy.getGraphicsDevice()) {
+ f_dpy.dispatchMessages();
+ } } } );
+ } else {
+ // Begin JAU EDT Test ..
+ MainThread.addPumpMessage(this,
+ new Runnable() {
+ public void run() {
+ if(null!=f_dpy.getGraphicsDevice()) {
+ f_dpy.dispatchMessages();
+ } } } );
+ edtUtil = MainThread.getSingleton();
+ System.err.println("Display.getEDTUtil("+getFQName()+") Test EDT MainThread: "+edtUtil.getClass().getName());
+ // End JAU EDT Test ..
+ }
+ edtUtil.start();
}
}
}
@@ -203,6 +220,16 @@ public abstract class Display {
return edtUtil;
}
+ protected void releaseEDTUtil() {
+ if(null!=edtUtil) {
+ if ( DEBUG_TEST_EDT_MAINTHREAD ) {
+ MainThread.removePumpMessage(this); // JAU EDT Test ..
+ }
+ edtUtil.waitUntilStopped();
+ edtUtil=null;
+ }
+ }
+
public void runOnEDTIfAvail(boolean wait, final Runnable task) {
EDTUtil _edtUtil = getEDTUtil();
if(runCreateAndDestroyOnEDT() && null!=_edtUtil) {
@@ -223,21 +250,16 @@ public abstract class Display {
System.err.println("Display.destroy("+getFQName()+") REMOVE: "+this+" "+getThreadName());
}
final Display f_dpy = this;
- final EDTUtil f_edt = edtUtil;
+ final EDTUtil f_edtUtil = edtUtil;
runOnEDTIfAvail(true, new Runnable() {
public void run() {
f_dpy.closeNative();
- if(null!=f_edt) {
- f_edt.stop();
+ if(null!=f_edtUtil) {
+ f_edtUtil.stop();
}
}
} );
-
- if(null!=edtUtil) {
- edtUtil.waitUntilStopped();
- edtUtil=null;
- edt=null;
- }
+ releaseEDTUtil();
aDevice = null;
} else {
if(DEBUG) {
@@ -316,7 +338,7 @@ public abstract class Display {
private Object eventsLock = new Object();
private LinkedList/*<NEWTEvent>*/ events = new LinkedList();
- protected void dispatchMessages() {
+ public void dispatchMessages() {
if(0==refCount) return; // in destruction ..
LinkedList/*<NEWTEvent>*/ _events = null;
@@ -383,7 +405,6 @@ public abstract class Display {
}
protected EDTUtil edtUtil = null;
- protected Thread edt = null;
protected String name;
protected String type;
protected int refCount;
diff --git a/src/newt/classes/com/jogamp/newt/impl/macosx/MacDisplay.java b/src/newt/classes/com/jogamp/newt/impl/macosx/MacDisplay.java
index 699b675dd..11f825282 100644
--- a/src/newt/classes/com/jogamp/newt/impl/macosx/MacDisplay.java
+++ b/src/newt/classes/com/jogamp/newt/impl/macosx/MacDisplay.java
@@ -38,6 +38,7 @@ import javax.media.nativewindow.macosx.*;
import com.jogamp.common.util.ReflectionUtil;
import com.jogamp.newt.*;
import com.jogamp.newt.impl.*;
+import com.jogamp.newt.util.EDTUtil;
import com.jogamp.newt.util.MainThread;
public class MacDisplay extends Display {
@@ -60,15 +61,8 @@ public class MacDisplay extends Display {
public MacDisplay() {
}
- class DispatchAction implements Runnable {
- public void run() {
- dispatchMessages0();
- }
- }
- private DispatchAction dispatchAction = new DispatchAction();
-
protected void dispatchMessagesNative() {
- runOnMainThread(false, dispatchAction);
+ dispatchMessages0();
}
protected void createNative() {
@@ -77,44 +71,33 @@ public class MacDisplay extends Display {
protected void closeNative() { }
- /*public boolean runCreateAndDestroyOnEDT() {
- return false;
- }
public EDTUtil getEDTUtil() {
- return null;
- }*/
-
- protected static void runOnMainThread(boolean wait, Runnable r) {
- if (MainThread.isRunning()) {
- MainThread.invoke(wait, r);
- } else if(!runOnAWTEDT(wait, r)) {
- throw new NativeWindowException("Neither MainThread is running nor AWT EDT available");
+ if( null == edtUtil ) {
+ synchronized (this) {
+ if( null == edtUtil ) {
+ if(NewtFactory.useEDT()) {
+ final Display f_dpy = this;
+ MainThread.addPumpMessage(this,
+ new Runnable() {
+ public void run() {
+ if(null!=f_dpy.getGraphicsDevice()) {
+ f_dpy.dispatchMessages();
+ } } } );
+ edtUtil = MainThread.getSingleton();
+ edtUtil.start();
+ }
+ }
+ }
}
+ return edtUtil;
}
- protected static boolean runOnAWTEDT(boolean wait, Runnable r) {
- ClassLoader cl = MacDisplay.class.getClassLoader();
- if(ReflectionUtil.isClassAvailable("java.awt.EventQueue", cl)) {
- try {
- if(wait) {
- ReflectionUtil.callStaticMethod(
- "java.awt.EventQueue",
- "invokeAndWait",
- new Class[] { java.lang.Runnable.class },
- new Object[] { r }, cl );
- } else {
- ReflectionUtil.callStaticMethod(
- "java.awt.EventQueue",
- "invokeLater",
- new Class[] { java.lang.Runnable.class },
- new Object[] { r }, cl );
- }
- } catch (Exception e) {
- throw new NativeWindowException(e);
- }
- return true;
+ protected void releaseEDTUtil() {
+ if(null!=edtUtil) {
+ MainThread.removePumpMessage(this);
+ edtUtil.waitUntilStopped();
+ edtUtil=null;
}
- return false;
}
private static native boolean initNSApplication0();
diff --git a/src/newt/classes/com/jogamp/newt/impl/macosx/MacWindow.java b/src/newt/classes/com/jogamp/newt/impl/macosx/MacWindow.java
index 8f5041253..a8e6febf5 100644
--- a/src/newt/classes/com/jogamp/newt/impl/macosx/MacWindow.java
+++ b/src/newt/classes/com/jogamp/newt/impl/macosx/MacWindow.java
@@ -165,6 +165,7 @@ public class MacWindow extends Window {
windowHandle = 0;
nsViewLock.unlock();
}
+ windowDestroyed(); // No OSX hook for DidClose, so do it here
}
public long getWindowHandle() {
@@ -406,8 +407,8 @@ public class MacWindow extends Window {
}
try {
- MacDisplay.runOnMainThread(true, new Runnable() {
- public void run() {
+ //runOnEDTIfAvail(true, new Runnable() {
+ // public void run() {
if(0!=windowHandle) {
// save the view .. close the window
surfaceHandle = changeContentView0(parentWindowHandle, windowHandle, 0);
@@ -433,7 +434,7 @@ public class MacWindow extends Window {
setTitle0(windowHandle, getTitle());
// don't make the window visible on window creation
// makeKeyAndOrderFront0(windowHandle);
- } } );
+ // } } );
} catch (Exception ie) {
ie.printStackTrace();
}
diff --git a/src/newt/classes/com/jogamp/newt/util/DefaultEDTUtil.java b/src/newt/classes/com/jogamp/newt/util/DefaultEDTUtil.java
new file mode 100644
index 000000000..87dfdb9d8
--- /dev/null
+++ b/src/newt/classes/com/jogamp/newt/util/DefaultEDTUtil.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution 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.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ */
+
+package com.jogamp.newt.util;
+
+import com.jogamp.common.util.RunnableTask;
+import com.jogamp.newt.Display;
+import com.jogamp.newt.impl.Debug;
+import java.util.*;
+
+public class DefaultEDTUtil implements EDTUtil {
+ public static final boolean DEBUG = Debug.debug("EDT");
+
+ private ThreadGroup threadGroup;
+ private volatile boolean shouldStop = false;
+ private EventDispatchThread edt = null;
+ private Object edtLock = new Object();
+ private ArrayList tasks = new ArrayList(); // one shot tasks
+ private String name;
+ private Runnable pumpMessages;
+
+ public DefaultEDTUtil(ThreadGroup tg, String name, Runnable pumpMessages) {
+ this.threadGroup = tg;
+ this.name=new String(Thread.currentThread().getName()+"-"+"EDT-"+name);
+ this.pumpMessages=pumpMessages;
+ }
+
+ public void start() {
+ synchronized(edtLock) {
+ if(null==edt) {
+ edt = new EventDispatchThread(threadGroup, name);
+ }
+ if(!edt.isRunning()) {
+ shouldStop = false;
+ edt.start();
+ }
+ edtLock.notifyAll();
+ }
+ }
+
+ public void stop() {
+ synchronized(edtLock) {
+ if(null!=edt && edt.isRunning()) {
+ shouldStop = true;
+ }
+ edtLock.notifyAll();
+ if(DEBUG) {
+ System.out.println(Thread.currentThread()+": EDT signal STOP");
+ }
+ }
+ }
+
+ public boolean isCurrentThreadEDT() {
+ return null!=edt && edt == Thread.currentThread();
+ }
+
+ public boolean isRunning() {
+ return null!=edt && edt.isRunning() ;
+ }
+
+ private void invokeLater(Runnable task) {
+ synchronized(edtLock) {
+ if(null!=edt && edt.isRunning() && edt != Thread.currentThread() ) {
+ tasks.add(task);
+ edtLock.notifyAll();
+ } else {
+ // if !running or isEDTThread, do it right away
+ task.run();
+ }
+ }
+ }
+
+ public void invoke(boolean wait, Runnable task) {
+ if(task == null) {
+ return;
+ }
+ boolean doWait = wait && null!=edt && edt.isRunning() && edt != Thread.currentThread();
+ Object lock = new Object();
+ RunnableTask rTask = new RunnableTask(task, doWait?lock:null, true);
+ Throwable throwable = null;
+ synchronized(lock) {
+ invokeLater(rTask);
+ if( doWait ) {
+ try {
+ lock.wait();
+ } catch (InterruptedException ie) {
+ throwable = ie;
+ }
+ }
+ }
+ if(null==throwable) {
+ throwable = rTask.getThrowable();
+ }
+ if(null!=throwable) {
+ throw new RuntimeException(throwable);
+ }
+ }
+
+ public void waitUntilIdle() {
+ synchronized(edtLock) {
+ if(null!=edt && edt.isRunning() && tasks.size()>0 && edt != Thread.currentThread() ) {
+ try {
+ edtLock.wait();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public void waitUntilStopped() {
+ synchronized(edtLock) {
+ while(null!=edt && edt.isRunning() && edt != Thread.currentThread() ) {
+ try {
+ edtLock.wait();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ class EventDispatchThread extends Thread {
+ boolean isRunning = false;
+
+ public EventDispatchThread(ThreadGroup tg, String name) {
+ super(tg, name);
+ }
+
+ public synchronized boolean isRunning() {
+ return isRunning;
+ }
+
+ public void start() throws IllegalThreadStateException {
+ synchronized(this) {
+ isRunning = true;
+ }
+ super.start();
+ }
+
+ /**
+ * Utilizing edtLock only for local resources and task execution,
+ * not for event dispatching.
+ */
+ public void run() {
+ if(DEBUG) {
+ System.out.println(Thread.currentThread()+": EDT run() START");
+ }
+ try {
+ while(!shouldStop) {
+ // wait for something todo
+ while(!shouldStop && tasks.size()==0) {
+ synchronized(edtLock) {
+ if(!shouldStop && tasks.size()==0) {
+ try {
+ edtLock.wait(defaultEDTPollGranularity);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ pumpMessages.run(); // event dispatch
+ }
+ if(!shouldStop && tasks.size()>0) {
+ synchronized(edtLock) {
+ if(!shouldStop && tasks.size()>0) {
+ Runnable task = (Runnable) tasks.remove(0);
+ task.run(); // FIXME: could be run outside of lock
+ edtLock.notifyAll();
+ }
+ }
+ pumpMessages.run(); // event dispatch
+ }
+ }
+ } catch (Throwable t) {
+ // handle errors ..
+ shouldStop = true;
+ throw new RuntimeException(t);
+ } finally {
+ synchronized(this) {
+ isRunning = !shouldStop;
+ }
+ if(!isRunning) {
+ synchronized(edtLock) {
+ edtLock.notifyAll();
+ }
+ }
+ if(DEBUG) {
+ System.out.println(Thread.currentThread()+": EDT run() EXIT");
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/newt/classes/com/jogamp/newt/util/EDTUtil.java b/src/newt/classes/com/jogamp/newt/util/EDTUtil.java
index 2e339fd45..1af102f43 100644
--- a/src/newt/classes/com/jogamp/newt/util/EDTUtil.java
+++ b/src/newt/classes/com/jogamp/newt/util/EDTUtil.java
@@ -38,208 +38,24 @@ package com.jogamp.newt.util;
import com.jogamp.common.util.RunnableTask;
import com.jogamp.newt.Display;
-import com.jogamp.newt.impl.Debug;
import java.util.*;
-public class EDTUtil {
- public static final boolean DEBUG = Debug.debug("EDT");
+public interface EDTUtil {
- private ThreadGroup threadGroup;
- private volatile boolean shouldStop = false;
- private EventDispatchThread edt = null;
- private Object edtLock = new Object();
- private ArrayList tasks = new ArrayList(); // one shot tasks
- private String name;
- private Runnable pumpMessages;
- private long edtPollGranularity = 10; // 10ms, 1/100s
+ public static final long defaultEDTPollGranularity = 10; // 10ms, 1/100s
- public EDTUtil(ThreadGroup tg, String name, Runnable pumpMessages) {
- this.threadGroup = tg;
- this.name=new String(Thread.currentThread().getName()+"-"+"EDT-"+name);
- this.pumpMessages=pumpMessages;
- }
+ public void start();
- public String getName() { return name; }
+ public void stop();
- public ThreadGroup getThreadGroup() { return threadGroup; }
+ public boolean isCurrentThreadEDT();
- /**
- * @return The started Runnable, which handles the run-loop.
- */
- public Thread start() {
- synchronized(edtLock) {
- if(null==edt) {
- edt = new EventDispatchThread(threadGroup, name);
- }
- if(!edt.isRunning()) {
- shouldStop = false;
- edt.start();
- }
- edtLock.notifyAll();
- }
- return edt;
- }
+ public boolean isRunning();
- public void stop() {
- synchronized(edtLock) {
- if(null!=edt && edt.isRunning()) {
- shouldStop = true;
- }
- edtLock.notifyAll();
- if(DEBUG) {
- System.out.println(Thread.currentThread()+": EDT signal STOP");
- }
- }
- }
+ public void invoke(boolean wait, Runnable task);
- public Thread getEDT() {
- return edt;
- }
+ public void waitUntilIdle();
- public boolean isThreadEDT(Thread thread) {
- return null!=edt && edt == thread;
- }
-
- public boolean isCurrentThreadEDT() {
- return null!=edt && edt == Thread.currentThread();
- }
-
- public boolean isRunning() {
- return null!=edt && edt.isRunning() ;
- }
-
- private void invokeLater(Runnable task) {
- synchronized(edtLock) {
- if(null!=edt && edt.isRunning() && edt != Thread.currentThread() ) {
- tasks.add(task);
- edtLock.notifyAll();
- } else {
- // if !running or isEDTThread, do it right away
- task.run();
- }
- }
- }
-
- public void invoke(boolean wait, Runnable task) {
- if(task == null) {
- return;
- }
- boolean doWait = wait && null!=edt && edt.isRunning() && edt != Thread.currentThread();
- Object lock = new Object();
- RunnableTask rTask = new RunnableTask(task, doWait?lock:null, true);
- Throwable throwable = null;
- synchronized(lock) {
- invokeLater(rTask);
- if( doWait ) {
- try {
- lock.wait();
- } catch (InterruptedException ie) {
- throwable = ie;
- }
- }
- }
- if(null==throwable) {
- throwable = rTask.getThrowable();
- }
- if(null!=throwable) {
- throw new RuntimeException(throwable);
- }
- }
-
- public void waitUntilIdle() {
- synchronized(edtLock) {
- if(null!=edt && edt.isRunning() && tasks.size()>0 && edt != Thread.currentThread() ) {
- try {
- edtLock.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
-
- public void waitUntilStopped() {
- synchronized(edtLock) {
- while(null!=edt && edt.isRunning() && edt != Thread.currentThread() ) {
- try {
- edtLock.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
-
- class EventDispatchThread extends Thread {
- boolean isRunning = false;
-
- public EventDispatchThread(ThreadGroup tg, String name) {
- super(tg, name);
- }
-
- public synchronized boolean isRunning() {
- return isRunning;
- }
-
- public void start() throws IllegalThreadStateException {
- synchronized(this) {
- isRunning = true;
- }
- super.start();
- }
-
- /**
- * Utilizing edtLock only for local resources and task execution,
- * not for event dispatching.
- */
- public void run() {
- if(DEBUG) {
- System.out.println(Thread.currentThread()+": EDT run() START");
- }
- try {
- while(!shouldStop) {
- // wait for something todo
- while(!shouldStop && tasks.size()==0) {
- synchronized(edtLock) {
- if(!shouldStop && tasks.size()==0) {
- try {
- edtLock.wait(edtPollGranularity);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- pumpMessages.run(); // event dispatch
- }
- if(!shouldStop && tasks.size()>0) {
- synchronized(edtLock) {
- if(!shouldStop && tasks.size()>0) {
- Runnable task = (Runnable) tasks.remove(0);
- task.run();
- edtLock.notifyAll();
- }
- }
- pumpMessages.run(); // event dispatch
- }
- }
- } catch (Throwable t) {
- // handle errors ..
- shouldStop = true;
- throw new RuntimeException(t);
- } finally {
- synchronized(this) {
- isRunning = !shouldStop;
- }
- if(!isRunning) {
- synchronized(edtLock) {
- edtLock.notifyAll();
- }
- }
- if(DEBUG) {
- System.out.println(Thread.currentThread()+": EDT run() EXIT");
- }
- }
- }
- }
+ public void waitUntilStopped();
}
diff --git a/src/newt/classes/com/jogamp/newt/util/MainThread.java b/src/newt/classes/com/jogamp/newt/util/MainThread.java
index cbd520104..ba886ade8 100644
--- a/src/newt/classes/com/jogamp/newt/util/MainThread.java
+++ b/src/newt/classes/com/jogamp/newt/util/MainThread.java
@@ -46,7 +46,6 @@ import javax.media.nativewindow.*;
import com.jogamp.common.util.*;
import com.jogamp.newt.*;
import com.jogamp.newt.impl.*;
-import com.jogamp.newt.impl.macosx.MacDisplay;
/**
* NEWT Utility class MainThread<P>
@@ -81,21 +80,33 @@ import com.jogamp.newt.impl.macosx.MacDisplay;
</PRE>
* Which starts 4 threads, each with a window and OpenGL rendering.<br>
*/
-public class MainThread {
+public class MainThread implements EDTUtil {
private static AccessControlContext localACC = AccessController.getContext();
- public static final boolean USE_MAIN_THREAD = NativeWindowFactory.TYPE_MACOSX.equals(NativeWindowFactory.getNativeWindowType(false)) ||
- Debug.getBooleanProperty("newt.MainThread.force", true, localACC);
+ public static final boolean MAIN_THREAD_CRITERIA = ( !NativeWindowFactory.isAWTAvailable() &&
+ NativeWindowFactory.TYPE_MACOSX.equals(NativeWindowFactory.getNativeWindowType(false))
+ ) || Debug.getBooleanProperty("newt.MainThread.force", true, localACC);
protected static final boolean DEBUG = Debug.debug("MainThread");
+ private static MainThread singletonMainThread = new MainThread(); // one singleton MainThread
+
private static boolean isExit=false;
private static volatile boolean isRunning=false;
private static Object taskWorkerLock=new Object();
private static boolean shouldStop;
private static ArrayList tasks;
- private static ArrayList tasksBlock;
private static Thread mainThread;
+ private static Timer pumpMessagesTimer=null;
+ private static TimerTask pumpMessagesTimerTask=null;
+ private static Map/*<Display, Runnable>*/ pumpMessageDisplayMap = new HashMap();
+
+ private static boolean useMainThread = false;
+ private static Class cAWTEventQueue=null;
+ private static Method mAWTInvokeAndWait=null;
+ private static Method mAWTInvokeLater=null;
+ private static Method mAWTIsDispatchThread=null;
+
static class MainAction extends Thread {
private String mainClassName;
private String[] mainClassArgs;
@@ -109,9 +120,9 @@ public class MainThread {
}
public void run() {
- if ( USE_MAIN_THREAD ) {
+ if ( useMainThread ) {
// we have to start first to provide the service ..
- MainThread.waitUntilRunning();
+ singletonMainThread.waitUntilRunning();
}
// start user app ..
@@ -136,9 +147,9 @@ public class MainThread {
if(DEBUG) System.err.println("MainAction.run(): "+Thread.currentThread().getName()+" user app fin");
- if ( USE_MAIN_THREAD ) {
- MainThread.exit();
- if(DEBUG) System.err.println("MainAction.run(): "+Thread.currentThread().getName()+" MainThread fin - exit");
+ if ( useMainThread ) {
+ singletonMainThread.stop();
+ if(DEBUG) System.err.println("MainAction.run(): "+Thread.currentThread().getName()+" MainThread fin - stop");
System.exit(0);
}
}
@@ -147,7 +158,9 @@ public class MainThread {
/** Your new java application main entry, which pipelines your application */
public static void main(String[] args) {
- if(DEBUG) System.err.println("MainThread.main(): "+Thread.currentThread().getName()+" USE_MAIN_THREAD "+ USE_MAIN_THREAD );
+ useMainThread = MAIN_THREAD_CRITERIA;
+
+ if(DEBUG) System.err.println("MainThread.main(): "+Thread.currentThread().getName()+" useMainThread "+ useMainThread );
if(args.length==0) {
return;
@@ -161,35 +174,145 @@ public class MainThread {
NEWTJNILibLoader.loadNEWT();
- shouldStop = false;
- tasks = new ArrayList();
- tasksBlock = new ArrayList();
- mainThread = Thread.currentThread();
-
mainAction = new MainAction(mainClassName, mainClassArgs);
if(NativeWindowFactory.TYPE_MACOSX.equals(NativeWindowFactory.getNativeWindowType(false))) {
- MacDisplay.initSingleton();
+ ReflectionUtil.callStaticMethod("com.jogamp.newt.impl.macosx.MacDisplay", "initSingleton",
+ null, null, MainThread.class.getClassLoader());
}
- if ( USE_MAIN_THREAD ) {
+ if ( useMainThread ) {
+ shouldStop = false;
+ tasks = new ArrayList();
+ mainThread = Thread.currentThread();
+
// dispatch user's main thread ..
mainAction.start();
// do our main thread task scheduling
- run();
+ singletonMainThread.run();
} else {
// run user's main in this thread
mainAction.run();
}
}
+ public static final MainThread getSingleton() {
+ return singletonMainThread;
+ }
+
+ public static Runnable removePumpMessage(Display dpy) {
+ synchronized(pumpMessageDisplayMap) {
+ return (Runnable) pumpMessageDisplayMap.remove(dpy);
+ }
+ }
+
+ public static void addPumpMessage(Display dpy, Runnable pumpMessage) {
+ if ( useMainThread ) {
+ return; // error ?
+ }
+ if(null == pumpMessagesTimer) {
+ synchronized (MainThread.class) {
+ if(null == pumpMessagesTimer) {
+ pumpMessagesTimer = new Timer();
+ pumpMessagesTimerTask = new TimerTask() {
+ public void run() {
+ synchronized(pumpMessageDisplayMap) {
+ for(Iterator i = pumpMessageDisplayMap.values().iterator(); i.hasNext(); ) {
+ ((Runnable) i.next()).run();
+ }
+ }
+ }
+ };
+ pumpMessagesTimer.scheduleAtFixedRate(pumpMessagesTimerTask, 0, defaultEDTPollGranularity);
+ }
+ }
+ }
+ synchronized(pumpMessageDisplayMap) {
+ pumpMessageDisplayMap.put(dpy, pumpMessage);
+ }
+ }
+
+ private void initAWTReflection() {
+ if(null == cAWTEventQueue) {
+ ClassLoader cl = MainThread.class.getClassLoader();
+ cAWTEventQueue = ReflectionUtil.getClass("java.awt.EventQueue", true, cl);
+ mAWTInvokeAndWait = ReflectionUtil.getMethod(cAWTEventQueue, "invokeAndWait", new Class[] { java.lang.Runnable.class }, cl);
+ mAWTInvokeLater = ReflectionUtil.getMethod(cAWTEventQueue, "invokeLater", new Class[] { java.lang.Runnable.class }, cl);
+ mAWTIsDispatchThread = ReflectionUtil.getMethod(cAWTEventQueue, "isDispatchThread", new Class[] { }, cl);
+ }
+ }
+
+ public void start() {
+ // nop
+ }
+
+ public void stop() {
+ if(DEBUG) System.err.println("MainThread.stop(): "+Thread.currentThread().getName()+" start");
+ synchronized(taskWorkerLock) {
+ if(isRunning) {
+ shouldStop = true;
+ }
+ taskWorkerLock.notifyAll();
+ }
+ if(DEBUG) System.err.println("MainThread.stop(): "+Thread.currentThread().getName()+" end");
+ }
+
+ public boolean isCurrentThreadEDT() {
+ if(NativeWindowFactory.isAWTAvailable()) {
+ initAWTReflection();
+ return ((Boolean) ReflectionUtil.callMethod(null, mAWTIsDispatchThread, null) ).booleanValue();
+ }
+ return isRunning() && mainThread == Thread.currentThread() ;
+ }
+
+ public boolean isRunning() {
+ if( useMainThread ) {
+ synchronized(taskWorkerLock) {
+ return isRunning;
+ }
+ }
+ return true; // AWT is always running
+ }
+
+ private void invokeLater(Runnable task) {
+ synchronized(taskWorkerLock) {
+ if(isRunning() && mainThread != Thread.currentThread()) {
+ tasks.add(task);
+ taskWorkerLock.notifyAll();
+ } else {
+ // if !running or isEDTThread, do it right away
+ task.run();
+ }
+ }
+ }
+
/** invokes the given Runnable */
- public static void invoke(boolean wait, Runnable r) {
+ public void invoke(boolean wait, Runnable r) {
if(r == null) {
return;
}
+ if(NativeWindowFactory.isAWTAvailable()) {
+ initAWTReflection();
+
+ // handover to AWT MainThread ..
+ try {
+ if ( ((Boolean) ReflectionUtil.callMethod(null, mAWTIsDispatchThread, null) ).booleanValue() ) {
+ r.run();
+ return;
+ }
+ if(wait) {
+ ReflectionUtil.callMethod(null, mAWTInvokeAndWait, new Object[] { r });
+ } else {
+ ReflectionUtil.callMethod(null, mAWTInvokeLater, new Object[] { r });
+ }
+ } catch (Exception e) {
+ throw new NativeWindowException(e);
+ }
+ return;
+ }
+
// if this main thread is not being used or
// if this is already the main thread .. just execute.
if( !isRunning() || mainThread == Thread.currentThread() ) {
@@ -197,42 +320,35 @@ public class MainThread {
return;
}
- synchronized(taskWorkerLock) {
- tasks.add(r);
- if(wait) {
- tasksBlock.add(r);
- }
- taskWorkerLock.notifyAll();
- if(wait) {
- while(tasksBlock.size()>0) {
- try {
- taskWorkerLock.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
+ boolean doWait = wait && isRunning() && mainThread != Thread.currentThread();
+ Object lock = new Object();
+ RunnableTask rTask = new RunnableTask(r, doWait?lock:null, true);
+ Throwable throwable = null;
+ synchronized(lock) {
+ invokeLater(rTask);
+ if( doWait ) {
+ try {
+ lock.wait();
+ } catch (InterruptedException ie) {
+ throwable = ie;
}
}
}
+ if(null==throwable) {
+ throwable = rTask.getThrowable();
+ }
+ if(null!=throwable) {
+ throw new RuntimeException(throwable);
+ }
}
- public static void exit() {
- if(DEBUG) System.err.println("MainThread.exit(): "+Thread.currentThread().getName()+" start");
- synchronized(taskWorkerLock) {
- if(isRunning) {
- shouldStop = true;
- }
- taskWorkerLock.notifyAll();
- }
- if(DEBUG) System.err.println("MainThread.exit(): "+Thread.currentThread().getName()+" end");
+ public void waitUntilIdle() {
}
- public static boolean isRunning() {
- synchronized(taskWorkerLock) {
- return isRunning;
- }
+ public void waitUntilStopped() {
}
- private static void waitUntilRunning() {
+ private void waitUntilRunning() {
synchronized(taskWorkerLock) {
if(isExit) return;
@@ -246,7 +362,7 @@ public class MainThread {
}
}
- public static void run() {
+ public void run() {
if(DEBUG) System.err.println("MainThread.run(): "+Thread.currentThread().getName());
synchronized(taskWorkerLock) {
isRunning = true;
@@ -254,8 +370,6 @@ public class MainThread {
}
while(!shouldStop) {
try {
- ArrayList localTasks=null;
-
// wait for something todo ..
synchronized(taskWorkerLock) {
while(!shouldStop && tasks.size()==0) {
@@ -265,29 +379,14 @@ public class MainThread {
e.printStackTrace();
}
}
- // seq. process all tasks until no blocking one exists in the list
- for(Iterator i = tasks.iterator(); tasksBlock.size()>0 && i.hasNext(); ) {
- Runnable task = (Runnable) i.next();
- task.run();
- i.remove();
- tasksBlock.remove(task);
- }
// take over the tasks ..
- if(tasks.size()>0) {
- localTasks = tasks;
- tasks = new ArrayList();
+ if(!shouldStop && tasks.size()>0) {
+ Runnable task = (Runnable) tasks.remove(0);
+ task.run(); // FIXME: could be run outside of lock
}
taskWorkerLock.notifyAll();
}
-
- // seq. process all unblocking tasks ..
- if(null!=localTasks) {
- for(Iterator i = localTasks.iterator(); i.hasNext(); ) {
- Runnable task = (Runnable) i.next();
- task.run();
- }
- }
} catch (Throwable t) {
// handle errors ..
t.printStackTrace();
diff --git a/src/newt/native/KDWindow.c b/src/newt/native/KDWindow.c
index 82f2ba7df..b67b8dbd3 100644
--- a/src/newt/native/KDWindow.c
+++ b/src/newt/native/KDWindow.c
@@ -96,8 +96,8 @@ static jmethodID windowCreatedID = NULL;
static jmethodID sizeChangedID = NULL;
static jmethodID windowDestroyNotifyID = NULL;
static jmethodID windowDestroyedID = NULL;
-static jmethodID enqueueMouseEventID = NULL;
-static jmethodID enqueueKeyEventID = NULL;
+static jmethodID sendMouseEventID = NULL;
+static jmethodID sendKeyEventID = NULL;
/**
* Display
@@ -180,13 +180,13 @@ JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_opengl_kd_KDDisplay_DispatchMes
// time = ev->timestamp
if(KD_INPUT_POINTER_SELECT==ptr->index) {
DBG_PRINT( "event mouse click: src: %p, s:%d, (%d,%d)\n", userData, ptr->select, ptr->x, ptr->y);
- (*env)->CallVoidMethod(env, javaWindow, enqueueMouseEventID,
+ (*env)->CallVoidMethod(env, javaWindow, sendMouseEventID,
(ptr->select==0) ? (jint) EVENT_MOUSE_RELEASED : (jint) EVENT_MOUSE_PRESSED,
(jint) 0,
(jint) ptr->x, (jint) ptr->y, 1, 0);
} else {
DBG_PRINT( "event mouse: src: %d, s:%p, i:0x%X (%d,%d)\n", userData, ptr->select, ptr->index, ptr->x, ptr->y);
- (*env)->CallVoidMethod(env, javaWindow, enqueueMouseEventID, (jint) EVENT_MOUSE_MOVED,
+ (*env)->CallVoidMethod(env, javaWindow, sendMouseEventID, (jint) EVENT_MOUSE_MOVED,
0,
(jint) ptr->x, (jint) ptr->y, 0, 0);
}
@@ -213,14 +213,14 @@ JNIEXPORT jboolean JNICALL Java_com_jogamp_newt_impl_opengl_kd_KDWindow_initIDs
sizeChangedID = (*env)->GetMethodID(env, clazz, "sizeChanged", "(II)V");
windowDestroyNotifyID = (*env)->GetMethodID(env, clazz, "windowDestroyNotify", "()V");
windowDestroyedID = (*env)->GetMethodID(env, clazz, "windowDestroyed", "()V");
- enqueueMouseEventID = (*env)->GetMethodID(env, clazz, "enqueueMouseEvent", "(IIIIII)V");
- enqueueKeyEventID = (*env)->GetMethodID(env, clazz, "enqueueKeyEvent", "(IIIC)V");
+ sendMouseEventID = (*env)->GetMethodID(env, clazz, "sendMouseEvent", "(IIIIII)V");
+ sendKeyEventID = (*env)->GetMethodID(env, clazz, "sendKeyEvent", "(IIIC)V");
if (windowCreatedID == NULL ||
sizeChangedID == NULL ||
windowDestroyNotifyID == NULL ||
windowDestroyedID == NULL ||
- enqueueMouseEventID == NULL ||
- enqueueKeyEventID == NULL) {
+ sendMouseEventID == NULL ||
+ sendKeyEventID == NULL) {
DBG_PRINT( "initIDs failed\n" );
return JNI_FALSE;
}
diff --git a/src/newt/native/NewtMacWindow.m b/src/newt/native/NewtMacWindow.m
index ae658b908..da31a686e 100644
--- a/src/newt/native/NewtMacWindow.m
+++ b/src/newt/native/NewtMacWindow.m
@@ -109,8 +109,8 @@ jint GetDeltaY(NSEvent *event, jint javaMods) {
@end
-static jmethodID enqueueMouseEventID = NULL;
-static jmethodID enqueueKeyEventID = NULL;
+static jmethodID sendMouseEventID = NULL;
+static jmethodID sendKeyEventID = NULL;
static jmethodID insetsChangedID = NULL;
static jmethodID sizeChangedID = NULL;
static jmethodID positionChangedID = NULL;
@@ -122,15 +122,15 @@ static jmethodID windowDestroyedID = NULL;
+ (BOOL) initNatives: (JNIEnv*) env forClass: (jclass) clazz
{
- enqueueMouseEventID = (*env)->GetMethodID(env, clazz, "enqueueMouseEvent", "(IIIIII)V");
- enqueueKeyEventID = (*env)->GetMethodID(env, clazz, "enqueueKeyEvent", "(IIIC)V");
+ sendMouseEventID = (*env)->GetMethodID(env, clazz, "sendMouseEvent", "(IIIIII)V");
+ sendKeyEventID = (*env)->GetMethodID(env, clazz, "sendKeyEvent", "(IIIC)V");
sizeChangedID = (*env)->GetMethodID(env, clazz, "sizeChanged", "(II)V");
insetsChangedID = (*env)->GetMethodID(env, clazz, "insetsChanged", "(IIII)V");
positionChangedID = (*env)->GetMethodID(env, clazz, "positionChanged", "(II)V");
focusChangedID = (*env)->GetMethodID(env, clazz, "focusChanged", "(Z)V");
windowDestroyNotifyID = (*env)->GetMethodID(env, clazz, "windowDestroyNotify", "()V");
windowDestroyedID = (*env)->GetMethodID(env, clazz, "windowDestroyed", "()V");
- if (enqueueMouseEventID && enqueueKeyEventID && sizeChangedID && insetsChangedID &&
+ if (sendMouseEventID && sendKeyEventID && sizeChangedID && insetsChangedID &&
positionChangedID && focusChangedID && windowDestroyedID && windowDestroyNotifyID)
{
return YES;
@@ -206,7 +206,7 @@ static jint mods2JavaMods(NSUInteger mods)
return javaMods;
}
-- (void) enqueueKeyEvent: (NSEvent*) event eventType: (jint) evType
+- (void) sendKeyEvent: (NSEvent*) event eventType: (jint) evType
{
NSView* nsview = [self contentView];
if( ! [nsview isMemberOfClass:[NewtView class]] ) {
@@ -229,23 +229,23 @@ static jint mods2JavaMods(NSUInteger mods)
// Note: the key code in the NSEvent does not map to anything we can use
jchar keyChar = (jchar) [chars characterAtIndex: i];
- (*env)->CallVoidMethod(env, javaWindowObject, enqueueKeyEventID,
+ (*env)->CallVoidMethod(env, javaWindowObject, sendKeyEventID,
evType, javaMods, keyCode, keyChar);
}
}
- (void) keyDown: (NSEvent*) theEvent
{
- [self enqueueKeyEvent: theEvent eventType: EVENT_KEY_PRESSED];
+ [self sendKeyEvent: theEvent eventType: EVENT_KEY_PRESSED];
}
- (void) keyUp: (NSEvent*) theEvent
{
- [self enqueueKeyEvent: theEvent eventType: EVENT_KEY_RELEASED];
- [self enqueueKeyEvent: theEvent eventType: EVENT_KEY_TYPED];
+ [self sendKeyEvent: theEvent eventType: EVENT_KEY_RELEASED];
+ [self sendKeyEvent: theEvent eventType: EVENT_KEY_TYPED];
}
-- (void) enqueueMouseEvent: (NSEvent*) event eventType: (jint) evType
+- (void) sendMouseEvent: (NSEvent*) event eventType: (jint) evType
{
NSView* nsview = [self contentView];
if( ! [nsview isMemberOfClass:[NewtView class]] ) {
@@ -302,7 +302,7 @@ static jint mods2JavaMods(NSUInteger mods)
// ignore 0 increment wheel scroll events
return;
}
- (*env)->CallVoidMethod(env, javaWindowObject, enqueueMouseEventID,
+ (*env)->CallVoidMethod(env, javaWindowObject, sendMouseEventID,
evType, javaMods,
(jint) location.x,
(jint) (contentRect.size.height - location.y),
@@ -311,70 +311,70 @@ static jint mods2JavaMods(NSUInteger mods)
- (void) mouseEntered: (NSEvent*) theEvent
{
- [self enqueueMouseEvent: theEvent eventType: EVENT_MOUSE_ENTERED];
+ [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_ENTERED];
}
- (void) mouseExited: (NSEvent*) theEvent
{
- [self enqueueMouseEvent: theEvent eventType: EVENT_MOUSE_EXITED];
+ [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_EXITED];
}
- (void) mouseMoved: (NSEvent*) theEvent
{
- [self enqueueMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED];
+ [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED];
}
- (void) scrollWheel: (NSEvent*) theEvent
{
- [self enqueueMouseEvent: theEvent eventType: EVENT_MOUSE_WHEEL_MOVED];
+ [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_WHEEL_MOVED];
}
- (void) mouseDown: (NSEvent*) theEvent
{
- [self enqueueMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED];
+ [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED];
}
- (void) mouseDragged: (NSEvent*) theEvent
{
// Note use of MOUSE_MOVED event type because mouse dragged events are synthesized by Java
- [self enqueueMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED];
+ [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED];
}
- (void) mouseUp: (NSEvent*) theEvent
{
- [self enqueueMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED];
+ [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED];
}
- (void) rightMouseDown: (NSEvent*) theEvent
{
- [self enqueueMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED];
+ [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED];
}
- (void) rightMouseDragged: (NSEvent*) theEvent
{
// Note use of MOUSE_MOVED event type because mouse dragged events are synthesized by Java
- [self enqueueMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED];
+ [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED];
}
- (void) rightMouseUp: (NSEvent*) theEvent
{
- [self enqueueMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED];
+ [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED];
}
- (void) otherMouseDown: (NSEvent*) theEvent
{
- [self enqueueMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED];
+ [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED];
}
- (void) otherMouseDragged: (NSEvent*) theEvent
{
// Note use of MOUSE_MOVED event type because mouse dragged events are synthesized by Java
- [self enqueueMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED];
+ [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED];
}
- (void) otherMouseUp: (NSEvent*) theEvent
{
- [self enqueueMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED];
+ [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED];
}
- (void)windowDidResize: (NSNotification*) notification
@@ -442,7 +442,8 @@ static jint mods2JavaMods(NSUInteger mods)
}
(*env)->CallVoidMethod(env, javaWindowObject, windowDestroyNotifyID);
- (*env)->CallVoidMethod(env, javaWindowObject, windowDestroyedID); // No OSX hook for DidClose, so do it here
+ // Can't issue call here - locked window state, done from Java method
+ // (*env)->CallVoidMethod(env, javaWindowObject, windowDestroyedID); // No OSX hook for DidClose, so do it here
// EOL ..
(*env)->DeleteGlobalRef(env, javaWindowObject);