diff options
Diffstat (limited to 'src')
21 files changed, 1632 insertions, 484 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java index e80079c20..0320c63ae 100644 --- a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java +++ b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java @@ -29,10 +29,16 @@ package com.jogamp.opengl.swt; import java.util.List; +import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.AbstractGraphicsScreen; +import javax.media.nativewindow.GraphicsConfigurationFactory; import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.ProxySurface; import javax.media.nativewindow.UpstreamSurfaceHook; +import javax.media.nativewindow.VisualIDHolder; +import javax.media.nativewindow.VisualIDHolder.VIDType; import javax.media.opengl.GL; import javax.media.opengl.GLAnimatorControl; import javax.media.opengl.GLAutoDrawable; @@ -48,21 +54,20 @@ import javax.media.opengl.GLProfile; import javax.media.opengl.GLRunnable; import javax.media.opengl.Threading; +import jogamp.nativewindow.x11.X11Util; import jogamp.opengl.Debug; import jogamp.opengl.GLContextImpl; import jogamp.opengl.GLDrawableHelper; import jogamp.opengl.GLDrawableImpl; import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ControlAdapter; -import org.eclipse.swt.events.ControlEvent; -import org.eclipse.swt.events.PaintEvent; -import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import com.jogamp.common.GlueGenVersion; @@ -71,13 +76,14 @@ import com.jogamp.common.util.VersionUtil; import com.jogamp.common.util.locks.LockFactory; import com.jogamp.common.util.locks.RecursiveLock; import com.jogamp.nativewindow.swt.SWTAccessor; +import com.jogamp.nativewindow.x11.X11GraphicsDevice; import com.jogamp.opengl.JoglVersion; /** * Native SWT Canvas implementing GLAutoDrawable - * - * <p>Note: To employ custom GLCapabilities, NewtCanvasSWT shall be used instead.</p> - * + * <p> + * Implementation allows use of custom {@link GLCapabilities}. + * </p> */ public class GLCanvas extends Canvas implements GLAutoDrawable { private static final boolean DEBUG = Debug.debug("GLCanvas"); @@ -101,11 +107,15 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { private final GLCapabilitiesImmutable capsRequested; private final GLCapabilitiesChooser capsChooser; + private volatile Rectangle clientArea; private volatile GLDrawableImpl drawable; // volatile: avoid locking for read-only access - private GLContextImpl context; + private volatile GLContextImpl context; /* Native window surface */ - private AbstractGraphicsDevice device; + private final boolean useX11GTK; + private volatile long gdkWindow; // either GDK child window .. + private volatile long x11Window; // .. or X11 child window (for GL rendering) + private final AbstractGraphicsScreen screen; /* Construction parameters stored for GLAutoDrawable accessor methods */ private int additionalCtxCreationFlags = 0; @@ -133,7 +143,7 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { @Override public void run() { if (sendReshape) { - helper.reshape(GLCanvas.this, 0, 0, getWidth(), getHeight()); + helper.reshape(GLCanvas.this, 0, 0, clientArea.width, clientArea.height); sendReshape = false; } helper.display(GLCanvas.this); @@ -141,13 +151,15 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { }; /* Action to make specified context current prior to running displayAction */ - private final Runnable makeCurrentAndDisplayOnEDTAction = new Runnable() { + private final Runnable makeCurrentAndDisplayOnGLAction = new Runnable() { @Override public void run() { final RecursiveLock _lock = lock; _lock.lock(); - try { - helper.invokeGL(drawable, context, displayAction, initAction); + try { + if( !GLCanvas.this.isDisposed() ) { + helper.invokeGL(drawable, context, displayAction, initAction); + } } finally { _lock.unlock(); } @@ -155,13 +167,14 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { }; /* Swaps buffers, assuming the GLContext is current */ - private final Runnable swapBuffersOnEDTAction = new Runnable() { + private final Runnable swapBuffersOnGLAction = new Runnable() { @Override public void run() { final RecursiveLock _lock = lock; _lock.lock(); try { - if(null != drawable) { + final boolean drawableOK = null != drawable && drawable.isRealized(); + if( drawableOK && !GLCanvas.this.isDisposed() ) { drawable.swapBuffers(); } } finally { @@ -193,7 +206,11 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { // Catch dispose GLExceptions by GLEventListener, just 'print' them // so we can continue with the destruction. try { - helper.disposeGL(GLCanvas.this, context); + if( !GLCanvas.this.isDisposed() ) { + helper.disposeGL(GLCanvas.this, context); + } else { + context.destroy(); + } } catch (GLException gle) { gle.printStackTrace(); } @@ -204,13 +221,14 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { drawable.setRealized(false); drawable = null; } - // SWT is owner of the device handle, not us. - // Hence close() operation is a NOP. - if (null != device) { - device.close(); - device = null; + if( 0 != x11Window) { + SWTAccessor.destroyX11Window(screen.getDevice(), x11Window); + x11Window = 0; + } else if( 0 != gdkWindow) { + SWTAccessor.destroyGDKWindow(gdkWindow); + gdkWindow = 0; } - SWTAccessor.setRealized(GLCanvas.this, false); // unrealize .. + screen.getDevice().close(); if (animatorPaused) { animator.resume(); @@ -235,18 +253,15 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { final RecursiveLock _lock = lock; _lock.lock(); try { - listener = helper.disposeGLEventListener(GLCanvas.this, drawable, context, listener, remove); + if( !GLCanvas.this.isDisposed() ) { + listener = helper.disposeGLEventListener(GLCanvas.this, drawable, context, listener, remove); + } } finally { _lock.unlock(); } } }; - /** - * Storage for the client area rectangle so that it may be accessed from outside of the SWT thread. - */ - private volatile Rectangle clientArea; - /** * Creates an instance using {@link #GLCanvas(Composite, int, GLCapabilitiesImmutable, GLCapabilitiesChooser, GLContext)} * on the SWT thread. @@ -286,61 +301,80 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { * @param style * Optional SWT style bit-field. The {@link SWT#NO_BACKGROUND} bit is set before passing this up to the * Canvas constructor, so OpenGL handles the background. - * @param caps + * @param capsReqUser * Optional GLCapabilities. If not provided, the default capabilities for the default GLProfile for the * graphics device determined by the parent Composite are used. Note that the GLCapabilities that are * actually used may differ based on the capabilities of the graphics device. - * @param chooser + * @param capsChooser * Optional GLCapabilitiesChooser to customize the selection of the used GLCapabilities based on the * requested GLCapabilities, and the available capabilities of the graphics device. * @param shareWith * Optional GLContext to share state (textures, vbos, shaders, etc.) with. */ - public GLCanvas(final Composite parent, final int style, GLCapabilitiesImmutable caps, - final GLCapabilitiesChooser chooser, final GLContext shareWith) { + public GLCanvas(final Composite parent, final int style, GLCapabilitiesImmutable capsReqUser, + final GLCapabilitiesChooser capsChooser, final GLContext shareWith) { /* NO_BACKGROUND required to avoid clearing bg in native SWT widget (we do this in the GL display) */ super(parent, style | SWT.NO_BACKGROUND); GLProfile.initSingleton(); // ensure JOGL is completly initialized SWTAccessor.setRealized(this, true); - + clientArea = GLCanvas.this.getClientArea(); /* Get the nativewindow-Graphics Device associated with this control (which is determined by the parent Composite). * Note: SWT is owner of the native handle, hence closing operation will be a NOP. */ - device = SWTAccessor.getDevice(this); + final AbstractGraphicsDevice swtDevice = SWTAccessor.getDevice(this); + + useX11GTK = SWTAccessor.useX11GTK(); + if(useX11GTK) { + // Decoupled X11 Device/Screen allowing X11 display lock-free off-thread rendering + final long x11DeviceHandle = X11Util.openDisplay(swtDevice.getConnection()); + if( 0 == x11DeviceHandle ) { + throw new RuntimeException("Error creating display(EDT): "+swtDevice.getConnection()); + } + final AbstractGraphicsDevice x11Device = new X11GraphicsDevice(x11DeviceHandle, AbstractGraphicsDevice.DEFAULT_UNIT, true /* owner */); + screen = SWTAccessor.getScreen(x11Device, -1 /* default */); + } else { + screen = SWTAccessor.getScreen(swtDevice, -1 /* default */); + } /* Select default GLCapabilities if none was provided, otherwise clone provided caps to ensure safety */ - if(null == caps) { - caps = new GLCapabilities(GLProfile.getDefault(device)); + if(null == capsReqUser) { + capsReqUser = new GLCapabilities(GLProfile.getDefault(screen.getDevice())); } - this.capsRequested = caps; - this.capsChooser = chooser; + + this.capsRequested = capsReqUser; + this.capsChooser = capsChooser; this.shareWith = shareWith; // post create .. when ready + gdkWindow = 0; + x11Window = 0; drawable = null; context = null; - /* Register SWT listeners (e.g. PaintListener) to render/resize GL surface. */ - /* TODO: verify that these do not need to be manually de-registered when destroying the SWT component */ - addPaintListener(new PaintListener() { - @Override - public void paintControl(final PaintEvent arg0) { - if ( !helper.isAnimatorAnimatingOnOtherThread() ) { - display(); // checks: null != drawable - } - } - }); - - addControlListener(new ControlAdapter() { - @Override - public void controlResized(final ControlEvent arg0) { - updateSizeCheck(); - } - }); + final Listener listener = new Listener () { + @Override + public void handleEvent (Event event) { + switch (event.type) { + case SWT.Paint: + displayIfNoAnimatorNoCheck(); + break; + case SWT.Resize: + updateSizeCheck(); + break; + case SWT.Dispose: + GLCanvas.this.dispose(); + break; + } + } + }; + addListener (SWT.Resize, listener); + addListener (SWT.Paint, listener); + addListener (SWT.Dispose, listener); } + private final UpstreamSurfaceHook swtCanvasUpStreamHook = new UpstreamSurfaceHook() { @Override public final void create(ProxySurface s) { /* nop */ } @@ -372,11 +406,13 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { ) { clientArea = nClientArea; // write back new value - GLDrawableImpl _drawable = drawable; - if( null != _drawable ) { - if(DEBUG) { - System.err.println("GLCanvas.sizeChanged: ("+Thread.currentThread().getName()+"): "+nClientArea.width+"x"+nClientArea.height+" - surfaceHandle 0x"+Long.toHexString(getNativeSurface().getSurfaceHandle())); - } + final GLDrawableImpl _drawable = drawable; + final boolean drawableOK = null != _drawable && _drawable.isRealized(); + if(DEBUG) { + final long dh = drawableOK ? _drawable.getHandle() : 0; + System.err.println("GLCanvas.sizeChanged: ("+Thread.currentThread().getName()+"): "+nClientArea.x+"/"+nClientArea.y+" "+nClientArea.width+"x"+nClientArea.height+" - drawableHandle 0x"+Long.toHexString(dh)); + } + if( drawableOK ) { if( ! _drawable.getChosenGLCapabilities().isOnscreen() ) { final RecursiveLock _lock = lock; _lock.lock(); @@ -389,66 +425,154 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { } finally { _lock.unlock(); } - sendReshape = true; // async if display() doesn't get called below, but avoiding deadlock - } - } + } + } + if(0 != x11Window) { + SWTAccessor.resizeX11Window(screen.getDevice(), clientArea, x11Window); + } else if(0 != gdkWindow) { + SWTAccessor.resizeGDKWindow(clientArea, gdkWindow); + } + sendReshape = true; // async if display() doesn't get called below, but avoiding deadlock } } - @Override - public void display() { - if( null != drawable || validateDrawableAndContext() ) { - runInGLThread(makeCurrentAndDisplayOnEDTAction); - } + private boolean isValidAndVisibleOnEDTActionResult; + private final Runnable isValidAndVisibleOnEDTAction = new Runnable() { + @Override + public void run() { + isValidAndVisibleOnEDTActionResult = !GLCanvas.this.isDisposed() && GLCanvas.this.isVisible(); + } }; + + private final boolean isValidAndVisibleOnEDT() { + synchronized(isValidAndVisibleOnEDTAction) { + runOnEDTIfAvail(true, isValidAndVisibleOnEDTAction); + return isValidAndVisibleOnEDTActionResult; + } } - - /** assumes drawable == null ! */ - protected final boolean validateDrawableAndContext() { - if( GLCanvas.this.isDisposed() ) { + /** assumes drawable == null || !drawable.isRealized() ! Checks of !isDispose() and isVisible() */ + protected final boolean validateDrawableAndContextWithCheck() { + if( !isValidAndVisibleOnEDT() ) { return false; } + return validateDrawableAndContextPostCheck(); + } + + /** assumes drawable == null || !drawable.isRealized() ! No check of !isDispose() and isVisible() */ + protected final boolean validateDrawableAndContextPostCheck() { final Rectangle nClientArea = clientArea; if(0 >= nClientArea.width || 0 >= nClientArea.height) { return false; } + final boolean res; final RecursiveLock _lock = lock; _lock.lock(); try { - final GLDrawableFactory glFactory = GLDrawableFactory.getFactory(capsRequested.getGLProfile()); - - /* Native handle for the control, used to associate with GLContext */ - final long nativeWindowHandle = SWTAccessor.getWindowHandle(this); - - /* Create a NativeWindow proxy for the SWT canvas */ - ProxySurface proxySurface = null; - try { - proxySurface = glFactory.createProxySurface(device, 0 /* screenIdx */, nativeWindowHandle, - capsRequested, capsChooser, swtCanvasUpStreamHook); - } catch (GLException gle) { - // not ready yet .. - if(DEBUG) { System.err.println(gle.getMessage()); } + if(null == drawable) { + createDrawableAndContext(); } - - if(null != proxySurface) { - /* Associate a GL surface with the proxy */ - drawable = (GLDrawableImpl) glFactory.createGLDrawable(proxySurface); + if(null != drawable) { drawable.setRealized(true); - - context = (GLContextImpl) drawable.createContext(shareWith); + res = drawable.isRealized(); + } else { + res = false; } } finally { _lock.unlock(); + } + + if(res) { + sendReshape = true; + if(DEBUG) { + System.err.println("SWT GLCanvas realized! "+this+", "+drawable); + // Thread.dumpStack(); + } } - final boolean res = null != drawable; - if(DEBUG && res) { - System.err.println("SWT GLCanvas realized! "+this+", "+drawable); - Thread.dumpStack(); - } - return res; + return res; } + private final void createDrawableAndContext() { + final AbstractGraphicsDevice device = screen.getDevice(); + device.open(); + + final long nativeWindowHandle; + if( useX11GTK ) { + final GraphicsConfigurationFactory factory = GraphicsConfigurationFactory.getFactory(device, capsRequested); + final AbstractGraphicsConfiguration cfg = factory.chooseGraphicsConfiguration( + capsRequested, capsRequested, capsChooser, screen, VisualIDHolder.VID_UNDEFINED); + if(DEBUG) { + System.err.println("SWT.GLCanvas.X11 factory: "+factory+", chosen config: "+cfg); + } + if (null == cfg) { + throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); + } + final int visualID = cfg.getVisualID(VIDType.NATIVE); + if( VisualIDHolder.VID_UNDEFINED != visualID ) { + // gdkWindow = SWTAccessor.createCompatibleGDKChildWindow(this, visualID, clientArea.width, clientArea.height); + // nativeWindowHandle = SWTAccessor.gdk_window_get_xwindow(gdkWindow); + x11Window = SWTAccessor.createCompatibleX11ChildWindow(screen, this, visualID, clientArea.width, clientArea.height); + nativeWindowHandle = x11Window; + } else { + throw new GLException("Could not choose valid visualID: 0x"+Integer.toHexString(visualID)+", "+this); + } + } else { + nativeWindowHandle = SWTAccessor.getWindowHandle(this); + } + final GLDrawableFactory glFactory = GLDrawableFactory.getFactory(capsRequested.getGLProfile()); + + // Create a NativeWindow proxy for the SWT canvas + ProxySurface proxySurface = glFactory.createProxySurface(device, screen.getIndex(), nativeWindowHandle, + capsRequested, capsChooser, swtCanvasUpStreamHook); + // Associate a GL surface with the proxy + drawable = (GLDrawableImpl) glFactory.createGLDrawable(proxySurface); + context = (GLContextImpl) drawable.createContext(shareWith); + context.setContextCreationFlags(additionalCtxCreationFlags); + } + + @Override + public void update() { + // don't paint background etc .. nop avoids flickering + // super.update(); + } + + /** + @Override + public boolean forceFocus() { + final boolean r = super.forceFocus(); + if(r && 0 != gdkWindow) { + SWTGTKUtil.focusGDKWindow(gdkWindow); + } + return r; + } */ + + @Override + public void dispose() { + runInGLThread(disposeOnEDTGLAction); + super.dispose(); + } + + private final void displayIfNoAnimatorNoCheck() { + if ( !helper.isAnimatorAnimatingOnOtherThread() ) { + final boolean drawableOK = null != drawable && drawable.isRealized(); + if( drawableOK || validateDrawableAndContextPostCheck() ) { + runInGLThread(makeCurrentAndDisplayOnGLAction); + } + } + } + + // + // GL[Auto]Drawable + // + + @Override + public void display() { + final boolean drawableOK = null != drawable && drawable.isRealized(); + if( drawableOK || validateDrawableAndContextWithCheck() ) { + runInGLThread(makeCurrentAndDisplayOnGLAction); + } + } + @Override public final Object getUpstreamWidget() { return this; @@ -674,18 +798,7 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { @Override public void swapBuffers() throws GLException { - runInGLThread(swapBuffersOnEDTAction); - } - - @Override - public void update() { - // don't paint background etc .. nop avoids flickering - } - - @Override - public void dispose() { - runInGLThread(disposeOnEDTGLAction); - super.dispose(); + runInGLThread(swapBuffersOnGLAction); } /** @@ -694,24 +807,66 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { * <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.</li--> + * <li>Current thread</li> * </ul></li> * <li>Linux, Windows, .. * <ul> - * <li>Use {@link Threading#invokeOnOpenGLThread(boolean, Runnable)}</li> + * <!--li>Use {@link Threading#invokeOnOpenGLThread(boolean, Runnable)}</li--> + * <li>Current thread</li> * </ul></li> * </ul> + * The current thread seems to be valid for all platforms, + * since no SWT lifecycle tasks are being performed w/ this call. + * Only GL task, which are independent from the SWT threading model. + * * @see Platform#AWT_AVAILABLE * @see Platform#getOSType() */ - private static void runInGLThread(final Runnable action) { + private void runInGLThread(final Runnable action) { + /** if(Platform.OSType.MACOS == Platform.OS_TYPE) { SWTAccessor.invoke(true, action); } else { Threading.invokeOnOpenGLThread(true, action); - } + } */ + /** + if( !isDisposed() ) { + final Display d = getDisplay(); + if( d.getThread() == Thread.currentThread() ) { + action.run(); + } else { + d.syncExec(action); + } + } */ + action.run(); + } + + private void runOnEDTIfAvail(boolean wait, final Runnable action) { + final Display d = isDisposed() ? null : getDisplay(); + if( null == d || d.isDisposed() || d.getThread() == Thread.currentThread() ) { + action.run(); + } else if(wait) { + d.syncExec(action); + } else { + d.asyncExec(action); + } } + @Override + public String toString() { + final GLDrawable _drawable = drawable; + final int dw = (null!=_drawable) ? _drawable.getWidth() : -1; + final int dh = (null!=_drawable) ? _drawable.getHeight() : -1; + + return "SWT-GLCanvas[Realized "+isRealized()+ + ",\n\t"+((null!=_drawable)?_drawable.getClass().getName():"null-drawable")+ + ",\n\tFactory "+getFactory()+ + ",\n\thandle 0x"+Long.toHexString(getHandle())+ + ",\n\tDrawable size "+dw+"x"+dh+ + ",\n\tSWT size "+getWidth()+"x"+getHeight()+"]"; + } + public static void main(final String[] args) { System.err.println(VersionUtil.getPlatformInfo()); System.err.println(GlueGenVersion.getInstance()); diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java index 1a18b3432..0f8b6b816 100644 --- a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java +++ b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java @@ -890,35 +890,38 @@ public class GLDrawableHelper { } } int res = GLContext.CONTEXT_NOT_CURRENT; - + try { - res = context.makeCurrent(); - if (GLContext.CONTEXT_NOT_CURRENT != res) { - perThreadInitAction.set(initAction); - if (GLContext.CONTEXT_CURRENT_NEW == res) { - if (DEBUG) { - System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running initAction"); - } - initAction.run(); - } - runnable.run(); - if ( autoSwapBufferMode ) { - drawable.swapBuffers(); + res = context.makeCurrent(); + if (GLContext.CONTEXT_NOT_CURRENT != res) { + try { + perThreadInitAction.set(initAction); + if (GLContext.CONTEXT_CURRENT_NEW == res) { + if (DEBUG) { + System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running initAction"); + } + initAction.run(); + } + runnable.run(); + if ( autoSwapBufferMode ) { + drawable.swapBuffers(); + } + } finally { + try { + context.release(); + } catch (Exception e) { + System.err.println("Catched: "+e.getMessage()); + e.printStackTrace(); + } + } } - } } finally { - try { - context.release(); - } catch (Exception e) { - System.err.println("Catched: "+e.getMessage()); - e.printStackTrace(); - } - if (lastContext != null) { - final int res2 = lastContext.makeCurrent(); - if (null != lastInitAction && res2 == GLContext.CONTEXT_CURRENT_NEW) { - lastInitAction.run(); + if (lastContext != null) { + final int res2 = lastContext.makeCurrent(); + if (null != lastInitAction && res2 == GLContext.CONTEXT_CURRENT_NEW) { + lastInitAction.run(); + } } - } } } diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java b/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java index fca132f3f..eba26c7d3 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java @@ -34,6 +34,7 @@ import java.security.AccessController; import java.security.PrivilegedAction; import org.eclipse.swt.graphics.GCData; +import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Control; import javax.media.nativewindow.AbstractGraphicsScreen; @@ -44,6 +45,7 @@ import javax.media.nativewindow.NativeWindowFactory; import javax.media.nativewindow.VisualIDHolder; import com.jogamp.common.util.ReflectionUtil; +import com.jogamp.common.util.VersionNumber; import com.jogamp.nativewindow.macosx.MacOSXGraphicsDevice; import com.jogamp.nativewindow.windows.WindowsGraphicsDevice; import com.jogamp.nativewindow.x11.X11GraphicsDevice; @@ -53,53 +55,93 @@ import jogamp.nativewindow.macosx.OSXUtil; import jogamp.nativewindow.x11.X11Lib; public class SWTAccessor { - static final Field swt_control_handle; - static final boolean swt_uses_long_handles; + private static final boolean DEBUG = true; + + private static final Field swt_control_handle; + private static final boolean swt_uses_long_handles; + + private static Object swt_osx_init = new Object(); + private static Field swt_osx_control_view = null; + private static Field swt_osx_view_id = null; + + private static final String nwt; + private static final boolean isOSX; + private static final boolean isWindows; + private static final boolean isX11; + private static final boolean isX11GTK; // X11/GTK, Windows/GDI, .. - static final String str_handle = "handle"; + private static final String str_handle = "handle"; // OSX/Cocoa - static final String str_view = "view"; // OSX - static final String str_id = "id"; // OSX + private static final String str_osx_view = "view"; // OSX + private static final String str_osx_id = "id"; // OSX // static final String str_NSView = "org.eclipse.swt.internal.cocoa.NSView"; - static final Method swt_control_internal_new_GC; - static final Method swt_control_internal_dispose_GC; - static final String str_internal_new_GC = "internal_new_GC"; - static final String str_internal_dispose_GC = "internal_dispose_GC"; + private static final Method swt_control_internal_new_GC; + private static final Method swt_control_internal_dispose_GC; + private static final String str_internal_new_GC = "internal_new_GC"; + private static final String str_internal_dispose_GC = "internal_dispose_GC"; - static final String str_OS_gtk_class = "org.eclipse.swt.internal.gtk.OS"; - static final Class<?> OS_gtk_class; - static final Method OS_gtk_widget_realize; - static final Method OS_gtk_widget_unrealize; // optional (removed in SWT 4.3) - static final Method OS_GTK_WIDGET_WINDOW; - static final Method OS_gdk_x11_drawable_get_xdisplay; - static final Method OS_gdk_x11_drawable_get_xid; - static final String str_gtk_widget_realize = "gtk_widget_realize"; - static final String str_gtk_widget_unrealize = "gtk_widget_unrealize"; - static final String str_GTK_WIDGET_WINDOW = "GTK_WIDGET_WINDOW"; - static final String str_gdk_x11_drawable_get_xdisplay = "gdk_x11_drawable_get_xdisplay"; - static final String str_gdk_x11_drawable_get_xid = "gdk_x11_drawable_get_xid"; + private static final String str_OS_gtk_class = "org.eclipse.swt.internal.gtk.OS"; + public static final Class<?> OS_gtk_class; + private static final String str_OS_gtk_version = "GTK_VERSION"; + public static final VersionNumber OS_gtk_version; + + private static final Method OS_gtk_widget_realize; + private static final Method OS_gtk_widget_unrealize; // optional (removed in SWT 4.3) + private static final Method OS_GTK_WIDGET_WINDOW; + private static final Method OS_gtk_widget_get_window; + private static final Method OS_gdk_x11_drawable_get_xdisplay; + private static final Method OS_gdk_x11_display_get_xdisplay; + private static final Method OS_gdk_window_get_display; + private static final Method OS_gdk_x11_drawable_get_xid; + private static final Method OS_gdk_x11_window_get_xid; + private static final Method OS_gdk_window_set_back_pixmap; + + private static final String str_gtk_widget_realize = "gtk_widget_realize"; + private static final String str_gtk_widget_unrealize = "gtk_widget_unrealize"; + private static final String str_GTK_WIDGET_WINDOW = "GTK_WIDGET_WINDOW"; + private static final String str_gtk_widget_get_window = "gtk_widget_get_window"; + private static final String str_gdk_x11_drawable_get_xdisplay = "gdk_x11_drawable_get_xdisplay"; + private static final String str_gdk_x11_display_get_xdisplay = "gdk_x11_display_get_xdisplay"; + private static final String str_gdk_window_get_display = "gdk_window_get_display"; + private static final String str_gdk_x11_drawable_get_xid = "gdk_x11_drawable_get_xid"; + private static final String str_gdk_x11_window_get_xid = "gdk_x11_window_get_xid"; + private static final String str_gdk_window_set_back_pixmap = "gdk_window_set_back_pixmap"; + + private static final VersionNumber GTK_VERSION_2_14_0 = new VersionNumber(2, 14, 0); + private static final VersionNumber GTK_VERSION_2_24_0 = new VersionNumber(2, 24, 0); + private static final VersionNumber GTK_VERSION_3_0_0 = new VersionNumber(3, 0, 0); + + private static VersionNumber GTK_VERSION(int version) { + // return (major << 16) + (minor << 8) + micro; + final int micro = ( version ) & 0x0f; + final int minor = ( version >> 8 ) & 0x0f; + final int major = ( version >> 16 ) & 0x0f; + return new VersionNumber(major, minor, micro); + } static { - Field f = null; - AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { NativeWindowFactory.initSingleton(); // last resort .. return null; } } ); - final String nwt = NativeWindowFactory.getNativeWindowType(false); - - if(NativeWindowFactory.TYPE_MACOSX != nwt ) { + nwt = NativeWindowFactory.getNativeWindowType(false); + isOSX = NativeWindowFactory.TYPE_MACOSX == nwt; + isWindows = NativeWindowFactory.TYPE_WINDOWS == nwt; + isX11 = NativeWindowFactory.TYPE_X11 == nwt; + + Field f = null; + if( !isOSX ) { try { f = Control.class.getField(str_handle); } catch (Exception ex) { throw new NativeWindowException(ex); } - } + } swt_control_handle = f; // maybe null ! boolean ulh; @@ -131,17 +173,34 @@ public class SWTAccessor { } swt_control_internal_dispose_GC = m; - Class<?> c=null; - Method m1=null, m2=null, m3=null, m4=null, m5=null; - Class<?> handleType = swt_uses_long_handles ? long.class : int.class ; - if( NativeWindowFactory.TYPE_X11 == nwt ) { + Class<?> c=null; + VersionNumber _gtk_version = new VersionNumber(0, 0, 0); + Method m1=null, m2=null, m3=null, m4=null, m5=null, m6=null, m7=null, m8=null, m9=null, ma=null; + final Class<?> handleType = swt_uses_long_handles ? long.class : int.class ; + if( isX11 ) { // mandatory try { c = ReflectionUtil.getClass(str_OS_gtk_class, false, SWTAccessor.class.getClassLoader()); + Field field_OS_gtk_version = c.getField(str_OS_gtk_version); + _gtk_version = GTK_VERSION(field_OS_gtk_version.getInt(null)); m1 = c.getDeclaredMethod(str_gtk_widget_realize, handleType); - m3 = c.getDeclaredMethod(str_GTK_WIDGET_WINDOW, handleType); - m4 = c.getDeclaredMethod(str_gdk_x11_drawable_get_xdisplay, handleType); - m5 = c.getDeclaredMethod(str_gdk_x11_drawable_get_xid, handleType); + if (_gtk_version.compareTo(GTK_VERSION_2_14_0) >= 0) { + m4 = c.getDeclaredMethod(str_gtk_widget_get_window, handleType); + } else { + m3 = c.getDeclaredMethod(str_GTK_WIDGET_WINDOW, handleType); + } + if (_gtk_version.compareTo(GTK_VERSION_2_24_0) >= 0) { + m6 = c.getDeclaredMethod(str_gdk_x11_display_get_xdisplay, handleType); + m7 = c.getDeclaredMethod(str_gdk_window_get_display, handleType); + } else { + m5 = c.getDeclaredMethod(str_gdk_x11_drawable_get_xdisplay, handleType); + } + if (_gtk_version.compareTo(GTK_VERSION_3_0_0) >= 0) { + m9 = c.getDeclaredMethod(str_gdk_x11_window_get_xid, handleType); + } else { + m8 = c.getDeclaredMethod(str_gdk_x11_drawable_get_xid, handleType); + } + ma = c.getDeclaredMethod(str_gdk_window_set_back_pixmap, handleType, handleType, boolean.class); } catch (Exception ex) { throw new NativeWindowException(ex); } // optional try { @@ -149,25 +208,41 @@ public class SWTAccessor { } catch (Exception ex) { } } OS_gtk_class = c; + OS_gtk_version = _gtk_version; OS_gtk_widget_realize = m1; OS_gtk_widget_unrealize = m2; OS_GTK_WIDGET_WINDOW = m3; - OS_gdk_x11_drawable_get_xdisplay = m4; - OS_gdk_x11_drawable_get_xid = m5; + OS_gtk_widget_get_window = m4; + OS_gdk_x11_drawable_get_xdisplay = m5; + OS_gdk_x11_display_get_xdisplay = m6; + OS_gdk_window_get_display = m7; + OS_gdk_x11_drawable_get_xid = m8; + OS_gdk_x11_window_get_xid = m9; + OS_gdk_window_set_back_pixmap = ma; + + isX11GTK = isX11 && null != OS_gtk_class; + + if(DEBUG) { + System.err.println("SWTAccessor.<init>: GTK Version: "+OS_gtk_version); + } } - - static Object getIntOrLong(long arg) { + + private static Number getIntOrLong(long arg) { if(swt_uses_long_handles) { return new Long(arg); } return new Integer((int) arg); } - static void callStaticMethodL2V(Method m, long arg) { + private static void callStaticMethodL2V(Method m, long arg) { ReflectionUtil.callMethod(null, m, new Object[] { getIntOrLong(arg) }); } - static long callStaticMethodL2L(Method m, long arg) { + private static void callStaticMethodLLZ2V(Method m, long arg0, long arg1, boolean arg3) { + ReflectionUtil.callMethod(null, m, new Object[] { getIntOrLong(arg0), getIntOrLong(arg1), Boolean.valueOf(arg3) }); + } + + private static long callStaticMethodL2L(Method m, long arg) { Object o = ReflectionUtil.callMethod(null, m, new Object[] { getIntOrLong(arg) }); if(o instanceof Number) { return ((Number)o).longValue(); @@ -175,33 +250,114 @@ public class SWTAccessor { throw new InternalError("SWT method "+m.getName()+" didn't return int or long but "+o.getClass()); } } - + + // + // Public properties + // + public static boolean isUsingLongHandles() { return swt_uses_long_handles; } - public static long getHandle(Control swtControl) { + public static boolean useX11GTK() { return isX11GTK; } + public static VersionNumber GTK_VERSION() { return OS_gtk_version; } + + // + // Common GTK + // + + public static long gdk_widget_get_window(long handle) { + final long window; + if (OS_gtk_version.compareTo(GTK_VERSION_2_14_0) >= 0) { + window = callStaticMethodL2L(OS_gtk_widget_get_window, handle); + } else { + window = callStaticMethodL2L(OS_GTK_WIDGET_WINDOW, handle); + } + if(0 == window) { + throw new NativeWindowException("Null gtk-window-handle of SWT handle 0x"+Long.toHexString(handle)); + } + return window; + } + + public static long gdk_window_get_xdisplay(long window) { + final long xdisplay; + if (OS_gtk_version.compareTo(GTK_VERSION_2_24_0) >= 0) { + final long display = callStaticMethodL2L(OS_gdk_window_get_display, window); + if(0 == display) { + throw new NativeWindowException("Null display-handle of gtk-window-handle 0x"+Long.toHexString(window)); + } + xdisplay = callStaticMethodL2L(OS_gdk_x11_display_get_xdisplay, display); + } else { + xdisplay = callStaticMethodL2L(OS_gdk_x11_drawable_get_xdisplay, window); + } + if(0 == xdisplay) { + throw new NativeWindowException("Null x11-display-handle of gtk-window-handle 0x"+Long.toHexString(window)); + } + return xdisplay; + } + + public static long gdk_window_get_xwindow(long window) { + final long xWindow; + if (OS_gtk_version.compareTo(GTK_VERSION_3_0_0) >= 0) { + xWindow = callStaticMethodL2L(OS_gdk_x11_window_get_xid, window); + } else { + xWindow = callStaticMethodL2L(OS_gdk_x11_drawable_get_xid, window); + } + if(0 == xWindow) { + throw new NativeWindowException("Null x11-window-handle of gtk-window-handle 0x"+Long.toHexString(window)); + } + return xWindow; + } + + public static void gdk_window_set_back_pixmap(long window, long pixmap, boolean parent_relative) { + callStaticMethodLLZ2V(OS_gdk_window_set_back_pixmap, window, pixmap, parent_relative); + } + + // + // Common any toolkit + // + + /** + * @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 getHandle(Control swtControl) throws NativeWindowException { long h = 0; - if(NativeWindowFactory.TYPE_MACOSX == NativeWindowFactory.getNativeWindowType(false) ) { + if( isOSX ) { + synchronized(swt_osx_init) { + try { + if(null == swt_osx_view_id) { + swt_osx_control_view = Control.class.getField(str_osx_view); + Object view = swt_osx_control_view.get(swtControl); + swt_osx_view_id = view.getClass().getField(str_osx_id); + h = swt_osx_view_id.getLong(view); + } else { + h = swt_osx_view_id.getLong( swt_osx_control_view.get(swtControl) ); + } + } catch (Exception ex) { + throw new NativeWindowException(ex); + } + } + } else { try { - Field fView = Control.class.getField(str_view); - Object view = fView.get(swtControl); - Field fId = view.getClass().getField(str_id); - return fId.getLong(view); + h = swt_control_handle.getLong(swtControl); } catch (Exception ex) { throw new NativeWindowException(ex); - } + } } - - try { - h = swt_control_handle.getLong(swtControl); - } catch (Exception ex) { - throw new NativeWindowException(ex); + if(0 == h) { + throw new NativeWindowException("Null widget-handle of SWT "+swtControl.getClass().getName()+": "+swtControl.toString()); } return h; } - public static void setRealized(final Control swtControl, final boolean realize) { + public static void setRealized(final Control swtControl, final boolean realize) + throws NativeWindowException + { + if(!realize && swtControl.isDisposed()) { + return; + } final long handle = getHandle(swtControl); if(null != OS_gtk_class) { @@ -216,55 +372,77 @@ public class SWTAccessor { }); } } - - public static AbstractGraphicsDevice getDevice(Control swtControl) { - long handle = getHandle(swtControl); - if( null != OS_gtk_class ) { - long widgedHandle = callStaticMethodL2L(OS_GTK_WIDGET_WINDOW, handle); - long displayHandle = callStaticMethodL2L(OS_gdk_x11_drawable_get_xdisplay, widgedHandle); - return new X11GraphicsDevice(displayHandle, AbstractGraphicsDevice.DEFAULT_UNIT, false /* owner */); + + /** + * @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(Control swtControl) throws NativeWindowException, UnsupportedOperationException { + final long handle = getHandle(swtControl); + if( isX11GTK ) { + final long xdisplay0 = gdk_window_get_xdisplay( gdk_widget_get_window( handle ) ); + return new X11GraphicsDevice(xdisplay0, AbstractGraphicsDevice.DEFAULT_UNIT, false /* owner */); } - final String nwt = NativeWindowFactory.getNativeWindowType(false); - if( NativeWindowFactory.TYPE_WINDOWS == nwt ) { + if( isWindows ) { return new WindowsGraphicsDevice(AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT); } - if( NativeWindowFactory.TYPE_MACOSX == nwt ) { + if( isOSX ) { return new MacOSXGraphicsDevice(AbstractGraphicsDevice.DEFAULT_UNIT); } throw new UnsupportedOperationException("n/a for this windowing system: "+nwt); } - public static AbstractGraphicsScreen getScreen(AbstractGraphicsDevice device, int screen) { - if( null != OS_gtk_class ) { - return new X11GraphicsScreen((X11GraphicsDevice)device, screen); + + /** + * + * @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 + * @throws UnsupportedOperationException + */ + public static AbstractGraphicsScreen getScreen(AbstractGraphicsDevice device, int screen) throws UnsupportedOperationException { + if( isX11 ) { + X11GraphicsDevice x11Device = (X11GraphicsDevice)device; + if(0 > screen) { + screen = x11Device.getDefaultScreen(); + } + return new X11GraphicsScreen(x11Device, screen); } - final String nwt = NativeWindowFactory.getNativeWindowType(false); - if( NativeWindowFactory.TYPE_WINDOWS == nwt || - NativeWindowFactory.TYPE_MACOSX == nwt ) { + if(0 > screen) { + screen = 0; // FIXME: Needs native API utilization + } + if( isWindows || isOSX ) { return new DefaultGraphicsScreen(device, screen); } throw new UnsupportedOperationException("n/a for this windowing system: "+nwt); } + public static int getNativeVisualID(AbstractGraphicsDevice device, long windowHandle) { - if( null != OS_gtk_class ) { + if( isX11 ) { return X11Lib.GetVisualIDFromWindow(device.getHandle(), windowHandle); } - final String nwt = NativeWindowFactory.getNativeWindowType(false); - if( NativeWindowFactory.TYPE_WINDOWS == nwt || - NativeWindowFactory.TYPE_MACOSX == nwt ) { + if( isWindows || isOSX ) { return VisualIDHolder.VID_UNDEFINED; } throw new UnsupportedOperationException("n/a for this windowing system: "+nwt); } - public static long getWindowHandle(Control swtControl) { - long handle = getHandle(swtControl); - if( null != OS_gtk_class ) { - long widgedHandle = callStaticMethodL2L(OS_GTK_WIDGET_WINDOW, handle); - return callStaticMethodL2L(OS_gdk_x11_drawable_get_xid, widgedHandle); + /** + * @param swtControl the SWT Control to retrieve the native window handle from + * @return the native window handle + * @throws NativeWindowException if the widget handle is null + * @throws UnsupportedOperationException if the windowing system is not supported + */ + public static long getWindowHandle(Control swtControl) throws NativeWindowException, UnsupportedOperationException { + final long handle = getHandle(swtControl); + if(0 == handle) { + throw new NativeWindowException("Null SWT handle of SWT control "+swtControl); + } + if( isX11GTK ) { + return gdk_window_get_xwindow( gdk_widget_get_window( handle ) ); } - final String nwt = NativeWindowFactory.getNativeWindowType(false); - if( NativeWindowFactory.TYPE_WINDOWS == nwt || - NativeWindowFactory.TYPE_MACOSX == nwt ) { + if( isWindows || isOSX ) { return handle; } throw new UnsupportedOperationException("n/a for this windowing system: "+nwt); @@ -283,7 +461,7 @@ public class SWTAccessor { throw new InternalError("SWT internal_new_GC did not return int or long but "+o[0].getClass()); } } - + public static void disposeGC(final Control swtControl, final long gc, final GCData gcData) { invoke(true, new Runnable() { public void run() { @@ -313,7 +491,7 @@ public class SWTAccessor { * @see Platform#getOSType() */ public static void invoke(boolean wait, Runnable runnable) { - if( Platform.OS_TYPE == Platform.OSType.MACOS ) { + if( isOSX ) { // Use SWT main thread! Only reliable config w/ -XStartOnMainThread !? OSXUtil.RunOnMainThread(wait, runnable); } else { @@ -321,4 +499,85 @@ public class SWTAccessor { } } + // + // Specific X11 GTK ChildWindow - Using plain X11 native parenting (works well) + // + + public static long createCompatibleX11ChildWindow(AbstractGraphicsScreen screen, Control swtControl, int visualID, int width, int height) { + final long handle = getHandle(swtControl); + final long parentWindow = gdk_widget_get_window( handle ); + gdk_window_set_back_pixmap (parentWindow, 0, false); + + final long x11ParentHandle = gdk_window_get_xwindow(parentWindow); + final long x11WindowHandle = X11Lib.CreateWindow(x11ParentHandle, screen.getDevice().getHandle(), screen.getIndex(), visualID, width, height, true, true); + + return x11WindowHandle; + } + + public static void resizeX11Window(AbstractGraphicsDevice device, Rectangle clientArea, long x11Window) { + X11Lib.SetWindowPosSize(device.getHandle(), x11Window, clientArea.x, clientArea.y, clientArea.width, clientArea.height); + } + public static void destroyX11Window(AbstractGraphicsDevice device, long x11Window) { + X11Lib.DestroyWindow(device.getHandle(), x11Window); + } + + // + // Specific X11 SWT/GTK ChildWindow - Using SWT/GTK native parenting (buggy - sporadic resize flickering, sporadic drop of rendering) + // + // FIXME: Need to use reflection for 32bit access as well ! + // + + // public static final int GDK_WA_TYPE_HINT = 1 << 9; + // public static final int GDK_WA_VISUAL = 1 << 6; + + public static long createCompatibleGDKChildWindow(Control swtControl, int visualID, int width, int height) { + return 0; + /** + final long handle = SWTAccessor.getHandle(swtControl); + final long parentWindow = gdk_widget_get_window( handle ); + + final long screen = OS.gdk_screen_get_default (); + final long gdkvisual = OS.gdk_x11_screen_lookup_visual (screen, visualID); + + final GdkWindowAttr attrs = new GdkWindowAttr(); + attrs.width = width > 0 ? width : 1; + attrs.height = height > 0 ? height : 1; + attrs.event_mask = OS.GDK_KEY_PRESS_MASK | OS.GDK_KEY_RELEASE_MASK | + OS.GDK_FOCUS_CHANGE_MASK | OS.GDK_POINTER_MOTION_MASK | + OS.GDK_BUTTON_PRESS_MASK | OS.GDK_BUTTON_RELEASE_MASK | + OS.GDK_ENTER_NOTIFY_MASK | OS.GDK_LEAVE_NOTIFY_MASK | + OS.GDK_EXPOSURE_MASK | OS.GDK_VISIBILITY_NOTIFY_MASK | + OS.GDK_POINTER_MOTION_HINT_MASK; + attrs.window_type = OS.GDK_WINDOW_CHILD; + attrs.visual = gdkvisual; + + final long childWindow = OS.gdk_window_new (parentWindow, attrs, OS.GDK_WA_VISUAL|GDK_WA_TYPE_HINT); + OS.gdk_window_set_user_data (childWindow, handle); + OS.gdk_window_set_back_pixmap (parentWindow, 0, false); + + OS.gdk_window_show (childWindow); + OS.gdk_flush(); + return childWindow; */ + } + + public static void showGDKWindow(long gdkWindow) { + /* OS.gdk_window_show (gdkWindow); + OS.gdk_flush(); */ + } + public static void focusGDKWindow(long gdkWindow) { + /* + OS.gdk_window_show (gdkWindow); + OS.gdk_window_focus(gdkWindow, 0); + OS.gdk_flush(); */ + } + public static void resizeGDKWindow(Rectangle clientArea, long gdkWindow) { + /** + OS.gdk_window_move (gdkWindow, clientArea.x, clientArea.y); + OS.gdk_window_resize (gdkWindow, clientArea.width, clientArea.height); + OS.gdk_flush(); */ + } + + public static void destroyGDKWindow(long gdkWindow) { + // OS.gdk_window_destroy (gdkWindow); + } } diff --git a/src/nativewindow/classes/jogamp/nativewindow/x11/X11DummyUpstreamSurfaceHook.java b/src/nativewindow/classes/jogamp/nativewindow/x11/X11DummyUpstreamSurfaceHook.java index 67a33e55c..827862002 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/x11/X11DummyUpstreamSurfaceHook.java +++ b/src/nativewindow/classes/jogamp/nativewindow/x11/X11DummyUpstreamSurfaceHook.java @@ -37,7 +37,7 @@ public class X11DummyUpstreamSurfaceHook extends UpstreamSurfaceHookMutableSize s.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); } if( 0 == s.getSurfaceHandle() ) { - final long windowHandle = X11Lib.CreateDummyWindow(device.getHandle(), screen.getIndex(), cfg.getXVisualID(), 64, 64); + final long windowHandle = X11Lib.CreateWindow(0, device.getHandle(), screen.getIndex(), cfg.getXVisualID(), 64, 64, false, false); if(0 == windowHandle) { throw new NativeWindowException("Creating dummy window failed w/ "+cfg); } @@ -59,7 +59,7 @@ public class X11DummyUpstreamSurfaceHook extends UpstreamSurfaceHookMutableSize } device.lock(); try { - X11Lib.DestroyDummyWindow(device.getHandle(), s.getSurfaceHandle()); + X11Lib.DestroyWindow(device.getHandle(), s.getSurfaceHandle()); s.setSurfaceHandle(0); s.clearUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ); } finally { diff --git a/src/nativewindow/native/x11/Xmisc.c b/src/nativewindow/native/x11/Xmisc.c index c73952693..a8d45f288 100644 --- a/src/nativewindow/native/x11/Xmisc.c +++ b/src/nativewindow/native/x11/Xmisc.c @@ -31,6 +31,8 @@ #include "jogamp_nativewindow_x11_X11Lib.h" #include "jogamp_nativewindow_x11_X11Util.h" +#include <X11/Xatom.h> + // #define VERBOSE_ON 1 #ifdef VERBOSE_ON @@ -85,6 +87,8 @@ Bool XF86VidModeSetGammaRamp( #define RTLD_DEFAULT NULL #endif +#define X11_MOUSE_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask) + static const char * const ClazzNameBuffers = "com/jogamp/common/nio/Buffers"; static const char * const ClazzNameBuffersStaticCstrName = "copyByteBuffer"; static const char * const ClazzNameBuffersStaticCstrSignature = "(Ljava/nio/ByteBuffer;)Ljava/nio/ByteBuffer;"; @@ -436,17 +440,62 @@ Java_jogamp_nativewindow_x11_X11Lib_XCloseDisplay__J(JNIEnv *env, jclass _unused return _res; } +static void NativewindowX11_setNormalWindowEWMH (Display *dpy, Window w) { + Atom _NET_WM_WINDOW_TYPE = XInternAtom( dpy, "_NET_WM_WINDOW_TYPE", False ); + Atom types[1]={0}; + types[0] = XInternAtom( dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False ); + XChangeProperty( dpy, w, _NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, (unsigned char *)&types, 1); + XSync(dpy, False); +} + +#define DECOR_USE_MWM 1 // works for known WMs +// #define DECOR_USE_EWMH 1 // haven't seen this to work (NORMAL->POPUP, never gets undecorated) + +/* see <http://tonyobryan.com/index.php?article=9> */ +#define MWM_HINTS_DECORATIONS (1L << 1) +#define PROP_MWM_HINTS_ELEMENTS 5 + +static void NativewindowX11_setDecorations (Display *dpy, Window w, Bool decorated) { + +#ifdef DECOR_USE_MWM + unsigned long mwmhints[PROP_MWM_HINTS_ELEMENTS] = { MWM_HINTS_DECORATIONS, 0, decorated, 0, 0 }; // flags, functions, decorations, input_mode, status + Atom _MOTIF_WM_HINTS = XInternAtom( dpy, "_MOTIF_WM_HINTS", False ); +#endif + +#ifdef DECOR_USE_EWMH + Atom _NET_WM_WINDOW_TYPE = XInternAtom( dpy, "_NET_WM_WINDOW_TYPE", False ); + Atom types[3]={0}; + int ntypes=0; + if(True==decorated) { + types[ntypes++] = XInternAtom( dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False ); + } else { + types[ntypes++] = XInternAtom( dpy, "_NET_WM_WINDOW_TYPE_POPUP_MENU", False ); + } +#endif + +#ifdef DECOR_USE_MWM + XChangeProperty( dpy, w, _MOTIF_WM_HINTS, _MOTIF_WM_HINTS, 32, PropModeReplace, (unsigned char *)&mwmhints, PROP_MWM_HINTS_ELEMENTS); +#endif + +#ifdef DECOR_USE_EWMH + XChangeProperty( dpy, w, _NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, (unsigned char *)&types, ntypes); +#endif + + XSync(dpy, False); +} + /* * Class: jogamp_nativewindow_x11_X11Lib - * Method: CreateDummyWindow - * Signature: (JIIII)J + * Method: CreateWindow + * Signature: (JJIIIIZZ)J */ -JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_x11_X11Lib_CreateDummyWindow - (JNIEnv *env, jclass unused, jlong display, jint screen_index, jint visualID, jint width, jint height) +JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_x11_X11Lib_CreateWindow + (JNIEnv *env, jclass unused, jlong parent, jlong display, jint screen_index, jint visualID, jint width, jint height, jboolean input, jboolean visible) { Display * dpy = (Display *)(intptr_t)display; int scrn_idx = (int)screen_index; - Window windowParent = 0; + Window root = RootWindow(dpy, scrn_idx); + Window windowParent = (Window) parent; Window window = 0; XVisualInfo visualTemplate; @@ -473,6 +522,9 @@ JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_x11_X11Lib_CreateDummyWindow NativewindowCommon_x11ErrorHandlerEnable(env, dpy, 0, 1, 0, 0); scrn = ScreenOfDisplay(dpy, scrn_idx); + if(0==windowParent) { + windowParent = root; + } // try given VisualID on screen memset(&visualTemplate, 0, sizeof(XVisualInfo)); @@ -500,9 +552,6 @@ JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_x11_X11Lib_CreateDummyWindow pVisualQuery=NULL; } - if(0==windowParent) { - windowParent = XRootWindowOfScreen(scrn); - } attrMask = ( CWBackingStore | CWBackingPlanes | CWBackingPixel | CWBackPixmap | CWBorderPixel | CWColormap | CWOverrideRedirect ) ; @@ -514,15 +563,22 @@ JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_x11_X11Lib_CreateDummyWindow xswa.backing_store=NotUseful; /* NotUseful, WhenMapped, Always */ xswa.backing_planes=0; /* planes to be preserved if possible */ xswa.backing_pixel=0; /* value to use in restoring planes */ + if( input ) { + xswa.event_mask = X11_MOUSE_EVENT_MASK; + xswa.event_mask |= KeyPressMask | KeyReleaseMask ; + } + if( visible ) { + xswa.event_mask |= FocusChangeMask | SubstructureNotifyMask | StructureNotifyMask | ExposureMask ; + } xswa.colormap = XCreateColormap(dpy, - XRootWindow(dpy, scrn_idx), + windowParent, visual, AllocNone); window = XCreateWindow(dpy, windowParent, - 0, 0, + 0, 0, // only a hint, WM most likely will override width, height, 0, // border width depth, @@ -530,9 +586,25 @@ JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_x11_X11Lib_CreateDummyWindow visual, attrMask, &xswa); + if(0==window) { + NativewindowCommon_throwNewRuntimeException(env, "could not create Window, bail out!"); + return 0; + } + + NativewindowX11_setNormalWindowEWMH(dpy, window); + NativewindowX11_setDecorations(dpy, window, False); + + if( visible ) { + XEvent event; + + XMapWindow(dpy, window); + } + XSync(dpy, False); - XSelectInput(dpy, window, 0); // no events + if( !input ) { + XSelectInput(dpy, window, 0); // no events + } // NativewindowCommon_x11ErrorHandlerEnable(env, dpy, 0, 0, 0, 1); @@ -544,10 +616,10 @@ JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_x11_X11Lib_CreateDummyWindow /* * Class: jogamp_nativewindow_x11_X11Lib - * Method: DestroyDummyWindow + * Method: DestroyWindow * Signature: (JJ)V */ -JNIEXPORT void JNICALL Java_jogamp_nativewindow_x11_X11Lib_DestroyDummyWindow +JNIEXPORT void JNICALL Java_jogamp_nativewindow_x11_X11Lib_DestroyWindow (JNIEnv *env, jclass unused, jlong display, jlong window) { Display * dpy = (Display *)(intptr_t)display; @@ -559,12 +631,37 @@ JNIEXPORT void JNICALL Java_jogamp_nativewindow_x11_X11Lib_DestroyDummyWindow } NativewindowCommon_x11ErrorHandlerEnable(env, dpy, 0, 1, 0, 0); + XSelectInput(dpy, w, 0); XUnmapWindow(dpy, w); XSync(dpy, False); XDestroyWindow(dpy, w); // NativewindowCommon_x11ErrorHandlerEnable(env, dpy, 0, 0, 0, 1); } +JNIEXPORT void JNICALL Java_jogamp_nativewindow_x11_X11Lib_SetWindowPosSize + (JNIEnv *env, jclass unused, jlong display, jlong window, jint x, jint y, jint width, jint height) { + Display * dpy = (Display *)(intptr_t)display; + Window w = (Window) window; + XWindowChanges xwc; + int flags = 0; + + memset(&xwc, 0, sizeof(XWindowChanges)); + + if(0<=x && 0<=y) { + flags |= CWX | CWY; + xwc.x=x; + xwc.y=y; + } + + if(0<width && 0<height) { + flags |= CWWidth | CWHeight; + xwc.width=width; + xwc.height=height; + } + XConfigureWindow(dpy, w, flags, &xwc); + XSync(dpy, False); +} + /* * Class: jogamp_nativewindow_x11_X11Lib * Method: GetRelativeLocation diff --git a/src/newt/classes/com/jogamp/newt/Display.java b/src/newt/classes/com/jogamp/newt/Display.java index e97dec88d..993aa33eb 100644 --- a/src/newt/classes/com/jogamp/newt/Display.java +++ b/src/newt/classes/com/jogamp/newt/Display.java @@ -158,19 +158,15 @@ public abstract class Display { * </p> * <p> * If a previous one exists and it differs from the new one, - * it's being stopped, wait-until-idle and reset to allow restart. + * it's being stopped, wait-until-idle and reset to allow a restart at a later time. * </p> * <p> * If <code>newEDTUtil</code> is not null and equals the previous one, - * <code>null</code> is returned and no change is being made. + * no change is being made. * </p> * <p> - * Note that <code>newEDTUtil</code> will not be started if not done so already, - * to do so you may issue {@link EDTUtil#invoke(boolean, Runnable) invoke} - * on the new EDTUtil: - * <pre> - * newEDTUtil.invoke(true, new Runnable() { public void run() { } } ); - * </pre> + * Note that <code>newEDTUtil</code> will be started by this method, + * if it is not running yet. * </p> */ public abstract EDTUtil setEDTUtil(EDTUtil newEDTUtil); diff --git a/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java b/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java index 525225804..dbe7c0d98 100644 --- a/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java +++ b/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java @@ -26,7 +26,6 @@ * or implied, of JogAmp Community. */ - package com.jogamp.newt.swt; import javax.media.nativewindow.AbstractGraphicsConfiguration; @@ -44,18 +43,18 @@ import javax.media.nativewindow.WindowClosingProtocol; import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.InsetsImmutable; import javax.media.nativewindow.util.Point; +import javax.media.opengl.GLCapabilities; import jogamp.nativewindow.macosx.OSXUtil; import jogamp.newt.Debug; +import jogamp.newt.swt.SWTEDTUtil; import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ControlAdapter; -import org.eclipse.swt.events.ControlEvent; -import org.eclipse.swt.events.PaintEvent; -import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; import com.jogamp.nativewindow.swt.SWTAccessor; import com.jogamp.newt.Display; @@ -65,6 +64,9 @@ import com.jogamp.newt.util.EDTUtil; /** * SWT {@link Canvas} containing a NEWT {@link Window} using native parenting. + * <p> + * Implementation allows use of custom {@link GLCapabilities}. + * </p> */ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { private static final boolean DEBUG = Debug.debug("Window"); @@ -77,6 +79,8 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { private volatile SWTNativeWindow nativeWindow; private volatile Window newtChild = null; + private volatile boolean newtChildReady = false; // ready if SWTEDTUtil is set and newtChild parented + private volatile boolean postSetSize = false; // pending resize /** * Creates an instance using {@link #NewtCanvasSWT(Composite, int, Window)} @@ -122,45 +126,51 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { clientArea = getClientArea(); final AbstractGraphicsDevice device = SWTAccessor.getDevice(this); - screen = SWTAccessor.getScreen(device, 0); + screen = SWTAccessor.getScreen(device, -1 /* default */); nativeWindow = null; if(null != child) { setNEWTChild(child); } - - /* Register SWT listeners (e.g. PaintListener) to render/resize GL surface. */ - /* TODO: verify that these do not need to be manually de-registered when destroying the SWT component */ - addPaintListener(new PaintListener() { + + final Listener listener = new Listener () { @Override - public void paintControl(final PaintEvent arg0) { - if( null != nativeWindow || validateNative() ) { - if( null !=newtChild ) { - newtChild.windowRepaint(0, 0, clientArea.width, clientArea.height); + public void handleEvent (Event event) { + switch (event.type) { + case SWT.Paint: + if( null != nativeWindow || validateNative() ) { + if( newtChildReady ) { + if( postSetSize ) { + newtChild.setSize(clientArea.width, clientArea.height); + postSetSize = false; + } + newtChild.windowRepaint(0, 0, clientArea.width, clientArea.height); + } } + break; + case SWT.Resize: + updateSizeCheck(); + break; + case SWT.Dispose: + NewtCanvasSWT.this.dispose(); + break; } } - }); - - addControlListener(new ControlAdapter() { - @Override - public void controlResized(final ControlEvent arg0) { - updateSizeCheck(); - } - }); + }; + addListener (SWT.Resize, listener); + addListener (SWT.Paint, listener); + addListener (SWT.Dispose, listener); } /** assumes nativeWindow == null ! */ protected final boolean validateNative() { - if( isDisposed() ) { - return false; - } updateSizeCheck(); final Rectangle nClientArea = clientArea; if(0 >= nClientArea.width || 0 >= nClientArea.height) { return false; } - + screen.getDevice().open(); + /* Native handle for the control, used to associate with GLContext */ final long nativeWindowHandle = SWTAccessor.getWindowHandle(this); final int visualID = SWTAccessor.getNativeVisualID(screen.getDevice(), nativeWindowHandle); @@ -196,12 +206,18 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { ( nClientArea.width != oClientArea.width || nClientArea.height != oClientArea.height ) ) { clientArea = nClientArea; // write back new value - if( null != newtChild ) { + if(DEBUG) { + final long nsh = newtChildReady ? newtChild.getSurfaceHandle() : 0; + System.err.println("NewtCanvasSWT.sizeChanged: ("+Thread.currentThread().getName()+"): newtChildReady "+newtChildReady+", "+nClientArea.x+"/"+nClientArea.y+" "+nClientArea.width+"x"+nClientArea.height+" - surfaceHandle 0x"+Long.toHexString(nsh)); + } + if( newtChildReady ) { newtChild.setSize(clientArea.width, clientArea.height); + } else { + postSetSize = true; } } } - + @Override public void update() { // don't paint background etc .. nop avoids flickering @@ -230,6 +246,7 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { newtChild.destroy(); newtChild = null; } + screen.getDevice().close(); nativeWindow = null; super.dispose(); } @@ -238,11 +255,11 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { public NativeWindow getNativeWindow() { return nativeWindow; } public WindowClosingMode getDefaultCloseOperation() { - return newtChildCloseOp; // FIXME + return newtChildCloseOp; // TODO: implement ?! } public WindowClosingMode setDefaultCloseOperation(WindowClosingMode op) { - return newtChildCloseOp = op; // FIXME + return newtChildCloseOp = op; // TODO: implement ?! } @@ -299,7 +316,7 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { } /* package */ void configureNewtChild(boolean attach) { - + newtChildReady = attach; if( null != newtChild ) { newtChild.setKeyboardFocusHandler(null); if(attach) { @@ -324,23 +341,21 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { updateSizeCheck(); final int w = clientArea.width; final int h = clientArea.height; - + // set SWT EDT and start it { final Display newtDisplay = newtChild.getScreen().getDisplay(); - final EDTUtil edt = new SWTEDTUtil(newtDisplay, getDisplay()); - newtDisplay.setEDTUtil(edt); - edt.invoke(true, new Runnable() { public void run() { } } ); // start EDT + newtDisplay.setEDTUtil( new SWTEDTUtil(newtDisplay, getDisplay()) ); } - newtChild.setSize(w, h); + newtChild.setSize(w, h); newtChild.reparentWindow(nativeWindow); newtChild.setVisible(true); - configureNewtChild(true); + configureNewtChild(true); newtChild.sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout to listener - + // force this SWT Canvas to be focus-able, - // since this it is completely covered by the newtChild (z-order). + // since it is completely covered by the newtChild (z-order). setEnabled(true); } else { configureNewtChild(false); @@ -353,7 +368,7 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { } private final void requestFocusNEWTChild() { - if( null != newtChild ) { + if( newtChildReady ) { newtChild.setFocusAction(null); newtChild.requestFocus(); } diff --git a/src/newt/classes/com/jogamp/newt/util/EDTUtil.java b/src/newt/classes/com/jogamp/newt/util/EDTUtil.java index 7e19d9de5..0183da592 100644 --- a/src/newt/classes/com/jogamp/newt/util/EDTUtil.java +++ b/src/newt/classes/com/jogamp/newt/util/EDTUtil.java @@ -113,7 +113,8 @@ public interface EDTUtil { /** * Append the final task to the EDT task queue, - * signals EDT to stop and wait until stopped.<br> + * signals EDT to stop and wait until stopped.<br/> + * <code>task</code> maybe <code>null</code><br/> * Due to the nature of this method: * <ul> * <li>All previous queued tasks will be finished.</li> @@ -125,7 +126,7 @@ public interface EDTUtil { public void invokeStop(Runnable finalTask); /** - * Shall start the thread if not running.<br> + * Shall start the thread if not running, <code>task</code> maybe null for this purpose.<br> * Append task to the EDT task queue.<br> * Wait until execution is finished if <code>wait == true</code>.<br> * Can be issued from within EDT, ie from within an enqueued task.<br> diff --git a/src/newt/classes/jogamp/newt/DefaultEDTUtil.java b/src/newt/classes/jogamp/newt/DefaultEDTUtil.java index 98987ef96..d8d04e79f 100644 --- a/src/newt/classes/jogamp/newt/DefaultEDTUtil.java +++ b/src/newt/classes/jogamp/newt/DefaultEDTUtil.java @@ -81,10 +81,10 @@ public class DefaultEDTUtil implements EDTUtil { waitUntilStopped(); if(DEBUG) { if(edt.tasks.size()>0) { - System.err.println(Thread.currentThread()+": EDT reset, remaining tasks: "+edt.tasks.size()+" - "+edt); + System.err.println(Thread.currentThread()+": Default-EDT reset, remaining tasks: "+edt.tasks.size()+" - "+edt); // Thread.dumpStack(); } - System.err.println(Thread.currentThread()+": EDT reset - edt: "+edt); + System.err.println(Thread.currentThread()+": Default-EDT reset - edt: "+edt); } this.edt = new EventDispatchThread(threadGroup, name); this.edt.setDaemon(true); // don't stop JVM from shutdown .. @@ -93,13 +93,13 @@ public class DefaultEDTUtil implements EDTUtil { private final void startImpl() { if(edt.isAlive()) { - throw new RuntimeException("EDT Thread.isAlive(): true, isRunning: "+edt.isRunning()+", edt: "+edt+", tasks: "+edt.tasks.size()); + throw new RuntimeException("Default-EDT Thread.isAlive(): true, isRunning: "+edt.isRunning()+", edt: "+edt+", tasks: "+edt.tasks.size()); } start_iter++; edt.setName(name+start_iter); edt.shouldStop = false; if(DEBUG) { - System.err.println(Thread.currentThread()+": EDT START - edt: "+edt); + System.err.println(Thread.currentThread()+": Default-EDT START - edt: "+edt); // Thread.dumpStack(); } edt.start(); @@ -135,10 +135,12 @@ public class DefaultEDTUtil implements EDTUtil { invokeImpl(wait, task, false); } + private static Runnable nullTask = new Runnable() { + @Override + public void run() { } + }; + private void invokeImpl(boolean wait, Runnable task, boolean stop) { - if(task == null) { - throw new RuntimeException("Null Runnable"); - } Throwable throwable = null; RunnableTask rTask = null; Object rTaskLock = new Object(); @@ -147,45 +149,57 @@ public class DefaultEDTUtil implements EDTUtil { if( edt.shouldStop ) { // drop task .. if(DEBUG) { - System.err.println("Warning: EDT about (1) to stop, won't enqueue new task: "+edt); + System.err.println(Thread.currentThread()+": Warning: Default-EDT about (1) to stop, won't enqueue new task: "+edt); Thread.dumpStack(); } return; } // System.err.println(Thread.currentThread()+" XXX stop: "+stop+", tasks: "+edt.tasks.size()+", task: "+task); // Thread.dumpStack(); - if(stop) { - edt.shouldStop = true; - if(DEBUG) { - System.err.println(Thread.currentThread()+": EDT signal STOP (on edt: "+isCurrentThreadEDT()+") - tasks: "+edt.tasks.size()+" - "+edt); - // Thread.dumpStack(); - } - } if( isCurrentThreadEDT() ) { - task.run(); + if(null != task) { + task.run(); + } wait = false; // running in same thread (EDT) -> no wait - if(stop && edt.tasks.size()>0) { - System.err.println("Warning: EDT about (2) to stop, having remaining tasks: "+edt.tasks.size()+" - "+edt); - if(DEBUG) { - Thread.dumpStack(); + if(stop) { + edt.shouldStop = true; + if( edt.tasks.size()>0 ) { + System.err.println(Thread.currentThread()+": Warning: Default-EDT about (2) to stop, task executed. Remaining tasks: "+edt.tasks.size()+" - "+edt); + if(DEBUG) { + Thread.dumpStack(); + } } } } else { - // start if should not stop && not started yet - if( !stop && !edt.isRunning() ) { - startImpl(); + if( !edt.isRunning() ) { + if( !stop ) { + startImpl(); + } else { + // drop task and don't wait + task = null; + System.err.println(Thread.currentThread()+": Warning: Default-EDT is about (3) to stop and stopped already, dropping task. Remaining tasks: "+edt.tasks.size()+" - "+edt); + if(true || DEBUG) { + Thread.dumpStack(); + } + } + } else if(stop && null == task) { + task = nullTask; } - synchronized(edt.tasks) { - wait = wait && edt.isRunning(); - rTask = new RunnableTask(task, - wait ? rTaskLock : null, - true /* always catch and report Exceptions, don't disturb EDT */); - if(stop) { - rTask.setAttachment(new Boolean(true)); // mark final task + + if(null != task) { + synchronized(edt.tasks) { + rTask = new RunnableTask(task, + wait ? rTaskLock : null, + true /* always catch and report Exceptions, don't disturb EDT */); + if(stop) { + rTask.setAttachment(new Boolean(true)); // mark final task, will imply shouldStop:=true + } + // append task .. + edt.tasks.add(rTask); + edt.tasks.notifyAll(); } - // append task .. - edt.tasks.add(rTask); - edt.tasks.notifyAll(); + } else { + wait = false; } } } @@ -207,7 +221,7 @@ public class DefaultEDTUtil implements EDTUtil { } } if(DEBUG && stop) { - System.err.println(Thread.currentThread()+": EDT signal STOP X edt: "+edt); + System.err.println(Thread.currentThread()+": Default-EDT signal STOP X edt: "+edt); } } @@ -282,7 +296,7 @@ public class DefaultEDTUtil implements EDTUtil { @Override final public void run() { if(DEBUG) { - System.err.println(getName()+": EDT run() START "+ getName()); + System.err.println(getName()+": Default-EDT run() START "+ getName()); } validateNoRecursiveLocksHold(); RuntimeException error = null; @@ -307,6 +321,9 @@ public class DefaultEDTUtil implements EDTUtil { if(tasks.size()>0) { task = tasks.remove(0); tasks.notifyAll(); + if( null != task.getAttachment() ) { + shouldStop = true; + } } } if(null!=task) { @@ -324,41 +341,19 @@ public class DefaultEDTUtil implements EDTUtil { if(t instanceof RuntimeException) { error = (RuntimeException) t; } else { - error = new RuntimeException("Within EDT", t); + error = new RuntimeException("Within Default-EDT", t); } } finally { if(DEBUG) { RunnableTask rt = ( tasks.size() > 0 ) ? tasks.get(0) : null ; - System.err.println(getName()+": EDT run() END "+ getName()+", tasks: "+tasks.size()+", "+rt+", "+error); + System.err.println(getName()+": Default-EDT run() END "+ getName()+", tasks: "+tasks.size()+", "+rt+", "+error); } synchronized(edtLock) { - if(null==error) { - synchronized(tasks) { - // drain remaining tasks (stop not on EDT), - // while having tasks and no previous-task, or previous-task is non final - RunnableTask task = null; - while ( ( null == task || task.getAttachment() == null ) && tasks.size() > 0 ) { - task = tasks.remove(0); - task.run(); - tasks.notifyAll(); - } - if(DEBUG) { - if(null!=task && task.getAttachment()==null) { - System.err.println(getName()+" Warning: EDT exit: Last task Not Final: "+tasks.size()+", "+task+" - "+edt); - } else if(tasks.size()>0) { - System.err.println(getName()+" Warning: EDT exit: Remaining tasks Post Final: "+tasks.size()); - } - Thread.dumpStack(); - } - } - } - isRunning = !shouldStop; - if(!isRunning) { - edtLock.notifyAll(); - } + isRunning = false; + edtLock.notifyAll(); } if(DEBUG) { - System.err.println(getName()+": EDT run() EXIT "+ getName()+", exception: "+error); + System.err.println(getName()+": Default-EDT run() EXIT "+ getName()+", exception: "+error); } if(null!=error) { throw error; diff --git a/src/newt/classes/jogamp/newt/DisplayImpl.java b/src/newt/classes/jogamp/newt/DisplayImpl.java index 317535805..20b915cae 100644 --- a/src/newt/classes/jogamp/newt/DisplayImpl.java +++ b/src/newt/classes/jogamp/newt/DisplayImpl.java @@ -84,9 +84,8 @@ public abstract class DisplayImpl extends Display { display.hashCode = display.fqname.hashCode(); displayList.add(display); } - if(null == display.edtUtil) { - display.setEDTUtil(null); // device's default if EDT is used, or null - } + display.setEDTUtil(display.edtUtil); // device's default if EDT is used, or null + if(DEBUG) { System.err.println("Display.create() NEW: "+display+" "+getThreadName()); } @@ -166,20 +165,24 @@ public abstract class DisplayImpl extends Display { @Override public EDTUtil setEDTUtil(EDTUtil newEDTUtil) { + final EDTUtil oldEDTUtil = edtUtil; if(null == newEDTUtil) { - newEDTUtil = createEDTUtil(); - } else if( newEDTUtil == edtUtil ) { if(DEBUG) { - System.err.println("Display.setEDTUtil: "+newEDTUtil+" - keep!"); + System.err.println("Display.setEDTUtil(default): "+oldEDTUtil+" -> "+newEDTUtil); + } + edtUtil = createEDTUtil(); + } else if( newEDTUtil != edtUtil ) { + if(DEBUG) { + System.err.println("Display.setEDTUtil(custom): "+oldEDTUtil+" -> "+newEDTUtil); } - return null; // no change + removeEDT( null ); + edtUtil = newEDTUtil; + } else if( DEBUG ) { + System.err.println("Display.setEDTUtil: "+newEDTUtil+" - keep!"); } - final EDTUtil oldEDTUtil = edtUtil; - if(DEBUG) { - System.err.println("Display.setEDTUtil: "+oldEDTUtil+" -> "+newEDTUtil); + if( !edtUtil.isRunning() ) { // start EDT if not running yet + edtUtil.invoke(true, null); } - removeEDT( new Runnable() { public void run() {} } ); - edtUtil = newEDTUtil; return oldEDTUtil; } @@ -209,11 +212,7 @@ public abstract class DisplayImpl extends Display { public boolean validateEDT() { if(0==refCount && null==aDevice && null != edtUtil && edtUtil.isRunning()) { - removeEDT( new Runnable() { - public void run() { - // nop - } - } ); + removeEDT( null ); return true; } return false; diff --git a/src/newt/classes/jogamp/newt/WindowImpl.java b/src/newt/classes/jogamp/newt/WindowImpl.java index 4f8eb3d3a..a9294c1ff 100644 --- a/src/newt/classes/jogamp/newt/WindowImpl.java +++ b/src/newt/classes/jogamp/newt/WindowImpl.java @@ -173,7 +173,6 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer window.parentWindowHandle = parentWindowHandle; window.screen = (ScreenImpl) screen; window.capsRequested = (CapabilitiesImmutable) caps.cloneMutable(); - window.setUndecorated(0!=parentWindowHandle); window.instantiationFinished(); return window; } catch (Throwable t) { @@ -573,6 +572,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } finally { if (LOCK_SURFACE_NOT_READY >= res) { + surfaceLockCount--; _wlock.unlock(); } } @@ -729,8 +729,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } else if(WindowImpl.this.visible != visible) { if(isNativeValid()) { setVisibleImpl(visible, getX(), getY(), getWidth(), getHeight()); - WindowImpl.this.waitForVisible(visible, true); + WindowImpl.this.waitForVisible(visible, false); madeVisible = visible; + } else { + WindowImpl.this.visible = true; } } @@ -804,11 +806,13 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } else if ( visible && !isNativeValid() && 0 < width && 0 < height ) { visibleAction = 2; // visible (create) defineSize(width, height); - } else if ( isNativeValid() ) { + } else if ( visible && isNativeValid() ) { visibleAction = 0; // this width/height will be set by windowChanged, called by the native implementation reconfigureWindowImpl(getX(), getY(), width, height, getReconfigureFlags(0, isVisible())); + WindowImpl.this.waitForSize(width, height, false, TIMEOUT_NATIVEWINDOW); } else { + // invisible or invalid w/ 0 size visibleAction = 0; defineSize(width, height); } @@ -1045,8 +1049,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(null!=newParentWindowNEWT) { setScreen( (ScreenImpl) newParentWindowNEWT.getScreen() ); } else { - Screen newScreen = NewtFactory.createCompatibleScreen(newParentWindow, getScreen()); - if( getScreen() != newScreen ) { + final Screen newScreen = NewtFactory.createCompatibleScreen(newParentWindow, screen); + if( screen != newScreen ) { // auto destroy on-the-fly created Screen/Display setScreen( (ScreenImpl) newScreen ); } @@ -1056,14 +1060,14 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } else { operation = ReparentOperation.ACTION_NATIVE_CREATION_PENDING; } - } else if ( forceDestroyCreate || !NewtFactory.isScreenCompatible(newParentWindow, getScreen()) ) { + } else if ( forceDestroyCreate || !NewtFactory.isScreenCompatible(newParentWindow, screen) ) { // Destroy this window, may create a new compatible Screen/Display, // and mark it for creation. destroy(); if(null!=newParentWindowNEWT) { setScreen( (ScreenImpl) newParentWindowNEWT.getScreen() ); } else { - setScreen( (ScreenImpl) NewtFactory.createCompatibleScreen(newParentWindow, getScreen()) ); + setScreen( (ScreenImpl) NewtFactory.createCompatibleScreen(newParentWindow, screen) ); } operation = ReparentOperation.ACTION_NATIVE_CREATION; } else { @@ -1078,7 +1082,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if( null != parentWindow ) { // child -> top // put client to current parent+child position - Point p = getLocationOnScreen(null); + final Point p = getLocationOnScreen(null); x = p.getX(); y = p.getY(); } @@ -1134,13 +1138,13 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } if( ReparentOperation.ACTION_NATIVE_REPARENTING == operation ) { - DisplayImpl display = (DisplayImpl) screen.getDisplay(); + final DisplayImpl display = (DisplayImpl) screen.getDisplay(); display.dispatchMessagesNative(); // status up2date if(wasVisible) { setVisibleImpl(false, x, y, width, height); - WindowImpl.this.waitForVisible(false, true); - // some composite WM behave slacky .. give 'em chance to change state -> invisible, + WindowImpl.this.waitForVisible(false, false); + // FIXME: Some composite WM behave slacky .. give 'em chance to change state -> invisible, // even though we do exactly that (KDE+Composite) try { Thread.sleep(100); } catch (InterruptedException e) { } display.dispatchMessagesNative(); // status up2date @@ -1166,6 +1170,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer parentWindowLocked.unlockSurface(); } } + definePosition(x, y); // position might not get updated by WM events (SWT parent apparently) // set visible again if(ok) { @@ -1173,7 +1178,6 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(wasVisible) { setVisibleImpl(true, x, y, width, height); ok = WindowImpl.this.waitForVisible(true, false); - display.dispatchMessagesNative(); // status up2date if(ok) { ok = WindowImpl.this.waitForSize(width, height, false, TIMEOUT_NATIVEWINDOW); } @@ -1202,7 +1206,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.reparentWindow: END-1 ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+ Display.hashCodeNullSafe(parentWindow)+" "+x+"/"+y+" "+width+"x"+height); + System.err.println("Window.reparentWindow: END-1 ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+ Display.hashCodeNullSafe(parentWindow)+" "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()); } } finally { _lock.unlock(); @@ -1223,7 +1227,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.reparentWindow: END-X ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+ Display.hashCodeNullSafe(parentWindow)+" "+x+"/"+y+" "+width+"x"+height); + System.err.println("Window.reparentWindow: END-X ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+ Display.hashCodeNullSafe(parentWindow)+" "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()); } } } @@ -1557,7 +1561,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer for (int i = 0; i < keyListeners.size(); i++ ) { sb.append(keyListeners.get(i)+", "); } - sb.append("], windowLock "+windowLock+"]"); + sb.append("], windowLock "+windowLock+", surfaceLockCount "+surfaceLockCount+"]"); return sb.toString(); } @@ -1566,15 +1570,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } public void runOnEDTIfAvail(boolean wait, final Runnable task) { - if(windowLock.isOwner(Thread.currentThread())) { + if( windowLock.isOwner( Thread.currentThread() ) ) { task.run(); } else { - Screen scrn = getScreen(); - if(null==scrn) { - throw new RuntimeException("Null screen of inner class: "+this); - } - DisplayImpl d = (DisplayImpl) scrn.getDisplay(); - d.runOnEDTIfAvail(wait, task); + ( (DisplayImpl) screen.getDisplay() ).runOnEDTIfAvail(wait, task); } } @@ -1899,7 +1898,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer public void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event) { if(isNativeValid()) { - ((DisplayImpl)getScreen().getDisplay()).enqueueEvent(wait, event); + ((DisplayImpl)screen.getDisplay()).enqueueEvent(wait, event); } } @@ -1914,7 +1913,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer repaintQueued=true; final boolean discardTO = QUEUED_EVENT_TO <= System.currentTimeMillis()-e.getWhen(); if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.consumeEvent: "+Thread.currentThread().getName()+" - queued "+e+", discard-to "+discardTO); + System.err.println("Window.consumeEvent: REPAINT "+Thread.currentThread().getName()+" - queued "+e+", discard-to "+discardTO); // Thread.dumpStack(); } return discardTO; // discardTO:=true -> consumed @@ -1930,7 +1929,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if( null != windowLock.getOwner() ) { final boolean discardTO = QUEUED_EVENT_TO <= System.currentTimeMillis()-e.getWhen(); if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.consumeEvent: "+Thread.currentThread().getName()+" - queued "+e+", discard-to "+discardTO); + System.err.println("Window.consumeEvent: RESIZED "+Thread.currentThread().getName()+" - queued "+e+", discard-to "+discardTO); // Thread.dumpStack(); } return discardTO; // discardTO:=true -> consumed @@ -2430,10 +2429,11 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } private boolean waitForVisible(boolean visible, boolean failFast, long timeOut) { - DisplayImpl display = (DisplayImpl) screen.getDisplay(); + final DisplayImpl display = (DisplayImpl) screen.getDisplay(); + display.dispatchMessagesNative(); // status up2date for(long sleep = timeOut; 0<sleep && this.visible != visible; sleep-=10 ) { - display.dispatchMessagesNative(); // status up2date try { Thread.sleep(10); } catch (InterruptedException ie) {} + display.dispatchMessagesNative(); // status up2date } if(this.visible != visible) { final String msg = "Visibility not reached as requested within "+timeOut+"ms : requested "+visible+", is "+this.visible; @@ -2441,9 +2441,12 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer throw new NativeWindowException(msg); } else if (DEBUG_IMPLEMENTATION) { System.err.println(msg); + Thread.dumpStack(); } + return false; + } else { + return true; } - return this.visible == visible; } /** Triggered by implementation's WM events to update the client-area size w/o insets/decorations. */ @@ -2467,18 +2470,14 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } private boolean waitForSize(int w, int h, boolean failFast, long timeOut) { - DisplayImpl display = (DisplayImpl) screen.getDisplay(); - boolean reached = false; - for(long sleep = timeOut; !reached && 0<sleep; sleep-=10 ) { - if( w==getWidth() && h==getHeight() ) { - // reached pos/size - reached = true; - } else { - display.dispatchMessagesNative(); // status up2date - try { Thread.sleep(10); } catch (InterruptedException ie) {} - } + final DisplayImpl display = (DisplayImpl) screen.getDisplay(); + display.dispatchMessagesNative(); // status up2date + long sleep; + for(sleep = timeOut; 0<sleep && w!=getWidth() && h!=getHeight(); sleep-=10 ) { + try { Thread.sleep(10); } catch (InterruptedException ie) {} + display.dispatchMessagesNative(); // status up2date } - if(!reached) { + if(0 >= sleep) { final String msg = "Size/Pos not reached as requested within "+timeOut+"ms : requested "+w+"x"+h+", is "+getWidth()+"x"+getHeight(); if(failFast) { throw new NativeWindowException(msg); @@ -2486,8 +2485,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer System.err.println(msg); Thread.dumpStack(); } + return false; + } else { + return true; } - return reached; } /** Triggered by implementation's WM events to update the position. */ diff --git a/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java b/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java index 2175f2190..27d0f3506 100644 --- a/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java +++ b/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java @@ -72,7 +72,7 @@ public class AWTEDTUtil implements EDTUtil { synchronized(edtLock) { waitUntilStopped(); if(DEBUG) { - System.err.println(Thread.currentThread()+": EDT reset - edt: "+nedt); + System.err.println(Thread.currentThread()+": AWT-EDT reset - edt: "+nedt); } this.nedt = new NewtEventDispatchThread(threadGroup, name); this.nedt.setDaemon(true); // don't stop JVM from shutdown .. @@ -81,13 +81,13 @@ public class AWTEDTUtil implements EDTUtil { private final void startImpl() { if(nedt.isAlive()) { - throw new RuntimeException("EDT Thread.isAlive(): true, isRunning: "+nedt.isRunning()+", edt: "+nedt); + throw new RuntimeException("AWT-EDT Thread.isAlive(): true, isRunning: "+nedt.isRunning()+", edt: "+nedt); } start_iter++; nedt.setName(name+start_iter); nedt.shouldStop = false; if(DEBUG) { - System.err.println(Thread.currentThread()+": EDT START - edt: "+nedt); + System.err.println(Thread.currentThread()+": AWT-EDT START - edt: "+nedt); // Thread.dumpStack(); } nedt.start(); @@ -124,9 +124,6 @@ public class AWTEDTUtil implements EDTUtil { } private void invokeImpl(boolean wait, Runnable task, boolean stop) { - if(task == null) { - throw new RuntimeException("Null Runnable"); - } Throwable throwable = null; RunnableTask rTask = null; Object rTaskLock = new Object(); @@ -135,7 +132,7 @@ public class AWTEDTUtil implements EDTUtil { if( nedt.shouldStop ) { // drop task .. if(DEBUG) { - System.err.println("Warning: EDT about (1) to stop, won't enqueue new task: "+nedt); + System.err.println(Thread.currentThread()+": Warning: AWT-EDT about (1) to stop, won't enqueue new task: "+nedt); Thread.dumpStack(); } return; @@ -143,20 +140,24 @@ public class AWTEDTUtil implements EDTUtil { // System.err.println(Thread.currentThread()+" XXX stop: "+stop+", tasks: "+edt.tasks.size()+", task: "+task); // Thread.dumpStack(); if(stop) { - nedt.shouldStop = true; + synchronized(nedt.sync) { + nedt.shouldStop = true; + nedt.sync.notifyAll(); // stop immediate if waiting (poll freq) + } if(DEBUG) { - System.err.println(Thread.currentThread()+": EDT signal STOP (on edt: "+isCurrentThreadEDT()+") - "+nedt); + System.err.println(Thread.currentThread()+": AWT-EDT signal STOP (on edt: "+isCurrentThreadEDT()+") - "+nedt); // Thread.dumpStack(); } + } else if( !nedt.isRunning() ) { + // start if should not stop && not started yet + startImpl(); } - if( isCurrentThreadEDT() ) { + if( null == task ) { + wait = false; + } else if( isCurrentThreadEDT() ) { task.run(); wait = false; // running in same thread (EDT) -> no wait } else { - // start if should not stop && not started yet - if( !stop && !nedt.isRunning() ) { - startImpl(); - } rTask = new RunnableTask(task, wait ? rTaskLock : null, true /* always catch and report Exceptions, don't disturb EDT */); @@ -239,7 +240,7 @@ public class AWTEDTUtil implements EDTUtil { @Override final public void run() { if(DEBUG) { - System.err.println(getName()+": EDT run() START "+ getName()); + System.err.println(getName()+": AWT-EDT run() START "+ getName()); } RuntimeException error = null; try { @@ -269,11 +270,11 @@ public class AWTEDTUtil implements EDTUtil { if(t instanceof RuntimeException) { error = (RuntimeException) t; } else { - error = new RuntimeException("Within EDT", t); + error = new RuntimeException("Within AWT-EDT", t); } } finally { if(DEBUG) { - System.err.println(getName()+": EDT run() END "+ getName()+", "+error); + System.err.println(getName()+": AWT-EDT run() END "+ getName()+", "+error); } synchronized(edtLock) { isRunning = !shouldStop; @@ -282,7 +283,7 @@ public class AWTEDTUtil implements EDTUtil { } } if(DEBUG) { - System.err.println(getName()+": EDT run() EXIT "+ getName()+", exception: "+error); + System.err.println(getName()+": AWT-EDT run() EXIT "+ getName()+", exception: "+error); } if(null!=error) { throw error; diff --git a/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java index 6aac8617c..2ec88852c 100644 --- a/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java @@ -71,13 +71,15 @@ public class WindowDriver extends WindowImpl { if (0 != hdc) { throw new InternalError("surface not released"); } - hdc = GDI.GetDC(getWindowHandle()); - hmon = MonitorFromWindow0(getWindowHandle()); + final long hWnd = getWindowHandle(); + hdc = GDI.GetDC(hWnd); // return ( 0 == hdc ) ? LOCK_SURFACE_NOT_READY : ( hdc_old != hdc ) ? LOCK_SURFACE_CHANGED : LOCK_SUCCESS ; if( 0 == hdc ) { return LOCK_SURFACE_NOT_READY; - } + } + hmon = MonitorFromWindow0(hWnd); + if( hdc_old == hdc ) { return LOCK_SUCCESS; } @@ -217,14 +219,14 @@ public class WindowDriver extends WindowImpl { @Override protected boolean setPointerVisibleImpl(final boolean pointerVisible) { - final Boolean[] res = new Boolean[] { Boolean.FALSE }; + final boolean[] res = new boolean[] { false }; this.runOnEDTIfAvail(true, new Runnable() { public void run() { - res[0] = Boolean.valueOf(setPointerVisible0(getWindowHandle(), pointerVisible)); + res[0] = setPointerVisible0(getWindowHandle(), pointerVisible); } }); - return res[0].booleanValue(); + return res[0]; } @Override diff --git a/src/newt/classes/com/jogamp/newt/swt/SWTEDTUtil.java b/src/newt/classes/jogamp/newt/swt/SWTEDTUtil.java index 42e1c9be5..7297e5858 100644 --- a/src/newt/classes/com/jogamp/newt/swt/SWTEDTUtil.java +++ b/src/newt/classes/jogamp/newt/swt/SWTEDTUtil.java @@ -25,9 +25,7 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ -package com.jogamp.newt.swt; - -import java.awt.EventQueue; +package jogamp.newt.swt; import javax.media.nativewindow.NativeWindowException; @@ -83,7 +81,7 @@ public class SWTEDTUtil implements EDTUtil { synchronized(edtLock) { waitUntilStopped(); if(DEBUG) { - System.err.println(Thread.currentThread()+": EDT reset - edt: "+nedt); + System.err.println(Thread.currentThread()+": SWT-EDT reset - edt: "+nedt); } this.nedt = new NewtEventDispatchThread(threadGroup, name); this.nedt.setDaemon(true); // don't stop JVM from shutdown .. @@ -92,13 +90,13 @@ public class SWTEDTUtil implements EDTUtil { private final void startImpl() { if(nedt.isAlive()) { - throw new RuntimeException("EDT Thread.isAlive(): true, isRunning: "+nedt.isRunning()+", edt: "+nedt); + throw new RuntimeException("SWT-EDT Thread.isAlive(): true, isRunning: "+nedt.isRunning()+", edt: "+nedt); } start_iter++; nedt.setName(name+start_iter); nedt.shouldStop = false; if(DEBUG) { - System.err.println(Thread.currentThread()+": EDT START - edt: "+nedt); + System.err.println(Thread.currentThread()+": SWT-EDT START - edt: "+nedt); // Thread.dumpStack(); } nedt.start(); @@ -136,9 +134,6 @@ public class SWTEDTUtil implements EDTUtil { } private void invokeImpl(boolean wait, Runnable task, boolean stop) { - if(task == null) { - throw new RuntimeException("Null Runnable"); - } Throwable throwable = null; RunnableTask rTask = null; Object rTaskLock = new Object(); @@ -147,7 +142,7 @@ public class SWTEDTUtil implements EDTUtil { if( nedt.shouldStop ) { // drop task .. if(DEBUG) { - System.err.println("Warning: EDT about (1) to stop, won't enqueue new task: "+nedt); + System.err.println(Thread.currentThread()+": Warning: SWT-EDT about (1) to stop, won't enqueue new task: "+nedt); Thread.dumpStack(); } return; @@ -155,22 +150,26 @@ public class SWTEDTUtil implements EDTUtil { // System.err.println(Thread.currentThread()+" XXX stop: "+stop+", tasks: "+edt.tasks.size()+", task: "+task); // Thread.dumpStack(); if(stop) { - nedt.shouldStop = true; + synchronized(nedt.sync) { + nedt.shouldStop = true; + nedt.sync.notifyAll(); // stop immediate if waiting (poll freq) + } if(DEBUG) { - System.err.println(Thread.currentThread()+": EDT signal STOP (on edt: "+isCurrentThreadEDT()+") - "+nedt); + System.err.println(Thread.currentThread()+": SWT-EDT signal STOP (on edt: "+isCurrentThreadEDT()+") - "+nedt); // Thread.dumpStack(); } + } else if( !nedt.isRunning() ) { + // start if should not stop && not started yet + startImpl(); } - if( isCurrentThreadEDT() ) { + if( null == task ) { + wait = false; + } else if( isCurrentThreadEDT() ) { task.run(); wait = false; // running in same thread (EDT) -> no wait } else if( swtDisplay.isDisposed() ) { wait = false; // drop task, SWT disposed } else { - // start if should not stop && not started yet - if( !stop && !nedt.isRunning() ) { - startImpl(); - } rTask = new RunnableTask(task, wait ? rTaskLock : null, true /* always catch and report Exceptions, don't disturb EDT */); @@ -255,7 +254,7 @@ public class SWTEDTUtil implements EDTUtil { @Override final public void run() { if(DEBUG) { - System.err.println(getName()+": EDT run() START "+ getName()); + System.err.println(getName()+": SWT-EDT run() START "+ getName()); } RuntimeException error = null; try { @@ -289,11 +288,11 @@ public class SWTEDTUtil implements EDTUtil { if(t instanceof RuntimeException) { error = (RuntimeException) t; } else { - error = new RuntimeException("Within EDT", t); + error = new RuntimeException("Within SWT-EDT", t); } } finally { if(DEBUG) { - System.err.println(getName()+": EDT run() END "+ getName()+", "+error); + System.err.println(getName()+": SWT-EDT run() END "+ getName()+", "+error); } synchronized(edtLock) { isRunning = !shouldStop; @@ -302,7 +301,7 @@ public class SWTEDTUtil implements EDTUtil { } } if(DEBUG) { - System.err.println(getName()+": EDT run() EXIT "+ getName()+", exception: "+error); + System.err.println(getName()+": SWT-EDT run() EXIT "+ getName()+", exception: "+error); } if(null!=error) { throw error; diff --git a/src/newt/classes/jogamp/newt/swt/event/SWTNewtEventFactory.java b/src/newt/classes/jogamp/newt/swt/event/SWTNewtEventFactory.java new file mode 100644 index 000000000..e238f5d9e --- /dev/null +++ b/src/newt/classes/jogamp/newt/swt/event/SWTNewtEventFactory.java @@ -0,0 +1,249 @@ +/** + * Copyright 2012 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.swt.event; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; + +import com.jogamp.common.util.IntIntHashMap; +import com.jogamp.newt.event.InputEvent; + +/** + * SWT event translator to NEWT, inclusive dispatch listener. + * <p> + * <b>Disclaimer:</b> This code is merely tested and subject to change. + * </p> + */ +public class SWTNewtEventFactory { + + protected static final IntIntHashMap eventTypeSWT2NEWT; + + static { + IntIntHashMap map = new IntIntHashMap(); + map.setKeyNotFoundValue(0xFFFFFFFF); + + // map.put(SWT.MouseXXX, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_CLICKED); + map.put(SWT.MouseDown, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_PRESSED); + map.put(SWT.MouseUp, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED); + map.put(SWT.MouseMove, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_MOVED); + map.put(SWT.MouseEnter, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_ENTERED); + map.put(SWT.MouseExit, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_EXITED); + // map.put(SWT.MouseXXX, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_DRAGGED); + map.put(SWT.MouseVerticalWheel, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_WHEEL_MOVED); + + map.put(SWT.KeyDown, com.jogamp.newt.event.KeyEvent.EVENT_KEY_PRESSED); + map.put(SWT.KeyUp, com.jogamp.newt.event.KeyEvent.EVENT_KEY_RELEASED); + // map.put(SWT.KeyXXX, com.jogamp.newt.event.KeyEvent.EVENT_KEY_TYPED); + + eventTypeSWT2NEWT = map; + } + + public static final int swtModifiers2Newt(int awtMods, boolean mouseHint) { + int newtMods = 0; + if ((awtMods & SWT.SHIFT) != 0) newtMods |= com.jogamp.newt.event.InputEvent.SHIFT_MASK; + if ((awtMods & SWT.CTRL) != 0) newtMods |= com.jogamp.newt.event.InputEvent.CTRL_MASK; + if ((awtMods & SWT.ALT) != 0) newtMods |= com.jogamp.newt.event.InputEvent.ALT_MASK; + return newtMods; + } + + public static final com.jogamp.newt.event.InputEvent createInputEvent(org.eclipse.swt.widgets.Event event, Object source) { + com.jogamp.newt.event.InputEvent res = createMouseEvent(event, source); + if(null == res) { + res = createKeyEvent(event, source); + } + return res; + } + + public static final com.jogamp.newt.event.MouseEvent createMouseEvent(org.eclipse.swt.widgets.Event event, Object source) { + switch(event.type) { + case SWT.MouseDown: + case SWT.MouseUp: + case SWT.MouseMove: + case SWT.MouseEnter: + case SWT.MouseExit: + case SWT.MouseVerticalWheel: + break; + default: + return null; + } + int type = eventTypeSWT2NEWT.get(event.type); + if(0xFFFFFFFF != type) { + int rotation = 0; + if (SWT.MouseVerticalWheel == event.type) { + // SWT/NEWT rotation is reversed - AWT +1 is down, NEWT +1 is up. + // rotation = -1 * (int) event.rotation; + rotation = (int) event.rotation; + } + + int mods = swtModifiers2Newt(event.stateMask, true); + + if( source instanceof com.jogamp.newt.Window) { + final com.jogamp.newt.Window newtSource = (com.jogamp.newt.Window)source; + if(newtSource.isPointerConfined()) { + mods |= InputEvent.CONFINED_MASK; + } + if(!newtSource.isPointerVisible()) { + mods |= InputEvent.INVISIBLE_MASK; + } + } + + return new com.jogamp.newt.event.MouseEvent( + type, (null==source)?(Object)event.data:source, (0xFFFFFFFFL & (long)event.time), + mods, event.x, event.y, event.count, event.button, rotation); + } + return null; // no mapping .. + } + + public static final com.jogamp.newt.event.KeyEvent createKeyEvent(org.eclipse.swt.widgets.Event event, Object source) { + switch(event.type) { + case SWT.KeyDown: + case SWT.KeyUp: + break; + default: + return null; + } + int type = eventTypeSWT2NEWT.get(event.type); + if(0xFFFFFFFF != type) { + return new com.jogamp.newt.event.KeyEvent( + type, (null==source)?(Object)event.data:source, (0xFFFFFFFFL & (long)event.time), + swtModifiers2Newt(event.stateMask, false), + event.keyCode, event.character); + } + return null; // no mapping .. + } + + // + // + // + + int dragButtonDown = 0; + + public SWTNewtEventFactory() { + resetButtonsDown(); + } + + final void resetButtonsDown() { + dragButtonDown = 0; + } + + public final boolean dispatchMouseEvent(org.eclipse.swt.widgets.Event event, Object source, com.jogamp.newt.event.MouseListener l) { + com.jogamp.newt.event.MouseEvent res = createMouseEvent(event, source); + if(null != res) { + if(null != l) { + switch(event.type) { + case SWT.MouseDown: + dragButtonDown = event.button; + l.mousePressed(res); break; + case SWT.MouseUp: + dragButtonDown = 0; + l.mouseReleased(res); + { + final com.jogamp.newt.event.MouseEvent res2 = new com.jogamp.newt.event.MouseEvent( + com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_CLICKED, + res.getSource(), + res.getWhen(), res.getModifiers(), + res.getX(), res.getY(), res.getClickCount(), + res.getButton(), res.getWheelRotation() ); + l.mouseClicked(res2); + } + break; + case SWT.MouseMove: + if( 0 < dragButtonDown ) { + final com.jogamp.newt.event.MouseEvent res2 = new com.jogamp.newt.event.MouseEvent( + com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_DRAGGED, + res.getSource(), + res.getWhen(), res.getModifiers(), + res.getX(), res.getY(), res.getClickCount(), + dragButtonDown, res.getWheelRotation() ); + l.mouseDragged( res2 ); + } else { + l.mouseMoved(res); + } + break; + case SWT.MouseEnter: + l.mouseEntered(res); + break; + case SWT.MouseExit: + resetButtonsDown(); + l.mouseExited(res); + break; + case SWT.MouseVerticalWheel: + l.mouseWheelMoved(res); + break; + } + } + return true; + } + return false; + } + + public final boolean dispatchKeyEvent(org.eclipse.swt.widgets.Event event, Object source, com.jogamp.newt.event.KeyListener l) { + com.jogamp.newt.event.KeyEvent res = createKeyEvent(event, source); + if(null != res) { + if(null != l) { + switch(event.type) { + case SWT.KeyDown: + l.keyPressed(res); + break; + case SWT.KeyUp: + l.keyReleased(res); + l.keyTyped(res); + break; + } + } + return true; + } + return false; + } + + public final void attachDispatchListener(final org.eclipse.swt.widgets.Control ctrl, final Object source, + final com.jogamp.newt.event.MouseListener ml, + final com.jogamp.newt.event.KeyListener kl) { + final Listener listener = new Listener () { + @Override + public void handleEvent (Event event) { + if( dispatchMouseEvent( event, source, ml ) ) { + return; + } + if( dispatchKeyEvent( event, source, kl ) ) { + return; + } + } }; + ctrl.addListener(SWT.MouseDown, listener); + ctrl.addListener(SWT.MouseUp, listener); + ctrl.addListener(SWT.MouseMove, listener); + ctrl.addListener(SWT.MouseEnter, listener); + ctrl.addListener(SWT.MouseExit, listener); + ctrl.addListener(SWT.MouseVerticalWheel, listener); + ctrl.addListener(SWT.KeyDown, listener); + ctrl.addListener(SWT.KeyUp, listener); + } +} + diff --git a/src/newt/native/WindowsWindow.c b/src/newt/native/WindowsWindow.c index e3d5cffa0..e7b454580 100644 --- a/src/newt/native/WindowsWindow.c +++ b/src/newt/native/WindowsWindow.c @@ -1034,23 +1034,22 @@ static LRESULT CALLBACK wndProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lP case WM_PAINT: { RECT r; useDefWindowProc = 0; - if (GetUpdateRect(wnd, &r, TRUE /* erase background */)) { - /* - jint width = r.right-r.left; - jint height = r.bottom-r.top; - if (width > 0 && height > 0) { - (*env)->CallVoidMethod(env, window, windowRepaintID, JNI_FALSE, r.left, r.top, width, height); - } - ValidateRect(wnd, &r); - */ + if (GetUpdateRect(wnd, &r, FALSE /* do not erase background */)) { + // clear the whole client area and issue repaint for it, w/o looping through erase background + ValidateRect(wnd, NULL); // clear all! + (*env)->CallVoidMethod(env, window, windowRepaintID, JNI_FALSE, 0, 0, -1, -1); + } else { + // shall not happen ? + ValidateRect(wnd, NULL); // clear all! } + // return 0 == done break; } case WM_ERASEBKGND: // ignore erase background (*env)->CallVoidMethod(env, window, windowRepaintID, JNI_FALSE, 0, 0, -1, -1); useDefWindowProc = 0; - res = 1; // OpenGL, etc .. erases the background, hence we claim to have just done this + res = 1; // return 1 == done, OpenGL, etc .. erases the background, hence we claim to have just done this break; diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java index e9fe9b401..a3023538f 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java @@ -60,8 +60,8 @@ public class GearsES2 implements GLEventListener { private int swapInterval = 0; private boolean pmvUseBackingArray = true; // the default for PMVMatrix now, since it's faster // private MouseListener gearsMouse = new TraceMouseAdapter(new GearsMouseAdapter()); - private MouseListener gearsMouse = new GearsMouseAdapter(); - private KeyListener gearsKeys = new GearsKeyAdapter(); + public MouseListener gearsMouse = new GearsMouseAdapter(); + public KeyListener gearsKeys = new GearsKeyAdapter(); private int prevMouseX, prevMouseY; private boolean doRotate = true; @@ -352,6 +352,10 @@ public class GearsES2 implements GLEventListener { window = (Window) source; width=window.getWidth(); height=window.getHeight(); + } else if (source instanceof GLAutoDrawable) { + GLAutoDrawable glad = (GLAutoDrawable) source; + width = glad.getWidth(); + height = glad.getHeight(); } else if (GLProfile.isAWTAvailable() && source instanceof java.awt.Component) { java.awt.Component comp = (java.awt.Component) source; width=comp.getWidth(); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestNewtCanvasSWTBug628ResizeDeadlock.java b/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestNewtCanvasSWTBug628ResizeDeadlock.java index 9c0762c12..1f3bf3156 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestNewtCanvasSWTBug628ResizeDeadlock.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestNewtCanvasSWTBug628ResizeDeadlock.java @@ -65,7 +65,7 @@ import com.jogamp.opengl.test.junit.util.UITestCase; public class TestNewtCanvasSWTBug628ResizeDeadlock extends UITestCase { - static int duration = 1000; + static int duration = 500; static class BigFlashingX implements GLEventListener { @@ -262,7 +262,7 @@ public class TestNewtCanvasSWTBug628ResizeDeadlock extends UITestCase { shell = new Shell( display ); Assert.assertNotNull( shell ); shell.setLayout( new FillLayout() ); - composite = new Composite( shell, SWT.NONE ); + composite = new Composite( shell, SWT.NO_BACKGROUND ); composite.setLayout( new FillLayout() ); Assert.assertNotNull( composite ); }}); @@ -301,10 +301,9 @@ public class TestNewtCanvasSWTBug628ResizeDeadlock extends UITestCase { dsc.init(); final GLWindow glWindow; - final NewtCanvasSWT canvas; { final GLProfile gl2Profile = GLProfile.get( GLProfile.GL2 ) ; - GLCapabilities caps = new GLCapabilities( gl2Profile ) ; + final GLCapabilities caps = new GLCapabilities( gl2Profile ) ; glWindow = GLWindow.create( caps ) ; glWindow.addGLEventListener( new BigFlashingX() ) ; glWindow.addKeyListener(new KeyAdapter() { @@ -314,7 +313,7 @@ public class TestNewtCanvasSWTBug628ResizeDeadlock extends UITestCase { glWindow.display(); } }); - canvas = NewtCanvasSWT.create( dsc.composite, 0, glWindow ) ; + NewtCanvasSWT.create( dsc.composite, 0, glWindow ) ; } dsc.display.syncExec( new Runnable() { @@ -342,7 +341,7 @@ public class TestNewtCanvasSWTBug628ResizeDeadlock extends UITestCase { } { - new Thread(new Runnable() { + final Thread t = new Thread(new Runnable() { @Override public void run() { try { @@ -360,13 +359,16 @@ public class TestNewtCanvasSWTBug628ResizeDeadlock extends UITestCase { } catch( InterruptedException e ) { } shallStop = true; dsc.display.wake(); - } } ).start(); + } } ); + t.setDaemon(true); + t.start(); } try { while( !shallStop && !dsc.display.isDisposed() ) { - if( !dsc.display.readAndDispatch() ) { - dsc.display.sleep(); + if( !dsc.display.readAndDispatch() && !shallStop ) { + // blocks on linux .. dsc.display.sleep(); + Thread.sleep(10); } } } catch (Exception e0) { @@ -374,8 +376,7 @@ public class TestNewtCanvasSWTBug628ResizeDeadlock extends UITestCase { Assert.assertTrue("Deadlock @ dispatch: "+e0, false); } - System.err.println("NewtCanvasAWT Dispose"); - canvas.dispose(); + // canvas is disposed implicit, due to it's disposed listener ! dsc.dispose(); } diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestSWTBug643AsyncExec.java b/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestSWTBug643AsyncExec.java new file mode 100644 index 000000000..97b3ab243 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestSWTBug643AsyncExec.java @@ -0,0 +1,343 @@ +/** + * Copyright 2012 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.swt; + +import java.awt.AWTException; +import java.lang.reflect.InvocationTargetException; + +import org.eclipse.swt.SWT ; + +import org.eclipse.swt.layout.FillLayout ; + +import org.eclipse.swt.widgets.Composite ; +import org.eclipse.swt.widgets.Display ; +import org.eclipse.swt.widgets.Shell ; +import org.junit.Assume; +import org.junit.Test; + +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities ; +import javax.media.opengl.GLProfile; + +import jogamp.newt.swt.SWTEDTUtil; +import jogamp.newt.swt.event.SWTNewtEventFactory; +import junit.framework.Assert; + +import com.jogamp.nativewindow.swt.SWTAccessor; +import com.jogamp.newt.opengl.GLWindow ; +import com.jogamp.newt.swt.NewtCanvasSWT ; +import com.jogamp.opengl.swt.GLCanvas; +import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; +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; + +//////////////////////////////////////////////////////////////////////////////// + + +public class TestSWTBug643AsyncExec extends UITestCase { + + static int duration = 500; + static boolean useAnimator = false; + + //////////////////////////////////////////////////////////////////////////////// + + static void resetSWTAndNEWTEDTCounter() { + synchronized(swtCountSync) { + swtCount=0; + } + synchronized(edtCountSync) { + edtCount=0; + } + } + static int incrSWTCount() { + synchronized(swtCountSync) { + swtCount++; + return swtCount; + } + } + static int getSWTCount() { + synchronized(swtCountSync) { + return swtCount; + } + } + static int incrNEWTCount() { + synchronized(edtCountSync) { + edtCount++; + return edtCount; + } + } + static int getNEWTCount() { + synchronized(edtCountSync) { + return edtCount; + } + } + static Object swtCountSync = new Object(); + static int swtCount = 0; + static Object edtCountSync = new Object(); + static int edtCount = 0; + + //////////////////////////////////////////////////////////////////////////////// + + static class AsyncExecEDTFeederThread extends Thread { + volatile boolean shallStop = false; + private Display swtDisplay ; + private jogamp.newt.DisplayImpl newtDisplay; + private int swtN, newtN ; + + public AsyncExecEDTFeederThread( Display swtDisplay, com.jogamp.newt.Display newtDisplay ) + { + super(); + this.swtDisplay = swtDisplay ; + this.newtDisplay = (jogamp.newt.DisplayImpl)newtDisplay; + } + + final Runnable swtAsyncAction = new Runnable() { + public void run() + { + ++swtN ; incrSWTCount(); + System.err.println("[SWT A-i shallStop "+shallStop+"]: Counter[loc "+swtN+", glob: "+getSWTCount()+"]"); + } }; + + final Runnable newtAsyncAction = new Runnable() { + public void run() + { + ++newtN ; incrNEWTCount(); + System.err.println("[NEWT A-i shallStop "+shallStop+"]: Counter[loc "+newtN+", glob: "+getNEWTCount()+"]"); + } }; + + public void run() + { + System.err.println("[A-0 shallStop "+shallStop+"]"); + + while( !shallStop && !swtDisplay.isDisposed() ) + { + try + { + swtDisplay.asyncExec( swtAsyncAction ); + if(null != newtDisplay && newtDisplay.isNativeValid() && newtDisplay.getEDTUtil().isRunning()) { + // only perform async exec on valid and already running NEWT EDT! + newtDisplay.runOnEDTIfAvail(false, newtAsyncAction); + } + Thread.sleep( 50L ) ; + } catch( InterruptedException e ) { + break ; + } + } + System.err.println("*R-Exit* shallStop "+shallStop); + } + } + + //////////////////////////////////////////////////////////////////////////////// + + private volatile boolean shallStop = false; + + static class SWT_DSC { + Display display; + Shell shell; + Composite composite; + + public void init() { + SWTAccessor.invoke(true, new Runnable() { + public void run() { + display = new Display(); + Assert.assertNotNull( display ); + }}); + + display.syncExec(new Runnable() { + public void run() { + shell = new Shell( display ); + Assert.assertNotNull( shell ); + shell.setLayout( new FillLayout() ); + composite = new Composite( shell, SWT.NO_BACKGROUND ); + composite.setLayout( new FillLayout() ); + Assert.assertNotNull( composite ); + }}); + } + + public void dispose() { + Assert.assertNotNull( display ); + Assert.assertNotNull( shell ); + Assert.assertNotNull( composite ); + try { + display.syncExec(new Runnable() { + public void run() { + composite.dispose(); + shell.dispose(); + }}); + SWTAccessor.invoke(true, new Runnable() { + public void run() { + display.dispose(); + }}); + } + catch( Throwable throwable ) { + throwable.printStackTrace(); + Assume.assumeNoException( throwable ); + } + display = null; + shell = null; + composite = null; + } + } + + private void testImpl(boolean useJOGLGLCanvas, boolean useNewtCanvasSWT, boolean glWindowPreVisible) throws InterruptedException, AWTException, InvocationTargetException { + resetSWTAndNEWTEDTCounter(); + + final SWT_DSC dsc = new SWT_DSC(); + dsc.init(); + + final com.jogamp.newt.Display newtDisplay; + { + final GLProfile gl2Profile = GLProfile.get( GLProfile.GL2 ) ; + final GLCapabilities caps = new GLCapabilities( gl2Profile ) ; + + final GLAutoDrawable glad; + if( useJOGLGLCanvas ) { + final GearsES2 demo = new GearsES2(); + final GLCanvas glc = GLCanvas.create(dsc.composite, 0, caps, null, null); + final SWTNewtEventFactory swtNewtEventFactory = new SWTNewtEventFactory(); + swtNewtEventFactory.attachDispatchListener(glc, glc, demo.gearsMouse, demo.gearsKeys); + glc.addGLEventListener( demo ) ; + glad = glc; + newtDisplay = null; + } else if( useNewtCanvasSWT ) { + final GLWindow glWindow = GLWindow.create( caps ) ; + glWindow.addGLEventListener( new GearsES2() ) ; + newtDisplay = glWindow.getScreen().getDisplay(); + if( glWindowPreVisible ) { + newtDisplay.setEDTUtil(new SWTEDTUtil(newtDisplay, dsc.display)); // Especially Windows requires creation access via same thread! + glWindow.setVisible(true); + AWTRobotUtil.waitForRealized(glWindow, true); + Thread.sleep(120); // let it render a bit, before consumed by SWT + } + glad = glWindow; + NewtCanvasSWT.create( dsc.composite, 0, glWindow ) ; + } else { + throw new InternalError("XXX"); + } + if(useAnimator) { + Animator animator = new Animator(glad); + animator.start(); + } + } + + System.err.println("**** Pre Shell Open"); + dsc.display.syncExec( new Runnable() { + public void run() { + dsc.shell.setText( "NewtCanvasSWT Resize Bug Demo" ) ; + dsc.shell.setSize( 400, 450 ) ; + dsc.shell.open() ; + } } ); + System.err.println("**** Post Shell Open"); + + shallStop = false; + + final int[] counterBeforeExit = new int[] { 0 /* SWT */, 0 /* NEWT */ }; + + final AsyncExecEDTFeederThread asyncExecFeeder; + { + asyncExecFeeder = new AsyncExecEDTFeederThread(dsc.display, newtDisplay) ; + asyncExecFeeder.start() ; + } + + { + final Thread t = new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(duration); + } catch (InterruptedException e) {} + + counterBeforeExit[0] = getSWTCount(); + counterBeforeExit[1] = getNEWTCount(); + asyncExecFeeder.shallStop = true; + try + { + asyncExecFeeder.join(); + } catch( InterruptedException e ) { } + shallStop = true; + dsc.display.wake(); + } } ); + t.setDaemon(true); + t.start(); + } + + try { + final Display d = dsc.display; + while( !shallStop && !d.isDisposed() ) { + if( !d.readAndDispatch() && !shallStop ) { + // blocks on linux .. dsc.display.sleep(); + Thread.sleep(10); + } + } + } catch (Exception e0) { + e0.printStackTrace(); + Assert.assertTrue("Deadlock @ dispatch: "+e0, false); + } + + // canvas is disposed implicit, due to it's disposed listener ! + + dsc.dispose(); + + System.err.println("EDT Counter before exit: SWT " + counterBeforeExit[0] + ", NEWT "+counterBeforeExit[1]); + Assert.assertTrue("SWT EDT Counter not greater zero before dispose!", 0 < counterBeforeExit[0]); + if( null != newtDisplay ) { + Assert.assertTrue("NEWT EDT Counter not greater zero before dispose!", 0 < counterBeforeExit[1]); + } + } + + @Test + public void test01JOGLGLCanvas() throws InterruptedException, AWTException, InvocationTargetException { + testImpl(true /* useJOGLGLCanvas */, false /* useNewtCanvasSWT */, false /* glWindowPreVisible */); + } + + @Test + public void test02NewtCanvasSWTSimple() throws InterruptedException, AWTException, InvocationTargetException { + testImpl(false /* useJOGLGLCanvas */, true /* useNewtCanvasSWT */, false /* glWindowPreVisible */); + } + + @Test + public void test02NewtCanvasSWTPreVisible() throws InterruptedException, AWTException, InvocationTargetException { + testImpl(false /* useJOGLGLCanvas */, true /* useNewtCanvasSWT */, true /* glWindowPreVisible */); + } + + public static void main( String[] args ) { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + duration = MiscUtils.atoi(args[++i], duration); + } else if(args[i].equals("-anim")) { + useAnimator = true; + } + } + System.out.println("durationPerTest: "+duration); + org.junit.runner.JUnitCore.main(TestSWTBug643AsyncExec.class.getName()); + } + +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestSWTJOGLGLCanvas01GLn.java b/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestSWTJOGLGLCanvas01GLn.java index 6d9e2219c..1822d2eaf 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestSWTJOGLGLCanvas01GLn.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestSWTJOGLGLCanvas01GLn.java @@ -50,17 +50,18 @@ import org.junit.Test; import com.jogamp.nativewindow.swt.SWTAccessor; import com.jogamp.opengl.swt.GLCanvas; 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.UITestCase; +import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.GLReadBufferUtil; import com.jogamp.opengl.util.texture.TextureIO; /** * Tests that a basic SWT app can open without crashing under different GL profiles. * <p> - * Uses JOGL's new SWT GLCanvas. - * </p> - * <p> - * Note: To employ custom GLCapabilities, NewtCanvasSWT shall be used. + * Uses JOGL's new SWT GLCanvas, + * which 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/ @@ -71,7 +72,8 @@ import com.jogamp.opengl.util.texture.TextureIO; public class TestSWTJOGLGLCanvas01GLn extends UITestCase { static int duration = 250; - + static boolean doAnimation = true; + static final int iwidth = 640; static final int iheight = 480; @@ -90,10 +92,13 @@ public class TestSWTJOGLGLCanvas01GLn extends UITestCase { public void run() { display = new Display(); Assert.assertNotNull( display ); + }}); + display.syncExec(new Runnable() { + public void run() { shell = new Shell( display ); Assert.assertNotNull( shell ); shell.setLayout( new FillLayout() ); - composite = new Composite( shell, SWT.NONE ); + composite = new Composite( shell, SWT.NO_BACKGROUND ); composite.setLayout( new FillLayout() ); Assert.assertNotNull( composite ); }}); @@ -105,10 +110,13 @@ public class TestSWTJOGLGLCanvas01GLn extends UITestCase { Assert.assertNotNull( shell ); Assert.assertNotNull( composite ); try { - SWTAccessor.invoke(true, new Runnable() { + display.syncExec(new Runnable() { public void run() { composite.dispose(); shell.dispose(); + }}); + SWTAccessor.invoke(true, new Runnable() { + public void run() { display.dispose(); }}); } @@ -140,13 +148,19 @@ public class TestSWTJOGLGLCanvas01GLn extends UITestCase { public void dispose(final GLAutoDrawable drawable) { } }); - SWTAccessor.invoke(true, new Runnable() { + display.syncExec(new Runnable() { public void run() { shell.setText( getSimpleTestName(".") ); shell.setSize( 640, 480 ); shell.open(); } } ); + Animator anim = new Animator(); + if(doAnimation) { + anim.add(canvas); + anim.start(); + } + long lStartTime = System.currentTimeMillis(); long lEndTime = lStartTime + duration; try { @@ -160,7 +174,10 @@ public class TestSWTJOGLGLCanvas01GLn extends UITestCase { throwable.printStackTrace(); Assume.assumeNoException( throwable ); } - SWTAccessor.invoke(true, new Runnable() { + + anim.stop(); + + display.syncExec(new Runnable() { public void run() { canvas.dispose(); } } ); @@ -171,6 +188,14 @@ public class TestSWTJOGLGLCanvas01GLn extends UITestCase { runTestAGL( new GLCapabilities(GLProfile.getGL2ES2()), new GearsES2() ); } + @Test + public void test_MultisampleAndAlpha() throws InterruptedException { + GLCapabilities caps = new GLCapabilities(GLProfile.getGL2ES2()); + caps.setSampleBuffers(true); + caps.setNumSamples(2); + runTestAGL( caps, new MultisampleDemoES2(true) ); + } + static int atoi(String a) { int i=0; try { @@ -183,6 +208,8 @@ public class TestSWTJOGLGLCanvas01GLn extends UITestCase { for(int i=0; i<args.length; i++) { if(args[i].equals("-time")) { duration = atoi(args[++i]); + } else if(args[i].equals("-still")) { + doAnimation = false; } } System.out.println("durationPerTest: "+duration); diff --git a/src/test/com/jogamp/opengl/test/junit/newt/TestDisplayLifecycle01NEWT.java b/src/test/com/jogamp/opengl/test/junit/newt/TestDisplayLifecycle01NEWT.java index c9450c2d6..e4867e3fe 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/TestDisplayLifecycle01NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/TestDisplayLifecycle01NEWT.java @@ -89,7 +89,7 @@ public class TestDisplayLifecycle01NEWT extends UITestCase { Assert.assertEquals(0,display.getReferenceCount()); Assert.assertEquals(false,display.isNativeValid()); Assert.assertNotNull(display.getEDTUtil()); - Assert.assertEquals(false,display.getEDTUtil().isRunning()); + Assert.assertEquals(true,display.getEDTUtil().isRunning()); Assert.assertEquals(0,screen.getReferenceCount()); Assert.assertEquals(false,screen.isNativeValid()); @@ -201,6 +201,8 @@ public class TestDisplayLifecycle01NEWT extends UITestCase { Assert.assertEquals(false,display.isNativeValid()); Assert.assertNotNull(display.getEDTUtil()); Assert.assertEquals(false,display.getEDTUtil().isRunning()); + display.getEDTUtil().invoke(true, null); + Assert.assertEquals(true,display.getEDTUtil().isRunning()); Assert.assertEquals(0,screen.getReferenceCount()); Assert.assertEquals(false,screen.isNativeValid()); |