Jogamp
Bug 741 HiDPI: Add ScalableSurface interface to get/set pixelScale w/ full OSX impl.
[jogl.git] / src / newt / classes / jogamp / newt / WindowImpl.java
1 /*
2  * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved.
3    Copyright (c) 2010 JogAmp Community. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * - Redistribution of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * - Redistribution in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of Sun Microsystems, Inc. or the names of
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * This software is provided "AS IS," without a warranty of any kind. ALL
21  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
22  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
23  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
24  * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
25  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
26  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
27  * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
28  * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
29  * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
30  * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
31  * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
32  *
33  */
34
35 package jogamp.newt;
36
37 import java.lang.ref.WeakReference;
38 import java.lang.reflect.Method;
39 import java.util.ArrayList;
40 import java.util.List;
41
42 import javax.media.nativewindow.AbstractGraphicsConfiguration;
43 import javax.media.nativewindow.AbstractGraphicsDevice;
44 import javax.media.nativewindow.CapabilitiesChooser;
45 import javax.media.nativewindow.CapabilitiesImmutable;
46 import javax.media.nativewindow.NativeSurface;
47 import javax.media.nativewindow.NativeWindow;
48 import javax.media.nativewindow.NativeWindowException;
49 import javax.media.nativewindow.NativeWindowFactory;
50 import javax.media.nativewindow.OffscreenLayerSurface;
51 import javax.media.nativewindow.ScalableSurface;
52 import javax.media.nativewindow.SurfaceUpdatedListener;
53 import javax.media.nativewindow.WindowClosingProtocol;
54 import javax.media.nativewindow.util.DimensionImmutable;
55 import javax.media.nativewindow.util.Insets;
56 import javax.media.nativewindow.util.InsetsImmutable;
57 import javax.media.nativewindow.util.PixelRectangle;
58 import javax.media.nativewindow.util.Point;
59 import javax.media.nativewindow.util.PointImmutable;
60 import javax.media.nativewindow.util.Rectangle;
61 import javax.media.nativewindow.util.RectangleImmutable;
62
63 import jogamp.nativewindow.SurfaceScaleUtils;
64 import jogamp.nativewindow.SurfaceUpdatedHelper;
65
66 import com.jogamp.common.util.ArrayHashSet;
67 import com.jogamp.common.util.IntBitfield;
68 import com.jogamp.common.util.ReflectionUtil;
69 import com.jogamp.common.util.locks.LockFactory;
70 import com.jogamp.common.util.locks.RecursiveLock;
71 import com.jogamp.newt.Display;
72 import com.jogamp.newt.Display.PointerIcon;
73 import com.jogamp.newt.MonitorDevice;
74 import com.jogamp.newt.NewtFactory;
75 import com.jogamp.newt.Screen;
76 import com.jogamp.newt.Window;
77 import com.jogamp.newt.event.DoubleTapScrollGesture;
78 import com.jogamp.newt.event.GestureHandler;
79 import com.jogamp.newt.event.InputEvent;
80 import com.jogamp.newt.event.KeyEvent;
81 import com.jogamp.newt.event.KeyListener;
82 import com.jogamp.newt.event.MonitorEvent;
83 import com.jogamp.newt.event.MonitorModeListener;
84 import com.jogamp.newt.event.MouseEvent;
85 import com.jogamp.newt.event.MouseEvent.PointerType;
86 import com.jogamp.newt.event.MouseListener;
87 import com.jogamp.newt.event.NEWTEvent;
88 import com.jogamp.newt.event.NEWTEventConsumer;
89 import com.jogamp.newt.event.WindowEvent;
90 import com.jogamp.newt.event.WindowListener;
91 import com.jogamp.newt.event.WindowUpdateEvent;
92
93 public abstract class WindowImpl implements Window, NEWTEventConsumer
94 {
95     public static final boolean DEBUG_TEST_REPARENT_INCOMPATIBLE;
96
97     static {
98         Debug.initSingleton();
99         DEBUG_TEST_REPARENT_INCOMPATIBLE = Debug.isPropertyDefined("newt.test.Window.reparent.incompatible", true);
100
101         ScreenImpl.initSingleton();
102     }
103
104     protected static final ArrayList<WeakReference<WindowImpl>> windowList = new ArrayList<WeakReference<WindowImpl>>();
105
106     /** Maybe utilized at a shutdown hook, impl. does not block. */
107     public static final void shutdownAll() {
108         final int wCount = windowList.size();
109         if(DEBUG_IMPLEMENTATION) {
110             System.err.println("Window.shutdownAll "+wCount+" instances, on thread "+getThreadName());
111         }
112         for(int i=0; i<wCount && windowList.size()>0; i++) { // be safe ..
113             final WindowImpl w = windowList.remove(0).get();
114             if(DEBUG_IMPLEMENTATION) {
115                 final long wh = null != w ? w.getWindowHandle() : 0;
116                 System.err.println("Window.shutdownAll["+(i+1)+"/"+wCount+"]: "+toHexString(wh)+", GCed "+(null==w));
117             }
118             if( null != w ) {
119                 w.shutdown();
120             }
121         }
122     }
123     private static void addWindow2List(WindowImpl window) {
124         synchronized(windowList) {
125             // GC before add
126             int i=0, gced=0;
127             while( i < windowList.size() ) {
128                 if( null == windowList.get(i).get() ) {
129                     gced++;
130                     windowList.remove(i);
131                 } else {
132                     i++;
133                 }
134             }
135             windowList.add(new WeakReference<WindowImpl>(window));
136             if(DEBUG_IMPLEMENTATION) {
137                 System.err.println("Window.addWindow2List: GCed "+gced+", size "+windowList.size());
138             }
139         }
140     }
141
142     /** Timeout of queued events (repaint and resize) */
143     static final long QUEUED_EVENT_TO = 1200; // ms
144
145     private static final PointerType[] constMousePointerTypes = new PointerType[] { PointerType.Mouse };
146
147     //
148     // Volatile: Multithread Mutable Access
149     //
150     private volatile long windowHandle = 0; // lifecycle critical
151     private volatile boolean visible = false; // lifecycle critical
152     private volatile boolean hasFocus = false;
153     private volatile int pixWidth = 128, pixHeight = 128; // client-area size w/o insets in pixel units, default: may be overwritten by user
154     private volatile int winWidth = 128, winHeight = 128; // client-area size w/o insets in window units, default: may be overwritten by user
155     protected int[] hasPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
156     protected int[] reqPixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE };
157
158     private volatile int x = 64, y = 64; // client-area pos w/o insets in window units
159     private volatile Insets insets = new Insets(); // insets of decoration (if top-level && decorated)
160     private boolean blockInsetsChange = false; // block insets change (from same thread)
161
162     private final RecursiveLock windowLock = LockFactory.createRecursiveLock();  // Window instance wide lock
163     private int surfaceLockCount = 0; // surface lock recursion count
164
165     private ScreenImpl screen; // never null after create - may change reference though (reparent)
166     private boolean screenReferenceAdded = false;
167     private NativeWindow parentWindow = null;
168     private long parentWindowHandle = 0;
169     private AbstractGraphicsConfiguration config = null; // control access due to delegation
170     protected CapabilitiesImmutable capsRequested = null;
171     protected CapabilitiesChooser capabilitiesChooser = null; // default null -> default
172     private boolean fullscreen = false, brokenFocusChange = false;
173     private List<MonitorDevice> fullscreenMonitors = null;
174     private boolean fullscreenUseMainMonitor = true;
175     private boolean autoPosition = true; // default: true (allow WM to choose top-level position, if not set by user)
176
177     private int nfs_width, nfs_height, nfs_x, nfs_y; // non fullscreen client-area size/pos w/o insets
178     private boolean nfs_alwaysOnTop; // non fullscreen alwaysOnTop setting
179     private NativeWindow nfs_parent = null;          // non fullscreen parent, in case explicit reparenting is performed (offscreen)
180     private String title = "Newt Window";
181     private boolean undecorated = false;
182     private boolean alwaysOnTop = false;
183     private PointerIconImpl pointerIcon = null;
184     private boolean pointerVisible = true;
185     private boolean pointerConfined = false;
186     private LifecycleHook lifecycleHook = null;
187
188     private Runnable windowDestroyNotifyAction = null;
189
190     private FocusRunnable focusAction = null;
191     private KeyListener keyboardFocusHandler = null;
192
193     private final SurfaceUpdatedHelper surfaceUpdatedHelper = new SurfaceUpdatedHelper();
194
195     private final Object childWindowsLock = new Object();
196     private final ArrayList<NativeWindow> childWindows = new ArrayList<NativeWindow>();
197
198     private ArrayList<MouseListener> mouseListeners = new ArrayList<MouseListener>();
199
200     /** from event passing: {@link WindowImpl#consumePointerEvent(MouseEvent)}. */
201     private static class PointerState0 {
202         /** Pointer entered window - is inside the window (may be synthetic) */
203         boolean insideSurface = false;
204         /** Mouse EXIT has been sent (only for MOUSE type enter/exit)*/
205         boolean exitSent = false;
206
207         /** last time when a pointer button was pressed */
208         long lastButtonPressTime = 0;
209
210         /** Pointer in dragging mode */
211         boolean dragging = false;
212
213         void clearButton() {
214             lastButtonPressTime = 0;
215         }
216         public String toString() { return "PState0[inside "+insideSurface+", exitSent "+exitSent+", lastPress "+lastButtonPressTime+", dragging "+dragging+"]"; }
217     }
218     private final PointerState0 pState0 = new PointerState0();
219
220     /** from direct input: {@link WindowImpl#doPointerEvent(boolean, boolean, int[], short, int, int, boolean, short[], int[], int[], float[], float, float[], float)}. */
221     private static class PointerState1 extends PointerState0 {
222         /** Current pressed mouse button number */
223         short buttonPressed = (short)0;
224         /** Current pressed mouse button modifier mask */
225         int buttonPressedMask = 0;
226         /** Last mouse button click count */
227         short lastButtonClickCount = (short)0;
228
229         @Override
230         final void clearButton() {
231             super.clearButton();
232             lastButtonClickCount = (short)0;
233             if( !dragging || 0 == buttonPressedMask ) {
234                 buttonPressed = 0;
235                 buttonPressedMask = 0;
236                 dragging = false;
237             }
238         }
239
240         /** Last pointer-move position for 8 touch-down pointers */
241         final Point[] movePositions = new Point[] {
242                 new Point(), new Point(), new Point(), new Point(),
243                 new Point(), new Point(), new Point(), new Point() };
244         final Point getMovePosition(int id) {
245             if( 0 <= id && id < movePositions.length ) {
246                 return movePositions[id];
247             }
248             return null;
249         }
250         public final String toString() { return "PState1[inside "+insideSurface+", exitSent "+exitSent+", lastPress "+lastButtonPressTime+
251                             ", pressed [button "+buttonPressed+", mask "+buttonPressedMask+", dragging "+dragging+", clickCount "+lastButtonClickCount+"]"; }
252     }
253     private final PointerState1 pState1 = new PointerState1();
254
255     /** Pointer names -> pointer ID (consecutive index, starting w/ 0) */
256     private final ArrayHashSet<Integer> pName2pID = new ArrayHashSet<Integer>();
257
258     private boolean defaultGestureHandlerEnabled = true;
259     private DoubleTapScrollGesture gesture2PtrTouchScroll = null;
260     private ArrayList<GestureHandler> pointerGestureHandler = new ArrayList<GestureHandler>();
261
262     private ArrayList<GestureHandler.GestureListener> gestureListeners = new ArrayList<GestureHandler.GestureListener>();
263
264     private ArrayList<KeyListener> keyListeners = new ArrayList<KeyListener>();
265
266     private ArrayList<WindowListener> windowListeners  = new ArrayList<WindowListener>();
267     private boolean repaintQueued = false;
268
269     //
270     // Construction Methods
271     //
272
273     private static Class<?> getWindowClass(String type)
274         throws ClassNotFoundException
275     {
276         final Class<?> windowClass = NewtFactory.getCustomClass(type, "WindowDriver");
277         if(null==windowClass) {
278             throw new ClassNotFoundException("Failed to find NEWT Window Class <"+type+".WindowDriver>");
279         }
280         return windowClass;
281     }
282
283     public static WindowImpl create(NativeWindow parentWindow, long parentWindowHandle, Screen screen, CapabilitiesImmutable caps) {
284         try {
285             Class<?> windowClass;
286             if(caps.isOnscreen()) {
287                 windowClass = getWindowClass(screen.getDisplay().getType());
288             } else {
289                 windowClass = OffscreenWindow.class;
290             }
291             WindowImpl window = (WindowImpl) windowClass.newInstance();
292             window.parentWindow = parentWindow;
293             window.parentWindowHandle = parentWindowHandle;
294             window.screen = (ScreenImpl) screen;
295             window.capsRequested = (CapabilitiesImmutable) caps.cloneMutable();
296             window.instantiationFinished();
297             addWindow2List(window);
298             return window;
299         } catch (Throwable t) {
300             t.printStackTrace();
301             throw new NativeWindowException(t);
302         }
303     }
304
305     public static WindowImpl create(Object[] cstrArguments, Screen screen, CapabilitiesImmutable caps) {
306         try {
307             Class<?> windowClass = getWindowClass(screen.getDisplay().getType());
308             Class<?>[] cstrArgumentTypes = getCustomConstructorArgumentTypes(windowClass);
309             if(null==cstrArgumentTypes) {
310                 throw new NativeWindowException("WindowClass "+windowClass+" doesn't support custom arguments in constructor");
311             }
312             int argsChecked = verifyConstructorArgumentTypes(cstrArgumentTypes, cstrArguments);
313             if ( argsChecked < cstrArguments.length ) {
314                 throw new NativeWindowException("WindowClass "+windowClass+" constructor mismatch at argument #"+argsChecked+"; Constructor: "+getTypeStrList(cstrArgumentTypes)+", arguments: "+getArgsStrList(cstrArguments));
315             }
316             WindowImpl window = (WindowImpl) ReflectionUtil.createInstance( windowClass, cstrArgumentTypes, cstrArguments ) ;
317             window.screen = (ScreenImpl) screen;
318             window.capsRequested = (CapabilitiesImmutable) caps.cloneMutable();
319             window.instantiationFinished();
320             addWindow2List(window);
321             return window;
322         } catch (Throwable t) {
323             throw new NativeWindowException(t);
324         }
325     }
326
327     /** Fast invalidation of instance w/o any blocking function call. */
328     private final void shutdown() {
329         if(null!=lifecycleHook) {
330             lifecycleHook.shutdownRenderingAction();
331         }
332         setWindowHandle(0);
333         visible = false;
334         fullscreen = false;
335         fullscreenMonitors = null;
336         fullscreenUseMainMonitor = true;
337         hasFocus = false;
338         parentWindowHandle = 0;
339     }
340
341     protected final void setGraphicsConfiguration(AbstractGraphicsConfiguration cfg) {
342         config = cfg;
343     }
344
345     public static interface LifecycleHook {
346         /**
347          * Reset of internal state counter, ie totalFrames, etc.
348          * Called from EDT while window is locked.
349          */
350         public abstract void resetCounter();
351
352         /**
353          * Invoked after Window setVisible,
354          * allows allocating resources depending on the native Window.
355          * Called from EDT while window is locked.
356          */
357         void setVisibleActionPost(boolean visible, boolean nativeWindowCreated);
358
359         /**
360          * Notifies the receiver to preserve resources (GL, ..)
361          * for the next destroy*() calls (only), if supported and if <code>value</code> is <code>true</code>, otherwise clears preservation flag.
362          * @param value <code>true</code> to set the one-shot preservation if supported, otherwise clears it.
363          */
364         void preserveGLStateAtDestroy(boolean value);
365
366         /**
367          * Invoked before Window destroy action,
368          * allows releasing of resources depending on the native Window.<br>
369          * Surface not locked yet.<br>
370          * Called not necessarily from EDT.
371          */
372         void destroyActionPreLock();
373
374         /**
375          * Invoked before Window destroy action,
376          * allows releasing of resources depending on the native Window.<br>
377          * Surface locked.<br>
378          * Called from EDT while window is locked.
379          */
380         void destroyActionInLock();
381
382         /**
383          * Invoked for expensive modifications, ie while reparenting and MonitorMode change.<br>
384          * No lock is hold when invoked.<br>
385          *
386          * @return true is paused, otherwise false. If true {@link #resumeRenderingAction()} shall be issued.
387          *
388          * @see #resumeRenderingAction()
389          */
390         boolean pauseRenderingAction();
391
392         /**
393          * Invoked for expensive modifications, ie while reparenting and MonitorMode change.
394          * No lock is hold when invoked.<br>
395          *
396          * @see #pauseRenderingAction()
397          */
398         void resumeRenderingAction();
399
400         /**
401          * Shutdown rendering action (thread) abnormally.
402          * <p>
403          * Should be called only at shutdown, if necessary.
404          * </p>
405          */
406         void shutdownRenderingAction();
407     }
408
409     private boolean createNative() {
410         long tStart;
411         if(DEBUG_IMPLEMENTATION) {
412             tStart = System.nanoTime();
413             System.err.println("Window.createNative() START ("+getThreadName()+", "+this+")");
414         } else {
415             tStart = 0;
416         }
417
418         if( null != parentWindow &&
419             NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindow.lockSurface() ) {
420             throw new NativeWindowException("Parent surface lock: not ready: "+parentWindow);
421         }
422
423         final boolean hasParent = null != parentWindow || 0 != this.parentWindowHandle;
424
425         // child window: position defaults to 0/0, no auto position, no negative position
426         if( hasParent && ( autoPosition || 0>getX() || 0>getY() ) ) {
427             definePosition(0, 0);
428         }
429         boolean postParentlockFocus = false;
430         try {
431             if(validateParentWindowHandle()) {
432                 if( !screenReferenceAdded ) {
433                     screen.addReference();
434                     screenReferenceAdded = true;
435                 }
436                 if(canCreateNativeImpl()) {
437                     final int wX, wY;
438                     final boolean usePosition;
439                     if( autoPosition  ) {
440                         wX = 0;
441                         wY = 0;
442                         usePosition = false;
443                     } else {
444                         wX = getX();
445                         wY = getY();
446                         usePosition = true;
447                     }
448                     final long t0 = System.currentTimeMillis();
449                     createNativeImpl();
450                     screen.addMonitorModeListener(monitorModeListenerImpl);
451                     setTitleImpl(title);
452                     setPointerIconIntern(pointerIcon);
453                     setPointerVisibleIntern(pointerVisible);
454                     confinePointerImpl(pointerConfined);
455                     setKeyboardVisible(keyboardVisible);
456                     final long remainingV = waitForVisible(true, false);
457                     if( 0 <= remainingV ) {
458                         if(isFullscreen()) {
459                             synchronized(fullScreenAction) {
460                                 fullscreen = false; // trigger a state change
461                                 fullScreenAction.init(true);
462                                 fullScreenAction.run();
463                             }
464                         } else if ( !hasParent ) {
465                             // Wait until position is reached within tolerances, either auto-position or custom position.
466                             waitForPosition(usePosition, wX, wY, Window.TIMEOUT_NATIVEWINDOW);
467                         }
468                         if (DEBUG_IMPLEMENTATION) {
469                             System.err.println("Window.createNative(): elapsed "+(System.currentTimeMillis()-t0)+" ms");
470                         }
471                         postParentlockFocus = true;
472                     }
473                 }
474             }
475         } finally {
476             if(null!=parentWindow) {
477                 parentWindow.unlockSurface();
478             }
479         }
480         if(postParentlockFocus) {
481             // harmonize focus behavior for all platforms: focus on creation
482             requestFocusInt(isFullscreen() /* skipFocusAction if fullscreen */);
483             ((DisplayImpl) screen.getDisplay()).dispatchMessagesNative(); // status up2date
484         }
485         if(DEBUG_IMPLEMENTATION) {
486             System.err.println("Window.createNative() END ("+getThreadName()+", "+this+") total "+ (System.nanoTime()-tStart)/1e6 +"ms");
487         }
488         return isNativeValid() ;
489     }
490
491     private void removeScreenReference() {
492         if(screenReferenceAdded) {
493             // be nice, probably already called recursive via
494             //   closeAndInvalidate() -> closeNativeIml() -> .. -> windowDestroyed() -> closeAndInvalidate() !
495             // or via reparentWindow .. etc
496             screenReferenceAdded = false;
497             screen.removeReference();
498         }
499     }
500
501     private boolean validateParentWindowHandle() {
502         if(null!=parentWindow) {
503             parentWindowHandle = getNativeWindowHandle(parentWindow);
504             return 0 != parentWindowHandle ;
505         }
506         return true;
507     }
508
509     private static long getNativeWindowHandle(NativeWindow nativeWindow) {
510         long handle = 0;
511         if(null!=nativeWindow) {
512             boolean wasLocked = false;
513             if( NativeSurface.LOCK_SURFACE_NOT_READY < nativeWindow.lockSurface() ) {
514                 wasLocked = true;
515                 try {
516                     handle = nativeWindow.getWindowHandle();
517                     if(0==handle) {
518                         throw new NativeWindowException("Parent native window handle is NULL, after succesful locking: "+nativeWindow);
519                     }
520                 } catch (NativeWindowException nwe) {
521                     if(DEBUG_IMPLEMENTATION) {
522                         System.err.println("Window.getNativeWindowHandle: not successful yet: "+nwe);
523                     }
524                 } finally {
525                     nativeWindow.unlockSurface();
526                 }
527             }
528             if(DEBUG_IMPLEMENTATION) {
529                 System.err.println("Window.getNativeWindowHandle: locked "+wasLocked+", "+nativeWindow);
530             }
531         }
532         return handle;
533     }
534
535
536     //----------------------------------------------------------------------
537     // NativeSurface: Native implementation
538     //
539
540     protected int lockSurfaceImpl() { return LOCK_SUCCESS; }
541
542     protected void unlockSurfaceImpl() { }
543
544     //----------------------------------------------------------------------
545     // WindowClosingProtocol implementation
546     //
547     private final Object closingListenerLock = new Object();
548     private WindowClosingMode defaultCloseOperation = WindowClosingMode.DISPOSE_ON_CLOSE;
549
550     @Override
551     public final WindowClosingMode getDefaultCloseOperation() {
552         synchronized (closingListenerLock) {
553             return defaultCloseOperation;
554         }
555     }
556
557     @Override
558     public final WindowClosingMode setDefaultCloseOperation(WindowClosingMode op) {
559         synchronized (closingListenerLock) {
560             WindowClosingMode _op = defaultCloseOperation;
561             defaultCloseOperation = op;
562             return _op;
563         }
564     }
565
566     //----------------------------------------------------------------------
567     // Window: Native implementation
568     //
569
570     /**
571      * Notifies the driver impl. that the instantiation is finished,
572      * ie. instance created and all fields set.
573      */
574     protected void instantiationFinished() {
575         // nop
576     }
577
578     protected boolean canCreateNativeImpl() {
579         return true; // default: always able to be created
580     }
581
582     /**
583      * The native implementation must set the native windowHandle.<br>
584      *
585      * <p>
586      * The implementation shall respect the states {@link #isAlwaysOnTop()}/{@link #FLAG_IS_ALWAYSONTOP} and
587      * {@link #isUndecorated()}/{@link #FLAG_IS_UNDECORATED}, ie. the created window shall reflect those settings.
588      * </p>
589      *
590      * <p>
591      * The implementation should invoke the referenced java state callbacks
592      * to notify this Java object of state changes.</p>
593      *
594      * @see #windowDestroyNotify(boolean)
595      * @see #focusChanged(boolean, boolean)
596      * @see #visibleChanged(boolean, boolean)
597      * @see #sizeChanged(int,int)
598      * @see #positionChanged(boolean,int, int)
599      * @see #windowDestroyNotify(boolean)
600      */
601     protected abstract void createNativeImpl();
602
603     protected abstract void closeNativeImpl();
604
605     /**
606      * Async request which shall be performed within {@link #TIMEOUT_NATIVEWINDOW}.
607      * <p>
608      * If if <code>force == false</code> the native implementation
609      * may only request focus if not yet owner.</p>
610      * <p>
611      * {@link #focusChanged(boolean, boolean)} should be called
612      * to notify about the focus traversal.
613      * </p>
614      *
615      * @param force if true, bypass {@link #focusChanged(boolean, boolean)} and force focus request
616      */
617     protected abstract void requestFocusImpl(boolean force);
618
619     public static final int FLAG_CHANGE_PARENTING       = 1 <<  0;
620     public static final int FLAG_CHANGE_DECORATION      = 1 <<  1;
621     public static final int FLAG_CHANGE_FULLSCREEN      = 1 <<  2;
622     public static final int FLAG_CHANGE_ALWAYSONTOP     = 1 <<  3;
623     public static final int FLAG_CHANGE_VISIBILITY      = 1 <<  4;
624
625     public static final int FLAG_HAS_PARENT             = 1 <<  8;
626     public static final int FLAG_IS_UNDECORATED         = 1 <<  9;
627     public static final int FLAG_IS_FULLSCREEN          = 1 << 10;
628     public static final int FLAG_IS_FULLSCREEN_SPAN     = 1 << 11;
629     public static final int FLAG_IS_ALWAYSONTOP         = 1 << 12;
630     public static final int FLAG_IS_VISIBLE             = 1 << 13;
631
632     /**
633      * The native implementation should invoke the referenced java state callbacks
634      * to notify this Java object of state changes.
635      *
636      * <p>
637      * Implementations shall set x/y to 0, in case it's negative. This could happen due
638      * to insets and positioning a decorated window to 0/0, which would place the frame
639      * outside of the screen.</p>
640      *
641      * @param x client-area position in window units, or <0 if unchanged
642      * @param y client-area position in window units, or <0 if unchanged
643      * @param width client-area size in window units, or <=0 if unchanged
644      * @param height client-area size in window units, or <=0 if unchanged
645      * @param flags bitfield of change and status flags
646      *
647      * @see #sizeChanged(int,int)
648      * @see #positionChanged(boolean,int, int)
649      */
650     protected abstract boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags);
651
652     /**
653      * Tests whether a single reconfigure flag is supported by implementation.
654      * <p>
655      * Default is all but {@link #FLAG_IS_FULLSCREEN_SPAN}
656      * </p>
657      */
658     protected boolean isReconfigureFlagSupported(int changeFlags) {
659         return 0 == ( changeFlags & FLAG_IS_FULLSCREEN_SPAN );
660     }
661
662     protected int getReconfigureFlags(int changeFlags, boolean visible) {
663         return changeFlags |= ( ( 0 != getParentWindowHandle() ) ? FLAG_HAS_PARENT : 0 ) |
664                               ( isUndecorated() ? FLAG_IS_UNDECORATED : 0 ) |
665                               ( isFullscreen() ? FLAG_IS_FULLSCREEN : 0 ) |
666                               ( isAlwaysOnTop() ? FLAG_IS_ALWAYSONTOP : 0 ) |
667                               ( visible ? FLAG_IS_VISIBLE : 0 ) ;
668     }
669     protected static String getReconfigureFlagsAsString(StringBuilder sb, int flags) {
670         if(null == sb) { sb = new StringBuilder(); }
671         sb.append("[");
672
673         if( 0 != ( FLAG_CHANGE_PARENTING & flags) ) {
674             sb.append("*");
675         }
676         sb.append("PARENT ");
677         sb.append(0 != ( FLAG_HAS_PARENT & flags));
678         sb.append(", ");
679
680         if( 0 != ( FLAG_CHANGE_FULLSCREEN & flags) ) {
681             sb.append("*");
682         }
683         sb.append("FS ");
684         sb.append(0 != ( FLAG_IS_FULLSCREEN & flags));
685         sb.append("[span ");
686         sb.append(0 != ( FLAG_IS_FULLSCREEN_SPAN & flags));
687         sb.append("], ");
688
689         if( 0 != ( FLAG_CHANGE_DECORATION & flags) ) {
690             sb.append("*");
691         }
692         sb.append("UNDECOR ");
693         sb.append(0 != ( FLAG_IS_UNDECORATED & flags));
694         sb.append(", ");
695
696         if( 0 != ( FLAG_CHANGE_ALWAYSONTOP & flags) ) {
697             sb.append("*");
698         }
699         sb.append("ALWAYSONTOP ");
700         sb.append(0 != ( FLAG_IS_ALWAYSONTOP & flags));
701         sb.append(", ");
702
703         if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) {
704             sb.append("*");
705         }
706         sb.append("VISIBLE ");
707         sb.append(0 != ( FLAG_IS_VISIBLE & flags));
708
709         sb.append("]");
710         return sb.toString();
711     }
712
713     protected void setTitleImpl(String title) {}
714
715     /**
716      * Translates the given window client-area coordinates with top-left origin
717      * to screen coordinates in window units.
718      * <p>
719      * Since the position reflects the client area, it does not include the insets.
720      * </p>
721      * <p>
722      * May return <code>null</code>, in which case the caller shall traverse through the NativeWindow tree
723      * as demonstrated in {@link #getLocationOnScreen(javax.media.nativewindow.util.Point)}.
724      * </p>
725      *
726      * @return if not null, the screen location of the given coordinates
727      */
728     protected abstract Point getLocationOnScreenImpl(int x, int y);
729
730     /**
731      * Triggered by user via {@link #getInsets()}.<br>
732      * Implementations may implement this hook to update the insets.<br>
733      * However, they may prefer the event driven path via {@link #insetsChanged(boolean, int, int, int, int)}.
734      *
735      * @see #getInsets()
736      * @see #insetsChanged(boolean, int, int, int, int)
737      */
738     protected abstract void updateInsetsImpl(Insets insets);
739
740     protected boolean setPointerVisibleImpl(boolean pointerVisible) { return false; }
741     protected boolean confinePointerImpl(boolean confine) { return false; }
742     protected void warpPointerImpl(int x, int y) { }
743     protected void setPointerIconImpl(final PointerIconImpl pi) { }
744
745     //----------------------------------------------------------------------
746     // NativeSurface
747     //
748
749     @Override
750     public final int lockSurface() throws NativeWindowException, RuntimeException {
751         final RecursiveLock _wlock = windowLock;
752         _wlock.lock();
753         surfaceLockCount++;
754         int res = ( 1 == surfaceLockCount ) ? LOCK_SURFACE_NOT_READY : LOCK_SUCCESS; // new lock ?
755
756         if ( LOCK_SURFACE_NOT_READY == res ) {
757             try {
758                 if( isNativeValid() ) {
759                     final AbstractGraphicsDevice adevice = getGraphicsConfiguration().getScreen().getDevice();
760                     adevice.lock();
761                     try {
762                         res = lockSurfaceImpl();
763                     } finally {
764                         if (LOCK_SURFACE_NOT_READY >= res) {
765                             adevice.unlock();
766                         }
767                     }
768                 }
769             } finally {
770                 if (LOCK_SURFACE_NOT_READY >= res) {
771                     surfaceLockCount--;
772                     _wlock.unlock();
773                 }
774             }
775         }
776         return res;
777     }
778
779     @Override
780     public final void unlockSurface() {
781         final RecursiveLock _wlock = windowLock;
782         _wlock.validateLocked();
783
784         if ( 1 == surfaceLockCount ) {
785             final AbstractGraphicsDevice adevice = getGraphicsConfiguration().getScreen().getDevice();
786             try {
787                 unlockSurfaceImpl();
788             } finally {
789                 adevice.unlock();
790             }
791         }
792         surfaceLockCount--;
793         _wlock.unlock();
794     }
795
796     @Override
797     public final boolean isSurfaceLockedByOtherThread() {
798         return windowLock.isLockedByOtherThread();
799     }
800
801     @Override
802     public final Thread getSurfaceLockOwner() {
803         return windowLock.getOwner();
804     }
805
806     public final RecursiveLock getLock() {
807         return windowLock;
808     }
809
810     @Override
811     public long getSurfaceHandle() {
812         return windowHandle; // default: return window handle
813     }
814
815     @Override
816     public boolean surfaceSwap() {
817         return false;
818     }
819
820     @Override
821     public final void addSurfaceUpdatedListener(SurfaceUpdatedListener l) {
822         surfaceUpdatedHelper.addSurfaceUpdatedListener(l);
823     }
824
825     @Override
826     public final void addSurfaceUpdatedListener(int index, SurfaceUpdatedListener l) throws IndexOutOfBoundsException {
827         surfaceUpdatedHelper.addSurfaceUpdatedListener(index, l);
828     }
829
830     @Override
831     public final void removeSurfaceUpdatedListener(SurfaceUpdatedListener l) {
832         surfaceUpdatedHelper.removeSurfaceUpdatedListener(l);
833     }
834
835     @Override
836     public final void surfaceUpdated(Object updater, NativeSurface ns, long when) {
837         surfaceUpdatedHelper.surfaceUpdated(updater, ns, when);
838     }
839
840     @Override
841     public final AbstractGraphicsConfiguration getGraphicsConfiguration() {
842         return config.getNativeGraphicsConfiguration();
843     }
844
845     @Override
846     public final long getDisplayHandle() {
847         return config.getNativeGraphicsConfiguration().getScreen().getDevice().getHandle();
848     }
849
850     @Override
851     public final int  getScreenIndex() {
852         return screen.getIndex();
853     }
854
855     //----------------------------------------------------------------------
856     // NativeWindow
857     //
858
859     // public final void destroy() - see below
860
861     @Override
862     public final NativeSurface getNativeSurface() { return this; }
863
864     @Override
865     public final NativeWindow getParent() {
866         return parentWindow;
867     }
868
869     @Override
870     public final long getWindowHandle() {
871         return windowHandle;
872     }
873
874     @Override
875     public Point getLocationOnScreen(Point storage) {
876         if(isNativeValid()) {
877             Point d;
878             final RecursiveLock _lock = windowLock;
879             _lock.lock();
880             try {
881                 d = getLocationOnScreenImpl(0, 0);
882             } finally {
883                 _lock.unlock();
884             }
885             if(null!=d) {
886                 if(null!=storage) {
887                     storage.translate(d.getX(),d.getY());
888                     return storage;
889                 }
890                 return d;
891             }
892             // fall through intended ..
893         }
894
895         if(null!=storage) {
896             storage.translate(getX(),getY());
897         } else {
898             storage = new Point(getX(),getY());
899         }
900         if(null!=parentWindow) {
901             // traverse through parent list ..
902             parentWindow.getLocationOnScreen(storage);
903         }
904         return storage;
905     }
906
907     //----------------------------------------------------------------------
908     // Window
909     //
910
911     @Override
912     public final boolean isNativeValid() {
913         return 0 != windowHandle ;
914     }
915
916     @Override
917     public final Screen getScreen() {
918         return screen;
919     }
920
921     protected void setScreen(ScreenImpl newScreen) { // never null !
922         removeScreenReference();
923         screen = newScreen;
924     }
925
926     @Override
927     public final MonitorDevice getMainMonitor() {
928         return screen.getMainMonitor( getBounds() );
929     }
930
931     /**
932      * @param visible
933      * @param x client-area position in window units, or <0 if unchanged
934      * @param y client-area position in window units, or <0 if unchanged
935      * @param width client-area size in window units, or <=0 if unchanged
936      * @param height client-area size in window units, or <=0 if unchanged
937      */
938     protected final void setVisibleImpl(boolean visible, int x, int y, int width, int height) {
939         reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(FLAG_CHANGE_VISIBILITY, visible));
940     }
941     final void setVisibleActionImpl(boolean visible) {
942         boolean nativeWindowCreated = false;
943         boolean madeVisible = false;
944
945         final RecursiveLock _lock = windowLock;
946         _lock.lock();
947         try {
948             if(!visible && null!=childWindows && childWindows.size()>0) {
949               synchronized(childWindowsLock) {
950                 for(int i = 0; i < childWindows.size(); i++ ) {
951                     NativeWindow nw = childWindows.get(i);
952                     if(nw instanceof WindowImpl) {
953                         ((WindowImpl)nw).setVisible(false);
954                     }
955                 }
956               }
957             }
958             if(!isNativeValid() && visible) {
959                 if( 0<getWidth()*getHeight() ) {
960                     nativeWindowCreated = createNative();
961                     madeVisible = nativeWindowCreated;
962                 }
963                 // always flag visible, allowing a retry ..
964                 WindowImpl.this.visible = true;
965             } else if(WindowImpl.this.visible != visible) {
966                 if(isNativeValid()) {
967                     setVisibleImpl(visible, getX(), getY(), getWidth(), getHeight());
968                     WindowImpl.this.waitForVisible(visible, false);
969                     madeVisible = visible;
970                 } else {
971                     WindowImpl.this.visible = true;
972                 }
973             }
974
975             if(null!=lifecycleHook) {
976                 lifecycleHook.setVisibleActionPost(visible, nativeWindowCreated);
977             }
978
979             if(isNativeValid() && visible && null!=childWindows && childWindows.size()>0) {
980               synchronized(childWindowsLock) {
981                 for(int i = 0; i < childWindows.size(); i++ ) {
982                     NativeWindow nw = childWindows.get(i);
983                     if(nw instanceof WindowImpl) {
984                         ((WindowImpl)nw).setVisible(true);
985                     }
986                 }
987               }
988             }
989             if(DEBUG_IMPLEMENTATION) {
990                 System.err.println("Window setVisible: END ("+getThreadName()+") "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible: "+WindowImpl.this.visible+", nativeWindowCreated: "+nativeWindowCreated+", madeVisible: "+madeVisible);
991             }
992         } finally {
993             if(null!=lifecycleHook) {
994                 lifecycleHook.resetCounter();
995             }
996             _lock.unlock();
997         }
998         if( nativeWindowCreated || madeVisible ) {
999             sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener
1000         }
1001     }
1002     private class VisibleAction implements Runnable {
1003         boolean visible;
1004
1005         private VisibleAction(boolean visible) {
1006             this.visible = visible;
1007         }
1008
1009         @Override
1010         public final void run() {
1011             setVisibleActionImpl(visible);
1012         }
1013     }
1014
1015     @Override
1016     public final void setVisible(boolean wait, boolean visible) {
1017         if(DEBUG_IMPLEMENTATION) {
1018             System.err.println("Window setVisible: START ("+getThreadName()+") "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible: "+this.visible+" -> "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+(null!=parentWindow));
1019         }
1020         runOnEDTIfAvail(wait, new VisibleAction(visible));
1021     }
1022
1023     @Override
1024     public final void setVisible(boolean visible) {
1025         setVisible(true, visible);
1026     }
1027
1028     private class SetSizeAction implements Runnable {
1029         int width, height;
1030         boolean force;
1031
1032         private SetSizeAction(int w, int h, boolean disregardFS) {
1033             this.width = w;
1034             this.height = h;
1035             this.force = disregardFS;
1036         }
1037
1038         @Override
1039         public final void run() {
1040             final RecursiveLock _lock = windowLock;
1041             _lock.lock();
1042             try {
1043                 if ( force || ( !isFullscreen() && ( getWidth() != width || getHeight() != height ) ) ) {
1044                     if(DEBUG_IMPLEMENTATION) {
1045                         System.err.println("Window setSize: START force "+force+", "+getWidth()+"x"+getHeight()+" -> "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible "+visible);
1046                     }
1047                     int visibleAction; // 0 nop, 1 invisible, 2 visible (create)
1048                     if ( visible && isNativeValid() && ( 0 >= width || 0 >= height ) ) {
1049                         visibleAction=1; // invisible
1050                         defineSize(0, 0);
1051                     } else if ( visible && !isNativeValid() && 0 < width && 0 < height ) {
1052                         visibleAction = 2; // visible (create)
1053                         defineSize(width, height);
1054                     } else if ( visible && isNativeValid() ) {
1055                         visibleAction = 0;
1056                         // this width/height will be set by windowChanged, called by the native implementation
1057                         reconfigureWindowImpl(getX(), getY(), width, height, getReconfigureFlags(0, isVisible()));
1058                         WindowImpl.this.waitForSize(width, height, false, TIMEOUT_NATIVEWINDOW);
1059                     } else {
1060                         // invisible or invalid w/ 0 size
1061                         visibleAction = 0;
1062                         defineSize(width, height);
1063                     }
1064                     if(DEBUG_IMPLEMENTATION) {
1065                         System.err.println("Window setSize: END "+getWidth()+"x"+getHeight()+", visibleAction "+visibleAction);
1066                     }
1067                     switch(visibleAction) {
1068                         case 1: setVisibleActionImpl(false); break;
1069                         case 2: setVisibleActionImpl(true); break;
1070                     }
1071                 }
1072             } finally {
1073                 _lock.unlock();
1074             }
1075         }
1076     }
1077
1078     private void setSize(final int width, final int height, final boolean force) {
1079         runOnEDTIfAvail(true, new SetSizeAction(width, height, force));
1080     }
1081     @Override
1082     public final void setSize(final int width, final int height) {
1083         runOnEDTIfAvail(true, new SetSizeAction(width, height, false));
1084     }
1085     @Override
1086     public final void setSurfaceSize(final int pixelWidth, final int pixelHeight) {
1087         // FIXME HiDPI: Shortcut, may need to adjust if we change scaling methodology
1088         setSize(pixelWidth / getPixelScaleX(), pixelHeight / getPixelScaleY());
1089     }
1090     @Override
1091     public final void setTopLevelSize(final int width, final int height) {
1092         setSize(width - getInsets().getTotalWidth(), height - getInsets().getTotalHeight());
1093     }
1094
1095     private class DestroyAction implements Runnable {
1096         @Override
1097         public final void run() {
1098             boolean animatorPaused = false;
1099             if(null!=lifecycleHook) {
1100                 animatorPaused = lifecycleHook.pauseRenderingAction();
1101             }
1102             if(null!=lifecycleHook) {
1103                 lifecycleHook.destroyActionPreLock();
1104             }
1105             final RecursiveLock _lock = windowLock;
1106             _lock.lock();
1107             try {
1108                 if(DEBUG_IMPLEMENTATION) {
1109                     System.err.println("Window DestroyAction() hasScreen "+(null != screen)+", isNativeValid "+isNativeValid()+" - "+getThreadName());
1110                 }
1111
1112                 // send synced destroy-notify notification
1113                 sendWindowEvent(WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY);
1114
1115                 // Childs first ..
1116                 synchronized(childWindowsLock) {
1117                   if(childWindows.size()>0) {
1118                     // avoid ConcurrentModificationException: parent -> child -> parent.removeChild(this)
1119                     @SuppressWarnings("unchecked")
1120                     ArrayList<NativeWindow> clonedChildWindows = (ArrayList<NativeWindow>) childWindows.clone();
1121                     while( clonedChildWindows.size() > 0 ) {
1122                       NativeWindow nw = clonedChildWindows.remove(0);
1123                       if(nw instanceof WindowImpl) {
1124                           ((WindowImpl)nw).windowDestroyNotify(true);
1125                       } else {
1126                           nw.destroy();
1127                       }
1128                     }
1129                   }
1130                 }
1131
1132                 if(null!=lifecycleHook) {
1133                     // send synced destroy notification for proper cleanup, eg GLWindow/OpenGL
1134                     lifecycleHook.destroyActionInLock();
1135                 }
1136
1137                 if( isNativeValid() ) {
1138                     screen.removeMonitorModeListener(monitorModeListenerImpl);
1139                     closeNativeImpl();
1140                     final AbstractGraphicsDevice cfgADevice = config.getScreen().getDevice();
1141                     if( cfgADevice != screen.getDisplay().getGraphicsDevice() ) { // don't pull display's device
1142                         cfgADevice.close(); // ensure a cfg's device is closed
1143                     }
1144                     setGraphicsConfiguration(null);
1145                 }
1146                 removeScreenReference();
1147                 Display dpy = screen.getDisplay();
1148                 if(null != dpy) {
1149                     dpy.validateEDTStopped();
1150                 }
1151
1152                 // send synced destroyed notification
1153                 sendWindowEvent(WindowEvent.EVENT_WINDOW_DESTROYED);
1154
1155                 if(DEBUG_IMPLEMENTATION) {
1156                     System.err.println("Window.destroy() END "+getThreadName()/*+", "+WindowImpl.this*/);
1157                 }
1158             } finally {
1159                 // update states before release window lock
1160                 setWindowHandle(0);
1161                 visible = false;
1162                 fullscreen = false;
1163                 fullscreenMonitors = null;
1164                 fullscreenUseMainMonitor = true;
1165                 hasFocus = false;
1166                 parentWindowHandle = 0;
1167
1168                 _lock.unlock();
1169             }
1170             if(animatorPaused) {
1171                 lifecycleHook.resumeRenderingAction();
1172             }
1173
1174             // these refs shall be kept alive - resurrection via setVisible(true)
1175             /**
1176             if(null!=parentWindow && parentWindow instanceof Window) {
1177                 ((Window)parentWindow).removeChild(WindowImpl.this);
1178             }
1179             childWindows = null;
1180             surfaceUpdatedListeners = null;
1181             mouseListeners = null;
1182             keyListeners = null;
1183             capsRequested = null;
1184             lifecycleHook = null;
1185
1186             screen = null;
1187             windowListeners = null;
1188             parentWindow = null;
1189             */
1190         }
1191     }
1192     private final DestroyAction destroyAction = new DestroyAction();
1193
1194     @Override
1195     public void destroy() {
1196         visible = false; // Immediately mark synchronized visibility flag, avoiding possible recreation
1197         runOnEDTIfAvail(true, destroyAction);
1198     }
1199
1200     protected void destroy(boolean preserveResources) {
1201         if( null != lifecycleHook ) {
1202             lifecycleHook.preserveGLStateAtDestroy( preserveResources );
1203         }
1204         destroy();
1205     }
1206
1207     /**
1208      * @param cWin child window, must not be null
1209      * @param pWin parent window, may be null
1210      * @return true if at least one of both window's configurations is offscreen
1211      */
1212     protected static boolean isOffscreenInstance(NativeWindow cWin, NativeWindow pWin) {
1213         boolean ofs = false;
1214         final AbstractGraphicsConfiguration cWinCfg = cWin.getGraphicsConfiguration();
1215         if( null != cWinCfg ) {
1216             ofs = !cWinCfg.getChosenCapabilities().isOnscreen();
1217         }
1218         if( !ofs && null != pWin ) {
1219             final AbstractGraphicsConfiguration pWinCfg = pWin.getGraphicsConfiguration();
1220             if( null != pWinCfg ) {
1221                 ofs = !pWinCfg.getChosenCapabilities().isOnscreen();
1222             }
1223         }
1224         return ofs;
1225     }
1226
1227     private class ReparentAction implements Runnable {
1228         final NativeWindow newParentWindow;
1229         final int topLevelX, topLevelY;
1230         final int hints;
1231         ReparentOperation operation;
1232
1233         private ReparentAction(NativeWindow newParentWindow, int topLevelX, int topLevelY, int hints) {
1234             this.newParentWindow = newParentWindow;
1235             this.topLevelX = topLevelX;
1236             this.topLevelY = topLevelY;
1237             if( DEBUG_TEST_REPARENT_INCOMPATIBLE ) {
1238                 hints |=  REPARENT_HINT_FORCE_RECREATION;
1239             }
1240             this.hints = hints;
1241             this.operation = ReparentOperation.ACTION_INVALID; // ensure it's set
1242         }
1243
1244         private ReparentOperation getOp() {
1245             return operation;
1246         }
1247
1248         @Override
1249         public final void run() {
1250             if( WindowImpl.this.isFullscreen() ) {
1251                 // Bug 924: Ignore reparent when in fullscreen - otherwise may confuse WM
1252                 if( DEBUG_IMPLEMENTATION) {
1253                     System.err.println("Window.reparent: NOP (in fullscreen, "+getThreadName()+") valid "+isNativeValid()+
1254                                        ", windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle));
1255                 }
1256                 return;
1257             }
1258             boolean animatorPaused = false;
1259             if(null!=lifecycleHook) {
1260                 animatorPaused = lifecycleHook.pauseRenderingAction();
1261             }
1262             reparent();
1263             if(animatorPaused) {
1264                 lifecycleHook.resumeRenderingAction();
1265             }
1266         }
1267
1268         private void reparent() {
1269             // mirror pos/size so native change notification can get overwritten
1270             final int oldX = getX();
1271             final int oldY = getY();
1272             final int oldWidth = getWidth();
1273             final int oldHeight = getHeight();
1274             final int x, y;
1275             int width = oldWidth;
1276             int height = oldHeight;
1277
1278             final boolean wasVisible;
1279             final boolean becomesVisible;
1280             final boolean forceDestroyCreate;
1281
1282             final RecursiveLock _lock = windowLock;
1283             _lock.lock();
1284             try {
1285                 {
1286                     boolean v = 0 != ( REPARENT_HINT_FORCE_RECREATION & hints );
1287                     if(isNativeValid()) {
1288                         // force recreation if offscreen, since it may become onscreen
1289                         v |= isOffscreenInstance(WindowImpl.this, newParentWindow);
1290                     }
1291                     forceDestroyCreate = v;
1292                 }
1293
1294                 wasVisible = isVisible();
1295                 becomesVisible = wasVisible || 0 != ( REPARENT_HINT_BECOMES_VISIBLE & hints );
1296
1297                 Window newParentWindowNEWT = null;
1298                 if(newParentWindow instanceof Window) {
1299                     newParentWindowNEWT = (Window) newParentWindow;
1300                 }
1301
1302                 long newParentWindowHandle = 0 ;
1303
1304                 if( DEBUG_IMPLEMENTATION) {
1305                     System.err.println("Window.reparent: START ("+getThreadName()+") valid "+isNativeValid()+
1306                                        ", windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)+
1307                                        ", visible "+wasVisible+", becomesVisible "+becomesVisible+
1308                                        ", forceDestroyCreate "+forceDestroyCreate+
1309                                        ", DEBUG_TEST_REPARENT_INCOMPATIBLE "+DEBUG_TEST_REPARENT_INCOMPATIBLE+
1310                                        ", HINT_FORCE_RECREATION "+( 0 != ( REPARENT_HINT_FORCE_RECREATION & hints ) )+
1311                                        ", HINT_BECOMES_VISIBLE "+( 0 != ( REPARENT_HINT_BECOMES_VISIBLE & hints ) ) +
1312                                        ", old parentWindow: "+Display.hashCodeNullSafe(parentWindow)+
1313                                        ", new parentWindow: "+Display.hashCodeNullSafe(newParentWindow) );
1314                 }
1315
1316                 if(null!=newParentWindow) {
1317                     // REPARENT TO CHILD WINDOW
1318
1319                     // reset position to 0/0 within parent space
1320                     x = 0;
1321                     y = 0;
1322
1323                     // refit if size is bigger than parent
1324                     if( width > newParentWindow.getWidth() ) {
1325                         width = newParentWindow.getWidth();
1326                     }
1327                     if( height > newParentWindow.getHeight() ) {
1328                         height = newParentWindow.getHeight();
1329                     }
1330
1331                     // Case: Child Window
1332                     newParentWindowHandle = getNativeWindowHandle(newParentWindow);
1333                     if(0 == newParentWindowHandle) {
1334                         // Case: Parent's native window not realized yet
1335                         if(null==newParentWindowNEWT) {
1336                             throw new NativeWindowException("Reparenting with non NEWT Window type only available after it's realized: "+newParentWindow);
1337                         }
1338                         // Destroy this window and use parent's Screen.
1339                         // It may be created properly when the parent is made visible.
1340                         destroy( becomesVisible );
1341                         setScreen( (ScreenImpl) newParentWindowNEWT.getScreen() );
1342                         operation = ReparentOperation.ACTION_NATIVE_CREATION_PENDING;
1343                     } else if(newParentWindow != getParent()) {
1344                         // Case: Parent's native window realized and changed
1345                         if( !isNativeValid() ) {
1346                             // May create a new compatible Screen/Display and
1347                             // mark it for creation.
1348                             if(null!=newParentWindowNEWT) {
1349                                 setScreen( (ScreenImpl) newParentWindowNEWT.getScreen() );
1350                             } else {
1351                                 final Screen newScreen = NewtFactory.createCompatibleScreen(newParentWindow, screen);
1352                                 if( screen != newScreen ) {
1353                                     // auto destroy on-the-fly created Screen/Display
1354                                     setScreen( (ScreenImpl) newScreen );
1355                                 }
1356                             }
1357                             if( 0 < width && 0 < height ) {
1358                                 operation = ReparentOperation.ACTION_NATIVE_CREATION;
1359                             } else {
1360                                 operation = ReparentOperation.ACTION_NATIVE_CREATION_PENDING;
1361                             }
1362                         } else if ( forceDestroyCreate || !NewtFactory.isScreenCompatible(newParentWindow, screen) ) {
1363                             // Destroy this window, may create a new compatible Screen/Display, while trying to preserve resources if becoming visible again.
1364                             destroy( becomesVisible );
1365                             if(null!=newParentWindowNEWT) {
1366                                 setScreen( (ScreenImpl) newParentWindowNEWT.getScreen() );
1367                             } else {
1368                                 setScreen( (ScreenImpl) NewtFactory.createCompatibleScreen(newParentWindow, screen) );
1369                             }
1370                             operation = ReparentOperation.ACTION_NATIVE_CREATION;
1371                         } else {
1372                             // Mark it for native reparenting
1373                             operation = ReparentOperation.ACTION_NATIVE_REPARENTING;
1374                         }
1375                     } else {
1376                         // Case: Parent's native window realized and not changed
1377                         operation = ReparentOperation.ACTION_NOP;
1378                     }
1379                 } else {
1380                     // REPARENT TO TOP-LEVEL WINDOW
1381                     if( 0 <= topLevelX && 0 <= topLevelY ) {
1382                         x = topLevelX;
1383                         y = topLevelY;
1384                     } else if( null != parentWindow ) {
1385                         // child -> top
1386                         // put client to current parent+child position
1387                         final Point p = getLocationOnScreen(null);
1388                         x = p.getX();
1389                         y = p.getY();
1390                     } else {
1391                         x = oldX;
1392                         y = oldY;
1393                     }
1394
1395                     // Case: Top Window
1396                     if( 0 == parentWindowHandle ) {
1397                         // Already Top Window
1398                         operation = ReparentOperation.ACTION_NOP;
1399                     } else if( !isNativeValid() || forceDestroyCreate ) {
1400                         // Destroy this window and mark it for [pending] creation.
1401                         // If isNativeValid() and becoming visible again - try to preserve resources, i.e. b/c on-/offscreen switch.
1402                         destroy( becomesVisible );
1403                         if( 0 < width && 0 < height ) {
1404                             operation = ReparentOperation.ACTION_NATIVE_CREATION;
1405                         } else {
1406                             operation = ReparentOperation.ACTION_NATIVE_CREATION_PENDING;
1407                         }
1408                     } else {
1409                         // Mark it for native reparenting
1410                         operation = ReparentOperation.ACTION_NATIVE_REPARENTING;
1411                     }
1412                 }
1413                 parentWindowHandle = newParentWindowHandle;
1414
1415                 if ( ReparentOperation.ACTION_INVALID == operation ) {
1416                     throw new NativeWindowException("Internal Error: reparentAction not set");
1417                 }
1418
1419                 if(DEBUG_IMPLEMENTATION) {
1420                     System.err.println("Window.reparent: ACTION ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+" new parentWindowHandle "+toHexString(newParentWindowHandle)+", reparentAction "+operation+", pos/size "+x+"/"+y+" "+width+"x"+height+", visible "+wasVisible);
1421                 }
1422
1423                 if( ReparentOperation.ACTION_NOP == operation ) {
1424                     return;
1425                 }
1426
1427                 if( null == newParentWindow ) {
1428                     // CLIENT -> TOP: Reset Parent's Pointer State
1429                     setOffscreenPointerIcon(null);
1430                     setOffscreenPointerVisible(true, null);
1431                 }
1432
1433                 // rearrange window tree
1434                 if(null!=parentWindow && parentWindow instanceof Window) {
1435                     ((Window)parentWindow).removeChild(WindowImpl.this);
1436                 }
1437                 parentWindow = newParentWindow;
1438                 if(parentWindow instanceof Window) {
1439                     ((Window)parentWindow).addChild(WindowImpl.this);
1440                 }
1441
1442                 if( ReparentOperation.ACTION_NATIVE_REPARENTING == operation ) {
1443                     final DisplayImpl display = (DisplayImpl) screen.getDisplay();
1444                     display.dispatchMessagesNative(); // status up2date
1445
1446                     // TOP -> CLIENT: !visible first (fixes X11 unsuccessful return to parent window)
1447                     if( null != parentWindow && wasVisible && NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(true) ) {
1448                         setVisibleImpl(false, oldX, oldY, oldWidth, oldHeight);
1449                         WindowImpl.this.waitForVisible(false, false);
1450                         // FIXME: Some composite WM behave slacky .. give 'em chance to change state -> invisible,
1451                         // even though we do exactly that (KDE+Composite)
1452                         try { Thread.sleep(100); } catch (InterruptedException e) { }
1453                         display.dispatchMessagesNative(); // status up2date
1454                     }
1455
1456                     // Lock parentWindow only during reparenting (attempt)
1457                     final NativeWindow parentWindowLocked;
1458                     if( null != parentWindow ) {
1459                         parentWindowLocked = parentWindow;
1460                         if( NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindowLocked.lockSurface() ) {
1461                             throw new NativeWindowException("Parent surface lock: not ready: "+parentWindowLocked);
1462                         }
1463                         // update native handle, locked state
1464                         parentWindowHandle = parentWindowLocked.getWindowHandle();
1465                     } else {
1466                         parentWindowLocked = null;
1467                     }
1468                     boolean ok = false;
1469                     try {
1470                         ok = reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(FLAG_CHANGE_PARENTING | FLAG_CHANGE_DECORATION, isVisible()));
1471                     } finally {
1472                         if(null!=parentWindowLocked) {
1473                             parentWindowLocked.unlockSurface();
1474                         }
1475                     }
1476                     definePosition(x, y); // position might not get updated by WM events (SWT parent apparently)
1477
1478                     // set visible again
1479                     if(ok) {
1480                         display.dispatchMessagesNative(); // status up2date
1481                         if(wasVisible) {
1482                             setVisibleImpl(true, x, y, width, height);
1483                             ok = 0 <= WindowImpl.this.waitForVisible(true, false);
1484                             if(ok) {
1485                                 if( isAlwaysOnTop() && 0 == parentWindowHandle && NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(true) ) {
1486                                     // Reinforce ALWAYSONTOP when CHILD -> TOP reparenting, since reparenting itself cause X11 WM to loose it's state.
1487                                     reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(FLAG_CHANGE_ALWAYSONTOP, isVisible()));
1488                                 }
1489                                 ok = WindowImpl.this.waitForSize(width, height, false, TIMEOUT_NATIVEWINDOW);
1490                             }
1491                             if(ok) {
1492                                 if( 0 == parentWindowHandle ) {
1493                                     // Position mismatch shall not lead to reparent failure
1494                                     WindowImpl.this.waitForPosition(true, x, y, TIMEOUT_NATIVEWINDOW);
1495                                 }
1496
1497                                 requestFocusInt( 0 == parentWindowHandle /* skipFocusAction if top-level */);
1498                                 display.dispatchMessagesNative(); // status up2date
1499                             }
1500                         }
1501                     }
1502
1503                     if(!ok || !wasVisible) {
1504                         // make size and position persistent manual,
1505                         // since we don't have a WM feedback (invisible or recreation)
1506                         definePosition(x, y);
1507                         defineSize(width, height);
1508                     }
1509
1510                     if(!ok) {
1511                         // native reparent failed -> try creation, while trying to preserve resources if becoming visible again.
1512                         if(DEBUG_IMPLEMENTATION) {
1513                             System.err.println("Window.reparent: native reparenting failed ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)+" -> "+toHexString(newParentWindowHandle)+" - Trying recreation");
1514                         }
1515                         destroy( becomesVisible );
1516                         operation = ReparentOperation.ACTION_NATIVE_CREATION ;
1517                     } else {
1518                         if( null != parentWindow ) {
1519                             // TOP -> CLIENT: Setup Parent's Pointer State
1520                             setOffscreenPointerIcon(pointerIcon);
1521                             setOffscreenPointerVisible(pointerVisible, pointerIcon);
1522                         }
1523                     }
1524                 } else {
1525                     // Case
1526                     //   ACTION_NATIVE_CREATION
1527                     //   ACTION_NATIVE_CREATION_PENDING;
1528
1529                     // make size and position persistent for proper [re]creation
1530                     definePosition(x, y);
1531                     defineSize(width, height);
1532                 }
1533
1534                 if(DEBUG_IMPLEMENTATION) {
1535                     System.err.println("Window.reparent: END-1 ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+
1536                                        ", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+
1537                                        ", parentWindow "+ Display.hashCodeNullSafe(parentWindow)+" "+
1538                                        getX()+"/"+getY()+" "+getWidth()+"x"+getHeight());
1539                 }
1540             } finally {
1541                 if(null!=lifecycleHook) {
1542                     lifecycleHook.resetCounter();
1543                 }
1544                 _lock.unlock();
1545             }
1546             if(wasVisible) {
1547                 switch (operation) {
1548                     case ACTION_NATIVE_REPARENTING:
1549                         // trigger a resize/relayout and repaint to listener
1550                         sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED);
1551                         break;
1552
1553                     case ACTION_NATIVE_CREATION:
1554                         // This may run on the new Display/Screen connection, hence a new EDT task
1555                         runOnEDTIfAvail(true, reparentActionRecreate);
1556                         break;
1557
1558                     default:
1559                 }
1560             }
1561             if(DEBUG_IMPLEMENTATION) {
1562                 System.err.println("Window.reparent: END-X ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+
1563                                    ", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+
1564                                    ", parentWindow "+ Display.hashCodeNullSafe(parentWindow)+" "+
1565                                    getX()+"/"+getY()+" "+getWidth()+"x"+getHeight());
1566             }
1567         }
1568     }
1569
1570     private class ReparentActionRecreate implements Runnable {
1571         @Override
1572         public final void run() {
1573             final RecursiveLock _lock = windowLock;
1574             _lock.lock();
1575             try {
1576                 if(DEBUG_IMPLEMENTATION) {
1577                     System.err.println("Window.reparent: ReparentActionRecreate ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+Display.hashCodeNullSafe(parentWindow));
1578                 }
1579                 setVisibleActionImpl(true); // native creation
1580                 requestFocusInt( 0 == parentWindowHandle /* skipFocusAction if top-level */);
1581             } finally {
1582                 _lock.unlock();
1583             }
1584         }
1585     }
1586     private final ReparentActionRecreate reparentActionRecreate = new ReparentActionRecreate();
1587
1588     @Override
1589     public final ReparentOperation reparentWindow(NativeWindow newParent) {
1590         return reparentWindow(newParent, -1, -1, 0);
1591     }
1592
1593     @Override
1594     public final ReparentOperation reparentWindow(NativeWindow newParent, int x, int y, boolean forceDestroyCreate) {
1595         return reparentWindow(newParent, x, y, forceDestroyCreate ? REPARENT_HINT_FORCE_RECREATION : 0);
1596     }
1597
1598     @Override
1599     public final ReparentOperation reparentWindow(NativeWindow newParent, int x, int y, int hints) {
1600         final ReparentAction reparentAction = new ReparentAction(newParent, x, y, hints);
1601         runOnEDTIfAvail(true, reparentAction);
1602         return reparentAction.getOp();
1603     }
1604
1605     @Override
1606     public final CapabilitiesChooser setCapabilitiesChooser(CapabilitiesChooser chooser) {
1607         CapabilitiesChooser old = this.capabilitiesChooser;
1608         this.capabilitiesChooser = chooser;
1609         return old;
1610     }
1611
1612     @Override
1613     public final CapabilitiesImmutable getChosenCapabilities() {
1614         return getGraphicsConfiguration().getChosenCapabilities();
1615     }
1616
1617     @Override
1618     public final CapabilitiesImmutable getRequestedCapabilities() {
1619         return capsRequested;
1620     }
1621
1622     private class DecorationAction implements Runnable {
1623         boolean undecorated;
1624
1625         private DecorationAction(boolean undecorated) {
1626             this.undecorated = undecorated;
1627         }
1628
1629         @Override
1630         public final void run() {
1631             final RecursiveLock _lock = windowLock;
1632             _lock.lock();
1633             try {
1634                 if(WindowImpl.this.undecorated != undecorated) {
1635                     // set current state
1636                     WindowImpl.this.undecorated = undecorated;
1637
1638                     if( isNativeValid() && !isFullscreen() ) {
1639                         // Mirror pos/size so native change notification can get overwritten
1640                         final int x = getX();
1641                         final int y = getY();
1642                         final int width = getWidth();
1643                         final int height = getHeight();
1644
1645                         DisplayImpl display = (DisplayImpl) screen.getDisplay();
1646                         display.dispatchMessagesNative(); // status up2date
1647                         reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(FLAG_CHANGE_DECORATION, isVisible()));
1648                         display.dispatchMessagesNative(); // status up2date
1649                     }
1650                 }
1651             } finally {
1652                 _lock.unlock();
1653             }
1654             sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener
1655         }
1656     }
1657
1658     @Override
1659     public final void setUndecorated(boolean value) {
1660         runOnEDTIfAvail(true, new DecorationAction(value));
1661     }
1662
1663     @Override
1664     public final boolean isUndecorated() {
1665         return 0 != parentWindowHandle || undecorated || fullscreen ;
1666     }
1667
1668     private class AlwaysOnTopAction implements Runnable {
1669         boolean alwaysOnTop;
1670
1671         private AlwaysOnTopAction(boolean alwaysOnTop) {
1672             this.alwaysOnTop = alwaysOnTop;
1673         }
1674
1675         @Override
1676         public final void run() {
1677             final RecursiveLock _lock = windowLock;
1678             _lock.lock();
1679             try {
1680                 if(WindowImpl.this.alwaysOnTop != alwaysOnTop) {
1681                     // set current state
1682                     WindowImpl.this.alwaysOnTop = alwaysOnTop;
1683
1684                     if( isNativeValid() ) {
1685                         // Mirror pos/size so native change notification can get overwritten
1686                         final int x = getX();
1687                         final int y = getY();
1688                         final int width = getWidth();
1689                         final int height = getHeight();
1690
1691                         DisplayImpl display = (DisplayImpl) screen.getDisplay();
1692                         display.dispatchMessagesNative(); // status up2date
1693                         reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(FLAG_CHANGE_ALWAYSONTOP, isVisible()));
1694                         display.dispatchMessagesNative(); // status up2date
1695                     }
1696                 }
1697             } finally {
1698                 _lock.unlock();
1699             }
1700             sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener
1701         }
1702     }
1703
1704     @Override
1705     public final void setAlwaysOnTop(boolean value) {
1706         if( isFullscreen() ) {
1707             nfs_alwaysOnTop = value;
1708         } else {
1709             runOnEDTIfAvail(true, new AlwaysOnTopAction(value));
1710         }
1711     }
1712
1713     @Override
1714     public final boolean isAlwaysOnTop() {
1715         return alwaysOnTop;
1716     }
1717
1718     @Override
1719     public final String getTitle() {
1720         return title;
1721     }
1722     @Override
1723     public final void setTitle(String title) {
1724         if (title == null) {
1725             title = "";
1726         }
1727         this.title = title;
1728         if(0 != getWindowHandle()) {
1729             setTitleImpl(title);
1730         }
1731     }
1732
1733     @Override
1734     public final boolean isPointerVisible() {
1735         return pointerVisible;
1736     }
1737     @Override
1738     public final void setPointerVisible(final boolean pointerVisible) {
1739         if(this.pointerVisible != pointerVisible) {
1740             boolean setVal = 0 == getWindowHandle();
1741             if(!setVal) {
1742                 setVal = setPointerVisibleIntern(pointerVisible);
1743             }
1744             if(setVal) {
1745                 this.pointerVisible = pointerVisible;
1746             }
1747         }
1748     }
1749     private boolean setPointerVisibleIntern(final boolean pointerVisible) {
1750         boolean res = setOffscreenPointerVisible(pointerVisible, pointerIcon);
1751         return setPointerVisibleImpl(pointerVisible) || res; // accept onscreen or offscreen positive result!
1752     }
1753     /**
1754      * Helper method to delegate {@link #setPointerVisibleImpl(boolean)} to
1755      * {@link OffscreenLayerSurface#hideCursor()} or {@link OffscreenLayerSurface#setCursor(PixelRectangle, PointImmutable)}.
1756      * <p>
1757      * Note: JAWTWindow is an OffscreenLayerSurface.
1758      * </p>
1759      * <p>
1760      * Performing OffscreenLayerSurface's setCursor(..)/hideCursor(), if available,
1761      * gives same behavior on all platforms.
1762      * </p>
1763      * <p>
1764      * If visible, implementation invokes {@link #setOffscreenPointerIcon(OffscreenLayerSurface, PointerIconImpl)} using the
1765      * given <code>defaultPointerIcon</code>, otherwise {@link OffscreenLayerSurface#hideCursor()} is invoked.
1766      * </p>
1767      * @param pointerVisible true for visible, otherwise invisible.
1768      * @param defaultPointerIcon default PointerIcon for visibility
1769      * @param ols the {@link OffscreenLayerSurface} instance, if null method does nothing.
1770      */
1771     private boolean setOffscreenPointerVisible(final boolean pointerVisible, final PointerIconImpl defaultPointerIcon) {
1772         if( pointerVisible ) {
1773             return setOffscreenPointerIcon(defaultPointerIcon);
1774         } else {
1775             final NativeWindow parent = getParent();
1776             if( parent instanceof OffscreenLayerSurface ) {
1777                 final OffscreenLayerSurface ols = (OffscreenLayerSurface) parent;
1778                 try {
1779                     return ols.hideCursor();
1780                 } catch (Exception e) {
1781                     e.printStackTrace();
1782                 }
1783             }
1784         }
1785         return false;
1786     }
1787
1788     @Override
1789     public final PointerIcon getPointerIcon() { return pointerIcon; }
1790
1791     @Override
1792     public final void setPointerIcon(final PointerIcon pi) {
1793         final PointerIconImpl piImpl = (PointerIconImpl)pi;
1794         if( this.pointerIcon != piImpl ) {
1795             if( isNativeValid() ) {
1796                 runOnEDTIfAvail(true, new Runnable() {
1797                     public void run() {
1798                         setPointerIconIntern(piImpl);
1799                     } } );
1800             }
1801             this.pointerIcon = piImpl;
1802         }
1803     }
1804     private void setPointerIconIntern(final PointerIconImpl pi) {
1805         setOffscreenPointerIcon(pi);
1806         setPointerIconImpl(pi);
1807     }
1808     /**
1809      * Helper method to delegate {@link #setPointerIconIntern(PointerIconImpl)} to
1810      * {@link OffscreenLayerSurface#setCursor(PixelRectangle, PointImmutable)}
1811      * <p>
1812      * Note: JAWTWindow is an OffscreenLayerSurface.
1813      * </p>
1814      * <p>
1815      * Performing OffscreenLayerSurface's setCursor(..), if available,
1816      * gives same behavior on all platforms.
1817      * </p>
1818      * <p>
1819      * Workaround for AWT/Windows bug within browser,
1820      * where the PointerIcon gets periodically overridden
1821      * by the AWT Component's icon.
1822      * </p>
1823      * @param ols the {@link OffscreenLayerSurface} instance, if null method does nothing.
1824      * @param pi the {@link PointerIconImpl} instance, if null PointerIcon gets reset.
1825      */
1826     private boolean setOffscreenPointerIcon(final PointerIconImpl pi) {
1827         final NativeWindow parent = getParent();
1828         if( parent instanceof OffscreenLayerSurface ) {
1829             final OffscreenLayerSurface ols = (OffscreenLayerSurface) parent;
1830             try {
1831                 if( null != pi ) {
1832                     return ols.setCursor(pi, pi.getHotspot());
1833                 } else {
1834                     return ols.setCursor(null, null); // default
1835                 }
1836             } catch (Exception e) {
1837                 e.printStackTrace();
1838             }
1839         }
1840         return false;
1841     }
1842
1843     @Override
1844     public final boolean isPointerConfined() {
1845         return pointerConfined;
1846     }
1847     @Override
1848     public final void confinePointer(boolean confine) {
1849         if(this.pointerConfined != confine) {
1850             boolean setVal = 0 == getWindowHandle();
1851             if(!setVal) {
1852                 if(confine) {
1853                     requestFocus();
1854                     warpPointer(getSurfaceWidth()/2, getSurfaceHeight()/2);
1855                 }
1856                 setVal = confinePointerImpl(confine);
1857                 if(confine) {
1858                     // give time to deliver mouse movements w/o confinement,
1859                     // this allows user listener to sync previous position value to the new centered position
1860                     try {
1861                         Thread.sleep(3 * screen.getDisplay().getEDTUtil().getPollPeriod());
1862                     } catch (InterruptedException e) { }
1863                 }
1864             }
1865             if(setVal) {
1866                 this.pointerConfined = confine;
1867             }
1868         }
1869     }
1870
1871     @Override
1872     public final void warpPointer(int x, int y) {
1873         if(0 != getWindowHandle()) {
1874             warpPointerImpl(x, y);
1875         }
1876     }
1877
1878     @Override
1879     public final InsetsImmutable getInsets() {
1880         if(isUndecorated()) {
1881             return Insets.getZero();
1882         }
1883         updateInsetsImpl(insets);
1884         return insets;
1885     }
1886
1887     @Override
1888     public final int getX() {
1889         return x;
1890     }
1891
1892     @Override
1893     public final int getY() {
1894         return y;
1895     }
1896
1897     @Override
1898     public final int getWidth() {
1899         return winWidth;
1900     }
1901
1902     @Override
1903     public final int getHeight() {
1904         return winHeight;
1905     }
1906
1907     @Override
1908     public final Rectangle getBounds() {
1909         return new Rectangle(x, y, winWidth, winHeight);
1910     }
1911
1912     @Override
1913     public final int getSurfaceWidth() {
1914         return pixWidth;
1915     }
1916
1917     @Override
1918     public final int getSurfaceHeight() {
1919         return pixHeight;
1920     }
1921
1922     @Override
1923     public final Rectangle getSurfaceBounds() {
1924         // FIXME HiDPI: Shortcut, may need to adjust if we change scaling methodology
1925         return new Rectangle(x * getPixelScaleX(), y * getPixelScaleY(), pixWidth, pixHeight);
1926     }
1927
1928     @Override
1929     public final int[] convertToWindowUnits(final int[] pixelUnitsAndResult) {
1930         pixelUnitsAndResult[0] /= getPixelScaleX();
1931         pixelUnitsAndResult[1] /= getPixelScaleY();
1932         return pixelUnitsAndResult;
1933     }
1934
1935     @Override
1936     public final int[] convertToPixelUnits(final int[] windowUnitsAndResult) {
1937         windowUnitsAndResult[0] *= getPixelScaleX();
1938         windowUnitsAndResult[1] *= getPixelScaleY();
1939         return windowUnitsAndResult;
1940     }
1941
1942     protected final Point convertToWindowUnits(final Point pixelUnitsAndResult) {
1943         return pixelUnitsAndResult.scaleInv(getPixelScaleX(), getPixelScaleY());
1944     }
1945
1946     protected final Point convertToPixelUnits(final Point windowUnitsAndResult) {
1947         return windowUnitsAndResult.scale(getPixelScaleX(), getPixelScaleY());
1948     }
1949
1950     @Override
1951     public final Rectangle convertToWindowUnits(final Rectangle pixelUnitsAndResult) {
1952         return pixelUnitsAndResult.scaleInv(getPixelScaleX(), getPixelScaleY());
1953     }
1954
1955     @Override
1956     public final Rectangle convertToPixelUnits(final Rectangle windowUnitsAndResult) {
1957         return windowUnitsAndResult.scale(getPixelScaleX(), getPixelScaleY());
1958     }
1959
1960     /** HiDPI: We currently base scaling of window units to pixel units on an integer scale factor per component. */
1961     protected final int getPixelScaleX() {
1962         return hasPixelScale[0];
1963     }
1964
1965     /** HiDPI: We currently base scaling of window units to pixel units on an integer scale factor per component. */
1966     protected final int getPixelScaleY() {
1967         return hasPixelScale[1];
1968     }
1969
1970     @Override
1971     public void setSurfaceScale(final int[] pixelScale) {
1972         SurfaceScaleUtils.validateReqPixelScale(reqPixelScale, pixelScale, DEBUG_IMPLEMENTATION ? getClass().getSimpleName() : null);
1973     }
1974
1975     @Override
1976     public final int[] getSurfaceScale(final int[] result) {
1977         System.arraycopy(isNativeValid() ? hasPixelScale : reqPixelScale, 0, result, 0, 2);
1978         return result;
1979     }
1980
1981     protected final boolean autoPosition() { return autoPosition; }
1982
1983     /** Sets the position fields {@link #x} and {@link #y} in window units to the given values and {@link #autoPosition} to false. */
1984     protected final void definePosition(int x, int y) {
1985         if(DEBUG_IMPLEMENTATION) {
1986             System.err.println("definePosition: "+this.x+"/"+this.y+" -> "+x+"/"+y);
1987             // Thread.dumpStack();
1988         }
1989         autoPosition = false;
1990         this.x = x; this.y = y;
1991     }
1992
1993     /**
1994      * Sets the size fields {@link #winWidth} and {@link #winHeight} in window units to the given values
1995      * and {@link #pixWidth} and {@link #pixHeight} in pixel units according to {@link #convertToPixelUnits(int[])}.
1996      */
1997     protected final void defineSize(int winWidth, int winHeight) {
1998         final int pixWidth = winWidth * getPixelScaleX();   // FIXME HiDPI: Shortcut, may need to adjust if we change scaling methodology
1999         final int pixHeight = winHeight * getPixelScaleY();
2000         if(DEBUG_IMPLEMENTATION) {
2001             System.err.println("defineSize: win["+this.winWidth+"x"+this.winHeight+" -> "+winWidth+"x"+winHeight+
2002                                "], pixel["+this.pixWidth+"x"+this.pixHeight+" -> "+pixWidth+"x"+pixHeight+"]");
2003             // Thread.dumpStack();
2004         }
2005         this.winWidth = winWidth; this.winHeight = winHeight;
2006         this.pixWidth = pixWidth; this.pixHeight = pixHeight;
2007     }
2008
2009     @Override
2010     public final boolean isVisible() {
2011         return visible;
2012     }
2013
2014     @Override
2015     public final boolean isFullscreen() {
2016         return fullscreen;
2017     }
2018
2019     //----------------------------------------------------------------------
2020     // Window
2021     //
2022
2023     @Override
2024     public final Window getDelegatedWindow() {
2025         return this;
2026     }
2027
2028     //----------------------------------------------------------------------
2029     // WindowImpl
2030     //
2031
2032     /**
2033      * If the implementation is capable of detecting a device change
2034      * return true and clear the status/reason of the change.
2035      */
2036     public boolean hasDeviceChanged() {
2037         return false;
2038     }
2039
2040     public final LifecycleHook getLifecycleHook() {
2041         return lifecycleHook;
2042     }
2043
2044     public final LifecycleHook setLifecycleHook(LifecycleHook hook) {
2045         LifecycleHook old = lifecycleHook;
2046         lifecycleHook = hook;
2047         return old;
2048     }
2049
2050     /**
2051      * If this Window actually wraps a {@link NativeSurface} from another instance or toolkit,
2052      * it will return such reference. Otherwise returns null.
2053      */
2054     public NativeSurface getWrappedSurface() {
2055         return null;
2056     }
2057
2058     @Override
2059     public final void setWindowDestroyNotifyAction(Runnable r) {
2060         windowDestroyNotifyAction = r;
2061     }
2062
2063     protected final long getParentWindowHandle() {
2064         return isFullscreen() ? 0 : parentWindowHandle;
2065     }
2066
2067     @Override
2068     public final String toString() {
2069         StringBuilder sb = new StringBuilder();
2070
2071         sb.append(getClass().getName()+"[Config "+config+
2072                     ",\n "+screen+
2073                     ",\n ParentWindow "+parentWindow+
2074                     ",\n ParentWindowHandle "+toHexString(parentWindowHandle)+" ("+(0!=getParentWindowHandle())+")"+
2075                     ",\n WindowHandle "+toHexString(getWindowHandle())+
2076                     ",\n SurfaceHandle "+toHexString(getSurfaceHandle())+ " (lockedExt window "+windowLock.isLockedByOtherThread()+", surface "+isSurfaceLockedByOtherThread()+")"+
2077                     ",\n window["+getX()+"/"+getY()+" (auto "+autoPosition()+") "+getWidth()+"x"+getHeight()+"], pixel["+getSurfaceWidth()+"x"+getSurfaceHeight()+
2078                     "],\n Visible "+isVisible()+", focus "+hasFocus()+
2079                     ",\n Undecorated "+undecorated+" ("+isUndecorated()+")"+
2080                     ",\n AlwaysOnTop "+alwaysOnTop+", Fullscreen "+fullscreen+
2081                     ",\n WrappedSurface "+getWrappedSurface()+
2082                     ",\n ChildWindows "+childWindows.size());
2083
2084         sb.append(", SurfaceUpdatedListeners num "+surfaceUpdatedHelper.size()+" [");
2085         for (int i = 0; i < surfaceUpdatedHelper.size(); i++ ) {
2086           sb.append(surfaceUpdatedHelper.get(i)+", ");
2087         }
2088         sb.append("], WindowListeners num "+windowListeners.size()+" [");
2089         for (int i = 0; i < windowListeners.size(); i++ ) {
2090           sb.append(windowListeners.get(i)+", ");
2091         }
2092         sb.append("], MouseListeners num "+mouseListeners.size()+" [");
2093         for (int i = 0; i < mouseListeners.size(); i++ ) {
2094           sb.append(mouseListeners.get(i)+", ");
2095         }
2096         sb.append("], PointerGestures default "+defaultGestureHandlerEnabled+", custom "+pointerGestureHandler.size()+" [");
2097         for (int i = 0; i < pointerGestureHandler.size(); i++ ) {
2098           sb.append(pointerGestureHandler.get(i)+", ");
2099         }
2100         sb.append("], KeyListeners num "+keyListeners.size()+" [");
2101         for (int i = 0; i < keyListeners.size(); i++ ) {
2102           sb.append(keyListeners.get(i)+", ");
2103         }
2104         sb.append("], windowLock "+windowLock+", surfaceLockCount "+surfaceLockCount+"]");
2105         return sb.toString();
2106     }
2107
2108     protected final void setWindowHandle(long handle) {
2109         windowHandle = handle;
2110     }
2111
2112     @Override
2113     public final void runOnEDTIfAvail(boolean wait, final Runnable task) {
2114         if( windowLock.isOwner( Thread.currentThread() ) ) {
2115             task.run();
2116         } else {
2117             ( (DisplayImpl) screen.getDisplay() ).runOnEDTIfAvail(wait, task);
2118         }
2119     }
2120
2121     private final Runnable requestFocusAction = new Runnable() {
2122         @Override
2123         public final void run() {
2124             if(DEBUG_IMPLEMENTATION) {
2125                 System.err.println("Window.RequestFocusAction: force 0 - ("+getThreadName()+"): "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle));
2126             }
2127             WindowImpl.this.requestFocusImpl(false);
2128         }
2129     };
2130     private final Runnable requestFocusActionForced = new Runnable() {
2131         @Override
2132         public final void run() {
2133             if(DEBUG_IMPLEMENTATION) {
2134                 System.err.println("Window.RequestFocusAction: force 1 - ("+getThreadName()+"): "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle));
2135             }
2136             WindowImpl.this.requestFocusImpl(true);
2137         }
2138     };
2139
2140     @Override
2141     public final boolean hasFocus() {
2142         return hasFocus;
2143     }
2144
2145     @Override
2146     public final void requestFocus() {
2147         requestFocus(true);
2148     }
2149
2150     @Override
2151     public final void requestFocus(boolean wait) {
2152         requestFocus(wait /* wait */, false /* skipFocusAction */, brokenFocusChange /* force */);
2153     }
2154
2155     private void requestFocus(boolean wait, boolean skipFocusAction, boolean force) {
2156         if( isNativeValid() &&
2157             ( force || !hasFocus() ) &&
2158             ( skipFocusAction || !focusAction() ) ) {
2159             runOnEDTIfAvail(wait, force ? requestFocusActionForced : requestFocusAction);
2160         }
2161     }
2162
2163     /** Internally forcing request focus on current thread */
2164     private void requestFocusInt(boolean skipFocusAction) {
2165         if( skipFocusAction || !focusAction() ) {
2166             if(DEBUG_IMPLEMENTATION) {
2167                 System.err.println("Window.RequestFocusInt: forcing - ("+getThreadName()+"): skipFocusAction "+skipFocusAction+", focus "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle));
2168             }
2169             requestFocusImpl(true);
2170         }
2171     }
2172
2173     @Override
2174     public final void setFocusAction(FocusRunnable focusAction) {
2175         this.focusAction = focusAction;
2176     }
2177
2178     private boolean focusAction() {
2179         if(DEBUG_IMPLEMENTATION) {
2180             System.err.println("Window.focusAction() START - "+getThreadName()+", focusAction: "+focusAction+" - windowHandle "+toHexString(getWindowHandle()));
2181         }
2182         boolean res;
2183         if(null!=focusAction) {
2184             res = focusAction.run();
2185         } else {
2186             res = false;
2187         }
2188         if(DEBUG_IMPLEMENTATION) {
2189             System.err.println("Window.focusAction() END - "+getThreadName()+", focusAction: "+focusAction+" - windowHandle "+toHexString(getWindowHandle())+", res: "+res);
2190         }
2191         return res;
2192     }
2193
2194     protected final void setBrokenFocusChange(boolean v) {
2195         brokenFocusChange = v;
2196     }
2197
2198     @Override
2199     public final void setKeyboardFocusHandler(KeyListener l) {
2200         keyboardFocusHandler = l;
2201     }
2202
2203     private class SetPositionAction implements Runnable {
2204         int x, y;
2205
2206         private SetPositionAction(int x, int y) {
2207             this.x = x;
2208             this.y = y;
2209         }
2210
2211         @Override
2212         public final void run() {
2213             final RecursiveLock _lock = windowLock;
2214             _lock.lock();
2215             try {
2216                 if(DEBUG_IMPLEMENTATION) {
2217                     System.err.println("Window setPosition: "+getX()+"/"+getY()+" -> "+x+"/"+y+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle));
2218                 }
2219                 // Let the window be positioned if !fullscreen and position changed or being a child window.
2220                 if ( !isFullscreen() && ( getX() != x || getY() != y || null != getParent()) ) {
2221                     if(isNativeValid()) {
2222                         // this.x/this.y will be set by sizeChanged, triggered by windowing event system
2223                         reconfigureWindowImpl(x, y, getWidth(), getHeight(), getReconfigureFlags(0, isVisible()));
2224                         if( null == parentWindow ) {
2225                             // Wait until custom position is reached within tolerances
2226                             waitForPosition(true, x, y, Window.TIMEOUT_NATIVEWINDOW);
2227                         }
2228                     } else {
2229                         definePosition(x, y); // set pos for createNative(..)
2230                     }
2231                 }
2232             } finally {
2233                 _lock.unlock();
2234             }
2235         }
2236     }
2237
2238     @Override
2239     public void setPosition(int x, int y) {
2240         autoPosition = false;
2241         runOnEDTIfAvail(true, new SetPositionAction(x, y));
2242     }
2243
2244     @Override
2245     public final void setTopLevelPosition(int x, int y) {
2246         setPosition(x + getInsets().getLeftWidth(), y + getInsets().getTopHeight());
2247     }
2248
2249     private class FullScreenAction implements Runnable {
2250         boolean _fullscreen;
2251
2252         private boolean init(boolean fullscreen) {
2253             if(isNativeValid()) {
2254                 this._fullscreen = fullscreen;
2255                 return isFullscreen() != fullscreen;
2256             } else {
2257                 WindowImpl.this.fullscreen = fullscreen; // set current state for createNative(..)
2258                 return false;
2259             }
2260         }
2261         public boolean fsOn() { return _fullscreen; }
2262
2263         @Override
2264         public final void run() {
2265             final RecursiveLock _lock = windowLock;
2266             _lock.lock();
2267             blockInsetsChange = true;
2268             try {
2269                 final int oldX = getX();
2270                 final int oldY = getY();
2271                 final int oldWidth = getWidth();
2272                 final int oldHeight = getHeight();
2273
2274                 int x,y,w,h;
2275
2276                 final RectangleImmutable sviewport = screen.getViewportInWindowUnits(); // window units
2277                 final RectangleImmutable viewport; // window units
2278                 final int fs_span_flag;
2279                 final boolean alwaysOnTopChange;
2280                 if(_fullscreen) {
2281                     if( null == fullscreenMonitors ) {
2282                         if( fullscreenUseMainMonitor ) {
2283                             fullscreenMonitors = new ArrayList<MonitorDevice>();
2284                             fullscreenMonitors.add( getMainMonitor() );
2285                         } else {
2286                             fullscreenMonitors = getScreen().getMonitorDevices();
2287                         }
2288                     }
2289                     {
2290                         final Rectangle viewportInWindowUnits = new Rectangle();
2291                         MonitorDevice.unionOfViewports(null, viewportInWindowUnits, fullscreenMonitors);
2292                         viewport = viewportInWindowUnits;
2293                     }
2294                     if( isReconfigureFlagSupported(FLAG_IS_FULLSCREEN_SPAN) &&
2295                         ( fullscreenMonitors.size() > 1 || sviewport.compareTo(viewport) > 0 ) ) {
2296                         fs_span_flag = FLAG_IS_FULLSCREEN_SPAN;
2297                     } else {
2298                         fs_span_flag = 0;
2299                     }
2300                     nfs_x = oldX;
2301                     nfs_y = oldY;
2302                     nfs_width = oldWidth;
2303                     nfs_height = oldHeight;
2304                     nfs_alwaysOnTop = alwaysOnTop;
2305                     x = viewport.getX();
2306                     y = viewport.getY();
2307                     w = viewport.getWidth();
2308                     h = viewport.getHeight();
2309                     alwaysOnTop = false;
2310                     alwaysOnTopChange = nfs_alwaysOnTop != alwaysOnTop;
2311                 } else {
2312                     fullscreenUseMainMonitor = true;
2313                     fullscreenMonitors = null;
2314                     fs_span_flag = 0;
2315                     viewport = null;
2316                     x = nfs_x;
2317                     y = nfs_y;
2318                     w = nfs_width;
2319                     h = nfs_height;
2320                     alwaysOnTopChange = nfs_alwaysOnTop != alwaysOnTop;
2321                     alwaysOnTop = nfs_alwaysOnTop;
2322
2323                     if(null!=parentWindow) {
2324                         // reset position to 0/0 within parent space
2325                         x = 0;
2326                         y = 0;
2327
2328                         // refit if size is bigger than parent
2329                         if( w > parentWindow.getWidth() ) {
2330                             w = parentWindow.getWidth();
2331                         }
2332                         if( h > parentWindow.getHeight() ) {
2333                             h = parentWindow.getHeight();
2334                         }
2335                     }
2336                 }
2337
2338                 final DisplayImpl display = (DisplayImpl) screen.getDisplay();
2339                 display.dispatchMessagesNative(); // status up2date
2340                 final boolean wasVisible = isVisible();
2341                 final boolean tempInvisible = !_fullscreen && wasVisible && NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(true);
2342
2343                 if(DEBUG_IMPLEMENTATION) {
2344                     System.err.println("Window fs: "+_fullscreen+" "+x+"/"+y+" "+w+"x"+h+", "+isUndecorated()+
2345                                        ", virtl-screenSize: "+sviewport+" [wu], monitorsViewport "+viewport+" [wu]"+
2346                                        ", spanning "+(0!=fs_span_flag)+
2347                                        ", alwaysOnTop "+alwaysOnTop+(alwaysOnTopChange?"*":"")+
2348                                        ", wasVisible "+wasVisible+", tempInvisible "+tempInvisible+
2349                                        ", hasParent "+(null!=parentWindow)+
2350                                        " @ "+Thread.currentThread().getName());
2351                 }
2352
2353                 // fullscreen off: !visible first (fixes X11 unsuccessful return to parent window _and_ wrong window size propagation)
2354                 if( tempInvisible ) {
2355                     setVisibleImpl(false, oldX, oldY, oldWidth, oldHeight);
2356                     WindowImpl.this.waitForVisible(false, false);
2357                     try { Thread.sleep(100); } catch (InterruptedException e) { }
2358                     display.dispatchMessagesNative(); // status up2date
2359                 }
2360
2361                 // Lock parentWindow only during reparenting (attempt)
2362                 final NativeWindow parentWindowLocked;
2363                 if( null != parentWindow ) {
2364                     parentWindowLocked = parentWindow;
2365                     if( NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindowLocked.lockSurface() ) {
2366                         throw new NativeWindowException("Parent surface lock: not ready: "+parentWindow);
2367                     }
2368                 } else {
2369                     parentWindowLocked = null;
2370                 }
2371                 try {
2372                     if(alwaysOnTopChange && _fullscreen) {
2373                         // Enter fullscreen - Disable alwaysOnTop
2374                         reconfigureWindowImpl(oldX, oldY, oldWidth, oldHeight, getReconfigureFlags(FLAG_CHANGE_ALWAYSONTOP, isVisible()));
2375                     }
2376
2377                     WindowImpl.this.fullscreen = _fullscreen;
2378                     reconfigureWindowImpl(x, y, w, h,
2379                                           getReconfigureFlags( ( ( null != parentWindowLocked ) ? FLAG_CHANGE_PARENTING : 0 ) |
2380                                                                fs_span_flag | FLAG_CHANGE_FULLSCREEN | FLAG_CHANGE_DECORATION, isVisible()) );
2381                     if(alwaysOnTopChange && !_fullscreen) {
2382                         // Leave fullscreen - Restore alwaysOnTop
2383                         reconfigureWindowImpl(x, y, w, h, getReconfigureFlags(FLAG_CHANGE_ALWAYSONTOP, isVisible()));
2384                     }
2385                 } finally {
2386                     if(null!=parentWindowLocked) {
2387                         parentWindowLocked.unlockSurface();
2388                     }
2389                 }
2390                 display.dispatchMessagesNative(); // status up2date
2391
2392                 if(wasVisible) {
2393                     if( NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(true) ) {
2394                         // Give sluggy WM's (e.g. Unity) a chance to properly restore window ..
2395                         try { Thread.sleep(100); } catch (InterruptedException e) { }
2396                         display.dispatchMessagesNative(); // status up2date
2397                     }
2398                     setVisibleImpl(true, x, y, w, h);
2399                     boolean ok = 0 <= WindowImpl.this.waitForVisible(true, false);
2400                     if(ok) {
2401                         ok = WindowImpl.this.waitForSize(w, h, false, TIMEOUT_NATIVEWINDOW);
2402                     }
2403                     if(ok && !_fullscreen && null == parentWindow) {
2404                         // Position mismatch shall not lead to fullscreen failure
2405                         WindowImpl.this.waitForPosition(true, x, y, TIMEOUT_NATIVEWINDOW);
2406                     }
2407                     if(ok) {
2408                         requestFocusInt(_fullscreen /* skipFocusAction if fullscreen */);
2409                         display.dispatchMessagesNative(); // status up2date
2410                     }
2411                     if(DEBUG_IMPLEMENTATION) {
2412                         System.err.println("Window fs done: ok " + ok + ", " + WindowImpl.this);
2413                     }
2414                 }
2415             } finally {
2416                 blockInsetsChange = false;
2417                 _lock.unlock();
2418             }
2419             sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener
2420         }
2421     }
2422     private final FullScreenAction fullScreenAction = new FullScreenAction();
2423
2424     @Override
2425     public boolean setFullscreen(boolean fullscreen) {
2426         return setFullscreenImpl(fullscreen, true, null);
2427     }
2428
2429     @Override
2430     public boolean setFullscreen(List<MonitorDevice> monitors) {
2431         return setFullscreenImpl(true, false, monitors);
2432     }
2433
2434     private boolean setFullscreenImpl(boolean fullscreen, boolean useMainMonitor, List<MonitorDevice> monitors) {
2435         synchronized(fullScreenAction) {
2436             fullscreenMonitors = monitors;
2437             fullscreenUseMainMonitor = useMainMonitor;
2438             if( fullScreenAction.init(fullscreen) ) {
2439                 if( fullScreenAction.fsOn() && isOffscreenInstance(WindowImpl.this, parentWindow) ) {
2440                     // enable fullscreen on offscreen instance
2441                     if(null != parentWindow) {
2442                         nfs_parent = parentWindow;
2443                         reparentWindow(null, -1, -1, REPARENT_HINT_FORCE_RECREATION | REPARENT_HINT_BECOMES_VISIBLE);
2444                     } else {
2445                         throw new InternalError("Offscreen instance w/o parent unhandled");
2446                     }
2447                 }
2448
2449                 runOnEDTIfAvail(true, fullScreenAction);
2450
2451                 if(!fullScreenAction.fsOn() && null != nfs_parent) {
2452                     // disable fullscreen on offscreen instance
2453                     reparentWindow(nfs_parent, -1, -1, REPARENT_HINT_FORCE_RECREATION | REPARENT_HINT_BECOMES_VISIBLE);
2454                     nfs_parent = null;
2455                 }
2456             }
2457             return this.fullscreen;
2458         }
2459     }
2460
2461     /** Notify WindowDriver about the finished monitor mode change. */
2462     protected void monitorModeChanged(MonitorEvent me, boolean success) {
2463     }
2464
2465     private class MonitorModeListenerImpl implements MonitorModeListener {
2466         boolean animatorPaused = false;
2467         boolean hidden = false;
2468         boolean hadFocus = false;
2469         boolean fullscreenPaused = false;
2470         List<MonitorDevice> _fullscreenMonitors = null;
2471         boolean _fullscreenUseMainMonitor = true;
2472
2473         @Override
2474         public void monitorModeChangeNotify(MonitorEvent me) {
2475             hadFocus = hasFocus();
2476             final boolean isOSX = NativeWindowFactory.TYPE_MACOSX == NativeWindowFactory.getNativeWindowType(true);
2477             final boolean quirkFSPause = fullscreen && isReconfigureFlagSupported(FLAG_IS_FULLSCREEN_SPAN);
2478             final boolean quirkHide = !quirkFSPause && !fullscreen && isVisible() && isOSX;
2479             if(DEBUG_IMPLEMENTATION) {
2480                 System.err.println("Window.monitorModeChangeNotify: hadFocus "+hadFocus+", qFSPause "+quirkFSPause+", qHide "+quirkHide+", "+me+" @ "+Thread.currentThread().getName());
2481             }
2482
2483             if(null!=lifecycleHook) {
2484                 animatorPaused = lifecycleHook.pauseRenderingAction();
2485             }
2486             if( quirkFSPause ) {
2487                 if(DEBUG_IMPLEMENTATION) {
2488                     System.err.println("Window.monitorModeChangeNotify: FS Pause");
2489                 }
2490                 fullscreenPaused = true;
2491                 _fullscreenMonitors = fullscreenMonitors;
2492                 _fullscreenUseMainMonitor = fullscreenUseMainMonitor;
2493                 setFullscreenImpl(false, true, null);
2494             }
2495             if( quirkHide ) {
2496                 // hiding & showing the window around mode-change solves issues w/ OSX,
2497                 // where the content would be black until a resize.
2498                 hidden = true;
2499                 WindowImpl.this.setVisible(false);
2500             }
2501         }
2502
2503         @Override
2504         public void monitorModeChanged(MonitorEvent me, boolean success) {
2505             if(!animatorPaused && success && null!=lifecycleHook) {
2506                 // Didn't pass above notify method. probably detected screen change after it happened.
2507                 animatorPaused = lifecycleHook.pauseRenderingAction();
2508             }
2509             if(DEBUG_IMPLEMENTATION) {
2510                 System.err.println("Window.monitorModeChanged.0: success: "+success+", hadFocus "+hadFocus+", animPaused "+animatorPaused+
2511                                    ", hidden "+hidden+", FS "+fullscreen+", FS-paused "+fullscreenPaused+
2512                                    " @ "+Thread.currentThread().getName());
2513                 System.err.println("Window.monitorModeChanged.0: "+getScreen());
2514                 System.err.println("Window.monitorModeChanged.0: "+me);
2515             }
2516             WindowImpl.this.monitorModeChanged(me, success);
2517
2518             if( success && !fullscreen && !fullscreenPaused ) {
2519                 // Simply move/resize window to fit in virtual screen if required
2520                 final RectangleImmutable viewport = screen.getViewportInWindowUnits();
2521                 if( viewport.getWidth() > 0 && viewport.getHeight() > 0 ) { // failsafe
2522                     final RectangleImmutable rect = new Rectangle(getX(), getY(), getWidth(), getHeight());
2523                     final RectangleImmutable isect = viewport.intersection(rect);
2524                     if ( getHeight() > isect.getHeight()  ||
2525                          getWidth() > isect.getWidth() ) {
2526                         if(DEBUG_IMPLEMENTATION) {
2527                             System.err.println("Window.monitorModeChanged.1: Non-FS - Fit window "+rect+" into screen viewport "+viewport+
2528                                                ", due to minimal intersection "+isect);
2529                         }
2530                         definePosition(viewport.getX(), viewport.getY()); // set pos for setVisible(..) or createNative(..) - reduce EDT roundtrip
2531                         setSize(viewport.getWidth(), viewport.getHeight(), true /* force */);
2532                     }
2533                 }
2534             } else if( fullscreenPaused ) {
2535                 if(DEBUG_IMPLEMENTATION) {
2536                     System.err.println("Window.monitorModeChanged.2: FS Restore");
2537                 }
2538                 setFullscreenImpl(true, _fullscreenUseMainMonitor, _fullscreenMonitors);
2539                 fullscreenPaused = false;
2540                 _fullscreenMonitors = null;
2541                 _fullscreenUseMainMonitor = true;
2542             } else if( success && fullscreen && null != fullscreenMonitors ) {
2543                 // If changed monitor is part of this fullscreen mode, reset size! (Bug 771)
2544                 final MonitorDevice md = me.getMonitor();
2545                 if( fullscreenMonitors.contains(md) ) {
2546                     final Rectangle viewportInWindowUnits = new Rectangle();
2547                     MonitorDevice.unionOfViewports(null, viewportInWindowUnits, fullscreenMonitors);
2548                     if(DEBUG_IMPLEMENTATION) {
2549                         final RectangleImmutable winBounds = WindowImpl.this.getBounds();
2550                         System.err.println("Window.monitorModeChanged.3: FS Monitor Match: Fit window "+winBounds+" into new viewport union "+viewportInWindowUnits+" [window], provoked by "+md);
2551                     }
2552                     definePosition(viewportInWindowUnits.getX(), viewportInWindowUnits.getY()); // set pos for setVisible(..) or createNative(..) - reduce EDT roundtrip
2553                     setSize(viewportInWindowUnits.getWidth(), viewportInWindowUnits.getHeight(), true /* force */);
2554                 }
2555             }
2556             if( hidden ) {
2557                 WindowImpl.this.setVisible(true);
2558                 hidden = false;
2559             }
2560             sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener
2561             if(animatorPaused) {
2562                 lifecycleHook.resumeRenderingAction();
2563             }
2564             if( hadFocus ) {
2565                 requestFocus(true);
2566             }
2567             if(DEBUG_IMPLEMENTATION) {
2568                 System.err.println("Window.monitorModeChanged.X: @ "+Thread.currentThread().getName()+", this: "+WindowImpl.this);
2569             }
2570         }
2571     }
2572     private final MonitorModeListenerImpl monitorModeListenerImpl = new MonitorModeListenerImpl();
2573
2574
2575     //----------------------------------------------------------------------
2576     // Child Window Management
2577     //
2578
2579     @Override
2580     public final boolean removeChild(NativeWindow win) {
2581         synchronized(childWindowsLock) {
2582             return childWindows.remove(win);
2583         }
2584     }
2585
2586     @Override
2587     public final boolean addChild(NativeWindow win) {
2588         if (win == null) {
2589             return false;
2590         }
2591         synchronized(childWindowsLock) {
2592             return childWindows.add(win);
2593         }
2594     }
2595
2596     //----------------------------------------------------------------------
2597     // Generic Event Support
2598     //
2599     private void doEvent(boolean enqueue, boolean wait, com.jogamp.newt.event.NEWTEvent event) {
2600         boolean done = false;
2601
2602         if(!enqueue) {
2603             done = consumeEvent(event);
2604             wait = done; // don't wait if event can't be consumed now
2605         }
2606
2607         if(!done) {
2608             enqueueEvent(wait, event);
2609         }
2610     }
2611
2612     @Override
2613     public final void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event) {
2614         if(isNativeValid()) {
2615             ((DisplayImpl)screen.getDisplay()).enqueueEvent(wait, event);
2616         }
2617     }
2618
2619     @Override
2620     public final boolean consumeEvent(NEWTEvent e) {
2621         switch(e.getEventType()) {
2622             // special repaint treatment
2623             case WindowEvent.EVENT_WINDOW_REPAINT:
2624                 // queue repaint event in case window is locked, ie in operation
2625                 if( null != windowLock.getOwner() ) {
2626                     // make sure only one repaint event is queued
2627                     if(!repaintQueued) {
2628                         repaintQueued=true;
2629                         final boolean discardTO = QUEUED_EVENT_TO <= System.currentTimeMillis()-e.getWhen();
2630                         if(DEBUG_IMPLEMENTATION) {
2631                             System.err.println("Window.consumeEvent: REPAINT "+Thread.currentThread().getName()+" - queued "+e+", discard-to "+discardTO);
2632                             // Thread.dumpStack();
2633                         }
2634                         return discardTO; // discardTO:=true -> consumed
2635                     }
2636                     return true;
2637                 }
2638                 repaintQueued=false; // no repaint event queued
2639                 break;
2640
2641             // common treatment
2642             case WindowEvent.EVENT_WINDOW_RESIZED:
2643                 // queue event in case window is locked, ie in operation
2644                 if( null != windowLock.getOwner() ) {
2645                     final boolean discardTO = QUEUED_EVENT_TO <= System.currentTimeMillis()-e.getWhen();
2646                     if(DEBUG_IMPLEMENTATION) {
2647                         System.err.println("Window.consumeEvent: RESIZED "+Thread.currentThread().getName()+" - queued "+e+", discard-to "+discardTO);
2648                         // Thread.dumpStack();
2649                     }
2650                     return discardTO; // discardTO:=true -> consumed
2651                 }
2652                 break;
2653             default:
2654                 break;
2655         }
2656         if(e instanceof WindowEvent) {
2657             consumeWindowEvent((WindowEvent)e);
2658         } else if(e instanceof KeyEvent) {
2659             consumeKeyEvent((KeyEvent)e);
2660         } else if(e instanceof MouseEvent) {
2661             consumePointerEvent((MouseEvent)e);
2662         } else {
2663             throw new NativeWindowException("Unexpected NEWTEvent type " + e);
2664         }
2665         return true;
2666     }
2667
2668     //
2669     // MouseListener/Event Support
2670     //
2671
2672     //
2673     // Native MouseEvents pre-processed to be enqueued or consumed directly
2674     //
2675
2676     public final void sendMouseEvent(final short eventType, final int modifiers,
2677                                final int x, final int y, final short button, final float rotation) {
2678         doMouseEvent(false, false, eventType, modifiers, x, y, button, MouseEvent.getRotationXYZ(rotation, modifiers), 1f);
2679     }
2680     public final void enqueueMouseEvent(final boolean wait, final short eventType, final int modifiers,
2681                                         final int x, final int y, final short button, final float rotation) {
2682         doMouseEvent(true, wait, eventType, modifiers, x, y, button, MouseEvent.getRotationXYZ(rotation, modifiers), 1f);
2683     }
2684     protected final void doMouseEvent(final boolean enqueue, final boolean wait, final short eventType, final int modifiers,
2685                                       final int x, final int y, final short button, final float rotation) {
2686         doMouseEvent(enqueue, wait, eventType, modifiers, x, y, button, MouseEvent.getRotationXYZ(rotation, modifiers), 1f);
2687     }
2688     /**
2689     public final void sendMouseEvent(final short eventType, final int modifiers,
2690                                      final int x, final int y, final short button, final float[] rotationXYZ, final float rotationScale) {
2691         doMouseEvent(false, false, eventType, modifiers, x, y, button, rotationXYZ, rotationScale);
2692     }
2693     public final void enqueueMouseEvent(final boolean wait, final short eventType, final int modifiers,
2694                                         final int x, final int y, final short button, final float[] rotationXYZ, final float rotationScale) {
2695         doMouseEvent(true, wait, eventType, modifiers, x, y, button, rotationXYZ, rotationScale);
2696     } */
2697
2698     /**
2699      * Send mouse event (one-pointer) either to be directly consumed or to be enqueued
2700      *
2701      * @param enqueue if true, event will be {@link #enqueueEvent(boolean, NEWTEvent) enqueued},
2702      *                otherwise {@link #consumeEvent(NEWTEvent) consumed} directly.
2703      * @param wait if true wait until {@link #consumeEvent(NEWTEvent) consumed}.
2704      */
2705     protected void doMouseEvent(final boolean enqueue, final boolean wait, final short eventType, final int modifiers,
2706                                 final int x, final int y, final short button, final float[] rotationXYZ, final float rotationScale) {
2707         if( 0 > button || button > MouseEvent.BUTTON_COUNT ) {
2708             throw new NativeWindowException("Invalid mouse button number" + button);
2709         }
2710         doPointerEvent(enqueue, wait, constMousePointerTypes, eventType, modifiers,
2711                        0 /*actionIdx*/, new short[] { (short)0 }, button,
2712                        new int[]{x}, new int[]{y}, new float[]{0f} /*pressure*/,
2713                        1f /*maxPressure*/, rotationXYZ, rotationScale);
2714     }
2715
2716     /**
2717      * Send multiple-pointer event either to be directly consumed or to be enqueued
2718      * <p>
2719      * The index for the element of multiple-pointer arrays represents the pointer which triggered the event
2720      * is passed via <i>actionIdx</i>.
2721      * </p>
2722      * <p>
2723      * The given pointer names, <code>pNames</code>, are mapped to consecutive pointer IDs starting w/ 0
2724      * using a hash-map if <code>normalPNames</code> is <code>false</code>.
2725      * Otherwise a simple <code>int</code> to <code>short</code> type cast is performed.
2726      * </p>
2727      * <p>
2728      * See {@link #doPointerEvent(boolean, boolean, PointerType[], short, int, int, short[], short, int[], int[], float[], float, float[], float)}
2729      * for details!
2730      * </p>
2731      *
2732      * @param enqueue if true, event will be {@link #enqueueEvent(boolean, NEWTEvent) enqueued},
2733      *                otherwise {@link #consumeEvent(NEWTEvent) consumed} directly.
2734      * @param wait if true wait until {@link #consumeEvent(NEWTEvent) consumed}.
2735      * @param pTypes {@link MouseEvent.PointerType} for each pointer (multiple pointer)
2736      * @param eventType
2737      * @param modifiers
2738      * @param actionIdx index of multiple-pointer arrays representing the pointer which triggered the event
2739      * @param normalPNames see pName below.
2740      * @param pNames Pointer name for each pointer (multiple pointer).
2741      *        We assume consecutive pointer names starting w/ 0 if <code>normalPIDs</code> is <code>true</code>.
2742      *        Otherwise we hash-map the values during state pressed to retrieve the normal ID.
2743      * @param pX X-axis for each pointer (multiple pointer)
2744      * @param pY Y-axis for each pointer (multiple pointer)
2745      * @param pPressure Pressure for each pointer (multiple pointer)
2746      * @param maxPressure Maximum pointer pressure for all pointer
2747      */
2748     public final void doPointerEvent(final boolean enqueue, final boolean wait,
2749                                      final PointerType[] pTypes, final short eventType, final int modifiers,
2750                                      final int actionIdx, final boolean normalPNames, final int[] pNames,
2751                                      final int[] pX, final int[] pY, final float[] pPressure,
2752                                      float maxPressure, final float[] rotationXYZ, final float rotationScale) {
2753         final int pCount = pNames.length;
2754         final short[] pIDs = new short[pCount];
2755         for(int i=0; i<pCount; i++) {
2756             if( !normalPNames ) {
2757                 // hash map int name -> short idx
2758                 final int sz0 = pName2pID.size();
2759                 final Integer pNameI1 = pName2pID.getOrAdd(Integer.valueOf(pNames[i]));
2760                 final short pID = (short)pName2pID.indexOf(pNameI1);
2761                 pIDs[i] = pID;
2762                 if(DEBUG_MOUSE_EVENT) {
2763                     final int sz1 = pName2pID.size();
2764                     if( sz0 != sz1 ) {
2765                         System.err.println("PointerName2ID[sz "+sz1+"]: Map "+pNameI1+" == "+pID);
2766                     }
2767                 }
2768                 if( MouseEvent.EVENT_MOUSE_RELEASED == eventType ) {
2769                     pName2pID.remove(pNameI1);
2770                     if(DEBUG_MOUSE_EVENT) {
2771                         System.err.println("PointerName2ID[sz "+pName2pID.size()+"]: Unmap "+pNameI1+" == "+pID);
2772                     }
2773                 }
2774             } else {
2775                 // simple type cast
2776                 pIDs[i] = (short)pNames[i];
2777             }
2778         }
2779         final short button = 0 < pCount ? (short) ( pIDs[0] + 1 ) : (short)0;
2780         doPointerEvent(enqueue, wait, pTypes, eventType, modifiers, actionIdx, pIDs, button,
2781                        pX, pY, pPressure, maxPressure, rotationXYZ, rotationScale);
2782     }
2783
2784     /**
2785      * Send multiple-pointer event either to be directly consumed or to be enqueued.
2786      * <p>
2787      * Pointer/Mouse Processing Pass 1 (Pass 2 is performed in {@link #consumePointerEvent(MouseEvent)}.
2788      * </p>
2789      * <p>
2790      * Usually directly called by event source to enqueue and process event.
2791      * </p>
2792      * <p>
2793      * The index for the element of multiple-pointer arrays represents the pointer which triggered the event
2794      * is passed via <i>actionIdx</i>.
2795      * </p>
2796      * <p>
2797      * <ul>
2798      * <li>Determine ENTERED/EXITED state</li>
2799      * <li>Remove redundant move/drag events</li>
2800      * <li>Reset states if applicable</li>
2801      * <li>Drop exterior events</li>
2802      * <li>Determine CLICK COUNT</li>
2803      * <li>Ignore sent CLICKED</li>
2804      * <li>Track buttonPressed incl. buttonPressedMask</li>
2805      * <li>Synthesize DRAGGED event (from MOVED if pointer is pressed)</li>
2806      * </ul>
2807      * </p>
2808      *
2809      * @param enqueue if true, event will be {@link #enqueueEvent(boolean, NEWTEvent) enqueued},
2810      *                otherwise {@link #consumeEvent(NEWTEvent) consumed} directly.
2811      * @param wait if true wait until {@link #consumeEvent(NEWTEvent) consumed}.
2812      * @param pTypes {@link MouseEvent.PointerType} for each pointer (multiple pointer)
2813      * @param eventType
2814      * @param modifiers
2815      * @param pActionIdx index of multiple-pointer arrays representing the pointer which triggered the event
2816      * @param pID Pointer ID for each pointer (multiple pointer). We assume consecutive pointerIDs starting w/ 0.
2817      * @param button Corresponding mouse-button, a button of 0 denotes no activity, i.e. {@link PointerType#Mouse} move.
2818      * @param pX X-axis for each pointer (multiple pointer)
2819      * @param pY Y-axis for each pointer (multiple pointer)
2820      * @param pPressure Pressure for each pointer (multiple pointer)
2821      * @param maxPressure Maximum pointer pressure for all pointer
2822      */
2823     public final void doPointerEvent(final boolean enqueue, final boolean wait,
2824                                      final PointerType[] pTypes, final short eventType, int modifiers,
2825                                      final int pActionIdx, final short[] pID, final short buttonIn, final int[] pX, final int[] pY,
2826                                      final float[] pPressure, final float maxPressure, final float[] rotationXYZ, final float rotationScale) {
2827         final long when = System.currentTimeMillis();
2828         final int pCount = pTypes.length;
2829
2830         if( 0 > pActionIdx || pActionIdx >= pCount) {
2831             throw new IllegalArgumentException("actionIdx out of bounds [0.."+(pCount-1)+"]");
2832         }
2833         if( 0 < pActionIdx ) {
2834             // swap values to make idx 0 the triggering pointer
2835             {
2836                 final PointerType aType = pTypes[pActionIdx];
2837                 pTypes[pActionIdx] = pTypes[0];
2838                 pTypes[0] = aType;
2839             }
2840             {
2841                 final short s = pID[pActionIdx];
2842                 pID[pActionIdx] = pID[0];
2843                 pID[0] = s;
2844             }
2845             {
2846                 int s = pX[pActionIdx];
2847                 pX[pActionIdx] = pX[0];
2848                 pX[0] = s;
2849                 s = pY[pActionIdx];
2850                 pY[pActionIdx] = pY[0];
2851                 pY[0] = s;
2852             }
2853             {
2854                 final float aPress = pPressure[pActionIdx];
2855                 pPressure[pActionIdx] = pPressure[0];
2856                 pPressure[0] = aPress;
2857             }
2858         }
2859         final short button;
2860         {
2861             // validate button
2862             if( 0 <= buttonIn && buttonIn <= com.jogamp.newt.event.MouseEvent.BUTTON_COUNT ) { // we allow button==0 for no button, i.e. mouse-ptr move
2863                 button = buttonIn;
2864             } else {
2865                 button = com.jogamp.newt.event.MouseEvent.BUTTON1;
2866             }
2867         }
2868
2869         //
2870         // - Determine ENTERED/EXITED state
2871         // - Remove redundant move/drag events
2872         // - Reset states if applicable
2873         //
2874         int x = pX[0];
2875         int y = pY[0];
2876         final boolean insideSurface = x >= 0 && y >= 0 && x < getSurfaceWidth() && y < getSurfaceHeight();
2877         final Point movePositionP0 = pState1.getMovePosition(pID[0]);
2878         switch( eventType ) {
2879             case MouseEvent.EVENT_MOUSE_EXITED:
2880                 if( pState1.dragging ) {
2881                     // Drop mouse EXIT if dragging, i.e. due to exterior dragging outside of window.
2882                     // NOTE-1: X11 produces the 'premature' EXIT, however it also produces 'EXIT' after exterior dragging!
2883                     // NOTE-2: consumePointerEvent(MouseEvent) will synthesize a missing EXIT event!
2884                     if(DEBUG_MOUSE_EVENT) {
2885                         System.err.println("doPointerEvent: drop "+MouseEvent.getEventTypeString(eventType)+" due to dragging: "+pState1);
2886                     }
2887                     return;
2888                 }
2889                 if( null != movePositionP0 ) {
2890                     if( x==-1 && y==-1 ) {
2891                         x = movePositionP0.getX();
2892                         y = movePositionP0.getY();
2893                     }
2894                     movePositionP0.set(0, 0);
2895                 }
2896                 // Fall through intended!
2897
2898             case MouseEvent.EVENT_MOUSE_ENTERED:
2899                 if( eventType == MouseEvent.EVENT_MOUSE_ENTERED ) {
2900                     pState1.insideSurface = true;
2901                     pState1.exitSent = false;
2902                 } else {
2903                     pState1.insideSurface = false;
2904                     pState1.exitSent = true;
2905                 }
2906                 pState1.clearButton();
2907                 if( pTypes[0] != PointerType.Mouse ) {
2908                     // Drop !MOUSE ENTER/EXIT Events - Safeguard for non compliant implementations only.
2909                     if(DEBUG_MOUSE_EVENT) {
2910                         System.err.println("doPointerEvent: drop "+MouseEvent.getEventTypeString(eventType)+" due to !Mouse but "+pTypes[0]+": "+pState1);
2911                     }
2912                     return;
2913                 }
2914                 // clip coordinates to window dimension
2915                 x = Math.min(Math.max(x,  0), getSurfaceWidth()-1);
2916                 y = Math.min(Math.max(y,  0), getSurfaceHeight()-1);
2917                 break;
2918
2919             case MouseEvent.EVENT_MOUSE_MOVED:
2920             case MouseEvent.EVENT_MOUSE_DRAGGED:
2921                 if( null != movePositionP0 ) {
2922                     if( movePositionP0.getX() == x && movePositionP0.getY() == y ) {
2923                         // Drop same position
2924                         if(DEBUG_MOUSE_EVENT) {
2925                             System.err.println("doPointerEvent: drop "+MouseEvent.getEventTypeString(eventType)+" w/ same position: "+movePositionP0+", "+pState1);
2926                         }
2927                         return;
2928                     }
2929                     movePositionP0.set(x, y);
2930                 }
2931
2932                 // Fall through intended !
2933
2934             default:
2935                 if( pState1.insideSurface != insideSurface ) {
2936                     // ENTER/EXIT!
2937                     pState1.insideSurface = insideSurface;
2938                     if( insideSurface ) {
2939                         pState1.exitSent = false;
2940                     }
2941                     pState1.clearButton();
2942                 }
2943         }
2944
2945         //
2946         // Drop exterior events if not dragging pointer and not EXIT event
2947         // Safeguard for non compliant implementations!
2948         //
2949         if( !pState1.dragging && !insideSurface && MouseEvent.EVENT_MOUSE_EXITED != eventType ) {
2950             if(DEBUG_MOUSE_EVENT) {
2951                 System.err.println("doPointerEvent: drop: "+MouseEvent.getEventTypeString(eventType)+
2952                                    ", mod "+modifiers+", pos "+x+"/"+y+", button "+button+", lastMousePosition: "+movePositionP0+", insideWindow "+insideSurface+", "+pState1);
2953             }
2954             return; // .. invalid ..
2955         }
2956         if(DEBUG_MOUSE_EVENT) {
2957             System.err.println("doPointerEvent: enqueue "+enqueue+", wait "+wait+", "+MouseEvent.getEventTypeString(eventType)+
2958                                ", mod "+modifiers+", pos "+x+"/"+y+", button "+button+", lastMousePosition: "+movePositionP0+", "+pState1);
2959         }
2960
2961         final int buttonMask = InputEvent.getButtonMask(button);
2962         modifiers |= buttonMask; // Always add current button to modifier mask (Bug 571)
2963         modifiers |= pState1.buttonPressedMask; // Always add currently pressed mouse buttons to modifier mask
2964
2965         if( isPointerConfined() ) {
2966             modifiers |= InputEvent.CONFINED_MASK;
2967         }
2968         if( !isPointerVisible() ) {
2969             modifiers |= InputEvent.INVISIBLE_MASK;
2970         }
2971
2972         pX[0] = x;
2973         pY[0] = y;
2974
2975         //
2976         // - Determine CLICK COUNT
2977         // - Ignore sent CLICKED
2978         // - Track buttonPressed incl. buttonPressedMask
2979         // - Synthesize DRAGGED event (from MOVED if pointer is pressed)
2980         //
2981         final MouseEvent e;
2982         switch( eventType ) {
2983             case MouseEvent.EVENT_MOUSE_CLICKED:
2984                 e = null;
2985                 break;
2986
2987             case MouseEvent.EVENT_MOUSE_PRESSED:
2988                 if( 0 >= pPressure[0] ) {
2989                     pPressure[0] = maxPressure;
2990                 }
2991                 pState1.buttonPressedMask |= buttonMask;
2992                 if( 1 == pCount ) {
2993                     if( when - pState1.lastButtonPressTime < MouseEvent.getClickTimeout() ) {
2994                         pState1.lastButtonClickCount++;
2995                     } else {
2996                         pState1.lastButtonClickCount=(short)1;
2997                     }
2998                     pState1.lastButtonPressTime = when;
2999                     pState1.buttonPressed = button;
3000                     e = new MouseEvent(eventType, this, when, modifiers, pTypes, pID,
3001                                        pX, pY, pPressure, maxPressure, button, pState1.lastButtonClickCount, rotationXYZ, rotationScale);
3002                 } else {
3003                     e = new MouseEvent(eventType, this, when, modifiers, pTypes, pID,
3004                                        pX, pY, pPressure, maxPressure, button, (short)1, rotationXYZ, rotationScale);
3005                 }
3006                 break;
3007             case MouseEvent.EVENT_MOUSE_RELEASED:
3008                 pState1.buttonPressedMask &= ~buttonMask;
3009                 if( 1 == pCount ) {
3010                     e = new MouseEvent(eventType, this, when, modifiers, pTypes, pID,
3011                                        pX, pY, pPressure, maxPressure, button, pState1.lastButtonClickCount, rotationXYZ, rotationScale);
3012                     if( when - pState1.lastButtonPressTime >= MouseEvent.getClickTimeout() ) {
3013                         pState1.lastButtonClickCount = (short)0;
3014                         pState1.lastButtonPressTime = 0;
3015                     }
3016                     pState1.buttonPressed = 0;
3017                     pState1.dragging = false;
3018                 } else {
3019                     e = new MouseEvent(eventType, this, when, modifiers, pTypes, pID,
3020                                        pX, pY, pPressure, maxPressure, button, (short)1, rotationXYZ, rotationScale);
3021                     if( 0 == pState1.buttonPressedMask ) {
3022                         pState1.clearButton();
3023                     }
3024                 }
3025                 if( null != movePositionP0 ) {
3026                     movePositionP0.set(0, 0);
3027                 }
3028     &