/* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. Copyright (c) 2010 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: * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * */ package jogamp.newt; import java.util.ArrayList; import java.lang.reflect.Method; import com.jogamp.common.util.ReflectionUtil; import com.jogamp.newt.NewtFactory; import com.jogamp.newt.Display; import com.jogamp.newt.Screen; import com.jogamp.newt.Window; import com.jogamp.common.util.locks.RecursiveLock; import com.jogamp.newt.ScreenMode; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.KeyListener; import com.jogamp.newt.event.MouseEvent; import com.jogamp.newt.event.MouseListener; import com.jogamp.newt.event.NEWTEvent; import com.jogamp.newt.event.NEWTEventConsumer; import com.jogamp.newt.event.ScreenModeListener; import com.jogamp.newt.event.WindowEvent; import com.jogamp.newt.event.WindowListener; import com.jogamp.newt.event.WindowUpdateEvent; import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.CapabilitiesChooser; import javax.media.nativewindow.CapabilitiesImmutable; import javax.media.nativewindow.NativeSurface; import javax.media.nativewindow.NativeWindow; import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.NativeWindowFactory; import javax.media.nativewindow.SurfaceUpdatedListener; import javax.media.nativewindow.util.DimensionReadOnly; import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.Point; import javax.media.nativewindow.util.Rectangle; public abstract class WindowImpl implements Window, NEWTEventConsumer { public static final boolean DEBUG_TEST_REPARENT_INCOMPATIBLE = Debug.isPropertyDefined("newt.test.Window.reparent.incompatible", true); private RecursiveLock windowLock = new RecursiveLock(); // Window instance wide lock private RecursiveLock surfaceLock = new RecursiveLock(); // Surface only lock private long windowHandle = 0; private ScreenImpl screen = null; private boolean screenReferenceAdded = false; private NativeWindow parentWindow = null; private long parentWindowHandle = 0; protected AbstractGraphicsConfiguration config = null; protected CapabilitiesImmutable capsRequested = null; protected CapabilitiesChooser capabilitiesChooser = null; // default null -> default protected boolean fullscreen = false, visible = false, hasFocus = false; protected int width = 128, height = 128, x = 0, y = 0; // default values protected int nfs_width, nfs_height, nfs_x, nfs_y; // non fullscreen dimensions .. protected String title = "Newt Window"; protected boolean undecorated = false; private LifecycleHook lifecycleHook = null; private DestroyAction destroyAction = new DestroyAction(); private boolean handleDestroyNotify = true; private ReparentActionRecreate reparentActionRecreate = new ReparentActionRecreate(); private RequestFocusAction requestFocusAction = new RequestFocusAction(); private FocusRunnable focusAction = null; private Object surfaceUpdatedListenersLock = new Object(); private ArrayList surfaceUpdatedListeners = new ArrayList(); private Object childWindowsLock = new Object(); private ArrayList childWindows = new ArrayList(); private ArrayList mouseListeners = new ArrayList(); private int mouseButtonPressed = 0; // current pressed mouse button number private long lastMousePressed = 0; // last time when a mouse button was pressed private int lastMouseClickCount = 0; // last mouse button click count private ArrayList keyListeners = new ArrayList(); private ArrayList windowListeners = new ArrayList(); private boolean repaintQueued = false; ScreenModeListenerImpl screenModeListenerImpl = new ScreenModeListenerImpl(); // Workaround for initialization order problems on Mac OS X // between native Newt and (apparently) Fmod -- if Fmod is // initialized first then the connection to the window server // breaks, leading to errors from deep within the AppKit public static void init(String type) { if (NativeWindowFactory.TYPE_MACOSX.equals(type)) { try { getWindowClass(type); } catch (Exception e) { e.printStackTrace(); } } } // // Construction Methods // private static Class getWindowClass(String type) throws ClassNotFoundException { Class windowClass = NewtFactory.getCustomClass(type, "Window"); if(null==windowClass) { if (NativeWindowFactory.TYPE_EGL.equals(type)) { windowClass = Class.forName("jogamp.newt.egl.kd.KDWindow"); } else if (NativeWindowFactory.TYPE_WINDOWS.equals(type)) { windowClass = Class.forName("jogamp.newt.windows.WindowsWindow"); } else if (NativeWindowFactory.TYPE_MACOSX.equals(type)) { windowClass = Class.forName("jogamp.newt.macosx.MacWindow"); } else if (NativeWindowFactory.TYPE_X11.equals(type)) { windowClass = Class.forName("jogamp.newt.x11.X11Window"); } else if (NativeWindowFactory.TYPE_AWT.equals(type)) { windowClass = Class.forName("jogamp.newt.awt.AWTWindow"); } else { throw new NativeWindowException("Unknown window type \"" + type + "\""); } } return windowClass; } public static WindowImpl create(NativeWindow parentWindow, long parentWindowHandle, Screen screen, CapabilitiesImmutable caps) { try { Class windowClass; if(caps.isOnscreen()) { windowClass = getWindowClass(screen.getDisplay().getType()); } else { windowClass = OffscreenWindow.class; } WindowImpl window = (WindowImpl) windowClass.newInstance(); window.parentWindow = parentWindow; window.parentWindowHandle = parentWindowHandle; window.screen = (ScreenImpl) screen; window.capsRequested = (CapabilitiesImmutable) caps.cloneMutable(); window.setUndecorated(0!=parentWindowHandle); return window; } catch (Throwable t) { t.printStackTrace(); throw new NativeWindowException(t); } } public static WindowImpl create(Object[] cstrArguments, Screen screen, CapabilitiesImmutable caps) { try { Class windowClass = getWindowClass(screen.getDisplay().getType()); Class[] cstrArgumentTypes = getCustomConstructorArgumentTypes(windowClass); if(null==cstrArgumentTypes) { throw new NativeWindowException("WindowClass "+windowClass+" doesn't support custom arguments in constructor"); } int argsChecked = verifyConstructorArgumentTypes(cstrArgumentTypes, cstrArguments); if ( argsChecked < cstrArguments.length ) { throw new NativeWindowException("WindowClass "+windowClass+" constructor mismatch at argument #"+argsChecked+"; Constructor: "+getTypeStrList(cstrArgumentTypes)+", arguments: "+getArgsStrList(cstrArguments)); } WindowImpl window = (WindowImpl) ReflectionUtil.createInstance( windowClass, cstrArgumentTypes, cstrArguments ) ; window.screen = (ScreenImpl) screen; window.capsRequested = (CapabilitiesImmutable) caps.cloneMutable(); return window; } catch (Throwable t) { throw new NativeWindowException(t); } } public static interface LifecycleHook { /** * Reset of internal state counter, ie totalFrames, etc. * Called from EDT while window is locked. */ public abstract void resetCounter(); /** * Invoked after Window setVisible, * allows allocating resources depending on the native Window. * Called from EDT while window is locked. */ void setVisibleActionPost(boolean visible, boolean nativeWindowCreated); /** * Invoked before Window destroy action, * allows releasing of resources depending on the native Window.
* Surface not locked yet.
* Called not necessarily from EDT. */ void destroyActionPreLock(); /** * Invoked before Window destroy action, * allows releasing of resources depending on the native Window.
* Surface locked.
* Called from EDT while window is locked. */ void destroyActionInLock(); /** * Invoked for expensive modifications, ie while reparenting and ScreenMode change.
* No lock is hold when invoked.
* * @return true is paused, otherwise false. If true {@link #resumeRenderingAction()} shall be issued. * * @see #resumeRenderingAction() */ boolean pauseRenderingAction(); /** * Invoked for expensive modifications, ie while reparenting and ScreenMode change. * No lock is hold when invoked.
* * @see #pauseRenderingAction() */ void resumeRenderingAction(); } private boolean createNative() { if(DEBUG_IMPLEMENTATION) { System.err.println("Window.createNative() START ("+getThreadName()+", "+this+")"); } if( null != parentWindow && NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindow.lockSurface() ) { throw new NativeWindowException("Parent surface lock: not ready: "+parentWindow); } try { if(validateParentWindowHandle()) { if(screenReferenceAdded) { throw new InternalError("XXX"); } screen.addReference(); screenReferenceAdded = true; createNativeImpl(); setVisibleImpl(true, x, y, width, height); screen.addScreenModeListener(screenModeListenerImpl); setTitleImpl(title); } } finally { if(null!=parentWindow) { parentWindow.unlockSurface(); } } if(DEBUG_IMPLEMENTATION) { System.err.println("Window.createNative() END ("+getThreadName()+", "+this+")"); } return 0 != windowHandle ; } private void removeScreenReference() { if(screenReferenceAdded) { // be nice, probably already called recursive via // closeAndInvalidate() -> closeNativeIml() -> .. -> windowDestroyed() -> closeAndInvalidate() ! // or via reparentWindow .. etc screenReferenceAdded = false; screen.removeReference(); } } private boolean validateParentWindowHandle() { if(null!=parentWindow) { parentWindowHandle = getNativeWindowHandle(parentWindow); return 0 != parentWindowHandle ; } return true; } private static long getNativeWindowHandle(NativeWindow nativeWindow) { long handle = 0; if(null!=nativeWindow) { boolean wasLocked = false; if( NativeSurface.LOCK_SURFACE_NOT_READY < nativeWindow.lockSurface() ) { wasLocked = true; try { handle = nativeWindow.getWindowHandle(); if(0==handle) { throw new NativeWindowException("Parent native window handle is NULL, after succesful locking: "+nativeWindow); } } catch (NativeWindowException nwe) { if(DEBUG_IMPLEMENTATION) { System.err.println("Window.getNativeWindowHandle: not successful yet: "+nwe); } } finally { nativeWindow.unlockSurface(); } } if(DEBUG_IMPLEMENTATION) { System.err.println("Window.getNativeWindowHandle: locked "+wasLocked+", "+nativeWindow); } } return handle; } //---------------------------------------------------------------------- // NativeSurface: Native implementation // protected int lockSurfaceImpl() { return LOCK_SUCCESS; } protected void unlockSurfaceImpl() { } //---------------------------------------------------------------------- // WindowClosingProtocol implementation // private Object closingListenerLock = new Object(); private int defaultCloseOperation = DISPOSE_ON_CLOSE; public int getDefaultCloseOperation() { synchronized (closingListenerLock) { return defaultCloseOperation; } } public int setDefaultCloseOperation(int op) { synchronized (closingListenerLock) { int _op = defaultCloseOperation; defaultCloseOperation = op; return _op; } } //---------------------------------------------------------------------- // Window: Native implementation // /** * The native implementation must set the native windowHandle.
* * The implementation should invoke the referenced java state callbacks * to notify this Java object of state changes. * * @see #windowDestroyNotify() * @see #focusChanged(boolean) * @see #visibleChanged(boolean) * @see #sizeChanged(int,int) * @see #positionChanged(int,int) * @see #windowDestroyNotify() */ protected abstract void createNativeImpl(); protected abstract void closeNativeImpl(); /** * The native implementation must invoke {@link #focusChanged(boolean)} * to change the focus state, if force == false. * This may happen asynchronous within {@link #TIMEOUT_NATIVEWINDOW}. * * @param force if true, bypass {@link #focusChanged(boolean)} and force focus request */ protected abstract void requestFocusImpl(boolean force); /** * The native implementation must invoke {@link #visibleChanged(boolean)} * to change the visibility state. This may happen asynchronous within * {@link #TIMEOUT_NATIVEWINDOW}. */ protected abstract void setVisibleImpl(boolean visible, int x, int y, int width, int height); /** * The native implementation should invoke the referenced java state callbacks * to notify this Java object of state changes. * * @param x -1 if no position change requested, otherwise greater than zero * @param y -1 if no position change requested, otherwise greater than zero * @param width -1 if no size change requested, otherwise greater than zero * @param height -1 if no size change requested, otherwise greater than zero * @param parentChange true if reparenting requested, otherwise false * @param fullScreenChange 0 if unchanged, -1 fullscreen off, 1 fullscreen on * @param decorationChange 0 if unchanged, -1 undecorated, 1 decorated * * @see #sizeChanged(int,int) * @see #positionChanged(int,int) */ protected abstract boolean reconfigureWindowImpl(int x, int y, int width, int height, boolean parentChange, int fullScreenChange, int decorationChange); protected void setTitleImpl(String title) {} /** * Return screen coordinates of the given coordinates * or null, in which case a NativeWindow traversal shall being used * as demonstrated in {@link #getLocationOnScreen(javax.media.nativewindow.util.Point)}. * * @return if not null, the screen location of the given coordinates */ protected abstract Point getLocationOnScreenImpl(int x, int y); //---------------------------------------------------------------------- // NativeSurface // public final int lockSurface() { windowLock.lock(); surfaceLock.lock(); int res = surfaceLock.getRecursionCount() == 0 ? LOCK_SURFACE_NOT_READY : LOCK_SUCCESS; if ( LOCK_SURFACE_NOT_READY == res ) { try { if( isNativeValid() ) { final AbstractGraphicsDevice adevice = config.getScreen().getDevice(); adevice.lock(); try { res = lockSurfaceImpl(); } finally { if (LOCK_SURFACE_NOT_READY >= res) { adevice.unlock(); } } } } finally { if (LOCK_SURFACE_NOT_READY >= res) { surfaceLock.unlock(); windowLock.unlock(); } } } return res; } public final void unlockSurface() { surfaceLock.validateLocked(); windowLock.validateLocked(); if (surfaceLock.getRecursionCount() == 0) { final AbstractGraphicsDevice adevice = config.getScreen().getDevice(); try { unlockSurfaceImpl(); } finally { adevice.unlock(); } } surfaceLock.unlock(); windowLock.unlock(); } public final boolean isWindowLockedByOtherThread() { return windowLock.isLockedByOtherThread(); } public final boolean isWindowLocked() { return windowLock.isLocked(); } public final Thread getWindowLockOwner() { return windowLock.getOwner(); } public final boolean isSurfaceLockedByOtherThread() { return surfaceLock.isLockedByOtherThread(); } public final boolean isSurfaceLocked() { return surfaceLock.isLocked(); } public final Thread getSurfaceLockOwner() { return surfaceLock.getOwner(); } public long getSurfaceHandle() { return windowHandle; // default: return window handle } public boolean surfaceSwap() { return false; } public AbstractGraphicsConfiguration getGraphicsConfiguration() { return config; } public final long getDisplayHandle() { return getScreen().getDisplay().getHandle(); } public final int getScreenIndex() { return getScreen().getIndex(); } //---------------------------------------------------------------------- // NativeWindow // // public final void destroy() - see below public final NativeWindow getParent() { return parentWindow; } public final long getWindowHandle() { return windowHandle; } public Point getLocationOnScreen(Point storage) { if(isNativeValid()) { Point d; windowLock.lock(); try { d = getLocationOnScreenImpl(0, 0); } finally { windowLock.unlock(); } if(null!=d) { if(null!=storage) { storage.translate(d.getX(),d.getY()); return storage; } return d; } // fall through intended .. } if(null!=storage) { storage.translate(getX(),getY()); } else { storage = new Point(getX(),getY()); } if(null!=parentWindow) { // traverse through parent list .. parentWindow.getLocationOnScreen(storage); } return storage; } //---------------------------------------------------------------------- // Window // public final boolean isNativeValid() { return null != getScreen() && 0 != getWindowHandle() ; } public final boolean isValid() { return null != getScreen() ; } public final Screen getScreen() { return screen; } final void setVisibleActionImpl(boolean visible) { boolean nativeWindowCreated = false; boolean madeVisible = false; windowLock.lock(); try { if(null!=lifecycleHook) { lifecycleHook.resetCounter(); } if(!visible && null!=childWindows && childWindows.size()>0) { synchronized(childWindowsLock) { for(int i = 0; i < childWindows.size(); i++ ) { NativeWindow nw = (NativeWindow) childWindows.get(i); if(nw instanceof WindowImpl) { ((WindowImpl)nw).setVisible(false); } } } } if(0==windowHandle && visible) { if( 00) { synchronized(childWindowsLock) { for(int i = 0; i < childWindows.size(); i++ ) { NativeWindow nw = (NativeWindow) childWindows.get(i); if(nw instanceof WindowImpl) { ((WindowImpl)nw).setVisible(true); } } } } if(DEBUG_IMPLEMENTATION) { System.err.println("Window setVisible: END ("+getThreadName()+") "+x+"/"+y+" "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible: "+WindowImpl.this.visible+", nativeWindowCreated: "+nativeWindowCreated+", madeVisible: "+madeVisible); } } finally { windowLock.unlock(); } if( nativeWindowCreated || madeVisible ) { sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener } } private class VisibleAction implements Runnable { boolean visible; private VisibleAction (boolean visible) { this.visible = visible; } public final void run() { setVisibleActionImpl(visible); } } public void setVisible(boolean visible) { if(isValid()) { if( 0==windowHandle && visible && 0>=width*height ) { // fast-path: not realized yet, make visible, but zero size return; } if(DEBUG_IMPLEMENTATION) { String msg = "Window setVisible: START ("+getThreadName()+") "+x+"/"+y+" "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible: "+this.visible+" -> "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+(null!=parentWindow); System.err.println(msg); Thread.dumpStack(); } runOnEDTIfAvail(true, new VisibleAction(visible)); } } private class SetSizeActionImpl implements Runnable { int width, height; private SetSizeActionImpl(int w, int h) { width = w; height = h; } public final void run() { windowLock.lock(); try { int visibleAction = 0; // 1 invisible, 2 visible (create) if ( !fullscreen && ( width != WindowImpl.this.width || WindowImpl.this.height != height ) ) { if(DEBUG_IMPLEMENTATION) { String msg = "Window setSize: START "+WindowImpl.this.width+"x"+WindowImpl.this.height+" -> "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible "+visible; System.err.println(msg); } if ( 0 != windowHandle && 0>=width*height && visible ) { visibleAction=1; // invisible WindowImpl.this.width = 0; WindowImpl.this.height = 0; } else if ( 0 == windowHandle && 00) { // avoid ConcurrentModificationException: parent -> child -> parent.removeChild(this) ArrayList clonedChildWindows = (ArrayList) childWindows.clone(); while( clonedChildWindows.size() > 0 ) { NativeWindow nw = (NativeWindow) clonedChildWindows.remove(0); if(nw instanceof WindowImpl) { ((WindowImpl)nw).sendWindowEvent(WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY); ((WindowImpl)nw).destroy(); } else { nw.destroy(); } } } } if(null!=lifecycleHook) { // send synced destroy notification for proper cleanup, eg GLWindow/OpenGL lifecycleHook.destroyActionInLock(); } if( null != screen ) { if( 0 != windowHandle ) { screen.removeScreenModeListener(screenModeListenerImpl); closeNativeImpl(); removeScreenReference(); } Display dpy = screen.getDisplay(); if(null != dpy) { dpy.validateEDT(); } } // send synced destroyed notification sendWindowEvent(WindowEvent.EVENT_WINDOW_DESTROYED); if(DEBUG_IMPLEMENTATION) { System.err.println("Window.destroy() END "+getThreadName()/*+", "+WindowImpl.this*/); } } finally { windowLock.unlock(); } if(animatorPaused) { lifecycleHook.resumeRenderingAction(); } windowHandle = 0; visible = false; fullscreen = false; hasFocus = false; parentWindowHandle = 0; // these refs shall be kept alive - resurrection /** if(null!=parentWindow && parentWindow instanceof Window) { ((Window)parentWindow).removeChild(WindowImpl.this); } childWindows = null; surfaceUpdatedListeners = null; mouseListeners = null; keyListeners = null; capsRequested = null; lifecycleHook = null; screen = null; windowListeners = null; parentWindow = null; */ } } public void destroy() { if( isValid() ) { if(DEBUG_IMPLEMENTATION) { String msg = "Window.destroy() START "+getThreadName(); System.err.println(msg); //Exception ee = new Exception(msg); //ee.printStackTrace(); } runOnEDTIfAvail(true, destroyAction); } } private class ReparentActionImpl implements Runnable, ReparentAction { NativeWindow newParentWindow; boolean forceDestroyCreate; int reparentAction; private ReparentActionImpl(NativeWindow newParentWindow, boolean forceDestroyCreate) { this.newParentWindow = newParentWindow; this.forceDestroyCreate = forceDestroyCreate; this.reparentAction = -1; // ensure it's set } private int getStrategy() { return reparentAction; } private void setScreen(ScreenImpl newScreen) { WindowImpl.this.removeScreenReference(); screen = newScreen; } public final void run() { boolean animatorPaused = false; if(null!=lifecycleHook) { animatorPaused = lifecycleHook.pauseRenderingAction(); } reparent(); if(animatorPaused) { lifecycleHook.resumeRenderingAction(); } } private void reparent() { // mirror pos/size so native change notification can get overwritten int x = WindowImpl.this.x; int y = WindowImpl.this.y; int width = WindowImpl.this.width; int height = WindowImpl.this.height; boolean wasVisible; windowLock.lock(); try { wasVisible = isVisible(); Window newParentWindowNEWT = null; if(newParentWindow instanceof Window) { newParentWindowNEWT = (Window) newParentWindow; } long newParentWindowHandle = 0 ; if(DEBUG_IMPLEMENTATION) { System.err.println("Window.reparent: START ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)+", visible "+wasVisible+", old parentWindow: "+Display.hashCodeNullSafe(parentWindow)+", new parentWindow: "+Display.hashCodeNullSafe(newParentWindow)+", forceDestroyCreate "+forceDestroyCreate+", DEBUG_TEST_REPARENT_INCOMPATIBLE "+DEBUG_TEST_REPARENT_INCOMPATIBLE+" "+x+"/"+y+" "+width+"x"+height); } if(null!=lifecycleHook) { lifecycleHook.resetCounter(); } if(null!=newParentWindow) { // reset position to 0/0 within parent space x = 0; y = 0; // refit if size is bigger than parent if( width > newParentWindow.getWidth() ) { width = newParentWindow.getWidth(); } if( height > newParentWindow.getHeight() ) { height = newParentWindow.getHeight(); } // Case: Child Window newParentWindowHandle = getNativeWindowHandle(newParentWindow); if(0 == newParentWindowHandle) { // Case: Parent's native window not realized yet if(null==newParentWindowNEWT) { throw new NativeWindowException("Reparenting with non NEWT Window type only available after it's realized: "+newParentWindow); } // Destroy this window and use parent's Screen. // It may be created properly when the parent is made visible. destroy(); setScreen( (ScreenImpl) newParentWindowNEWT.getScreen() ); reparentAction = ACTION_NATIVE_CREATION_PENDING; } else if(newParentWindow != getParent()) { // Case: Parent's native window realized and changed if( !isNativeValid() ) { // May create a new compatible Screen/Display and // mark it for creation. if(null!=newParentWindowNEWT) { setScreen( (ScreenImpl) newParentWindowNEWT.getScreen() ); } else { Screen newScreen = NewtFactory.createCompatibleScreen(newParentWindow, getScreen()); if( getScreen() != newScreen ) { // auto destroy on-the-fly created Screen/Display setScreen( (ScreenImpl) newScreen ); } } if( 0 top // put client to current parent+child position Point p = getLocationOnScreen(null); x = p.getX(); y = p.getY(); } // Case: Top Window if( 0 == getParentWindowHandle() ) { // Already Top Window reparentAction = ACTION_UNCHANGED; } else if( !isNativeValid() || DEBUG_TEST_REPARENT_INCOMPATIBLE || forceDestroyCreate ) { // Destroy this window and mark it for [pending] creation. destroy(); if( 0 reparentAction ) { throw new NativeWindowException("Internal Error: reparentAction not set"); } if( ACTION_UNCHANGED == reparentAction ) { if(DEBUG_IMPLEMENTATION) { System.err.println("Window.reparent: NO CHANGE ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+" new parentWindowHandle "+toHexString(newParentWindowHandle)+", visible "+wasVisible); } return; } if(DEBUG_IMPLEMENTATION) { System.err.println("Window.reparent: ACTION ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+" new parentWindowHandle "+toHexString(newParentWindowHandle)+", reparentAction "+reparentAction+", visible "+wasVisible); } // rearrange window tree if(null!=parentWindow && parentWindow instanceof Window) { ((Window)parentWindow).removeChild(WindowImpl.this); } parentWindow = newParentWindow; if(parentWindow instanceof Window) { ((Window)parentWindow).addChild(WindowImpl.this); } if( ACTION_NATIVE_CREATION_PENDING == reparentAction ) { return; } if( ACTION_NATIVE_REPARENTING == reparentAction ) { DisplayImpl display = (DisplayImpl) screen.getDisplay(); display.dispatchMessagesNative(); // status up2date if(wasVisible) { setVisibleImpl(false, x, y, width, height); WindowImpl.this.waitForVisible(false, true); } // Lock parentWindow only during reparenting (attempt) NativeWindow parentWindowLocked = null; if( null != parentWindow ) { parentWindowLocked = parentWindow; if( NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindowLocked.lockSurface() ) { throw new NativeWindowException("Parent surface lock: not ready: "+parentWindow); } } boolean ok = false; try { // write back mirrored values, to be able to detect satisfaction WindowImpl.this.x = x; WindowImpl.this.y = y; WindowImpl.this.width = width; WindowImpl.this.height = height; ok = reconfigureWindowImpl(x, y, width, height, true, 0, isUndecorated()?-1:1); } finally { if(null!=parentWindowLocked) { parentWindowLocked.unlockSurface(); } } // set visible again, and revalidate 'ok', // since it has been experience that in some cases the reparented window gets hidden if(ok) { display.dispatchMessagesNative(); // status up2date if(wasVisible) { setVisibleImpl(true, x, y, width, height); ok = WindowImpl.this.waitForVisible(true, false); display.dispatchMessagesNative(); // status up2date if( ok && ( WindowImpl.this.x != x || WindowImpl.this.y != y || WindowImpl.this.width != width || WindowImpl.this.height != height ) ) { if(DEBUG_IMPLEMENTATION) { System.err.println("Window.reparent (reconfig)"); } // reset pos/size .. due to some native impl flakyness reconfigureWindowImpl(x, y, width, height, false, 0, 0); display.dispatchMessagesNative(); // status up2date WindowImpl.this.waitForVisible(true, false); display.dispatchMessagesNative(); // status up2date } } } if(ok) { if(wasVisible) { requestFocusImpl(true); display.dispatchMessagesNative(); // status up2date } } else { // native reparent failed -> try creation if(DEBUG_IMPLEMENTATION) { System.err.println("Window.reparent: native reparenting failed ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)+" -> "+toHexString(newParentWindowHandle)+" - Trying recreation"); } destroy(); reparentAction = ACTION_NATIVE_CREATION ; } } // write back mirrored values, ensuring persitence // and not relying on native messaging WindowImpl.this.x = x; WindowImpl.this.y = y; WindowImpl.this.width = width; WindowImpl.this.height = height; if(DEBUG_IMPLEMENTATION) { System.err.println("Window.reparentWindow: END ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+ Display.hashCodeNullSafe(parentWindow)+" "+x+"/"+y+" "+width+"x"+height); } } finally { windowLock.unlock(); } if(wasVisible) { switch (reparentAction) { case ACTION_NATIVE_REPARENTING: // trigger a resize/relayout and repaint to listener sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); break; case ACTION_NATIVE_CREATION: // This may run on the new Display/Screen connection, hence a new EDT task runOnEDTIfAvail(true, reparentActionRecreate); break; } } } } private class ReparentActionRecreate implements Runnable { public final void run() { windowLock.lock(); try { visible = true; if(DEBUG_IMPLEMENTATION) { System.err.println("Window.reparentWindow: ReparentActionRecreate ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+Display.hashCodeNullSafe(parentWindow)); } setVisible(true); // native creation } finally { windowLock.unlock(); } } } public final int reparentWindow(NativeWindow newParent) { return reparentWindow(newParent, false); } public int reparentWindow(NativeWindow newParent, boolean forceDestroyCreate) { int reparentActionStrategy = ReparentAction.ACTION_INVALID; if(isValid()) { ReparentActionImpl reparentAction = new ReparentActionImpl(newParent, forceDestroyCreate); runOnEDTIfAvail(true, reparentAction); reparentActionStrategy = reparentAction.getStrategy(); } return reparentActionStrategy; } public CapabilitiesChooser setCapabilitiesChooser(CapabilitiesChooser chooser) { CapabilitiesChooser old = this.capabilitiesChooser; this.capabilitiesChooser = chooser; return old; } public final CapabilitiesImmutable getChosenCapabilities() { return config.getNativeGraphicsConfiguration().getChosenCapabilities(); } public final CapabilitiesImmutable getRequestedCapabilities() { return capsRequested; } public String getTitle() { return title; } public void setTitle(String title) { if (title == null) { title = ""; } this.title = title; if(0 != getWindowHandle()) { setTitleImpl(title); } } private class DecorationActionImpl implements Runnable { boolean undecorated; private DecorationActionImpl(boolean undecorated) { this.undecorated = undecorated; } public final void run() { windowLock.lock(); try { if(!fullscreen && isNativeValid() && WindowImpl.this.undecorated != undecorated) { WindowImpl.this.undecorated = undecorated; // mirror pos/size so native change notification can get overwritten int x = WindowImpl.this.x; int y = WindowImpl.this.y; int width = WindowImpl.this.width; int height = WindowImpl.this.height; if( 0 != windowHandle ) { DisplayImpl display = (DisplayImpl) screen.getDisplay(); display.dispatchMessagesNative(); // status up2date boolean wasVisible = isVisible(); setVisibleImpl(false, x, y, width, height); WindowImpl.this.waitForVisible(false, true); display.dispatchMessagesNative(); // status up2date reconfigureWindowImpl(x, y, width, height, false, 0, undecorated?-1:1); display.dispatchMessagesNative(); // status up2date if(wasVisible) { setVisibleImpl(true, x, y, width, height); WindowImpl.this.waitForVisible(true, true); display.dispatchMessagesNative(); // status up2date if( WindowImpl.this.x != x || WindowImpl.this.y != y || WindowImpl.this.width != width || WindowImpl.this.height != height ) { // reset pos/size .. due to some native impl flakyness reconfigureWindowImpl(x, y, width, height, false, 0, 0); display.dispatchMessagesNative(); // status up2date } requestFocusImpl(true); display.dispatchMessagesNative(); // status up2date } } } } finally { windowLock.unlock(); } sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener } } public void setUndecorated(boolean value) { if(isValid()) { runOnEDTIfAvail(true, new DecorationActionImpl(value)); } } public boolean isUndecorated() { return 0 != parentWindowHandle || undecorated || fullscreen ; } public void requestFocus() { enqueueRequestFocus(true); } public boolean hasFocus() { return hasFocus; } public Insets getInsets() { return new Insets(0,0,0,0); } public final int getWidth() { return width; } public final int getHeight() { return height; } public final int getX() { return x; } public final int getY() { return y; } public final boolean isVisible() { return visible; } public final boolean isFullscreen() { return fullscreen; } //---------------------------------------------------------------------- // Window // /** * If the implementation is capable of detecting a device change * return true and clear the status/reason of the change. */ public boolean hasDeviceChanged() { return false; } public LifecycleHook getLifecycleHook() { return lifecycleHook; } public LifecycleHook setLifecycleHook(LifecycleHook hook) { LifecycleHook old = lifecycleHook; lifecycleHook = hook; return old; } /** If this Window actually wraps one from another toolkit such as the AWT, this will return a non-null value. */ public Object getWrappedWindow() { return null; } /** * If set to true, the default value, this NEWT Window implementation will * handle the destruction (ie {@link #destroy()} call) within {@link #windowDestroyNotify()} implementation.
* If set to false, it's up to the caller/owner to handle destruction within {@link #windowDestroyNotify()}. */ public void setHandleDestroyNotify(boolean b) { handleDestroyNotify = b; } //---------------------------------------------------------------------- // WindowImpl // protected final long getParentWindowHandle() { return parentWindowHandle; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getName()+"[Config "+config+ "\n, "+screen+ "\n, ParentWindow "+parentWindow+ "\n, ParentWindowHandle "+toHexString(parentWindowHandle)+ "\n, WindowHandle "+toHexString(getWindowHandle())+ "\n, SurfaceHandle "+toHexString(getSurfaceHandle())+ " (lockedExt window "+isWindowLockedByOtherThread()+", surface "+isSurfaceLockedByOtherThread()+")"+ "\n, Pos "+getX()+"/"+getY()+", size "+getWidth()+"x"+getHeight()+ "\n, Visible "+isVisible()+ "\n, Undecorated "+undecorated+ "\n, Fullscreen "+fullscreen+ "\n, WrappedWindow "+getWrappedWindow()+ "\n, ChildWindows "+childWindows.size()); sb.append(", SurfaceUpdatedListeners num "+surfaceUpdatedListeners.size()+" ["); for (int i = 0; i < surfaceUpdatedListeners.size(); i++ ) { sb.append(surfaceUpdatedListeners.get(i)+", "); } sb.append("], WindowListeners num "+windowListeners.size()+" ["); for (int i = 0; i < windowListeners.size(); i++ ) { sb.append(windowListeners.get(i)+", "); } sb.append("], MouseListeners num "+mouseListeners.size()+" ["); for (int i = 0; i < mouseListeners.size(); i++ ) { sb.append(mouseListeners.get(i)+", "); } sb.append("], KeyListeners num "+keyListeners.size()+" ["); for (int i = 0; i < keyListeners.size(); i++ ) { sb.append(keyListeners.get(i)+", "); } sb.append("] ]"); return sb.toString(); } protected final void setWindowHandle(long handle) { windowHandle = handle; } public void runOnEDTIfAvail(boolean wait, final Runnable task) { Screen scrn = getScreen(); if(null==scrn) { throw new RuntimeException("Null screen of inner class: "+this); } DisplayImpl d = (DisplayImpl) scrn.getDisplay(); d.runOnEDTIfAvail(wait, task); } private class RequestFocusAction implements Runnable { public final void run() { if(DEBUG_IMPLEMENTATION) { System.err.println("Window.RequestFocusAction: ("+getThreadName()+"): "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); } WindowImpl.this.requestFocusImpl(false); } } protected void enqueueRequestFocus(boolean wait) { runOnEDTIfAvail(wait, requestFocusAction); } /** * May set to a {@link FocusRunnable}, {@link FocusRunnable#run()} before Newt requests the native focus. * This allows notifying a covered window toolkit like AWT that the focus is requested, * hence focus traversal can be made transparent. */ public void setFocusAction(FocusRunnable focusAction) { this.focusAction = focusAction; } protected boolean focusAction() { if(DEBUG_IMPLEMENTATION) { System.err.println("Window.focusAction() START - "+getThreadName()+", focusAction: "+focusAction+" - windowHandle "+toHexString(getWindowHandle())); } boolean res; if(null!=focusAction) { res = focusAction.run(); } else { res = false; } if(DEBUG_IMPLEMENTATION) { System.err.println("Window.focusAction() END - "+getThreadName()+", focusAction: "+focusAction+" - windowHandle "+toHexString(getWindowHandle())+", res: "+res); } return res; } private class SetPositionActionImpl implements Runnable { int x, y; private SetPositionActionImpl(int x, int y) { this.x = x; this.y = y; } public final void run() { windowLock.lock(); try { if(DEBUG_IMPLEMENTATION) { System.err.println("Window setPosition: "+WindowImpl.this.x+"/"+WindowImpl.this.y+" -> "+x+"/"+y+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)); } if ( WindowImpl.this.x != x || WindowImpl.this.y != y ) { if(!fullscreen) { if(0!=windowHandle) { // this.x/this.y will be set by windowChanged, called by the native implementation reconfigureWindowImpl(x, y, -1, -1, false, 0, 0); } else { WindowImpl.this.x = x; WindowImpl.this.y = y; } } } } finally { windowLock.unlock(); } } } public void setPosition(int x, int y) { if(isValid()) { runOnEDTIfAvail(true, new SetPositionActionImpl(x, y)); } } private class FullScreenActionImpl implements Runnable { boolean fullscreen; private FullScreenActionImpl (boolean fullscreen) { this.fullscreen = fullscreen; } public final void run() { windowLock.lock(); try { if(isNativeValid() && WindowImpl.this.fullscreen != fullscreen) { int x,y,w,h; WindowImpl.this.fullscreen = fullscreen; if(fullscreen) { x = 0; y = 0; w = screen.getWidth(); h = screen.getHeight(); nfs_width = width; nfs_height = height; nfs_x = x; nfs_y = y; } else { x = nfs_x; y = nfs_y; w = nfs_width; h = nfs_height; } if(DEBUG_IMPLEMENTATION || DEBUG_WINDOW_EVENT) { System.err.println("Window fs: "+fullscreen+" "+x+"/"+y+" "+w+"x"+h+", "+isUndecorated()+", "+screen); } DisplayImpl display = (DisplayImpl) screen.getDisplay(); display.dispatchMessagesNative(); // status up2date boolean wasVisible = isVisible(); setVisibleImpl(false, x, y, width, height); WindowImpl.this.waitForVisible(false, true); display.dispatchMessagesNative(); // status up2date // write back mirrored values, to be able to detect satisfaction WindowImpl.this.x = x; WindowImpl.this.y = y; WindowImpl.this.width = w; WindowImpl.this.height = h; reconfigureWindowImpl(x, y, w, h, getParentWindowHandle()!=0, fullscreen?1:-1, isUndecorated()?-1:1); display.dispatchMessagesNative(); // status up2date if(wasVisible) { setVisibleImpl(true, x, y, width, height); boolean ok = WindowImpl.this.waitForVisible(true, true, Screen.SCREEN_MODE_CHANGE_TIMEOUT); display.dispatchMessagesNative(); // status up2date if( ok && ( WindowImpl.this.x != x || WindowImpl.this.y != y || WindowImpl.this.width != w || WindowImpl.this.height != h ) ) { if(DEBUG_IMPLEMENTATION || DEBUG_WINDOW_EVENT) { System.err.println("Window fs (reconfig): "+x+"/"+y+" "+w+"x"+h+", "+screen); } // reset pos/size .. due to some native impl flakyness reconfigureWindowImpl(x, y, width, height, false, 0, 0); display.dispatchMessagesNative(); // status up2date } requestFocusImpl(true); display.dispatchMessagesNative(); // status up2date if(DEBUG_IMPLEMENTATION || DEBUG_WINDOW_EVENT) { System.err.println("Window fs done"); } } } } finally { windowLock.unlock(); } sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener } } public boolean setFullscreen(boolean fullscreen) { if(isValid()) { runOnEDTIfAvail(true, new FullScreenActionImpl(fullscreen)); } return this.fullscreen; } private class ScreenModeListenerImpl implements ScreenModeListener { boolean animatorPaused = false; public void screenModeChangeNotify(ScreenMode sm) { if(DEBUG_IMPLEMENTATION || DEBUG_WINDOW_EVENT) { System.err.println("Window.screenModeChangeNotify: "+sm); } if(null!=lifecycleHook) { animatorPaused = lifecycleHook.pauseRenderingAction(); } } public void screenModeChanged(ScreenMode sm, boolean success) { if(DEBUG_IMPLEMENTATION || DEBUG_WINDOW_EVENT) { System.err.println("Window.screenModeChanged: "+sm+", success: "+success); } if(success) { DimensionReadOnly screenSize = sm.getMonitorMode().getSurfaceSize().getResolution(); if ( getHeight() > screenSize.getHeight() || getWidth() > screenSize.getWidth() ) { setSize(screenSize.getWidth(), screenSize.getHeight()); } } if(animatorPaused) { lifecycleHook.resumeRenderingAction(); } sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener } } //---------------------------------------------------------------------- // Child Window Management // public final void removeChild(NativeWindow win) { synchronized(childWindowsLock) { childWindows.remove(win); } } public final void addChild(NativeWindow win) { if (win == null) { return; } synchronized(childWindowsLock) { childWindows.add(win); } } //---------------------------------------------------------------------- // Generic Event Support // private void doEvent(boolean enqueue, boolean wait, com.jogamp.newt.event.NEWTEvent event) { boolean done = false; if(!enqueue) { done = consumeEvent(event); wait = done; // don't wait if event can't be consumed now } if(!done) { enqueueEvent(wait, event); } } public void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event) { if(isValid()) { ((DisplayImpl)getScreen().getDisplay()).enqueueEvent(wait, event); } } public boolean consumeEvent(NEWTEvent e) { switch(e.getEventType()) { // special repaint treatment case WindowEvent.EVENT_WINDOW_REPAINT: // queue repaint event in case window is locked, ie in operation if( isWindowLocked() ) { // make sure only one repaint event is queued if(!repaintQueued) { repaintQueued=true; if(DEBUG_IMPLEMENTATION) { System.err.println("Window.consumeEvent: queued "+e); // Exception ee = new Exception("Window.windowRepaint: "+e); // ee.printStackTrace(); } return false; } return true; } repaintQueued=false; // no repaint event queued break; // common treatment case WindowEvent.EVENT_WINDOW_RESIZED: // queue event in case window is locked, ie in operation if( isWindowLocked() ) { if(DEBUG_IMPLEMENTATION) { System.err.println("Window.consumeEvent: queued "+e); // Exception ee = new Exception("Window.windowRepaint: "+e); // ee.printStackTrace(); } return false; } break; default: break; } if(e instanceof WindowEvent) { consumeWindowEvent((WindowEvent)e); } else if(e instanceof KeyEvent) { consumeKeyEvent((KeyEvent)e); } else if(e instanceof MouseEvent) { consumeMouseEvent((MouseEvent)e); } else { throw new NativeWindowException("Unexpected NEWTEvent type " + e); } return true; } // // SurfaceUpdatedListener Support // public void addSurfaceUpdatedListener(SurfaceUpdatedListener l) { addSurfaceUpdatedListener(-1, l); } public void addSurfaceUpdatedListener(int index, SurfaceUpdatedListener l) throws IndexOutOfBoundsException { if(l == null) { return; } synchronized(surfaceUpdatedListenersLock) { if(0>index) { index = surfaceUpdatedListeners.size(); } surfaceUpdatedListeners.add(index, l); } } public void removeSurfaceUpdatedListener(SurfaceUpdatedListener l) { if (l == null) { return; } synchronized(surfaceUpdatedListenersLock) { surfaceUpdatedListeners.remove(l); } } public void removeAllSurfaceUpdatedListener() { synchronized(surfaceUpdatedListenersLock) { surfaceUpdatedListeners = new ArrayList(); } } public SurfaceUpdatedListener getSurfaceUpdatedListener(int index) { synchronized(surfaceUpdatedListenersLock) { if(0>index) { index = surfaceUpdatedListeners.size()-1; } return (SurfaceUpdatedListener) surfaceUpdatedListeners.get(index); } } public SurfaceUpdatedListener[] getSurfaceUpdatedListeners() { synchronized(surfaceUpdatedListenersLock) { return (SurfaceUpdatedListener[]) surfaceUpdatedListeners.toArray(); } } public void surfaceUpdated(Object updater, NativeSurface ns, long when) { synchronized(surfaceUpdatedListenersLock) { for(int i = 0; i < surfaceUpdatedListeners.size(); i++ ) { SurfaceUpdatedListener l = (SurfaceUpdatedListener) surfaceUpdatedListeners.get(i); l.surfaceUpdated(updater, ns, when); } } } // // MouseListener/Event Support // public void sendMouseEvent(int eventType, int modifiers, int x, int y, int button, int rotation) { doMouseEvent(false, false, eventType, modifiers, x, y, button, rotation); } public void enqueueMouseEvent(boolean wait, int eventType, int modifiers, int x, int y, int button, int rotation) { doMouseEvent(true, wait, eventType, modifiers, x, y, button, rotation); } private void doMouseEvent(boolean enqueue, boolean wait, int eventType, int modifiers, int x, int y, int button, int rotation) { if(x<0||y<0||x>=width||y>=height) { return; // .. invalid .. } if(DEBUG_MOUSE_EVENT) { System.err.println("doMouseEvent: enqueue"+enqueue+", wait "+wait+", "+MouseEvent.getEventTypeString(eventType)+ ", mod "+modifiers+", pos "+x+"/"+y+", button "+button); } if(button<0||button>MouseEvent.BUTTON_NUMBER) { throw new NativeWindowException("Invalid mouse button number" + button); } long when = System.currentTimeMillis(); MouseEvent eClicked = null; MouseEvent e = null; if(MouseEvent.EVENT_MOUSE_PRESSED==eventType) { if(when-lastMousePressed0) { e = new MouseEvent(MouseEvent.EVENT_MOUSE_DRAGGED, this, when, modifiers, x, y, 1, mouseButtonPressed, 0); } else { e = new MouseEvent(eventType, this, when, modifiers, x, y, 0, button, 0); } } else if(MouseEvent.EVENT_MOUSE_WHEEL_MOVED==eventType) { e = new MouseEvent(eventType, this, when, modifiers, x, y, 0, button, rotation); } else { e = new MouseEvent(eventType, this, when, modifiers, x, y, 0, button, 0); } doEvent(enqueue, wait, e); if(null!=eClicked) { if(DEBUG_MOUSE_EVENT) { System.err.println("doMouseEvent: synthesized MOUSE_CLICKED event"); } doEvent(enqueue, wait, eClicked); } } public void addMouseListener(MouseListener l) { addMouseListener(-1, l); } public void addMouseListener(int index, MouseListener l) { if(l == null) { return; } ArrayList clonedListeners = (ArrayList) mouseListeners.clone(); if(0>index) { index = clonedListeners.size(); } clonedListeners.add(index, l); mouseListeners = clonedListeners; } public void removeMouseListener(MouseListener l) { if (l == null) { return; } ArrayList clonedListeners = (ArrayList) mouseListeners.clone(); clonedListeners.remove(l); mouseListeners = clonedListeners; } public MouseListener getMouseListener(int index) { ArrayList clonedListeners = (ArrayList) mouseListeners.clone(); if(0>index) { index = clonedListeners.size()-1; } return (MouseListener) clonedListeners.get(index); } public MouseListener[] getMouseListeners() { return (MouseListener[]) mouseListeners.toArray(); } protected void consumeMouseEvent(MouseEvent e) { if(DEBUG_MOUSE_EVENT) { System.err.println("consumeMouseEvent: event: "+e); } for(int i = 0; i < mouseListeners.size(); i++ ) { MouseListener l = (MouseListener) mouseListeners.get(i); switch(e.getEventType()) { case MouseEvent.EVENT_MOUSE_CLICKED: l.mouseClicked(e); break; case MouseEvent.EVENT_MOUSE_ENTERED: l.mouseEntered(e); break; case MouseEvent.EVENT_MOUSE_EXITED: l.mouseExited(e); break; case MouseEvent.EVENT_MOUSE_PRESSED: l.mousePressed(e); break; case MouseEvent.EVENT_MOUSE_RELEASED: l.mouseReleased(e); break; case MouseEvent.EVENT_MOUSE_MOVED: l.mouseMoved(e); break; case MouseEvent.EVENT_MOUSE_DRAGGED: l.mouseDragged(e); break; case MouseEvent.EVENT_MOUSE_WHEEL_MOVED: l.mouseWheelMoved(e); break; default: throw new NativeWindowException("Unexpected mouse event type " + e.getEventType()); } } } // // KeyListener/Event Support // public void sendKeyEvent(int eventType, int modifiers, int keyCode, char keyChar) { consumeKeyEvent(new KeyEvent(eventType, this, System.currentTimeMillis(), modifiers, keyCode, keyChar) ); } public void enqueueKeyEvent(boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { enqueueEvent(wait, new KeyEvent(eventType, this, System.currentTimeMillis(), modifiers, keyCode, keyChar) ); } public void addKeyListener(KeyListener l) { addKeyListener(-1, l); } public void addKeyListener(int index, KeyListener l) { if(l == null) { return; } ArrayList clonedListeners = (ArrayList) keyListeners.clone(); if(0>index) { index = clonedListeners.size(); } clonedListeners.add(index, l); keyListeners = clonedListeners; } public void removeKeyListener(KeyListener l) { if (l == null) { return; } ArrayList clonedListeners = (ArrayList) keyListeners.clone(); clonedListeners.remove(l); keyListeners = clonedListeners; } public KeyListener getKeyListener(int index) { ArrayList clonedListeners = (ArrayList) keyListeners.clone(); if(0>index) { index = clonedListeners.size()-1; } return (KeyListener) clonedListeners.get(index); } public KeyListener[] getKeyListeners() { return (KeyListener[]) keyListeners.toArray(); } protected void consumeKeyEvent(KeyEvent e) { if(DEBUG_KEY_EVENT) { System.err.println("consumeKeyEvent: "+e); } for(int i = 0; i < keyListeners.size(); i++ ) { KeyListener l = (KeyListener) keyListeners.get(i); switch(e.getEventType()) { case KeyEvent.EVENT_KEY_PRESSED: l.keyPressed(e); break; case KeyEvent.EVENT_KEY_RELEASED: l.keyReleased(e); break; case KeyEvent.EVENT_KEY_TYPED: l.keyTyped(e); break; default: throw new NativeWindowException("Unexpected key event type " + e.getEventType()); } } } // // WindowListener/Event Support // public void sendWindowEvent(int eventType) { consumeWindowEvent( new WindowEvent(eventType, this, System.currentTimeMillis()) ); } public void enqueueWindowEvent(boolean wait, int eventType) { enqueueEvent( wait, new WindowEvent(eventType, this, System.currentTimeMillis()) ); } public void addWindowListener(WindowListener l) { addWindowListener(-1, l); } public void addWindowListener(int index, WindowListener l) throws IndexOutOfBoundsException { if(l == null) { return; } ArrayList clonedListeners = (ArrayList) windowListeners.clone(); if(0>index) { index = clonedListeners.size(); } clonedListeners.add(index, l); windowListeners = clonedListeners; } public final void removeWindowListener(WindowListener l) { if (l == null) { return; } ArrayList clonedListeners = (ArrayList) windowListeners.clone(); clonedListeners.remove(l); windowListeners = clonedListeners; } public WindowListener getWindowListener(int index) { ArrayList clonedListeners = (ArrayList) windowListeners.clone(); if(0>index) { index = clonedListeners.size()-1; } return (WindowListener) clonedListeners.get(index); } public WindowListener[] getWindowListeners() { return (WindowListener[]) windowListeners.toArray(); } protected void consumeWindowEvent(WindowEvent e) { if(DEBUG_WINDOW_EVENT) { System.err.println("consumeWindowEvent: "+e+", visible "+isVisible()+" "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()); } for(int i = 0; i < windowListeners.size(); i++ ) { WindowListener l = (WindowListener) windowListeners.get(i); switch(e.getEventType()) { case WindowEvent.EVENT_WINDOW_RESIZED: l.windowResized(e); break; case WindowEvent.EVENT_WINDOW_MOVED: l.windowMoved(e); break; case WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY: l.windowDestroyNotify(e); break; case WindowEvent.EVENT_WINDOW_DESTROYED: l.windowDestroyed(e); break; case WindowEvent.EVENT_WINDOW_GAINED_FOCUS: l.windowGainedFocus(e); break; case WindowEvent.EVENT_WINDOW_LOST_FOCUS: l.windowLostFocus(e); break; case WindowEvent.EVENT_WINDOW_REPAINT: l.windowRepaint((WindowUpdateEvent)e); break; default: throw new NativeWindowException("Unexpected window event type " + e.getEventType()); } } } /** * @param focusGained */ protected void focusChanged(boolean focusGained) { if(DEBUG_IMPLEMENTATION) { System.err.println("Window.focusChanged: ("+getThreadName()+"): "+this.hasFocus+" -> "+focusGained+" - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); } hasFocus = focusGained; if (focusGained) { sendWindowEvent(WindowEvent.EVENT_WINDOW_GAINED_FOCUS); } else { sendWindowEvent(WindowEvent.EVENT_WINDOW_LOST_FOCUS); } } protected void visibleChanged(boolean visible) { if(DEBUG_IMPLEMENTATION) { System.err.println("Window.visibleChanged ("+getThreadName()+"): "+this.visible+" -> "+visible+" - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); } this.visible = visible ; } private boolean waitForVisible(boolean visible, boolean failFast) { return waitForVisible(visible, failFast, TIMEOUT_NATIVEWINDOW); } private boolean waitForVisible(boolean visible, boolean failFast, long timeOut) { DisplayImpl display = (DisplayImpl) screen.getDisplay(); for(long sleep = timeOut; 0 "+newWidth+"x"+newHeight+" - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); } width = newWidth; height = newHeight; if(isNativeValid()) { sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); } } } protected void positionChanged(int newX, int newY) { if( 0==parentWindowHandle && ( x != newX || y != newY ) ) { if(DEBUG_IMPLEMENTATION) { System.err.println("Window.positionChanged: ("+getThreadName()+"): "+x+"/"+y+" -> "+newX+"/"+newY+" - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); } x = newX; y = newY; sendWindowEvent(WindowEvent.EVENT_WINDOW_MOVED); } } protected void windowDestroyNotify() { if(DEBUG_IMPLEMENTATION) { System.err.println("Window.windowDestroyNotify START "+getThreadName()); } // send synced destroy notifications enqueueWindowEvent(true, WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY); if(handleDestroyNotify && DISPOSE_ON_CLOSE == defaultCloseOperation && isValid()) { destroy(); } if(DEBUG_IMPLEMENTATION) { System.err.println("Window.windowDestroyeNotify END "+getThreadName()); } } public void windowRepaint(int x, int y, int width, int height) { if(DEBUG_IMPLEMENTATION) { System.err.println("Window.windowRepaint "+getThreadName()+" - "+x+"/"+y+" "+width+"x"+height); // Exception ee = new Exception("Window.windowRepaint: "+" - "+x+"/"+y+" "+width+"x"+height); // ee.printStackTrace(); } if(isNativeValid()) { if(0>width) { width=this.width; } if(0>height) { height=this.height; } NEWTEvent e = new WindowUpdateEvent(WindowEvent.EVENT_WINDOW_REPAINT, this, System.currentTimeMillis(), new Rectangle(x, y, width, height)); doEvent(false, false, e); } } // // Reflection helper .. // private static Class[] getCustomConstructorArgumentTypes(Class windowClass) { Class[] argTypes = null; try { Method m = windowClass.getDeclaredMethod("getCustomConstructorArgumentTypes", new Class[] {}); argTypes = (Class[]) m.invoke(null, (Object[])null); } catch (Throwable t) {} return argTypes; } private static int verifyConstructorArgumentTypes(Class[] types, Object[] args) { if(types.length != args.length) { return -1; } for(int i=0; i