From 811bd23ed37e392abb349f850a0b1dac635d021e Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Fri, 28 May 2010 16:53:08 +0200 Subject: NEWT: Fix AWT Parenting ; Multithreading Issues ; Semantics: destroy(), .. ; Misc. Due to incapabilities of the previous AWT/NEWT reparenting the implementation and spec had to be changed to support this feature. See the first 2 comments below. - Tested on GNU/Linux (OK), Windows (a few bugs left) - TODO: - Clarify the size/layout issue, ie who is responsible etc In the test, incl AWT/NEWT, we set the size on the GLWindow and ie pack the AWT Frame. - Fix remaining [Windows] bugs .. - Fix/Implement MacOSX port .. Fix AWT/NEWT reparenting: =========================== - Now NewtFactory's createWindow() method for parenting handles NativeWindow only and is no more responsible for creating a child window upon an AWT Component. See class com.jogamp.newt.awt.NewtCanvasAWT for NEWT/AWT parenting. - New com.jogamp.newt.awt.NewtCanvasAWT, responsible for handling AWT's reparent events via addNotify/removeNotify. Reparenting is implemented via the new NEWT Window's reparentWindow() method. Also sets the background erase to false, if supported. - Fix zero size semantics in Window (setSize/setVisible) Since a zero size window is not supported by many compoenent (Windowing system, OpenGL, ..) we use the visibility methodology to not show a 0x0 window. See Javadoc. AWT components may start with zero size. - New NEWT Window: reparentWindow(NativeWindow newParent, Screen newScreen) Allowing to change the parent of a window. Similar with the fullscreen toggle, but without size/position change. Native reparenting allows to keep alive the native window while changing the container, hence it is preferred to a destroy/create cycle. To benefit from the native reparenting, a NEWT implementation has to implement 'protected boolean reparentWindowImpl(long newWindowHandle)' and return true, otherwise reparenting will be 'emulated' via the expensive destroy/create cycle. - NEWT's Window references all of it's children, if any - NEWT's Window propagates setVisible/destroy actions to it's children. - Fix NEWT's destroy() semantics. A call of destroy() or destroy(false) shall only result in the destruction of the native window (handle) nothing more. A subsequent setVisible(true) shall allow the complete recreation of the Window into a usable state. A call of destroy(true) destroys all resources the Window holds, may include Screen/Display and OpenGL resources in case of GLWindow. This is necessary to allow proper reparenting, where a native window may become destroyed, but should be recreated via setVisible(true) later on. - Fix NEWT set[Size|Position|Fullscreen|Visible] synchronization. Use a recursive lock instead of the Window instance, otherwise arbitrary Window access via AWT's EDT, NEWT's EDT or other threads can block. Also removed a use pattern like: key.lock() try { EDT.invoke(action()); } finally { key.unlock(); } Where action() itself uses the same lock object (here key), the result is a deadlock. NativeWindow Changes: ====================== - We can use XInitThreads() now (concurrent threading support) in combination with AWT. Might have been some async in our NEWT locking in regards to AWT (sync()), and the X11 Display changes made in c787f50d77e2491eb0d8201d534a6fa4885a929e. - NativeWindow's window handle is _not_ transient like surface handle, fixed documentation. JOGL Changes: ============= - New 'isRealized()' method in GLDrawable. - Misc Fixes ============ - Fix NEWT set[Size|Position|Fullscreen|Visible] duplicate code Due to pure abstract signatures, the set[Size|Position|Fullscreen|Visible] implementations of X11, OSX, .. contained duplicate code and state handling (size, pos, ..). These are now decoupled, ie generic set[Size|Position|Fullscreen|Visible] implementations calling simple set[Size|Position|Fullscreen|Visible]Impl implementations. - Fix NEWT: Renamed setAutoDrawableClient(boolean) to setHandleDestroyNotify(boolean) The semantic of setAutoDrawableClient(boolean) defaults to false was too complicated and specific, hence changed to setHandleDestroyNotify(boolean) defaults to true since its more clear and the name refers the window itself.. - Fix NEWT: Removed GLWindow's unused global window list - Fix NEWT: Remove Window's unused event mask - Rename com.jogamp.newt.impl.awt.AWTNewtFactory -> com.jogamp.newt.awt.NewtFactoryAWT --- .../com/jogamp/test/junit/newt/KeyAction.java | 1 - .../test/junit/newt/TestGLWindows01NEWT.java | 220 +++++++++++++ .../jogamp/test/junit/newt/TestParenting01AWT.java | 353 ++++++++++++++++----- .../test/junit/newt/TestParenting01NEWT.java | 283 +++++++++++------ .../jogamp/test/junit/newt/TestParenting02AWT.java | 260 +++++++++++++++ .../test/junit/newt/TestParenting02NEWT.java | 231 ++++++++++++++ .../jogamp/test/junit/newt/TestWindows01NEWT.java | 29 +- .../com/jogamp/test/junit/newt/WindowAction.java | 1 - 8 files changed, 1188 insertions(+), 190 deletions(-) create mode 100755 src/junit/com/jogamp/test/junit/newt/TestGLWindows01NEWT.java create mode 100755 src/junit/com/jogamp/test/junit/newt/TestParenting02AWT.java create mode 100755 src/junit/com/jogamp/test/junit/newt/TestParenting02NEWT.java (limited to 'src/junit/com/jogamp/test') diff --git a/src/junit/com/jogamp/test/junit/newt/KeyAction.java b/src/junit/com/jogamp/test/junit/newt/KeyAction.java index 29b3d2ee2..3ca12a840 100644 --- a/src/junit/com/jogamp/test/junit/newt/KeyAction.java +++ b/src/junit/com/jogamp/test/junit/newt/KeyAction.java @@ -33,7 +33,6 @@ package com.jogamp.test.junit.newt; -import com.jogamp.opengl.util.Animator; import com.jogamp.newt.event.*; class KeyAction extends KeyAdapter { diff --git a/src/junit/com/jogamp/test/junit/newt/TestGLWindows01NEWT.java b/src/junit/com/jogamp/test/junit/newt/TestGLWindows01NEWT.java new file mode 100755 index 000000000..b96e74182 --- /dev/null +++ b/src/junit/com/jogamp/test/junit/newt/TestGLWindows01NEWT.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2010 Sven Gothel. 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 Sven Gothel 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 + * SVEN GOTHEL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +package com.jogamp.test.junit.newt; + +import java.lang.reflect.*; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Test; + +import javax.media.nativewindow.*; +import javax.media.opengl.*; + +import com.jogamp.opengl.util.Animator; +import com.jogamp.newt.*; +import com.jogamp.newt.opengl.*; +import java.io.IOException; + +import com.jogamp.test.junit.util.MiscUtils; +import com.jogamp.test.junit.jogl.demos.gl2.gears.Gears; + +public class TestGLWindows01NEWT { + static GLProfile glp; + static int width, height; + static long duration = 100; // ms + + @BeforeClass + public static void initClass() { + width = 640; + height = 480; + glp = GLProfile.getDefault(); + } + + static GLWindow createWindow(Screen screen, GLCapabilities caps, int width, int height, boolean onscreen, boolean undecorated) { + Assert.assertNotNull(caps); + caps.setOnscreen(onscreen); + // System.out.println("Requested: "+caps); + + // + // Create native windowing resources .. X11/Win/OSX + // + GLWindow glWindow; + if(null!=screen) { + Window window = NewtFactory.createWindow(screen, caps, onscreen && undecorated); + Assert.assertNotNull(window); + glWindow = GLWindow.create(window); + } else { + glWindow = GLWindow.create(caps, onscreen && undecorated); + } + Assert.assertNotNull(glWindow); + Assert.assertEquals(false,glWindow.isNativeWindowValid()); + glWindow.setSize(width, height); + Assert.assertEquals(false,glWindow.isVisible()); + glWindow.setVisible(true); + Assert.assertEquals(true,glWindow.isVisible()); + Assert.assertEquals(true,glWindow.isNativeWindowValid()); + // Assert.assertEquals(width,glWindow.getWidth()); + // Assert.assertEquals(height,glWindow.getHeight()); + // System.out.println("Created: "+glWindow); + + // + // Create native OpenGL resources .. XGL/WGL/CGL .. + // equivalent to GLAutoDrawable methods: setVisible(true) + // + caps = (GLCapabilities) glWindow.getGraphicsConfiguration().getNativeGraphicsConfiguration().getChosenCapabilities(); + Assert.assertNotNull(caps); + Assert.assertTrue(caps.getGreenBits()>5); + Assert.assertTrue(caps.getBlueBits()>5); + Assert.assertTrue(caps.getRedBits()>5); + Assert.assertEquals(caps.isOnscreen(),onscreen); + + GLEventListener demo = new Gears(); + setDemoFields(demo, glWindow); + glWindow.addGLEventListener(demo); + + return glWindow; + } + + static void destroyWindow(GLWindow glWindow, boolean deep) { + if(null!=glWindow) { + glWindow.destroy(deep); + } + } + + @Test + public void testWindowNativeRecreate01Simple() throws InterruptedException { + GLCapabilities caps = new GLCapabilities(glp); + Assert.assertNotNull(caps); + GLWindow window = createWindow(null, caps, width, height, true /* onscreen */, false /* undecorated */); + + window.display(); + window.destroy(); + Assert.assertEquals(false,window.isNativeWindowValid()); + Assert.assertEquals(true,window.isVisible()); + + window.display(); + Assert.assertEquals(true,window.isNativeWindowValid()); + + Animator animator = new Animator(window); + animator.start(); + while(animator.isAnimating() && animator.getDuration()0 && !glWindow.isDestroyed()) { - glWindow.display(); - Thread.sleep(step); - duration -= step; - - while( null != ( event = (NEWTEvent) eventFifo.get() ) ) { - Window source = (Window) event.getSource(); - if(event instanceof KeyEvent) { - KeyEvent keyEvent = (KeyEvent) event; - switch(keyEvent.getKeyChar()) { - case 'q': - glWindow.destroy(); - break; - case 'f': - source.setFullscreen(!source.isFullscreen()); - break; - } - } + frame.dispose(); + glWindow1.destroy(true); + } + + @Test + public void testWindowParenting05ReparentAWTWinHopFrame2Frame() throws InterruptedException { + int x = 0; + int y = 0; + + NEWTEventFiFo eventFifo = new NEWTEventFiFo(); + + GLWindow glWindow1 = GLWindow.create(glCaps); + GLEventListener demo1 = new RedSquare(); + setDemoFields(demo1, glWindow1, false); + glWindow1.addGLEventListener(demo1); + glWindow1.setSize(600, 300); + + NewtCanvasAWT newtCanvasAWT = new NewtCanvasAWT(glWindow1); + + Frame frame1 = new Frame("AWT Parent Frame"); + frame1.setLayout(new BorderLayout()); + frame1.add(new Button("North"), BorderLayout.NORTH); + frame1.add(new Button("South"), BorderLayout.SOUTH); + frame1.add(new Button("East"), BorderLayout.EAST); + frame1.add(new Button("West"), BorderLayout.WEST); + frame1.setSize(width, height); + frame1.setLocation(0, 0); + frame1.setVisible(true); + + Frame frame2 = new Frame("AWT Parent Frame"); + frame2.setLayout(new BorderLayout()); + frame2.add(new Button("North"), BorderLayout.NORTH); + frame2.add(new Button("South"), BorderLayout.SOUTH); + frame2.add(new Button("East"), BorderLayout.EAST); + frame2.add(new Button("West"), BorderLayout.WEST); + frame2.setSize(width, height); + frame2.setLocation(640, 480); + frame2.setVisible(true); + + frame1.add(newtCanvasAWT, BorderLayout.CENTER); + Assert.assertEquals(newtCanvasAWT.getNativeWindow(),glWindow1.getParentNativeWindow()); + frame1.pack(); + + Animator animator1 = new Animator(glWindow1); + animator1.start(); + + int state = 0; + while(animator1.isAnimating() && animator1.getDuration()<3*durationPerTest) { + Thread.sleep(durationPerTest); + switch(state) { + case 0: + frame1.remove(newtCanvasAWT); + frame2.add(newtCanvasAWT, BorderLayout.CENTER); + frame2.pack(); + break; + case 1: + frame2.remove(newtCanvasAWT); + frame1.add(newtCanvasAWT, BorderLayout.CENTER); + frame1.pack(); + break; } + state++; } - glWindow.destroy(); - if(useLayout) { - frame.remove(overlayedAWTComponent); - } - frame.dispose(); + + frame1.dispose(); + frame2.dispose(); + glWindow1.destroy(true); } public static void setDemoFields(GLEventListener demo, GLWindow glWindow, boolean debug) { Assert.assertNotNull(demo); Assert.assertNotNull(glWindow); - Window window = glWindow.getWindow(); + Window window = glWindow.getInnerWindow(); if(debug) { MiscUtils.setFieldIfExists(demo, "glDebug", true); MiscUtils.setFieldIfExists(demo, "glTrace", true); @@ -193,8 +364,22 @@ public class TestParenting01AWT { } } + static int atoi(String a) { + int i=0; + try { + durationPerTest = Integer.parseInt(a); + } catch (Exception ex) { ex.printStackTrace(); } + return i; + } + public static void main(String args[]) throws IOException { - durationPerTest = 5000; + for(int i=0; i0 && !shouldQuit) { - glWindow1.display(); - glWindow2.display(); - Thread.sleep(step); - duration -= step; - x += 1; - y += 1; - glWindow1.setPosition(x,y); - glWindow2.setPosition(glWindow1.getWidth()/2,glWindow1.getHeight()/2-y); - - while( null != ( event = (NEWTEvent) eventFifo.get() ) ) { - Window source = (Window) event.getSource(); - if(WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY == event.getEventType()) { - shouldQuit = true; - } else if(event instanceof KeyEvent) { - KeyEvent keyEvent = (KeyEvent) event; - switch(keyEvent.getKeyChar()) { - case 'q': - shouldQuit = true; - break; - case 'f': - source.setFullscreen(!source.isFullscreen()); - break; - } - } + glWindow1.setVisible(true); + + Animator animator1 = new Animator(glWindow1); + animator1.start(); + Animator animator2 = new Animator(glWindow2); + animator2.start(); + + int state = 0; + while(animator1.isAnimating() && animator1.getDuration()<3*durationPerTest) { + Thread.sleep(durationPerTest); + switch(state) { + case 0: + glWindow2.reparentWindow(null, null); + Assert.assertEquals(true, glWindow2.isVisible()); + Assert.assertEquals(true, glWindow2.isNativeWindowValid()); + Assert.assertNull(glWindow2.getParentNativeWindow()); + break; + case 1: + glWindow2.reparentWindow(glWindow1, null); + Assert.assertEquals(true, glWindow2.isVisible()); + Assert.assertEquals(true, glWindow2.isNativeWindowValid()); + Assert.assertEquals(glWindow1,glWindow2.getParentNativeWindow()); + break; } + state++; } - destroyWindow(null, null, window2, glWindow2); - destroyWindow(display, screen, window1, glWindow1); + animator1.stop(); + animator2.stop(); + + glWindow1.destroy(true); } - public static void setDemoFields(GLEventListener demo, Window window, GLWindow glWindow, boolean debug) { + public static void setDemoFields(GLEventListener demo, GLWindow glWindow, boolean debug) { Assert.assertNotNull(demo); - Assert.assertNotNull(window); + Assert.assertNotNull(glWindow); + Window window = glWindow.getInnerWindow(); if(debug) { MiscUtils.setFieldIfExists(demo, "glDebug", true); MiscUtils.setFieldIfExists(demo, "glTrace", true); @@ -200,8 +267,22 @@ public class TestParenting01NEWT { } } + static int atoi(String a) { + int i=0; + try { + durationPerTest = Integer.parseInt(a); + } catch (Exception ex) { ex.printStackTrace(); } + return i; + } + public static void main(String args[]) throws IOException { - durationPerTest = 5000; + for(int i=0; i0 && !shouldQuit) { + glWindow.display(); + Thread.sleep(step); + duration -= step; + + while( null != ( event = (NEWTEvent) eventFifo.get() ) ) { + Window source = (Window) event.getSource(); + if(event instanceof KeyEvent) { + KeyEvent keyEvent = (KeyEvent) event; + switch(keyEvent.getKeyChar()) { + case 'q': + shouldQuit = true; + break; + case 'f': + source.setFullscreen(!source.isFullscreen()); + break; + } + } + } + } + System.out.println("+++++++++++++++++++ END"); + Thread.sleep(waitReparent); + + glWindow.destroy(); + if(useLayout) { + frame.remove(newtCanvasAWT); + } + frame.dispose(); + } + + public static void setDemoFields(GLEventListener demo, GLWindow glWindow, boolean debug) { + Assert.assertNotNull(demo); + Assert.assertNotNull(glWindow); + Window window = glWindow.getInnerWindow(); + if(debug) { + MiscUtils.setFieldIfExists(demo, "glDebug", true); + MiscUtils.setFieldIfExists(demo, "glTrace", true); + } + if(!MiscUtils.setFieldIfExists(demo, "window", window)) { + MiscUtils.setFieldIfExists(demo, "glWindow", glWindow); + } + } + + static int atoi(String a) { + int i=0; + try { + durationPerTest = Integer.parseInt(a); + } catch (Exception ex) { ex.printStackTrace(); } + return i; + } + + public static void main(String args[]) throws IOException { + for(int i=0; i0 && !shouldQuit) { + glWindow1.display(); + glWindow2.display(); + Thread.sleep(step); + duration -= step; + x += 1; + y += 1; + glWindow1.setPosition(x,y); + glWindow2.setPosition(glWindow1.getWidth()/2,glWindow1.getHeight()/2-y); + + while( null != ( event = (NEWTEvent) eventFifo.get() ) ) { + Window source = (Window) event.getSource(); + if(WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY == event.getEventType()) { + shouldQuit = true; + } else if(event instanceof KeyEvent) { + KeyEvent keyEvent = (KeyEvent) event; + switch(keyEvent.getKeyChar()) { + case 'q': + shouldQuit = true; + break; + case 'f': + source.setFullscreen(!source.isFullscreen()); + break; + } + } + } + } + destroyWindow(null, null, window2, glWindow2); + destroyWindow(display, screen, window1, glWindow1); + } + + public static void setDemoFields(GLEventListener demo, Window window, GLWindow glWindow, boolean debug) { + Assert.assertNotNull(demo); + Assert.assertNotNull(window); + if(debug) { + MiscUtils.setFieldIfExists(demo, "glDebug", true); + MiscUtils.setFieldIfExists(demo, "glTrace", true); + } + if(!MiscUtils.setFieldIfExists(demo, "window", window)) { + MiscUtils.setFieldIfExists(demo, "glWindow", glWindow); + } + } + + static int atoi(String a) { + int i=0; + try { + durationPerTest = Integer.parseInt(a); + } catch (Exception ex) { ex.printStackTrace(); } + return i; + } + + public static void main(String args[]) throws IOException { + for(int i=0; i Date: Fri, 28 May 2010 16:53:32 +0200 Subject: test duration .. --- src/junit/com/jogamp/test/junit/newt/TestParenting01AWT.java | 2 +- src/junit/com/jogamp/test/junit/newt/TestParenting02AWT.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/junit/com/jogamp/test') diff --git a/src/junit/com/jogamp/test/junit/newt/TestParenting01AWT.java b/src/junit/com/jogamp/test/junit/newt/TestParenting01AWT.java index 39bd84e75..87d0288b3 100755 --- a/src/junit/com/jogamp/test/junit/newt/TestParenting01AWT.java +++ b/src/junit/com/jogamp/test/junit/newt/TestParenting01AWT.java @@ -67,7 +67,7 @@ import com.jogamp.test.junit.jogl.demos.gl2.gears.Gears; public class TestParenting01AWT { static int width, height; - static long durationPerTest = 500; + static long durationPerTest = 800; static long waitReparent = 0; static GLCapabilities glCaps; diff --git a/src/junit/com/jogamp/test/junit/newt/TestParenting02AWT.java b/src/junit/com/jogamp/test/junit/newt/TestParenting02AWT.java index dc330c4a9..6d0791c1c 100755 --- a/src/junit/com/jogamp/test/junit/newt/TestParenting02AWT.java +++ b/src/junit/com/jogamp/test/junit/newt/TestParenting02AWT.java @@ -66,7 +66,7 @@ import com.jogamp.test.junit.jogl.demos.gl2.gears.Gears; public class TestParenting02AWT { static int width, height; - static long durationPerTest = 300; + static long durationPerTest = 500; static long waitReparent = 300; @BeforeClass -- cgit v1.2.3