Jogamp
Bug 741 HiDPI: Simplify ScalableSurface (2): Add request pixelScale API entry, fixed...
[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[] getRequestedSurfaceScale(final int[] result) {
1977         System.arraycopy(reqPixelScale, 0, result, 0, 2);
1978         return result;
1979     }
1980
1981     @Override
1982     public final int[] getCurrentSurfaceScale(final int[] result) {
1983         System.arraycopy(hasPixelScale, 0, result, 0, 2);
1984         return result;
1985     }
1986
1987     protected final boolean autoPosition() { return autoPosition; }
1988
1989     /** Sets the position fields {@link #x} and {@link #y} in window units to the given values and {@link #autoPosition} to false. */
1990     protected final void definePosition(int x, int y) {
1991         if(DEBUG_IMPLEMENTATION) {
1992             System.err.println("definePosition: "+this.x+"/"+this.y+" -> "+x+"/"+y);
1993             // Thread.dumpStack();
1994         }
1995         autoPosition = false;
1996         this.x = x; this.y = y;
1997     }
1998
1999     /**
2000      * Sets the size fields {@link #winWidth} and {@link #winHeight} in window units to the given values
2001      * and {@link #pixWidth} and {@link #pixHeight} in pixel units according to {@link #convertToPixelUnits(int[])}.
2002      */
2003     protected final void defineSize(int winWidth, int winHeight) {
2004         final int pixWidth = winWidth * getPixelScaleX();   // FIXME HiDPI: Shortcut, may need to adjust if we change scaling methodology
2005         final int pixHeight = winHeight * getPixelScaleY();
2006         if(DEBUG_IMPLEMENTATION) {
2007             System.err.println("defineSize: win["+this.winWidth+"x"+this.winHeight+" -> "+winWidth+"x"+winHeight+
2008                                "], pixel["+this.pixWidth+"x"+this.pixHeight+" -> "+pixWidth+"x"+pixHeight+"]");
2009             // Thread.dumpStack();
2010         }
2011         this.winWidth = winWidth; this.winHeight = winHeight;
2012         this.pixWidth = pixWidth; this.pixHeight = pixHeight;
2013     }
2014
2015     @Override
2016     public final boolean isVisible() {
2017         return visible;
2018     }
2019
2020     @Override
2021     public final boolean isFullscreen() {
2022         return fullscreen;
2023     }
2024
2025     //----------------------------------------------------------------------
2026     // Window
2027     //
2028
2029     @Override
2030     public final Window getDelegatedWindow() {
2031         return this;
2032     }
2033
2034     //----------------------------------------------------------------------
2035     // WindowImpl
2036     //
2037
2038     /**
2039      * If the implementation is capable of detecting a device change
2040      * return true and clear the status/reason of the change.
2041      */
2042     public boolean hasDeviceChanged() {
2043         return false;
2044     }
2045
2046     public final LifecycleHook getLifecycleHook() {
2047         return lifecycleHook;
2048     }
2049
2050     public final LifecycleHook setLifecycleHook(LifecycleHook hook) {
2051         LifecycleHook old = lifecycleHook;
2052         lifecycleHook = hook;
2053         return old;
2054     }
2055
2056     /**
2057      * If this Window actually wraps a {@link NativeSurface} from another instance or toolkit,
2058      * it will return such reference. Otherwise returns null.
2059      */
2060     public NativeSurface getWrappedSurface() {
2061         return null;
2062     }
2063
2064     @Override
2065     public final void setWindowDestroyNotifyAction(Runnable r) {
2066         windowDestroyNotifyAction = r;
2067     }
2068
2069     protected final long getParentWindowHandle() {
2070         return isFullscreen() ? 0 : parentWindowHandle;
2071     }
2072
2073     @Override
2074     public final String toString() {
2075         StringBuilder sb = new StringBuilder();
2076
2077         sb.append(getClass().getName()+"[Config "+config+
2078                     ",\n "+screen+
2079                     ",\n ParentWindow "+parentWindow+
2080                     ",\n ParentWindowHandle "+toHexString(parentWindowHandle)+" ("+(0!=getParentWindowHandle())+")"+
2081                     ",\n WindowHandle "+toHexString(getWindowHandle())+
2082                     ",\n SurfaceHandle "+toHexString(getSurfaceHandle())+ " (lockedExt window "+windowLock.isLockedByOtherThread()+", surface "+isSurfaceLockedByOtherThread()+")"+
2083                     ",\n window["+getX()+"/"+getY()+" (auto "+autoPosition()+") "+getWidth()+"x"+getHeight()+"], pixel["+getSurfaceWidth()+"x"+getSurfaceHeight()+
2084                     "],\n Visible "+isVisible()+", focus "+hasFocus()+
2085                     ",\n Undecorated "+undecorated+" ("+isUndecorated()+")"+
2086                     ",\n AlwaysOnTop "+alwaysOnTop+", Fullscreen "+fullscreen+
2087                     ",\n WrappedSurface "+getWrappedSurface()+
2088                     ",\n ChildWindows "+childWindows.size());
2089
2090         sb.append(", SurfaceUpdatedListeners num "+surfaceUpdatedHelper.size()+" [");
2091         for (int i = 0; i < surfaceUpdatedHelper.size(); i++ ) {
2092           sb.append(surfaceUpdatedHelper.get(i)+", ");
2093         }
2094         sb.append("], WindowListeners num "+windowListeners.size()+" [");
2095         for (int i = 0; i < windowListeners.size(); i++ ) {
2096           sb.append(windowListeners.get(i)+", ");
2097         }
2098         sb.append("], MouseListeners num "+mouseListeners.size()+" [");
2099         for (int i = 0; i < mouseListeners.size(); i++ ) {
2100           sb.append(mouseListeners.get(i)+", ");
2101         }
2102         sb.append("], PointerGestures default "+defaultGestureHandlerEnabled+", custom "+pointerGestureHandler.size()+" [");
2103         for (int i = 0; i < pointerGestureHandler.size(); i++ ) {
2104           sb.append(pointerGestureHandler.get(i)+", ");
2105         }
2106         sb.append("], KeyListeners num "+keyListeners.size()+" [");
2107         for (int i = 0; i < keyListeners.size(); i++ ) {
2108           sb.append(keyListeners.get(i)+", ");
2109         }
2110         sb.append("], windowLock "+windowLock+", surfaceLockCount "+surfaceLockCount+"]");
2111         return sb.toString();
2112     }
2113
2114     protected final void setWindowHandle(long handle) {
2115         windowHandle = handle;
2116     }
2117
2118     @Override
2119     public final void runOnEDTIfAvail(boolean wait, final Runnable task) {
2120         if( windowLock.isOwner( Thread.currentThread() ) ) {
2121             task.run();
2122         } else {
2123             ( (DisplayImpl) screen.getDisplay() ).runOnEDTIfAvail(wait, task);
2124         }
2125     }
2126
2127     private final Runnable requestFocusAction = new Runnable() {
2128         @Override
2129         public final void run() {
2130             if(DEBUG_IMPLEMENTATION) {
2131                 System.err.println("Window.RequestFocusAction: force 0 - ("+getThreadName()+"): "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle));
2132             }
2133             WindowImpl.this.requestFocusImpl(false);
2134         }
2135     };
2136     private final Runnable requestFocusActionForced = new Runnable() {
2137         @Override
2138         public final void run() {
2139             if(DEBUG_IMPLEMENTATION) {
2140                 System.err.println("Window.RequestFocusAction: force 1 - ("+getThreadName()+"): "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle));
2141             }
2142             WindowImpl.this.requestFocusImpl(true);
2143         }
2144     };
2145
2146     @Override
2147     public final boolean hasFocus() {
2148         return hasFocus;
2149     }
2150
2151     @Override
2152     public final void requestFocus() {
2153         requestFocus(true);
2154     }
2155
2156     @Override
2157     public final void requestFocus(boolean wait) {
2158         requestFocus(wait /* wait */, false /* skipFocusAction */, brokenFocusChange /* force */);
2159     }
2160
2161     private void requestFocus(boolean wait, boolean skipFocusAction, boolean force) {
2162         if( isNativeValid() &&
2163             ( force || !hasFocus() ) &&
2164             ( skipFocusAction || !focusAction() ) ) {
2165             runOnEDTIfAvail(wait, force ? requestFocusActionForced : requestFocusAction);
2166         }
2167     }
2168
2169     /** Internally forcing request focus on current thread */
2170     private void requestFocusInt(boolean skipFocusAction) {
2171         if( skipFocusAction || !focusAction() ) {
2172             if(DEBUG_IMPLEMENTATION) {
2173                 System.err.println("Window.RequestFocusInt: forcing - ("+getThreadName()+"): skipFocusAction "+skipFocusAction+", focus "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle));
2174             }
2175             requestFocusImpl(true);
2176         }
2177     }
2178
2179     @Override
2180     public final void setFocusAction(FocusRunnable focusAction) {
2181         this.focusAction = focusAction;
2182     }
2183
2184     private boolean focusAction() {
2185         if(DEBUG_IMPLEMENTATION) {
2186             System.err.println("Window.focusAction() START - "+getThreadName()+", focusAction: "+focusAction+" - windowHandle "+toHexString(getWindowHandle()));
2187         }
2188         boolean res;
2189         if(null!=focusAction) {
2190             res = focusAction.run();
2191         } else {
2192             res = false;
2193         }
2194         if(DEBUG_IMPLEMENTATION) {
2195             System.err.println("Window.focusAction() END - "+getThreadName()+", focusAction: "+focusAction+" - windowHandle "+toHexString(getWindowHandle())+", res: "+res);
2196         }
2197         return res;
2198     }
2199
2200     protected final void setBrokenFocusChange(boolean v) {
2201         brokenFocusChange = v;
2202     }
2203
2204     @Override
2205     public final void setKeyboardFocusHandler(KeyListener l) {
2206         keyboardFocusHandler = l;
2207     }
2208
2209     private class SetPositionAction implements Runnable {
2210         int x, y;
2211
2212         private SetPositionAction(int x, int y) {
2213             this.x = x;
2214             this.y = y;
2215         }
2216
2217         @Override
2218         public final void run() {
2219             final RecursiveLock _lock = windowLock;
2220             _lock.lock();
2221             try {
2222                 if(DEBUG_IMPLEMENTATION) {
2223                     System.err.println("Window setPosition: "+getX()+"/"+getY()+" -> "+x+"/"+y+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle));
2224                 }
2225                 // Let the window be positioned if !fullscreen and position changed or being a child window.
2226                 if ( !isFullscreen() && ( getX() != x || getY() != y || null != getParent()) ) {
2227                     if(isNativeValid()) {
2228                         // this.x/this.y will be set by sizeChanged, triggered by windowing event system
2229                         reconfigureWindowImpl(x, y, getWidth(), getHeight(), getReconfigureFlags(0, isVisible()));
2230                         if( null == parentWindow ) {
2231                             // Wait until custom position is reached within tolerances
2232                             waitForPosition(true, x, y, Window.TIMEOUT_NATIVEWINDOW);
2233                         }
2234                     } else {
2235                         definePosition(x, y); // set pos for createNative(..)
2236                     }
2237                 }
2238             } finally {
2239                 _lock.unlock();
2240             }
2241         }
2242     }
2243
2244     @Override
2245     public void setPosition(int x, int y) {
2246         autoPosition = false;
2247         runOnEDTIfAvail(true, new SetPositionAction(x, y));
2248     }
2249
2250     @Override
2251     public final void setTopLevelPosition(int x, int y) {
2252         setPosition(x + getInsets().getLeftWidth(), y + getInsets().getTopHeight());
2253     }
2254
2255     private class FullScreenAction implements Runnable {
2256         boolean _fullscreen;
2257
2258         private boolean init(boolean fullscreen) {
2259             if(isNativeValid()) {
2260                 this._fullscreen = fullscreen;
2261                 return isFullscreen() != fullscreen;
2262             } else {
2263                 WindowImpl.this.fullscreen = fullscreen; // set current state for createNative(..)
2264                 return false;
2265             }
2266         }
2267         public boolean fsOn() { return _fullscreen; }
2268
2269         @Override
2270         public final void run() {
2271             final RecursiveLock _lock = windowLock;
2272             _lock.lock();
2273             blockInsetsChange = true;
2274             try {
2275                 final int oldX = getX();
2276                 final int oldY = getY();
2277                 final int oldWidth = getWidth();
2278                 final int oldHeight = getHeight();
2279
2280                 int x,y,w,h;
2281
2282                 final RectangleImmutable sviewport = screen.getViewportInWindowUnits(); // window units
2283                 final RectangleImmutable viewport; // window units
2284                 final int fs_span_flag;
2285                 final boolean alwaysOnTopChange;
2286                 if(_fullscreen) {
2287                     if( null == fullscreenMonitors ) {
2288                         if( fullscreenUseMainMonitor ) {
2289                             fullscreenMonitors = new ArrayList<MonitorDevice>();
2290                             fullscreenMonitors.add( getMainMonitor() );
2291                         } else {
2292                             fullscreenMonitors = getScreen().getMonitorDevices();
2293                         }
2294                     }
2295                     {
2296                         final Rectangle viewportInWindowUnits = new Rectangle();
2297                         MonitorDevice.unionOfViewports(null, viewportInWindowUnits, fullscreenMonitors);
2298                         viewport = viewportInWindowUnits;
2299                     }
2300                     if( isReconfigureFlagSupported(FLAG_IS_FULLSCREEN_SPAN) &&
2301                         ( fullscreenMonitors.size() > 1 || sviewport.compareTo(viewport) > 0 ) ) {
2302                         fs_span_flag = FLAG_IS_FULLSCREEN_SPAN;
2303                     } else {
2304                         fs_span_flag = 0;
2305                     }
2306                     nfs_x = oldX;
2307                     nfs_y = oldY;
2308                     nfs_width = oldWidth;
2309                     nfs_height = oldHeight;
2310                     nfs_alwaysOnTop = alwaysOnTop;
2311                     x = viewport.getX();
2312                     y = viewport.getY();
2313                     w = viewport.getWidth();
2314                     h = viewport.getHeight();
2315                     alwaysOnTop = false;
2316                     alwaysOnTopChange = nfs_alwaysOnTop != alwaysOnTop;
2317                 } else {
2318                     fullscreenUseMainMonitor = true;
2319                     fullscreenMonitors = null;
2320                     fs_span_flag = 0;
2321                     viewport = null;
2322                     x = nfs_x;
2323                     y = nfs_y;
2324                     w = nfs_width;
2325                     h = nfs_height;
2326                     alwaysOnTopChange = nfs_alwaysOnTop != alwaysOnTop;
2327                     alwaysOnTop = nfs_alwaysOnTop;
2328
2329                     if(null!=parentWindow) {
2330                         // reset position to 0/0 within parent space
2331                         x = 0;
2332                         y = 0;
2333
2334                         // refit if size is bigger than parent
2335                         if( w > parentWindow.getWidth() ) {
2336                             w = parentWindow.getWidth();
2337                         }
2338                         if( h > parentWindow.getHeight() ) {
2339                             h = parentWindow.getHeight();
2340                         }
2341                     }
2342                 }
2343
2344                 final DisplayImpl display = (DisplayImpl) screen.getDisplay();
2345                 display.dispatchMessagesNative(); // status up2date
2346                 final boolean wasVisible = isVisible();
2347                 final boolean tempInvisible = !_fullscreen && wasVisible && NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(true);
2348
2349                 if(DEBUG_IMPLEMENTATION) {
2350                     System.err.println("Window fs: "+_fullscreen+" "+x+"/"+y+" "+w+"x"+h+", "+isUndecorated()+
2351                                        ", virtl-screenSize: "+sviewport+" [wu], monitorsViewport "+viewport+" [wu]"+
2352                                        ", spanning "+(0!=fs_span_flag)+
2353                                        ", alwaysOnTop "+alwaysOnTop+(alwaysOnTopChange?"*":"")+
2354                                        ", wasVisible "+wasVisible+", tempInvisible "+tempInvisible+
2355                                        ", hasParent "+(null!=parentWindow)+
2356                                        " @ "+Thread.currentThread().getName());
2357                 }
2358
2359                 // fullscreen off: !visible first (fixes X11 unsuccessful return to parent window _and_ wrong window size propagation)
2360                 if( tempInvisible ) {
2361                     setVisibleImpl(false, oldX, oldY, oldWidth, oldHeight);
2362                     WindowImpl.this.waitForVisible(false, false);
2363                     try { Thread.sleep(100); } catch (InterruptedException e) { }
2364                     display.dispatchMessagesNative(); // status up2date
2365                 }
2366
2367                 // Lock parentWindow only during reparenting (attempt)
2368                 final NativeWindow parentWindowLocked;
2369                 if( null != parentWindow ) {
2370                     parentWindowLocked = parentWindow;
2371                     if( NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindowLocked.lockSurface() ) {
2372                         throw new NativeWindowException("Parent surface lock: not ready: "+parentWindow);
2373                     }
2374                 } else {
2375                     parentWindowLocked = null;
2376                 }
2377                 try {
2378                     if(alwaysOnTopChange && _fullscreen) {
2379                         // Enter fullscreen - Disable alwaysOnTop
2380                         reconfigureWindowImpl(oldX, oldY, oldWidth, oldHeight, getReconfigureFlags(FLAG_CHANGE_ALWAYSONTOP, isVisible()));
2381                     }
2382
2383                     WindowImpl.this.fullscreen = _fullscreen;
2384                     reconfigureWindowImpl(x, y, w, h,
2385                                           getReconfigureFlags( ( ( null != parentWindowLocked ) ? FLAG_CHANGE_PARENTING : 0 ) |
2386                                                                fs_span_flag | FLAG_CHANGE_FULLSCREEN | FLAG_CHANGE_DECORATION, isVisible()) );
2387                     if(alwaysOnTopChange && !_fullscreen) {
2388                         // Leave fullscreen - Restore alwaysOnTop
2389                         reconfigureWindowImpl(x, y, w, h, getReconfigureFlags(FLAG_CHANGE_ALWAYSONTOP, isVisible()));
2390                     }
2391                 } finally {
2392                     if(null!=parentWindowLocked) {
2393                         parentWindowLocked.unlockSurface();
2394                     }
2395                 }
2396                 display.dispatchMessagesNative(); // status up2date
2397
2398                 if(wasVisible) {
2399                     if( NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(true) ) {
2400                         // Give sluggy WM's (e.g. Unity) a chance to properly restore window ..
2401                         try { Thread.sleep(100); } catch (InterruptedException e) { }
2402                         display.dispatchMessagesNative(); // status up2date
2403                     }
2404                     setVisibleImpl(true, x, y, w, h);
2405                     boolean ok = 0 <= WindowImpl.this.waitForVisible(true, false);
2406                     if(ok) {
2407                         ok = WindowImpl.this.waitForSize(w, h, false, TIMEOUT_NATIVEWINDOW);
2408                     }
2409                     if(ok && !_fullscreen && null == parentWindow) {
2410                         // Position mismatch shall not lead to fullscreen failure
2411                         WindowImpl.this.waitForPosition(true, x, y, TIMEOUT_NATIVEWINDOW);
2412                     }
2413                     if(ok) {
2414                         requestFocusInt(_fullscreen /* skipFocusAction if fullscreen */);
2415                         display.dispatchMessagesNative(); // status up2date
2416                     }
2417                     if(DEBUG_IMPLEMENTATION) {
2418                         System.err.println("Window fs done: ok " + ok + ", " + WindowImpl.this);
2419                     }
2420                 }
2421             } finally {
2422                 blockInsetsChange = false;
2423                 _lock.unlock();
2424             }
2425             sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener
2426         }
2427     }
2428     private final FullScreenAction fullScreenAction = new FullScreenAction();
2429
2430     @Override
2431     public boolean setFullscreen(boolean fullscreen) {
2432         return setFullscreenImpl(fullscreen, true, null);
2433     }
2434
2435     @Override
2436     public boolean setFullscreen(List<MonitorDevice> monitors) {
2437         return setFullscreenImpl(true, false, monitors);
2438     }
2439
2440     private boolean setFullscreenImpl(boolean fullscreen, boolean useMainMonitor, List<MonitorDevice> monitors) {
2441         synchronized(fullScreenAction) {
2442             fullscreenMonitors = monitors;
2443             fullscreenUseMainMonitor = useMainMonitor;
2444             if( fullScreenAction.init(fullscreen) ) {
2445                 if( fullScreenAction.fsOn() && isOffscreenInstance(WindowImpl.this, parentWindow) ) {
2446                     // enable fullscreen on offscreen instance
2447                     if(null != parentWindow) {
2448                         nfs_parent = parentWindow;
2449                         reparentWindow(null, -1, -1, REPARENT_HINT_FORCE_RECREATION | REPARENT_HINT_BECOMES_VISIBLE);
2450                     } else {
2451                         throw new InternalError("Offscreen instance w/o parent unhandled");
2452                     }
2453                 }
2454
2455                 runOnEDTIfAvail(true, fullScreenAction);
2456
2457                 if(!fullScreenAction.fsOn() && null != nfs_parent) {
2458                     // disable fullscreen on offscreen instance
2459                     reparentWindow(nfs_parent, -1, -1, REPARENT_HINT_FORCE_RECREATION | REPARENT_HINT_BECOMES_VISIBLE);
2460                     nfs_parent = null;
2461                 }
2462             }
2463             return this.fullscreen;
2464         }
2465     }
2466
2467     /** Notify WindowDriver about the finished monitor mode change. */
2468     protected void monitorModeChanged(MonitorEvent me, boolean success) {
2469     }
2470
2471     private class MonitorModeListenerImpl implements MonitorModeListener {
2472         boolean animatorPaused = false;
2473         boolean hidden = false;
2474         boolean hadFocus = false;
2475         boolean fullscreenPaused = false;
2476         List<MonitorDevice> _fullscreenMonitors = null;
2477         boolean _fullscreenUseMainMonitor = true;
2478
2479         @Override
2480         public void monitorModeChangeNotify(MonitorEvent me) {
2481             hadFocus = hasFocus();
2482             final boolean isOSX = NativeWindowFactory.TYPE_MACOSX == NativeWindowFactory.getNativeWindowType(true);
2483             final boolean quirkFSPause = fullscreen && isReconfigureFlagSupported(FLAG_IS_FULLSCREEN_SPAN);
2484             final boolean quirkHide = !quirkFSPause && !fullscreen && isVisible() && isOSX;
2485             if(DEBUG_IMPLEMENTATION) {
2486                 System.err.println("Window.monitorModeChangeNotify: hadFocus "+hadFocus+", qFSPause "+quirkFSPause+", qHide "+quirkHide+", "+me+" @ "+Thread.currentThread().getName());
2487             }
2488
2489             if(null!=lifecycleHook) {
2490                 animatorPaused = lifecycleHook.pauseRenderingAction();
2491             }
2492             if( quirkFSPause ) {
2493                 if(DEBUG_IMPLEMENTATION) {
2494                     System.err.println("Window.monitorModeChangeNotify: FS Pause");
2495                 }
2496                 fullscreenPaused = true;
2497                 _fullscreenMonitors = fullscreenMonitors;
2498                 _fullscreenUseMainMonitor = fullscreenUseMainMonitor;
2499                 setFullscreenImpl(false, true, null);
2500             }
2501             if( quirkHide ) {
2502                 // hiding & showing the window around mode-change solves issues w/ OSX,
2503                 // where the content would be black until a resize.
2504                 hidden = true;
2505                 WindowImpl.this.setVisible(false);
2506             }
2507         }
2508
2509         @Override
2510         public void monitorModeChanged(MonitorEvent me, boolean success) {
2511             if(!animatorPaused && success && null!=lifecycleHook) {
2512                 // Didn't pass above notify method. probably detected screen change after it happened.
2513                 animatorPaused = lifecycleHook.pauseRenderingAction();
2514             }
2515             if(DEBUG_IMPLEMENTATION) {
2516                 System.err.println("Window.monitorModeChanged.0: success: "+success+", hadFocus "+hadFocus+", animPaused "+animatorPaused+
2517                                    ", hidden "+hidden+", FS "+fullscreen+", FS-paused "+fullscreenPaused+
2518                                    " @ "+Thread.currentThread().getName());
2519                 System.err.println("Window.monitorModeChanged.0: "+getScreen());
2520                 System.err.println("Window.monitorModeChanged.0: "+me);
2521             }
2522             WindowImpl.this.monitorModeChanged(me, success);
2523
2524             if( success && !fullscreen && !fullscreenPaused ) {
2525                 // Simply move/resize window to fit in virtual screen if required
2526                 final RectangleImmutable viewport = screen.getViewportInWindowUnits();
2527                 if( viewport.getWidth() > 0 && viewport.getHeight() > 0 ) { // failsafe
2528                     final RectangleImmutable rect = new Rectangle(getX(), getY(), getWidth(), getHeight());
2529                     final RectangleImmutable isect = viewport.intersection(rect);
2530                     if ( getHeight() > isect.getHeight()  ||
2531                          getWidth() > isect.getWidth() ) {
2532                         if(DEBUG_IMPLEMENTATION) {
2533                             System.err.println("Window.monitorModeChanged.1: Non-FS - Fit window "+rect+" into screen viewport "+viewport+
2534                                                ", due to minimal intersection "+isect);
2535                         }
2536                         definePosition(viewport.getX(), viewport.getY()); // set pos for setVisible(..) or createNative(..) - reduce EDT roundtrip
2537                         setSize(viewport.getWidth(), viewport.getHeight(), true /* force */);
2538                     }
2539                 }
2540             } else if( fullscreenPaused ) {
2541                 if(DEBUG_IMPLEMENTATION) {
2542                     System.err.println("Window.monitorModeChanged.2: FS Restore");
2543                 }
2544                 setFullscreenImpl(true, _fullscreenUseMainMonitor, _fullscreenMonitors);
2545                 fullscreenPaused = false;
2546                 _fullscreenMonitors = null;
2547                 _fullscreenUseMainMonitor = true;
2548             } else if( success && fullscreen && null != fullscreenMonitors ) {
2549                 // If changed monitor is part of this fullscreen mode, reset size! (Bug 771)
2550                 final MonitorDevice md = me.getMonitor();
2551                 if( fullscreenMonitors.contains(md) ) {
2552                     final Rectangle viewportInWindowUnits = new Rectangle();
2553                     MonitorDevice.unionOfViewports(null, viewportInWindowUnits, fullscreenMonitors);
2554                     if(DEBUG_IMPLEMENTATION) {
2555                         final RectangleImmutable winBounds = WindowImpl.this.getBounds();
2556                         System.err.println("Window.monitorModeChanged.3: FS Monitor Match: Fit window "+winBounds+" into new viewport union "+viewportInWindowUnits+" [window], provoked by "+md);
2557                     }
2558                     definePosition(viewportInWindowUnits.getX(), viewportInWindowUnits.getY()); // set pos for setVisible(..) or createNative(..) - reduce EDT roundtrip
2559                     setSize(viewportInWindowUnits.getWidth(), viewportInWindowUnits.getHeight(), true /* force */);
2560                 }
2561             }
2562             if( hidden ) {
2563                 WindowImpl.this.setVisible(true);
2564                 hidden = false;
2565             }
2566             sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener
2567             if(animatorPaused) {
2568                 lifecycleHook.resumeRenderingAction();
2569             }
2570             if( hadFocus ) {
2571                 requestFocus(true);
2572             }
2573             if(DEBUG_IMPLEMENTATION) {
2574                 System.err.println("Window.monitorModeChanged.X: @ "+Thread.currentThread().getName()+", this: "+WindowImpl.this);
2575             }
2576         }
2577     }
2578     private final MonitorModeListenerImpl monitorModeListenerImpl = new MonitorModeListenerImpl();
2579
2580
2581     //----------------------------------------------------------------------
2582     // Child Window Management
2583     //
2584
2585     @Override
2586     public final boolean removeChild(NativeWindow win) {
2587         synchronized(childWindowsLock) {
2588             return childWindows.remove(win);
2589         }
2590     }
2591
2592     @Override
2593     public final boolean addChild(NativeWindow win) {
2594         if (win == null) {
2595             return false;
2596         }
2597         synchronized(childWindowsLock) {
2598             return childWindows.add(win);
2599         }
2600     }
2601
2602     //----------------------------------------------------------------------
2603     // Generic Event Support
2604     //
2605     private void doEvent(boolean enqueue, boolean wait, com.jogamp.newt.event.NEWTEvent event) {
2606         boolean done = false;
2607
2608         if(!enqueue) {
2609             done = consumeEvent(event);
2610             wait = done; // don't wait if event can't be consumed now
2611         }
2612
2613         if(!done) {
2614             enqueueEvent(wait, event);
2615         }
2616     }
2617
2618     @Override
2619     public final void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event) {
2620         if(isNativeValid()) {
2621             ((DisplayImpl)screen.getDisplay()).enqueueEvent(wait, event);
2622         }
2623     }
2624
2625     @Override
2626     public final boolean consumeEvent(NEWTEvent e) {
2627         switch(e.getEventType()) {
2628             // special repaint treatment
2629             case WindowEvent.EVENT_WINDOW_REPAINT:
2630                 // queue repaint event in case window is locked, ie in operation
2631                 if( null != windowLock.getOwner() ) {
2632                     // make sure only one repaint event is queued
2633                     if(!repaintQueued) {
2634                         repaintQueued=true;
2635                         final boolean discardTO = QUEUED_EVENT_TO <= System.currentTimeMillis()-e.getWhen();
2636                         if(DEBUG_IMPLEMENTATION) {
2637                             System.err.println("Window.consumeEvent: REPAINT "+Thread.currentThread().getName()+" - queued "+e+", discard-to "+discardTO);
2638                             // Thread.dumpStack();
2639                         }
2640                         return discardTO; // discardTO:=true -> consumed
2641                     }
2642                     return true;
2643                 }
2644                 repaintQueued=false; // no repaint event queued
2645                 break;
2646
2647             // common treatment
2648             case WindowEvent.EVENT_WINDOW_RESIZED:
2649                 // queue event in case window is locked, ie in operation
2650                 if( null != windowLock.getOwner() ) {
2651                     final boolean discardTO = QUEUED_EVENT_TO <= System.currentTimeMillis()-e.getWhen();
2652                     if(DEBUG_IMPLEMENTATION) {
2653                         System.err.println("Window.consumeEvent: RESIZED "+Thread.currentThread().getName()+" - queued "+e+", discard-to "+discardTO);
2654                         // Thread.dumpStack();
2655                     }
2656                     return discardTO; // discardTO:=true -> consumed
2657                 }
2658                 break;
2659             default:
2660                 break;
2661         }
2662         if(e instanceof WindowEvent) {
2663             consumeWindowEvent((WindowEvent)e);
2664         } else if(e instanceof KeyEvent) {
2665             consumeKeyEvent((KeyEvent)e);
2666         } else if(e instanceof MouseEvent) {
2667             consumePointerEvent((MouseEvent)e);
2668         } else {
2669             throw new NativeWindowException("Unexpected NEWTEvent type " + e);
2670         }
2671         return true;
2672     }
2673
2674     //
2675     // MouseListener/Event Support
2676     //
2677
2678     //
2679     // Native MouseEvents pre-processed to be enqueued or consumed directly
2680     //
2681
2682     public final void sendMouseEvent(final short eventType, final int modifiers,
2683                                final int x, final int y, final short button, final float rotation) {
2684         doMouseEvent(false, false, eventType, modifiers, x, y, button, MouseEvent.getRotationXYZ(rotation, modifiers), 1f);
2685     }
2686     public final void enqueueMouseEvent(final boolean wait, final short eventType, final int modifiers,
2687                                         final int x, final int y, final short button, final float rotation) {
2688         doMouseEvent(true, wait, eventType, modifiers, x, y, button, MouseEvent.getRotationXYZ(rotation, modifiers), 1f);
2689     }
2690     protected final void doMouseEvent(final boolean enqueue, final boolean wait, final short eventType, final int modifiers,
2691                                       final int x, final int y, final short button, final float rotation) {
2692         doMouseEvent(enqueue, wait, eventType, modifiers, x, y, button, MouseEvent.getRotationXYZ(rotation, modifiers), 1f);
2693     }
2694     /**
2695     public final void sendMouseEvent(final short eventType, final int modifiers,
2696                                      final int x, final int y, final short button, final float[] rotationXYZ, final float rotationScale) {
2697         doMouseEvent(false, false, eventType, modifiers, x, y, button, rotationXYZ, rotationScale);
2698     }
2699     public final void enqueueMouseEvent(final boolean wait, final short eventType, final int modifiers,
2700                                         final int x, final int y, final short button, final float[] rotationXYZ, final float rotationScale) {
2701         doMouseEvent(true, wait, eventType, modifiers, x, y, button, rotationXYZ, rotationScale);
2702     } */
2703
2704     /**
2705      * Send mouse event (one-pointer) either to be directly consumed or to be enqueued
2706      *
2707      * @param enqueue if true, event will be {@link #enqueueEvent(boolean, NEWTEvent) enqueued},
2708      *                otherwise {@link #consumeEvent(NEWTEvent) consumed} directly.
2709      * @param wait if true wait until {@link #consumeEvent(NEWTEvent) consumed}.
2710      */
2711     protected void doMouseEvent(final boolean enqueue, final boolean wait, final short eventType, final int modifiers,
2712                                 final int x, final int y, final short button, final float[] rotationXYZ, final float rotationScale) {
2713         if( 0 > button || button > MouseEvent.BUTTON_COUNT ) {
2714             throw new NativeWindowException("Invalid mouse button number" + button);
2715         }
2716         doPointerEvent(enqueue, wait, constMousePointerTypes, eventType, modifiers,
2717                        0 /*actionIdx*/, new short[] { (short)0 }, button,
2718                        new int[]{x}, new int[]{y}, new float[]{0f} /*pressure*/,
2719                        1f /*maxPressure*/, rotationXYZ, rotationScale);
2720     }
2721
2722     /**
2723      * Send multiple-pointer event either to be directly consumed or to be enqueued
2724      * <p>
2725      * The index for the element of multiple-pointer arrays represents the pointer which triggered the event
2726      * is passed via <i>actionIdx</i>.
2727      * </p>
2728      * <p>
2729      * The given pointer names, <code>pNames</code>, are mapped to consecutive pointer IDs starting w/ 0
2730      * using a hash-map if <code>normalPNames</code> is <code>false</code>.
2731      * Otherwise a simple <code>int</code> to <code>short</code> type cast is performed.
2732      * </p>
2733      * <p>
2734      * See {@link #doPointerEvent(boolean, boolean, PointerType[], short, int, int, short[], short, int[], int[], float[], float, float[], float)}
2735      * for details!
2736      * </p>
2737      *
2738      * @param enqueue if true, event will be {@link #enqueueEvent(boolean, NEWTEvent) enqueued},
2739      *                otherwise {@link #consumeEvent(NEWTEvent) consumed} directly.
2740      * @param wait if true wait until {@link #consumeEvent(NEWTEvent) consumed}.
2741      * @param pTypes {@link MouseEvent.PointerType} for each pointer (multiple pointer)
2742      * @param eventType
2743      * @param modifiers
2744      * @param actionIdx index of multiple-pointer arrays representing the pointer which triggered the event
2745      * @param normalPNames see pName below.
2746      * @param pNames Pointer name for each pointer (multiple pointer).
2747      *        We assume consecutive pointer names starting w/ 0 if <code>normalPIDs</code> is <code>true</code>.
2748      *        Otherwise we hash-map the values during state pressed to retrieve the normal ID.
2749      * @param pX X-axis for each pointer (multiple pointer)
2750      * @param pY Y-axis for each pointer (multiple pointer)
2751      * @param pPressure Pressure for each pointer (multiple pointer)
2752      * @param maxPressure Maximum pointer pressure for all pointer
2753      */
2754     public final void doPointerEvent(final boolean enqueue, final boolean wait,
2755                                      final PointerType[] pTypes, final short eventType, final int modifiers,
2756                                      final int actionIdx, final boolean normalPNames, final int[] pNames,
2757                                      final int[] pX, final int[] pY, final float[] pPressure,
2758                                      float maxPressure, final float[] rotationXYZ, final float rotationScale) {
2759         final int pCount = pNames.length;
2760         final short[] pIDs = new short[pCount];
2761         for(int i=0; i<pCount; i++) {
2762             if( !normalPNames ) {
2763                 // hash map int name -> short idx
2764                 final int sz0 = pName2pID.size();
2765                 final Integer pNameI1 = pName2pID.getOrAdd(Integer.valueOf(pNames[i]));
2766                 final short pID = (short)pName2pID.indexOf(pNameI1);
2767                 pIDs[i] = pID;
2768                 if(DEBUG_MOUSE_EVENT) {
2769                     final int sz1 = pName2pID.size();
2770                     if( sz0 != sz1 ) {
2771                         System.err.println("PointerName2ID[sz "+sz1+"]: Map "+pNameI1+" == "+pID);
2772                     }
2773                 }
2774                 if( MouseEvent.EVENT_MOUSE_RELEASED == eventType ) {
2775                     pName2pID.remove(pNameI1);
2776                     if(DEBUG_MOUSE_EVENT) {
2777                         System.err.println("PointerName2ID[sz "+pName2pID.size()+"]: Unmap "+pNameI1+" == "+pID);
2778                     }
2779                 }
2780             } else {
2781                 // simple type cast
2782                 pIDs[i] = (short)pNames[i];
2783             }
2784         }
2785         final short button = 0 < pCount ? (short) ( pIDs[0] + 1 ) : (short)0;
2786         doPointerEvent(enqueue, wait, pTypes, eventType, modifiers, actionIdx, pIDs, button,
2787                        pX, pY, pPressure, maxPressure, rotationXYZ, rotationScale);
2788     }
2789
2790     /**
2791      * Send multiple-pointer event either to be directly consumed or to be enqueued.
2792      * <p>
2793      * Pointer/Mouse Processing Pass 1 (Pass 2 is performed in {@link #consumePointerEvent(MouseEvent)}.
2794      * </p>
2795      * <p>
2796      * Usually directly called by event source to enqueue and process event.
2797      * </p>
2798      * <p>
2799      * The index for the element of multiple-pointer arrays represents the pointer which triggered the event
2800      * is passed via <i>actionIdx</i>.
2801      * </p>
2802      * <p>
2803      * <ul>
2804      * <li>Determine ENTERED/EXITED state</li>
2805      * <li>Remove redundant move/drag events</li>
2806      * <li>Reset states if applicable</li>
2807      * <li>Drop exterior events</li>
2808      * <li>Determine CLICK COUNT</li>
2809      * <li>Ignore sent CLICKED</li>
2810      * <li>Track buttonPressed incl. buttonPressedMask</li>
2811      * <li>Synthesize DRAGGED event (from MOVED if pointer is pressed)</li>
2812      * </ul>
2813      * </p>
2814      *
2815      * @param enqueue if true, event will be {@link #enqueueEvent(boolean, NEWTEvent) enqueued},
2816      *                otherwise {@link #consumeEvent(NEWTEvent) consumed} directly.
2817      * @param wait if true wait until {@link #consumeEvent(NEWTEvent) consumed}.
2818      * @param pTypes {@link MouseEvent.PointerType} for each pointer (multiple pointer)
2819      * @param eventType
2820      * @param modifiers
2821      * @param pActionIdx index of multiple-pointer arrays representing the pointer which triggered the event
2822      * @param pID Pointer ID for each pointer (multiple pointer). We assume consecutive pointerIDs starting w/ 0.
2823      * @param button Corresponding mouse-button, a button of 0 denotes no activity, i.e. {@link PointerType#Mouse} move.
2824      * @param pX X-axis for each pointer (multiple pointer)
2825      * @param pY Y-axis for each pointer (multiple pointer)
2826      * @param pPressure Pressure for each pointer (multiple pointer)
2827      * @param maxPressure Maximum pointer pressure for all pointer
2828      */
2829     public final void doPointerEvent(final boolean enqueue, final boolean wait,
2830                                      final PointerType[] pTypes, final short eventType, int modifiers,
2831                                      final int pActionIdx, final short[] pID, final short buttonIn, final int[] pX, final int[] pY,
2832                                      final float[] pPressure, final float maxPressure, final float[] rotationXYZ, final float rotationScale) {
2833         final long when = System.currentTimeMillis();
2834         final int pCount = pTypes.length;
2835
2836         if( 0 > pActionIdx || pActionIdx >= pCount) {
2837             throw new IllegalArgumentException("actionIdx out of bounds [0.."+(pCount-1)+"]");
2838         }
2839         if( 0 < pActionIdx ) {
2840             // swap values to make idx 0 the triggering pointer
2841             {
2842                 final PointerType aType = pTypes[pActionIdx];
2843                 pTypes[pActionIdx] = pTypes[0];
2844                 pTypes[0] = aType;
2845             }
2846             {
2847                 final short s = pID[pActionIdx];
2848                 pID[pActionIdx] = pID[0];
2849                 pID[0] = s;
2850             }
2851             {
2852                 int s = pX[pActionIdx];
2853                 pX[pActionIdx] = pX[0];
2854                 pX[0] = s;
2855                 s = pY[pActionIdx];
2856                 pY[pActionIdx] = pY[0];
2857                 pY[0] = s;
2858             }
2859             {
2860                 final float aPress = pPressure[pActionIdx];
2861                 pPressure[pActionIdx] = pPressure[0];
2862                 pPressure[0] = aPress;
2863             }
2864         }
2865         final short button;
2866         {
2867             // validate button
2868             if( 0 <= buttonIn && buttonIn <= com.jogamp.newt.event.MouseEvent.BUTTON_COUNT ) { // we allow button==0 for no button, i.e. mouse-ptr move
2869                 button = buttonIn;
2870             } else {
2871                 button = com.jogamp.newt.event.MouseEvent.BUTTON1;
2872             }
2873         }
2874
2875         //
2876         // - Determine ENTERED/EXITED state
2877         // - Remove redundant move/drag events
2878         // - Reset states if applicable
2879         //
2880         int x = pX[0];
2881         int y = pY[0];
2882         final boolean insideSurface = x >= 0 && y >= 0 && x < getSurfaceWidth() && y < getSurfaceHeight();
2883         final Point movePositionP0 = pState1.getMovePosition(pID[0]);
2884         switch( eventType ) {
2885             case MouseEvent.EVENT_MOUSE_EXITED:
2886                 if( pState1.dragging ) {
2887                     // Drop mouse EXIT if dragging, i.e. due to exterior dragging outside of window.
2888                     // NOTE-1: X11 produces the 'premature' EXIT, however it also produces 'EXIT' after exterior dragging!
2889                     // NOTE-2: consumePointerEvent(MouseEvent) will synthesize a missing EXIT event!
2890                     if(DEBUG_MOUSE_EVENT) {
2891                         System.err.println("doPointerEvent: drop "+MouseEvent.getEventTypeString(eventType)+" due to dragging: "+pState1);
2892                     }
2893                     return;
2894                 }
2895                 if( null != movePositionP0 ) {
2896                     if( x==-1 && y==-1 ) {
2897                         x = movePositionP0.getX();
2898                         y = movePositionP0.getY();
2899                     }
2900                     movePositionP0.set(0, 0);
2901                 }
2902                 // Fall through intended!
2903
2904             case MouseEvent.EVENT_MOUSE_ENTERED:
2905                 if( eventType == MouseEvent.EVENT_MOUSE_ENTERED ) {
2906                     pState1.insideSurface = true;
2907                     pState1.exitSent = false;
2908                 } else {
2909                     pState1.insideSurface = false;
2910                     pState1.exitSent = true;
2911                 }
2912                 pState1.clearButton();
2913                 if( pTypes[0] != PointerType.Mouse ) {
2914                     // Drop !MOUSE ENTER/EXIT Events - Safeguard for non compliant implementations only.
2915                     if(DEBUG_MOUSE_EVENT) {
2916                         System.err.println("doPointerEvent: drop "+MouseEvent.getEventTypeString(eventType)+" due to !Mouse but "+pTypes[0]+": "+pState1);
2917                     }
2918                     return;
2919                 }
2920                 // clip coordinates to window dimension
2921                 x = Math.min(Math.max(x,  0), getSurfaceWidth()-1);
2922                 y = Math.min(Math.max(y,  0), getSurfaceHeight()-1);
2923                 break;
2924
2925             case MouseEvent.EVENT_MOUSE_MOVED:
2926             case MouseEvent.EVENT_MOUSE_DRAGGED:
2927                 if( null != movePositionP0 ) {
2928                     if( movePositionP0.getX() == x && movePositionP0.getY() == y ) {
2929                         // Drop same position
2930                         if(DEBUG_MOUSE_EVENT) {
2931                             System.err.println("doPointerEvent: drop "+MouseEvent.getEventTypeString(eventType)+" w/ same position: "+movePositionP0+", "+pState1);
2932                         }
2933                         return;
2934                     }
2935                     movePositionP0.set(x, y);
2936                 }
2937
2938                 // Fall through intended !
2939
2940             default:
2941                 if( pState1.insideSurface != insideSurface ) {
2942                     // ENTER/EXIT!
2943                     pState1.insideSurface = insideSurface;
2944                     if( insideSurface ) {
2945                         pState1.exitSent = false;
2946                     }
2947                     pState1.clearButton();
2948                 }
2949         }
2950
2951         //
2952         // Drop exterior events if not dragging pointer and not EXIT event
2953         // Safeguard for non compliant implementations!
2954         //
2955         if( !pState1.dragging && !insideSurface && MouseEvent.EVENT_MOUSE_EXITED != eventType ) {
2956             if(DEBUG_MOUSE_EVENT) {
2957                 System.err.println("doPointerEvent: drop: "+MouseEvent.getEventTypeString(eventType)+
2958                                    ", mod "+modifiers+", pos "+x+"/"+y+", button "+button+", lastMousePosition: "+movePositionP0+", insideWindow "+insideSurface+", "+pState1);
2959             }
2960             return; // .. invalid ..
2961         }
2962         if(DEBUG_MOUSE_EVENT) {
2963             System.err.println("doPointerEvent: enqueue "+enqueue+", wait "+wait+", "+MouseEvent.getEventTypeString(eventType)+
2964                                ", mod "+modifiers+", pos "+x+"/"+y+", button "+button+", lastMousePosition: "+movePositionP0+", "+pState1);
2965         }
2966
2967         final int buttonMask = InputEvent.getButtonMask(button);
2968         modifiers |= buttonMask; // Always add current button to modifier mask (Bug 571)
2969         modifiers |= pState1.buttonPressedMask; // Always add currently pressed mouse buttons to modifier mask
2970
2971         if( isPointerConfined() ) {
2972             modifiers |= InputEvent.CONFINED_MASK;
2973         }
2974         if( !isPointerVisible() ) {
2975             modifiers |= InputEvent.INVISIBLE_MASK;
2976         }
2977
2978         pX[0] = x;
2979         pY[0] = y;
2980
2981         //
2982         // - Determine CLICK COUNT
2983         // - Ignore sent CLICKED
2984         // - Track buttonPressed incl. buttonPressedMask
2985         // - Synthesize DRAGGED event (from MOVED if pointer is pressed)
2986         //
2987         final MouseEvent e;
2988         switch( eventType ) {
2989             case MouseEvent.EVENT_MOUSE_CLICKED:
2990                 e = null;
2991                 break;
2992
2993             case MouseEvent.EVENT_MOUSE_PRESSED:
2994                 if( 0 >= pPressure[0] ) {
2995                     pPressure[0] = maxPressure;
2996                 }
2997                 pState1.buttonPressedMask |= buttonMask;
2998                 if( 1 == pCount ) {
2999                     if( when - pState1.lastButtonPressTime < MouseEvent.getClickTimeout() ) {
3000                         pState1.lastButtonClickCount++;
3001                     } else {
3002                         pState1.lastButtonClickCount=(short)1;
3003                     }
3004                     pState1.lastButtonPressTime = when;
3005                     pState1.buttonPressed = button;
3006                     e = new MouseEvent(eventType, this, when, modifiers, pTypes, pID,
3007                                        pX, pY, pPressure, maxPressure, button, pState1.lastButtonClickCount, rotationXYZ, rotationScale);
3008                 } else {
3009                     e = new MouseEvent(eventType, this, when, modifiers, pTypes, pID,
3010                                        pX, pY, pPressure, maxPressure, button, (short)1, rotationXYZ, rotationScale);
3011                 }
3012                 break;
3013             case MouseEvent.EVENT_MOUSE_RELEASED:
3014                 pState1.buttonPressedMask &= ~buttonMask;
3015                 if( 1 == pCount ) {
3016                     e = new MouseEvent(eventType, this, when, modifiers, pTypes, pID,
3017                                        pX, pY, pPressure, maxPressure, button, pState1.lastButtonClickCount, rotationXYZ, rotationScale);
3018                     if( when - pState1.lastButtonPressTime >= MouseEvent.getClickTimeout() ) {
3019                         pState1.lastButtonClickCount = (short)0;
3020                         pState1.lastButtonPressTime = 0;
3021                     }
3022                     pState1.buttonPressed = 0;
3023                     pState1.dragging = false;
3024                 } else {
3025                     e = new MouseEvent(eventType, this, when, modifiers, pTypes, pID,
3026                                        pX, pY, pPressure, maxPressure, button, (short)1, rotationXYZ, rotationScale);
3027                     if( 0 == pState1.buttonPressedMask ) {
3028                         pState1.clearButton();
3029