diff options
author | Sven Gothel <[email protected]> | 2012-12-25 03:50:40 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2012-12-25 03:50:40 +0100 |
commit | b0be3d764fc6ff90457fcb96ea81d53ba04cd420 (patch) | |
tree | 098336cb932eb91af2a77b25a150cea67fa32547 | |
parent | a35beb22d712b6da85a794115b19d484a12c8643 (diff) |
Misc OSX/SWT: OSXUtil.RunOnMainThread(..) refinement; Fix Test*NewtEventModifiers for SWT (TestNewtEventModifiersNewtCanvasSWT)
- Misc OSX/SWT: OSXUtil.RunOnMainThread(..) refinement
- 'waitUntilDone' is implemented on Java site via lock/wait on RunnableTask to not freeze OSX main thread.
- Fix Test*NewtEventModifiers for SWT (TestNewtEventModifiersNewtCanvasSWT)
- Deal with SWT's requirement to run the SWT event dispatch on the TK thread,
which must be the main thread on OSX.
We spawn off the actual test-action into another thread,
while dispatching the events until the test-action is completed.
- AWTRobot: Add 'void requestFocus(Robot robot, Object obj, int x, int y)'
- Use waitForIdle() only if programmed in Robot (Deadlock w/ OSX SWT)
- Required for SWT usage (see above)
7 files changed, 206 insertions, 92 deletions
diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh index b91e155a0..db061b4bc 100755 --- a/make/scripts/tests.sh +++ b/make/scripts/tests.sh @@ -405,7 +405,7 @@ function testawtswt() { #testawt com.jogamp.opengl.test.junit.newt.event.TestNewtEventModifiersNEWTWindowAWT $* #testawt com.jogamp.opengl.test.junit.newt.event.TestNewtEventModifiersAWTCanvas $* #testawt com.jogamp.opengl.test.junit.newt.event.TestNewtEventModifiersNewtCanvasAWT $* -testawt com.jogamp.opengl.test.junit.newt.event.TestNewtEventModifiersNewtCanvasSWT $* +testawtswt com.jogamp.opengl.test.junit.newt.event.TestNewtEventModifiersNewtCanvasSWT $* #testawt com.jogamp.opengl.test.junit.newt.TestListenerCom01AWT #testawt com.jogamp.opengl.test.junit.newt.parenting.TestParenting01aAWT $* #testawt com.jogamp.opengl.test.junit.newt.parenting.TestParenting01bAWT $* diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java b/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java index eba26c7d3..d3e339586 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java @@ -480,7 +480,7 @@ public class SWTAccessor { * <li>Mac OSX * <ul> * <!--li>AWT EDT: In case AWT is available, the AWT EDT is the OSX UI main thread</li--> - * <li><i>Main Thread</i>: Run on OSX UI main thread.</li> + * <li><i>Main Thread</i>: Run on OSX UI main thread. 'wait' is implemented on Java site via lock/wait on {@link RunnableTask} to not freeze OSX main thread.</li> * </ul></li> * <li>Linux, Windows, .. * <ul> diff --git a/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java b/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java index a195f137e..f06f97064 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java +++ b/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java @@ -32,6 +32,8 @@ import javax.media.nativewindow.NativeWindowFactory; import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.Point; +import com.jogamp.common.util.RunnableTask; + import jogamp.nativewindow.Debug; import jogamp.nativewindow.NWJNILibLoader; import jogamp.nativewindow.ToolkitProperties; @@ -154,11 +156,42 @@ public class OSXUtil implements ToolkitProperties { DestroyCALayer0(caLayer); } + /** + * Run on OSX UI main thread. + * <p> + * 'waitUntilDone' is implemented on Java site via lock/wait on {@link RunnableTask} to not freeze OSX main thread. + * </p> + * + * @param waitUntilDone + * @param runnable + */ public static void RunOnMainThread(boolean waitUntilDone, Runnable runnable) { - if(IsMainThread0()) { + if( IsMainThread0() ) { runnable.run(); // don't leave the JVM } else { - RunOnMainThread0(waitUntilDone, runnable); + if( waitUntilDone ) { + // Utilize Java side lock/wait and simply pass the Runnable async to OSX main thread, + // otherwise we may freeze the OSX main thread. + Throwable throwable = null; + final Object sync = new Object(); + final RunnableTask rt = new RunnableTask( runnable, sync, true ); + synchronized(sync) { + RunOnMainThread0(rt); + try { + sync.wait(); + } catch (InterruptedException ie) { + throwable = ie; + } + if(null==throwable) { + throwable = rt.getThrowable(); + } + if(null!=throwable) { + throw new RuntimeException(throwable); + } + } + } else { + RunOnMainThread0(runnable); + } } } @@ -205,7 +238,7 @@ public class OSXUtil implements ToolkitProperties { private static native void AddCASublayer0(long rootCALayer, long subCALayer); private static native void RemoveCASublayer0(long rootCALayer, long subCALayer); private static native void DestroyCALayer0(long caLayer); - private static native void RunOnMainThread0(boolean waitUntilDone, Runnable runnable); + private static native void RunOnMainThread0(Runnable runnable); private static native boolean IsMainThread0(); private static native int GetScreenRefreshRate0(int scrn_idx); } diff --git a/src/nativewindow/native/macosx/OSXmisc.m b/src/nativewindow/native/macosx/OSXmisc.m index 1cf41fc9d..72bb2338c 100644 --- a/src/nativewindow/native/macosx/OSXmisc.m +++ b/src/nativewindow/native/macosx/OSXmisc.m @@ -519,13 +519,18 @@ JNIEXPORT jboolean JNICALL Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow { int shallBeDetached = 0; JNIEnv* env = NativewindowCommon_GetJNIEnv(jvmHandle, jvmVersion, &shallBeDetached); + DBG_PRINT("MainRunnable.0 env: %d\n", (int)(NULL!=env)); if(NULL!=env) { + DBG_PRINT("MainRunnable.1.0\n"); (*env)->CallVoidMethod(env, runnableObj, runnableRunID); + DBG_PRINT("MainRunnable.1.1\n"); if (shallBeDetached) { + DBG_PRINT("MainRunnable.1.2\n"); (*jvmHandle)->DetachCurrentThread(jvmHandle); } } + DBG_PRINT("MainRunnable.X\n"); } @end @@ -537,14 +542,16 @@ JNIEXPORT jboolean JNICALL Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow * Signature: (ZLjava/lang/Runnable;)V */ JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_RunOnMainThread0 - (JNIEnv *env, jclass unused, jboolean jwait, jobject runnable) + (JNIEnv *env, jclass unused, jobject runnable) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + DBG_PRINT( "RunOnMainThread0: isMainThread %d, NSApp %d, NSApp-isRunning %d\n", + (int)([NSThread isMainThread]), (int)(NULL!=NSApp), (int)([NSApp isRunning])); + if ( NO == [NSThread isMainThread] ) { jobject runnableGlob = (*env)->NewGlobalRef(env, runnable); - BOOL wait = (JNI_TRUE == jwait) ? YES : NO; JavaVM *jvmHandle = NULL; int jvmVersion = 0; @@ -554,14 +561,18 @@ JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_RunOnMainThread0 jvmVersion = (*env)->GetVersion(env); } + DBG_PRINT( "RunOnMainThread0.1.0\n"); MainRunnable * mr = [[MainRunnable alloc] initWithRunnable: runnableGlob jvmHandle: jvmHandle jvmVersion: jvmVersion]; - [mr performSelectorOnMainThread:@selector(jRun) withObject:nil waitUntilDone:wait]; + [mr performSelectorOnMainThread:@selector(jRun) withObject:nil waitUntilDone:NO]; [mr release]; + DBG_PRINT( "RunOnMainThread0.1.1\n"); (*env)->DeleteGlobalRef(env, runnableGlob); } else { + DBG_PRINT( "RunOnMainThread0.2\n"); (*env)->CallVoidMethod(env, runnable, runnableRunID); } + DBG_PRINT( "RunOnMainThread0.X\n"); [pool release]; } @@ -620,7 +631,7 @@ JNIEXPORT jint JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_GetScreenRefreshR DBG_PRINT("GetScreenRefreshRate.0: screen %p\n", (void *)screen); if(NULL != screen) { CGDirectDisplayID display = NewtScreen_getCGDirectDisplayIDByNSScreen(screen); - DBG_PRINT("GetScreenRefreshRate.1: display %p\n", (void *)display); + DBG_PRINT("GetScreenRefreshRate.1: display %p\n", (void *)(intptr_t)display); if(0 != display) { CFDictionaryRef mode = CGDisplayCurrentMode(display); DBG_PRINT("GetScreenRefreshRate.2: mode %p\n", (void *)mode); @@ -633,7 +644,7 @@ JNIEXPORT jint JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_GetScreenRefreshR if(0 == res) { res = 60; // default .. (experienced on OSX 10.6.8) } - DBG_PRINT(stderr, "GetScreenRefreshRate.X: %d\n", res); + DBG_PRINT(stderr, "GetScreenRefreshRate.X: %d\n", (int)res); // [pool release]; JNF_COCOA_EXIT(env); return res; diff --git a/src/test/com/jogamp/opengl/test/junit/newt/event/BaseNewtEventModifiers.java b/src/test/com/jogamp/opengl/test/junit/newt/event/BaseNewtEventModifiers.java index 85a5f24cb..6cbbc675f 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/event/BaseNewtEventModifiers.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/event/BaseNewtEventModifiers.java @@ -34,12 +34,12 @@ import java.util.ArrayList ; import javax.media.opengl.GLProfile ; import org.junit.After ; -import org.junit.AfterClass ; import org.junit.Assert ; import org.junit.Before ; import org.junit.BeforeClass ; import org.junit.Test ; +import com.jogamp.common.util.RunnableTask; import com.jogamp.newt.event.MouseEvent; import com.jogamp.opengl.test.junit.util.UITestCase ; @@ -59,18 +59,18 @@ public abstract class BaseNewtEventModifiers extends UITestCase { protected static final int TEST_FRAME_WIDTH = 400 ; protected static final int TEST_FRAME_HEIGHT = 400 ; - private static final int INITIAL_MOUSE_X = TEST_FRAME_X + ( TEST_FRAME_WIDTH / 2 ) ; - private static final int INITIAL_MOUSE_Y = TEST_FRAME_Y + ( TEST_FRAME_HEIGHT / 2 ) ; + protected static final int INITIAL_MOUSE_X = TEST_FRAME_X + ( TEST_FRAME_WIDTH / 2 ) ; + protected static final int INITIAL_MOUSE_Y = TEST_FRAME_Y + ( TEST_FRAME_HEIGHT / 2 ) ; - private static final int MS_ROBOT_KEY_PRESS_DELAY = 50 ; - private static final int MS_ROBOT_KEY_RELEASE_DELAY = 50 ; - private static final int MS_ROBOT_MOUSE_MOVE_DELAY = 100 ; + protected static final int MS_ROBOT_KEY_PRESS_DELAY = 50 ; + protected static final int MS_ROBOT_KEY_RELEASE_DELAY = 50 ; + protected static final int MS_ROBOT_MOUSE_MOVE_DELAY = 100 ; - private static final int MS_ROBOT_AUTO_DELAY = 50 ; - private static final int MS_ROBOT_POST_TEST_DELAY = 100; + protected static final int MS_ROBOT_AUTO_DELAY = 50 ; + protected static final int MS_ROBOT_POST_TEST_DELAY = 100; - private static final boolean _debug = true ; - private static PrintStream _debugPrintStream = System.err ; + protected static final boolean _debug = true ; + protected static PrintStream _debugPrintStream = System.err ; //////////////////////////////////////////////////////////////////////////// @@ -336,6 +336,35 @@ public abstract class BaseNewtEventModifiers extends UITestCase { } //////////////////////////////////////////////////////////////////////////// + // Following both methods are mandatory to deal with SWT's requirement + // to run the SWT event dispatch on the TK thread - which must be the main thread on OSX. + // We spawn off the actual test-action into another thread, + // while dispatching the events until the test-action is completed. + // YES: This is sort of ideal - NOT :) + + protected void eventDispatch() { + try { + Thread.sleep(100); + } catch (InterruptedException e) { } + } + + private void execOffThreadWithOnThreadEventDispatch(Runnable testAction) { + Throwable throwable = null; + final Object sync = new Object(); + final RunnableTask rt = new RunnableTask( testAction, sync, true ); + new Thread(rt, "Test-Thread").start(); + while( !rt.isExecuted() && null == throwable ) { + eventDispatch(); + } + if(null==throwable) { + throwable = rt.getThrowable(); + } + if(null!=throwable) { + throw new RuntimeException(throwable); + } + } + + //////////////////////////////////////////////////////////////////////////// // The approach on all these tests is to tell the test mouse listener what // modifiers we think it should receive. Then when the events are delivered @@ -357,17 +386,32 @@ public abstract class BaseNewtEventModifiers extends UITestCase { @Test public void testSingleButtonPressAndRelease() throws Exception { - _doSingleButtonPressAndRelease( 0, 0 ) ; + execOffThreadWithOnThreadEventDispatch(new Runnable() { + public void run() { + try { + _doSingleButtonPressAndRelease( 0, 0 ); + } catch (Exception e) { throw new RuntimeException(e); } + } } ); } @Test public void testSingleButtonPressAndReleaseWithShift() throws Exception { - _doSingleButtonPressAndRelease( java.awt.event.KeyEvent.VK_SHIFT, java.awt.event.InputEvent.SHIFT_DOWN_MASK ) ; + execOffThreadWithOnThreadEventDispatch(new Runnable() { + public void run() { + try { + _doSingleButtonPressAndRelease( java.awt.event.KeyEvent.VK_SHIFT, java.awt.event.InputEvent.SHIFT_DOWN_MASK ) ; + } catch (Exception e) { throw new RuntimeException(e); } + } } ); } @Test public void testSingleButtonPressAndReleaseWithCtrl() throws Exception { - _doSingleButtonPressAndRelease( java.awt.event.KeyEvent.VK_CONTROL, java.awt.event.InputEvent.CTRL_DOWN_MASK ) ; + execOffThreadWithOnThreadEventDispatch(new Runnable() { + public void run() { + try { + _doSingleButtonPressAndRelease( java.awt.event.KeyEvent.VK_CONTROL, java.awt.event.InputEvent.CTRL_DOWN_MASK ) ; + } catch (Exception e) { throw new RuntimeException(e); } + } } ); } /** @@ -375,12 +419,22 @@ public abstract class BaseNewtEventModifiers extends UITestCase { * so it's probably best to leave them commented out. @Test public void testSingleButtonPressAndReleaseWithMeta() throws Exception { - _doSingleButtonPressAndRelease( java.awt.event.KeyEvent.VK_META, java.awt.event.InputEvent.META_DOWN_MASK ) ; + execOffThreadWithOnThreadEventDispatch(new Runnable() { + public void run() { + try { + _doSingleButtonPressAndRelease( java.awt.event.KeyEvent.VK_META, java.awt.event.InputEvent.META_DOWN_MASK ) ; + } catch (Exception e) { throw new RuntimeException(e); } + } } ); } @Test public void testSingleButtonPressAndReleaseWithAlt() throws Exception { - _doSingleButtonPressAndRelease( java.awt.event.KeyEvent.VK_ALT, java.awt.event.InputEvent.ALT_DOWN_MASK ) ; + execOffThreadWithOnThreadEventDispatch(new Runnable() { + public void run() { + try { + _doSingleButtonPressAndRelease( java.awt.event.KeyEvent.VK_ALT, java.awt.event.InputEvent.ALT_DOWN_MASK ) ; + } catch (Exception e) { throw new RuntimeException(e); } + } } ); } */ @@ -392,7 +446,12 @@ public abstract class BaseNewtEventModifiers extends UITestCase { * enough to not let this modifier slip through (?). @Test public void testSingleButtonPressAndReleaseWithAltGraph() throws Exception { - _doSingleButtonPressAndRelease( java.awt.event.KeyEvent.VK_ALT_GRAPH, java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK ) ; + execOffThreadWithOnThreadEventDispatch(new Runnable() { + public void run() { + try { + _doSingleButtonPressAndRelease( java.awt.event.KeyEvent.VK_ALT_GRAPH, java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK ) ; + } catch (Exception e) { throw new RuntimeException(e); } + } } ); } */ @@ -400,17 +459,32 @@ public abstract class BaseNewtEventModifiers extends UITestCase { @Test public void testHoldOneButtonAndPressAnother() throws Exception { - _doHoldOneButtonAndPressAnother( 0, 0 ) ; + execOffThreadWithOnThreadEventDispatch(new Runnable() { + public void run() { + try { + _doHoldOneButtonAndPressAnother( 0, 0 ) ; + } catch (Exception e) { throw new RuntimeException(e); } + } } ); } @Test public void testPressAllButtonsInSequence() throws Exception { - _doPressAllButtonsInSequence( 0, 0 ) ; + execOffThreadWithOnThreadEventDispatch(new Runnable() { + public void run() { + try { + _doPressAllButtonsInSequence( 0, 0 ) ; + } catch (Exception e) { throw new RuntimeException(e); } + } } ); } @Test public void testSingleButtonClickAndDrag() throws Exception { - _doSingleButtonClickAndDrag( 0, 0 ) ; + execOffThreadWithOnThreadEventDispatch(new Runnable() { + public void run() { + try { + _doSingleButtonClickAndDrag( 0, 0 ) ; + } catch (Exception e) { throw new RuntimeException(e); } + } } ); } //////////////////////////////////////////////////////////////////////////// diff --git a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNewtCanvasSWT.java b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNewtCanvasSWT.java index 83b7ded7a..cafc3dd46 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNewtCanvasSWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNewtCanvasSWT.java @@ -59,69 +59,42 @@ public class TestNewtEventModifiersNewtCanvasSWT extends BaseNewtEventModifiers private static Shell _shell = null; private static Composite _composite = null; private static GLWindow _glWindow ; - private static DisplayThread _displayThread ; //////////////////////////////////////////////////////////////////////////// - - private static class DisplayThread extends Thread - { - public volatile boolean shallStop = false; - public volatile boolean isInit = false; - - public DisplayThread() - { - super( "SWT Display Thread" ) ; - } - - public void run() { - - synchronized(this) { - SWTAccessor.invoke(true, new Runnable() { - public void run() { - _display = new Display(); - Assert.assertNotNull( _display ); - }}); - - isInit = true; - this.notifyAll(); - } - - while( !_display.isDisposed() && !shallStop && isInterrupted() == false ) { - if( !_display.readAndDispatch() ) { - try { - Thread.sleep(10); - } catch (InterruptedException e) { } - } - } - - synchronized(this) { + + protected static void eventDispatch2xImpl() { + eventDispatchImpl(); + try { + Thread.sleep(100); + } catch (InterruptedException e) { } + eventDispatchImpl(); + } + + protected static void eventDispatchImpl() { + if( !_display.isDisposed() ) { + if( !_display.readAndDispatch() ) { try { - if(!_display.isDisposed()) { - SWTAccessor.invoke(true, new Runnable() { - public void run() { - _display.dispose(); - }}); - } - } finally { - isInit = false; - this.notifyAll(); - } + Thread.sleep(10); + } catch (InterruptedException e) { } } - } + } } - + + @Override + protected void eventDispatch() { + eventDispatchImpl(); + } + //////////////////////////////////////////////////////////////////////////// @BeforeClass public static void beforeClass() throws Exception { - _displayThread = new DisplayThread() ; - synchronized(_displayThread) { - _displayThread.start() ; - while(!_displayThread.isInit) { - _displayThread.wait(); - } - } + SWTAccessor.invoke(true, new Runnable() { + public void run() { + _display = new Display(); + }}); + Assert.assertNotNull( _display ); _display.syncExec(new Runnable() { public void run() { @@ -149,10 +122,16 @@ public class TestNewtEventModifiersNewtCanvasSWT extends BaseNewtEventModifiers } }); - AWTRobotUtil.assertRequestFocusAndWait(null, _glWindow, _glWindow, null, null); // programmatic + // no AWT idling, may deadlock on OSX! Assert.assertNotNull(_robot); - AWTRobotUtil.requestFocus(_robot, _glWindow, false); // within unit framework, prev. tests (TestFocus02SwingAWTRobot) 'confuses' Windows keyboard input - + _robot.setAutoWaitForIdle( false ) ; + + // no waiting for results .. + AWTRobotUtil.requestFocus(null, _glWindow, false); // programmatic + eventDispatch2xImpl(); + AWTRobotUtil.requestFocus(_robot, _glWindow, INITIAL_MOUSE_X, INITIAL_MOUSE_Y); + eventDispatch2xImpl(); + _glWindow.addMouseListener( _testMouseListener ) ; } @@ -171,12 +150,12 @@ public class TestNewtEventModifiersNewtCanvasSWT extends BaseNewtEventModifiers _shell.dispose(); }}); - synchronized(_displayThread) { - _displayThread.shallStop = true; - while( _displayThread.isInit && _displayThread.isAlive() ) { - _displayThread.wait(); - } - } + if(!_display.isDisposed()) { + SWTAccessor.invoke(true, new Runnable() { + public void run() { + _display.dispose(); + }}); + } } catch( Throwable throwable ) { throwable.printStackTrace(); diff --git a/src/test/com/jogamp/opengl/test/junit/util/AWTRobotUtil.java b/src/test/com/jogamp/opengl/test/junit/util/AWTRobotUtil.java index b5c96d10f..45648bedf 100644 --- a/src/test/com/jogamp/opengl/test/junit/util/AWTRobotUtil.java +++ b/src/test/com/jogamp/opengl/test/junit/util/AWTRobotUtil.java @@ -287,7 +287,24 @@ public class AWTRobotUtil { System.err.println("requestFocus: click, d: "+d+" ms"); } } - + + public static void requestFocus(Robot robot, Object obj, int x, int y) + throws AWTException, InterruptedException, InvocationTargetException { + + final boolean idling = robot.isAutoWaitForIdle(); + final int mouseButton = java.awt.event.InputEvent.BUTTON1_MASK; + robot.mouseMove( x, y ); + if( idling ) { + robot.waitForIdle(); + } else { + try { Thread.sleep(50); } catch (InterruptedException e) { } + } + robot.mousePress(mouseButton); + robot.mouseRelease(mouseButton); + final int d = getClickTimeout(obj) + 1; + robot.delay( d ); + } + public static boolean hasFocus(Object obj) { if(obj instanceof Component) { final Component comp = (Component) obj; |