Jogamp
Bug 741 HiDPI: Add ScalableSurface interface to get/set pixelScale w/ full OSX impl.
[jogl.git] / src / newt / classes / jogamp / newt / WindowImpl.java
CommitLineData
0feca163
SG
1/*
2 * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved.
3 Copyright (c) 2010 JogAmp Community. All rights reserved.
5e9c02bc 4 *
0feca163
SG
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
5e9c02bc 8 *
0feca163
SG
9 * - Redistribution of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
5e9c02bc 11 *
0feca163
SG
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.
5e9c02bc 15 *
0feca163
SG
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.
5e9c02bc 19 *
0feca163
SG
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.
5e9c02bc 32 *
0feca163
SG
33 */
34
360b6716 35package jogamp.newt;
0feca163 36
99479bf3 37import java.lang.ref.WeakReference;
09fc0654 38import java.lang.reflect.Method;
58756bbd
SG
39import java.util.ArrayList;
40import java.util.List;
41
42import javax.media.nativewindow.AbstractGraphicsConfiguration;
43import javax.media.nativewindow.AbstractGraphicsDevice;
44import javax.media.nativewindow.CapabilitiesChooser;
45import javax.media.nativewindow.CapabilitiesImmutable;
46import javax.media.nativewindow.NativeSurface;
47import javax.media.nativewindow.NativeWindow;
48import javax.media.nativewindow.NativeWindowException;
49import javax.media.nativewindow.NativeWindowFactory;
50import javax.media.nativewindow.OffscreenLayerSurface;
2571ed0b 51import javax.media.nativewindow.ScalableSurface;
58756bbd
SG
52import javax.media.nativewindow.SurfaceUpdatedListener;
53import javax.media.nativewindow.WindowClosingProtocol;
54import javax.media.nativewindow.util.DimensionImmutable;
55import javax.media.nativewindow.util.Insets;
56import javax.media.nativewindow.util.InsetsImmutable;
bd98b927 57import javax.media.nativewindow.util.PixelRectangle;
58756bbd
SG
58import javax.media.nativewindow.util.Point;
59import javax.media.nativewindow.util.PointImmutable;
60import javax.media.nativewindow.util.Rectangle;
61import javax.media.nativewindow.util.RectangleImmutable;
62
2571ed0b 63import jogamp.nativewindow.SurfaceScaleUtils;
58756bbd 64import jogamp.nativewindow.SurfaceUpdatedHelper;
09fc0654 65
8815245e 66import com.jogamp.common.util.ArrayHashSet;
2f9c77a3 67import com.jogamp.common.util.IntBitfield;
a9e946d3 68import com.jogamp.common.util.ReflectionUtil;
58756bbd
SG
69import com.jogamp.common.util.locks.LockFactory;
70import com.jogamp.common.util.locks.RecursiveLock;
71import com.jogamp.newt.Display;
e7ffa68b 72import com.jogamp.newt.Display.PointerIcon;
6ebf649d 73import com.jogamp.newt.MonitorDevice;
0feca163 74import com.jogamp.newt.NewtFactory;
0feca163
SG
75import com.jogamp.newt.Screen;
76import com.jogamp.newt.Window;
bc72e232
SG
77import com.jogamp.newt.event.DoubleTapScrollGesture;
78import com.jogamp.newt.event.GestureHandler;
bdf1876f 79import com.jogamp.newt.event.InputEvent;
a9e946d3
SG
80import com.jogamp.newt.event.KeyEvent;
81import com.jogamp.newt.event.KeyListener;
6ebf649d 82import com.jogamp.newt.event.MonitorEvent;
58756bbd 83import com.jogamp.newt.event.MonitorModeListener;
a9e946d3 84import com.jogamp.newt.event.MouseEvent;
58756bbd 85import com.jogamp.newt.event.MouseEvent.PointerType;
a9e946d3
SG
86import com.jogamp.newt.event.MouseListener;
87import com.jogamp.newt.event.NEWTEvent;
88import com.jogamp.newt.event.NEWTEventConsumer;
a9e946d3
SG
89import com.jogamp.newt.event.WindowEvent;
90import com.jogamp.newt.event.WindowListener;
91import com.jogamp.newt.event.WindowUpdateEvent;
ccb7213c 92
2aa29677 93public abstract class WindowImpl implements Window, NEWTEventConsumer
0feca163 94{
8ac3f344 95 public static final boolean DEBUG_TEST_REPARENT_INCOMPATIBLE;
5e9c02bc 96
2d32b056 97 static {
8ac3f344
SG
98 Debug.initSingleton();
99 DEBUG_TEST_REPARENT_INCOMPATIBLE = Debug.isPropertyDefined("newt.test.Window.reparent.incompatible", true);
5e9c02bc 100
2d32b056
SG
101 ScreenImpl.initSingleton();
102 }
5e9c02bc
HH
103
104 protected static final ArrayList<WeakReference<WindowImpl>> windowList = new ArrayList<WeakReference<WindowImpl>>();
105
cc64ad2b 106 /** Maybe utilized at a shutdown hook, impl. does not block. */
2d32b056 107 public static final void shutdownAll() {
5e9c02bc 108 final int wCount = windowList.size();
2d32b056
SG
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 ..
99479bf3 113 final WindowImpl w = windowList.remove(0).get();
2d32b056 114 if(DEBUG_IMPLEMENTATION) {
99479bf3
SG
115 final long wh = null != w ? w.getWindowHandle() : 0;
116 System.err.println("Window.shutdownAll["+(i+1)+"/"+wCount+"]: "+toHexString(wh)+", GCed "+(null==w));
2d32b056 117 }
d4c022b6
SG
118 if( null != w ) {
119 w.shutdown();
120 }
2d32b056
SG
121 }
122 }
99479bf3
SG
123 private static void addWindow2List(WindowImpl window) {
124 synchronized(windowList) {
125 // GC before add
0e8cd28f 126 int i=0, gced=0;
99479bf3
SG
127 while( i < windowList.size() ) {
128 if( null == windowList.get(i).get() ) {
0e8cd28f 129 gced++;
99479bf3
SG
130 windowList.remove(i);
131 } else {
132 i++;
133 }
134 }
135 windowList.add(new WeakReference<WindowImpl>(window));
0e8cd28f
SG
136 if(DEBUG_IMPLEMENTATION) {
137 System.err.println("Window.addWindow2List: GCed "+gced+", size "+windowList.size());
138 }
99479bf3
SG
139 }
140 }
5e9c02bc 141
d8bbddae 142 /** Timeout of queued events (repaint and resize) */
5e9c02bc 143 static final long QUEUED_EVENT_TO = 1200; // ms
81cbcdc8 144
8815245e 145 private static final PointerType[] constMousePointerTypes = new PointerType[] { PointerType.Mouse };
5e9c02bc 146
81cbcdc8
SG
147 //
148 // Volatile: Multithread Mutable Access
5e9c02bc 149 //
28b0df6c
SG
150 private volatile long windowHandle = 0; // lifecycle critical
151 private volatile boolean visible = false; // lifecycle critical
5e9c02bc 152 private volatile boolean hasFocus = false;
f9a00b91
SG
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
2571ed0b
SG
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
f9a00b91 158 private volatile int x = 64, y = 64; // client-area pos w/o insets in window units
81cbcdc8 159 private volatile Insets insets = new Insets(); // insets of decoration (if top-level && decorated)
ded0a8fb 160 private boolean blockInsetsChange = false; // block insets change (from same thread)
5e9c02bc 161
24eab4dc 162 private final RecursiveLock windowLock = LockFactory.createRecursiveLock(); // Window instance wide lock
fbe331f0 163 private int surfaceLockCount = 0; // surface lock recursion count
5e9c02bc 164
28b0df6c 165 private ScreenImpl screen; // never null after create - may change reference though (reparent)
db4d7d19 166 private boolean screenReferenceAdded = false;
f47230cb
SG
167 private NativeWindow parentWindow = null;
168 private long parentWindowHandle = 0;
bafd9b99 169 private AbstractGraphicsConfiguration config = null; // control access due to delegation
f47230cb 170 protected CapabilitiesImmutable capsRequested = null;
7c159772 171 protected CapabilitiesChooser capabilitiesChooser = null; // default null -> default
6ebf649d
SG
172 private boolean fullscreen = false, brokenFocusChange = false;
173 private List<MonitorDevice> fullscreenMonitors = null;
174 private boolean fullscreenUseMainMonitor = true;
319fdccb 175 private boolean autoPosition = true; // default: true (allow WM to choose top-level position, if not set by user)
5e9c02bc 176
319fdccb 177 private int nfs_width, nfs_height, nfs_x, nfs_y; // non fullscreen client-area size/pos w/o insets
4cb35d98 178 private boolean nfs_alwaysOnTop; // non fullscreen alwaysOnTop setting
319fdccb
SG
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;
58756bbd 183 private PointerIconImpl pointerIcon = null;
24e0591b
SG
184 private boolean pointerVisible = true;
185 private boolean pointerConfined = false;
db4d7d19
SG
186 private LifecycleHook lifecycleHook = null;
187
808a9a27 188 private Runnable windowDestroyNotifyAction = null;
db4d7d19 189
db4d7d19 190 private FocusRunnable focusAction = null;
3db4e89c 191 private KeyListener keyboardFocusHandler = null;
db4d7d19 192
24eab4dc 193 private final SurfaceUpdatedHelper surfaceUpdatedHelper = new SurfaceUpdatedHelper();
5e9c02bc 194
24eab4dc
SG
195 private final Object childWindowsLock = new Object();
196 private final ArrayList<NativeWindow> childWindows = new ArrayList<NativeWindow>();
db4d7d19 197
cb4e7318 198 private ArrayList<MouseListener> mouseListeners = new ArrayList<MouseListener>();
5e9c02bc 199
a90bf31f 200 /** from event passing: {@link WindowImpl#consumePointerEvent(MouseEvent)}. */
bc72e232 201 private static class PointerState0 {
88f6e001 202 /** Pointer entered window - is inside the window (may be synthetic) */
fb57c652 203 boolean insideSurface = false;
88f6e001
SG
204 /** Mouse EXIT has been sent (only for MOUSE type enter/exit)*/
205 boolean exitSent = false;
5e9c02bc 206
88f6e001 207 /** last time when a pointer button was pressed */
a90bf31f 208 long lastButtonPressTime = 0;
5e9c02bc 209
88f6e001
SG
210 /** Pointer in dragging mode */
211 boolean dragging = false;
212
a90bf31f
SG
213 void clearButton() {
214 lastButtonPressTime = 0;
215 }
fb57c652 216 public String toString() { return "PState0[inside "+insideSurface+", exitSent "+exitSent+", lastPress "+lastButtonPressTime+", dragging "+dragging+"]"; }
bc72e232 217 }
24eab4dc 218 private final PointerState0 pState0 = new PointerState0();
5e9c02bc 219
8815245e 220 /** from direct input: {@link WindowImpl#doPointerEvent(boolean, boolean, int[], short, int, int, boolean, short[], int[], int[], float[], float, float[], float)}. */
a90bf31f 221 private static class PointerState1 extends PointerState0 {
88f6e001 222 /** Current pressed mouse button number */
a90bf31f 223 short buttonPressed = (short)0;
88f6e001 224 /** Current pressed mouse button modifier mask */
a90bf31f 225 int buttonPressedMask = 0;
88f6e001 226 /** Last mouse button click count */
a90bf31f 227 short lastButtonClickCount = (short)0;
5e9c02bc 228
f1ae8ddb 229 @Override
a90bf31f
SG
230 final void clearButton() {
231 super.clearButton();
5e9c02bc 232 lastButtonClickCount = (short)0;
88f6e001
SG
233 if( !dragging || 0 == buttonPressedMask ) {
234 buttonPressed = 0;
235 buttonPressedMask = 0;
236 dragging = false;
237 }
a90bf31f 238 }
5e9c02bc 239
88f6e001 240 /** Last pointer-move position for 8 touch-down pointers */
a90bf31f
SG
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 }
fb57c652 250 public final String toString() { return "PState1[inside "+insideSurface+", exitSent "+exitSent+", lastPress "+lastButtonPressTime+
88f6e001 251 ", pressed [button "+buttonPressed+", mask "+buttonPressedMask+", dragging "+dragging+", clickCount "+lastButtonClickCount+"]"; }
a90bf31f 252 }
24eab4dc 253 private final PointerState1 pState1 = new PointerState1();
5e9c02bc 254
88f6e001 255 /** Pointer names -> pointer ID (consecutive index, starting w/ 0) */
8815245e 256 private final ArrayHashSet<Integer> pName2pID = new ArrayHashSet<Integer>();
5e9c02bc 257
bc72e232
SG
258 private boolean defaultGestureHandlerEnabled = true;
259 private DoubleTapScrollGesture gesture2PtrTouchScroll = null;
260 private ArrayList<GestureHandler> pointerGestureHandler = new ArrayList<GestureHandler>();
5e9c02bc 261
bc72e232 262 private ArrayList<GestureHandler.GestureListener> gestureListeners = new ArrayList<GestureHandler.GestureListener>();
5e9c02bc 263
cb4e7318 264 private ArrayList<KeyListener> keyListeners = new ArrayList<KeyListener>();
db4d7d19 265
cb4e7318 266 private ArrayList<WindowListener> windowListeners = new ArrayList<WindowListener>();
db4d7d19
SG
267 private boolean repaintQueued = false;
268
0feca163
SG
269 //
270 // Construction Methods
271 //
272
cb4e7318 273 private static Class<?> getWindowClass(String type)
db4d7d19 274 throws ClassNotFoundException
0feca163 275 {
a694cadc 276 final Class<?> windowClass = NewtFactory.getCustomClass(type, "WindowDriver");
0feca163 277 if(null==windowClass) {
5e9c02bc 278 throw new ClassNotFoundException("Failed to find NEWT Window Class <"+type+".WindowDriver>");
a338d1c9 279 }
0feca163
SG
280 return windowClass;
281 }
282
29e3b223 283 public static WindowImpl create(NativeWindow parentWindow, long parentWindowHandle, Screen screen, CapabilitiesImmutable caps) {
0feca163 284 try {
cb4e7318 285 Class<?> windowClass;
0feca163 286 if(caps.isOnscreen()) {
fbb30f2e 287 windowClass = getWindowClass(screen.getDisplay().getType());
0feca163
SG
288 } else {
289 windowClass = OffscreenWindow.class;
290 }
291 WindowImpl window = (WindowImpl) windowClass.newInstance();
0feca163
SG
292 window.parentWindow = parentWindow;
293 window.parentWindowHandle = parentWindowHandle;
294 window.screen = (ScreenImpl) screen;
29e3b223 295 window.capsRequested = (CapabilitiesImmutable) caps.cloneMutable();
e29d1929 296 window.instantiationFinished();
99479bf3 297 addWindow2List(window);
0feca163
SG
298 return window;
299 } catch (Throwable t) {
300 t.printStackTrace();
301 throw new NativeWindowException(t);
302 }
303 }
5e9c02bc 304
29e3b223 305 public static WindowImpl create(Object[] cstrArguments, Screen screen, CapabilitiesImmutable caps) {
0feca163 306 try {
cb4e7318
SG
307 Class<?> windowClass = getWindowClass(screen.getDisplay().getType());
308 Class<?>[] cstrArgumentTypes = getCustomConstructorArgumentTypes(windowClass);
0feca163
SG
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 ) ;
0feca163 317 window.screen = (ScreenImpl) screen;
29e3b223 318 window.capsRequested = (CapabilitiesImmutable) caps.cloneMutable();
2d32b056 319 window.instantiationFinished();
99479bf3 320 addWindow2List(window);
0feca163
SG
321 return window;
322 } catch (Throwable t) {
323 throw new NativeWindowException(t);
324 }
325 }
326
2d32b056 327 /** Fast invalidation of instance w/o any blocking function call. */
cc64ad2b
SG
328 private final void shutdown() {
329 if(null!=lifecycleHook) {
330 lifecycleHook.shutdownRenderingAction();
331 }
2d32b056
SG
332 setWindowHandle(0);
333 visible = false;
334 fullscreen = false;
335 fullscreenMonitors = null;
336 fullscreenUseMainMonitor = true;
337 hasFocus = false;
338 parentWindowHandle = 0;
339 }
5e9c02bc 340
bafd9b99
SG
341 protected final void setGraphicsConfiguration(AbstractGraphicsConfiguration cfg) {
342 config = cfg;
343 }
5e9c02bc 344
0feca163 345 public static interface LifecycleHook {
2c1a870f
SG
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
5e9c02bc
HH
352 /**
353 * Invoked after Window setVisible,
0feca163 354 * allows allocating resources depending on the native Window.
2c1a870f 355 * Called from EDT while window is locked.
0feca163 356 */
2c1a870f 357 void setVisibleActionPost(boolean visible, boolean nativeWindowCreated);
0feca163 358
e2506d76
SG
359 /**
360 * Notifies the receiver to preserve resources (GL, ..)
2d32b056
SG
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.
e2506d76 363 */
2d32b056 364 void preserveGLStateAtDestroy(boolean value);
5e9c02bc
HH
365
366 /**
367 * Invoked before Window destroy action,
e6225fce
SG
368 * allows releasing of resources depending on the native Window.<br>
369 * Surface not locked yet.<br>
370 * Called not necessarily from EDT.
371 */
db4d7d19 372 void destroyActionPreLock();
e6225fce
SG
373
374 /**
375 * Invoked before Window destroy action,
376 * allows releasing of resources depending on the native Window.<br>
377 * Surface locked.<br>
2c1a870f 378 * Called from EDT while window is locked.
0feca163 379 */
db4d7d19
SG
380 void destroyActionInLock();
381
382 /**
84576bc0 383 * Invoked for expensive modifications, ie while reparenting and MonitorMode change.<br>
a0e9d6c8
SG
384 * No lock is hold when invoked.<br>
385 *
2aa29677
SG
386 * @return true is paused, otherwise false. If true {@link #resumeRenderingAction()} shall be issued.
387 *
a0e9d6c8
SG
388 * @see #resumeRenderingAction()
389 */
2aa29677 390 boolean pauseRenderingAction();
0feca163 391
a0e9d6c8 392 /**
84576bc0 393 * Invoked for expensive modifications, ie while reparenting and MonitorMode change.
a0e9d6c8
SG
394 * No lock is hold when invoked.<br>
395 *
396 * @see #pauseRenderingAction()
397 */
398 void resumeRenderingAction();
5e9c02bc 399
cc64ad2b
SG
400 /**
401 * Shutdown rendering action (thread) abnormally.
402 * <p>
403 * Should be called only at shutdown, if necessary.
5e9c02bc 404 * </p>
cc64ad2b
SG
405 */
406 void shutdownRenderingAction();
0feca163
SG
407 }
408
0feca163 409 private boolean createNative() {
5b05aa83 410 long tStart;
0feca163 411 if(DEBUG_IMPLEMENTATION) {
5b05aa83 412 tStart = System.nanoTime();
0feca163 413 System.err.println("Window.createNative() START ("+getThreadName()+", "+this+")");
5b05aa83
SG
414 } else {
415 tStart = 0;
416 }
5e9c02bc
HH
417
418 if( null != parentWindow &&
c8a9c59e 419 NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindow.lockSurface() ) {
de0fe22e 420 throw new NativeWindowException("Parent surface lock: not ready: "+parentWindow);
319fdccb 421 }
5e9c02bc 422
2aa8d4bb
SG
423 final boolean hasParent = null != parentWindow || 0 != this.parentWindowHandle;
424
319fdccb 425 // child window: position defaults to 0/0, no auto position, no negative position
2aa8d4bb 426 if( hasParent && ( autoPosition || 0>getX() || 0>getY() ) ) {
319fdccb 427 definePosition(0, 0);
0feca163 428 }
ddd37537 429 boolean postParentlockFocus = false;
0feca163
SG
430 try {
431 if(validateParentWindowHandle()) {
50f99755 432 if( !screenReferenceAdded ) {
11387451
SG
433 screen.addReference();
434 screenReferenceAdded = true;
50f99755
SG
435 }
436 if(canCreateNativeImpl()) {
6ebf649d
SG
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();
11387451 449 createNativeImpl();
84576bc0 450 screen.addMonitorModeListener(monitorModeListenerImpl);
11387451 451 setTitleImpl(title);
58756bbd
SG
452 setPointerIconIntern(pointerIcon);
453 setPointerVisibleIntern(pointerVisible);
24e0591b 454 confinePointerImpl(pointerConfined);
533e072a 455 setKeyboardVisible(keyboardVisible);
6ebf649d
SG
456 final long remainingV = waitForVisible(true, false);
457 if( 0 <= remainingV ) {
1b7844b9 458 if(isFullscreen()) {
d8bbddae
SG
459 synchronized(fullScreenAction) {
460 fullscreen = false; // trigger a state change
205a17de 461 fullScreenAction.init(true);
d8bbddae
SG
462 fullScreenAction.run();
463 }
2aa8d4bb 464 } else if ( !hasParent ) {
6ebf649d
SG
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");
1b7844b9 470 }
ddd37537 471 postParentlockFocus = true;
de0fe22e 472 }
11387451 473 }
0feca163
SG
474 }
475 } finally {
476 if(null!=parentWindow) {
477 parentWindow.unlockSurface();
478 }
479 }
ddd37537
SG
480 if(postParentlockFocus) {
481 // harmonize focus behavior for all platforms: focus on creation
8be1fc98 482 requestFocusInt(isFullscreen() /* skipFocusAction if fullscreen */);
ddd37537
SG
483 ((DisplayImpl) screen.getDisplay()).dispatchMessagesNative(); // status up2date
484 }
0feca163 485 if(DEBUG_IMPLEMENTATION) {
5b05aa83 486 System.err.println("Window.createNative() END ("+getThreadName()+", "+this+") total "+ (System.nanoTime()-tStart)/1e6 +"ms");
0feca163 487 }
28b0df6c 488 return isNativeValid() ;
0feca163
SG
489 }
490
db4d7d19
SG
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
0feca163
SG
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) {
c8a9c59e
SG
512 boolean wasLocked = false;
513 if( NativeSurface.LOCK_SURFACE_NOT_READY < nativeWindow.lockSurface() ) {
514 wasLocked = true;
515 try {
0feca163
SG
516 handle = nativeWindow.getWindowHandle();
517 if(0==handle) {
518 throw new NativeWindowException("Parent native window handle is NULL, after succesful locking: "+nativeWindow);
519 }
c8a9c59e
SG
520 } catch (NativeWindowException nwe) {
521 if(DEBUG_IMPLEMENTATION) {
522 System.err.println("Window.getNativeWindowHandle: not successful yet: "+nwe);
523 }
524 } finally {
0feca163
SG
525 nativeWindow.unlockSurface();
526 }
527 }
528 if(DEBUG_IMPLEMENTATION) {
c8a9c59e 529 System.err.println("Window.getNativeWindowHandle: locked "+wasLocked+", "+nativeWindow);
0feca163
SG
530 }
531 }
532 return handle;
533 }
534
535
536 //----------------------------------------------------------------------
018c7e86 537 // NativeSurface: Native implementation
0feca163
SG
538 //
539
540 protected int lockSurfaceImpl() { return LOCK_SUCCESS; }
541
542 protected void unlockSurfaceImpl() { }
543
544 //----------------------------------------------------------------------
2cbab63b
SG
545 // WindowClosingProtocol implementation
546 //
24eab4dc 547 private final Object closingListenerLock = new Object();
935523a8 548 private WindowClosingMode defaultCloseOperation = WindowClosingMode.DISPOSE_ON_CLOSE;
2cbab63b 549
f1ae8ddb 550 @Override
852fcbf1 551 public final WindowClosingMode getDefaultCloseOperation() {
2cbab63b
SG
552 synchronized (closingListenerLock) {
553 return defaultCloseOperation;
554 }
555 }
556
f1ae8ddb 557 @Override
852fcbf1 558 public final WindowClosingMode setDefaultCloseOperation(WindowClosingMode op) {
2cbab63b 559 synchronized (closingListenerLock) {
935523a8 560 WindowClosingMode _op = defaultCloseOperation;
2cbab63b
SG
561 defaultCloseOperation = op;
562 return _op;
563 }
564 }
565
566 //----------------------------------------------------------------------
0feca163
SG
567 // Window: Native implementation
568 //
569
e29d1929
SG
570 /**
571 * Notifies the driver impl. that the instantiation is finished,
5e9c02bc 572 * ie. instance created and all fields set.
e29d1929
SG
573 */
574 protected void instantiationFinished() {
575 // nop
576 }
5e9c02bc 577
11387451
SG
578 protected boolean canCreateNativeImpl() {
579 return true; // default: always able to be created
580 }
5e9c02bc
HH
581
582 /**
18bf27fa
SG
583 * The native implementation must set the native windowHandle.<br>
584 *
de9a419a 585 * <p>
1b7844b9
SG
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>
5e9c02bc 589 *
1b7844b9 590 * <p>
18bf27fa 591 * The implementation should invoke the referenced java state callbacks
de9a419a 592 * to notify this Java object of state changes.</p>
5e9c02bc 593 *
4e0eb391 594 * @see #windowDestroyNotify(boolean)
472a9c60
SG
595 * @see #focusChanged(boolean, boolean)
596 * @see #visibleChanged(boolean, boolean)
18bf27fa 597 * @see #sizeChanged(int,int)
472a9c60 598 * @see #positionChanged(boolean,int, int)
4e0eb391 599 * @see #windowDestroyNotify(boolean)
18bf27fa 600 */
0feca163
SG
601 protected abstract void createNativeImpl();
602
603 protected abstract void closeNativeImpl();
604
5e9c02bc 605 /**
7bb5885f
SG
606 * Async request which shall be performed within {@link #TIMEOUT_NATIVEWINDOW}.
607 * <p>
5e9c02bc 608 * If if <code>force == false</code> the native implementation
7bb5885f
SG
609 * may only request focus if not yet owner.</p>
610 * <p>
611 * {@link #focusChanged(boolean, boolean)} should be called
5e9c02bc
HH
612 * to notify about the focus traversal.
613 * </p>
614 *
472a9c60 615 * @param force if true, bypass {@link #focusChanged(boolean, boolean)} and force focus request
18bf27fa
SG
616 */
617 protected abstract void requestFocusImpl(boolean force);
0feca163 618
a245d530
SG
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;
5e9c02bc 624
a245d530
SG
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;
6ebf649d
SG
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;
0feca163 631
18bf27fa
SG
632 /**
633 * The native implementation should invoke the referenced java state callbacks
634 * to notify this Java object of state changes.
5e9c02bc 635 *
4faa65ee
SG
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>
5e9c02bc 640 *
fb57c652
SG
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
4faa65ee 645 * @param flags bitfield of change and status flags
18bf27fa
SG
646 *
647 * @see #sizeChanged(int,int)
472a9c60 648 * @see #positionChanged(boolean,int, int)
18bf27fa 649 */
4faa65ee 650 protected abstract boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags);
5e9c02bc
HH
651
652 /**
fa6e868a
SG
653 * Tests whether a single reconfigure flag is supported by implementation.
654 * <p>
655 * Default is all but {@link #FLAG_IS_FULLSCREEN_SPAN}
5e9c02bc 656 * </p>
fa6e868a
SG
657 */
658 protected boolean isReconfigureFlagSupported(int changeFlags) {
659 return 0 == ( changeFlags & FLAG_IS_FULLSCREEN_SPAN );
660 }
4faa65ee
SG
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 ) |
a245d530 666 ( isAlwaysOnTop() ? FLAG_IS_ALWAYSONTOP : 0 ) |
4faa65ee
SG
667 ( visible ? FLAG_IS_VISIBLE : 0 ) ;
668 }
ffb47c66
SG
669 protected static String getReconfigureFlagsAsString(StringBuilder sb, int flags) {
670 if(null == sb) { sb = new StringBuilder(); }
4faa65ee 671 sb.append("[");
5e9c02bc 672
4faa65ee
SG
673 if( 0 != ( FLAG_CHANGE_PARENTING & flags) ) {
674 sb.append("*");
675 }
0237bde0 676 sb.append("PARENT ");
4faa65ee
SG
677 sb.append(0 != ( FLAG_HAS_PARENT & flags));
678 sb.append(", ");
5e9c02bc 679
4faa65ee
SG
680 if( 0 != ( FLAG_CHANGE_FULLSCREEN & flags) ) {
681 sb.append("*");
682 }
0237bde0 683 sb.append("FS ");
4faa65ee 684 sb.append(0 != ( FLAG_IS_FULLSCREEN & flags));
0237bde0 685 sb.append("[span ");
6ebf649d 686 sb.append(0 != ( FLAG_IS_FULLSCREEN_SPAN & flags));
0237bde0 687 sb.append("], ");
0feca163 688
4faa65ee
SG
689 if( 0 != ( FLAG_CHANGE_DECORATION & flags) ) {
690 sb.append("*");
691 }
0237bde0 692 sb.append("UNDECOR ");
4faa65ee
SG
693 sb.append(0 != ( FLAG_IS_UNDECORATED & flags));
694 sb.append(", ");
5e9c02bc 695
e5ab9757
SG
696 if( 0 != ( FLAG_CHANGE_ALWAYSONTOP & flags) ) {
697 sb.append("*");
698 }
0237bde0 699 sb.append("ALWAYSONTOP ");
e5ab9757
SG
700 sb.append(0 != ( FLAG_IS_ALWAYSONTOP & flags));
701 sb.append(", ");
5e9c02bc 702
4faa65ee
SG
703 if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) {
704 sb.append("*");
705 }
0237bde0 706 sb.append("VISIBLE ");
4faa65ee 707 sb.append(0 != ( FLAG_IS_VISIBLE & flags));
5e9c02bc 708
4faa65ee
SG
709 sb.append("]");
710 return sb.toString();
711 }
5e9c02bc 712
0feca163
SG
713 protected void setTitleImpl(String title) {}
714
18bf27fa 715 /**
af384deb 716 * Translates the given window client-area coordinates with top-left origin
fb57c652 717 * to screen coordinates in window units.
af384deb
SG
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
18bf27fa 723 * as demonstrated in {@link #getLocationOnScreen(javax.media.nativewindow.util.Point)}.
af384deb 724 * </p>
18bf27fa
SG
725 *
726 * @return if not null, the screen location of the given coordinates
727 */
728 protected abstract Point getLocationOnScreenImpl(int x, int y);
5e9c02bc 729
fb57c652
SG
730 /**
731 * Triggered by user via {@link #getInsets()}.<br>
5e9c02bc 732 * Implementations may implement this hook to update the insets.<br>
472a9c60 733 * However, they may prefer the event driven path via {@link #insetsChanged(boolean, int, int, int, int)}.
5e9c02bc 734 *
d4670328 735 * @see #getInsets()
472a9c60 736 * @see #insetsChanged(boolean, int, int, int, int)
d4670328
SG
737 */
738 protected abstract void updateInsetsImpl(Insets insets);
18bf27fa 739
24e0591b
SG
740 protected boolean setPointerVisibleImpl(boolean pointerVisible) { return false; }
741 protected boolean confinePointerImpl(boolean confine) { return false; }
742 protected void warpPointerImpl(int x, int y) { }
e7ffa68b 743 protected void setPointerIconImpl(final PointerIconImpl pi) { }
5e9c02bc 744
0feca163 745 //----------------------------------------------------------------------
018c7e86 746 // NativeSurface
0feca163
SG
747 //
748
a06e40cc 749 @Override
d10c7916 750 public final int lockSurface() throws NativeWindowException, RuntimeException {
4b5a0f65 751 final RecursiveLock _wlock = windowLock;
4b5a0f65 752 _wlock.lock();
fbe331f0
SG
753 surfaceLockCount++;
754 int res = ( 1 == surfaceLockCount ) ? LOCK_SURFACE_NOT_READY : LOCK_SUCCESS; // new lock ?
0feca163 755
bb3d3743 756 if ( LOCK_SURFACE_NOT_READY == res ) {
2aa29677 757 try {
bb3d3743 758 if( isNativeValid() ) {
bafd9b99 759 final AbstractGraphicsDevice adevice = getGraphicsConfiguration().getScreen().getDevice();
bb3d3743
SG
760 adevice.lock();
761 try {
762 res = lockSurfaceImpl();
763 } finally {
764 if (LOCK_SURFACE_NOT_READY >= res) {
765 adevice.unlock();
766 }
767 }
768 }
2aa29677 769 } finally {
bb3d3743 770 if (LOCK_SURFACE_NOT_READY >= res) {
b6f54d17 771 surfaceLockCount--;
4b5a0f65 772 _wlock.unlock();
2aa29677 773 }
77413854 774 }
0feca163
SG
775 }
776 return res;
777 }
778
a06e40cc 779 @Override
0feca163 780 public final void unlockSurface() {
4b5a0f65 781 final RecursiveLock _wlock = windowLock;
4b5a0f65 782 _wlock.validateLocked();
0feca163 783
fbe331f0 784 if ( 1 == surfaceLockCount ) {
bafd9b99 785 final AbstractGraphicsDevice adevice = getGraphicsConfiguration().getScreen().getDevice();
bb3d3743
SG
786 try {
787 unlockSurfaceImpl();
788 } finally {
789 adevice.unlock();
790 }
77413854 791 }
fbe331f0 792 surfaceLockCount--;
4b5a0f65 793 _wlock.unlock();
0feca163
SG
794 }
795
a06e40cc 796 @Override
e587f2a7 797 public final boolean isSurfaceLockedByOtherThread() {
fbe331f0 798 return windowLock.isLockedByOtherThread();
e587f2a7
SG
799 }
800
a06e40cc 801 @Override
e587f2a7 802 public final Thread getSurfaceLockOwner() {
fbe331f0 803 return windowLock.getOwner();
e587f2a7
SG
804 }
805
4b5a0f65
SG
806 public final RecursiveLock getLock() {
807 return windowLock;
3334a924 808 }
5e9c02bc 809
fbe331f0 810 @Override
018c7e86
SG
811 public long getSurfaceHandle() {
812 return windowHandle; // default: return window handle
813 }
814
fbe331f0 815 @Override
018c7e86
SG
816 public boolean surfaceSwap() {
817 return false;
818 }
819
fbe331f0 820 @Override
8be9d9b5
SG
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
bafd9b99
SG
841 public final AbstractGraphicsConfiguration getGraphicsConfiguration() {
842 return config.getNativeGraphicsConfiguration();
018c7e86
SG
843 }
844
fbe331f0 845 @Override
b7389836
SG
846 public final long getDisplayHandle() {
847 return config.getNativeGraphicsConfiguration().getScreen().getDevice().getHandle();
5e9c02bc 848 }
0feca163 849
fbe331f0 850 @Override
0feca163 851 public final int getScreenIndex() {
fbe331f0 852 return screen.getIndex();
0feca163
SG
853 }
854
018c7e86
SG
855 //----------------------------------------------------------------------
856 // NativeWindow
857 //
858
db4d7d19 859 // public final void destroy() - see below
0feca163 860
af384deb 861 @Override
ebe980ad
SG
862 public final NativeSurface getNativeSurface() { return this; }
863
864 @Override
018c7e86
SG
865 public final NativeWindow getParent() {
866 return parentWindow;
0feca163
SG
867 }
868
af384deb 869 @Override
018c7e86
SG
870 public final long getWindowHandle() {
871 return windowHandle;
0feca163
SG
872 }
873
af384deb 874 @Override
018c7e86 875 public Point getLocationOnScreen(Point storage) {
18bf27fa
SG
876 if(isNativeValid()) {
877 Point d;
4b5a0f65
SG
878 final RecursiveLock _lock = windowLock;
879 _lock.lock();
18bf27fa
SG
880 try {
881 d = getLocationOnScreenImpl(0, 0);
882 } finally {
4b5a0f65 883 _lock.unlock();
18bf27fa
SG
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
018c7e86
SG
895 if(null!=storage) {
896 storage.translate(getX(),getY());
897 } else {
898 storage = new Point(getX(),getY());
899 }
900 if(null!=parentWindow) {
18bf27fa 901 // traverse through parent list ..
018c7e86
SG
902 parentWindow.getLocationOnScreen(storage);
903 }
904 return storage;
0feca163
SG
905 }
906
907 //----------------------------------------------------------------------
908 // Window
909 //
910
af384deb 911 @Override
0feca163 912 public final boolean isNativeValid() {
28b0df6c 913 return 0 != windowHandle ;
0feca163
SG
914 }
915
af384deb 916 @Override
0feca163
SG
917 public final Screen getScreen() {
918 return screen;
919 }
5e9c02bc 920
56d60b36
SG
921 protected void setScreen(ScreenImpl newScreen) { // never null !
922 removeScreenReference();
923 screen = newScreen;
924 }
925
6ebf649d
SG
926 @Override
927 public final MonitorDevice getMainMonitor() {
56d60b36 928 return screen.getMainMonitor( getBounds() );
6ebf649d 929 }
5e9c02bc 930
fb57c652
SG
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 */
51ad6992 938 protected final void setVisibleImpl(boolean visible, int x, int y, int width, int height) {
5e9c02bc
HH
939 reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(FLAG_CHANGE_VISIBILITY, visible));
940 }
5681c25c
SG
941 final void setVisibleActionImpl(boolean visible) {
942 boolean nativeWindowCreated = false;
943 boolean madeVisible = false;
5e9c02bc 944
4b5a0f65
SG
945 final RecursiveLock _lock = windowLock;
946 _lock.lock();
5681c25c 947 try {
5681c25c
SG
948 if(!visible && null!=childWindows && childWindows.size()>0) {
949 synchronized(childWindowsLock) {
950 for(int i = 0; i < childWindows.size(); i++ ) {
cb4e7318 951 NativeWindow nw = childWindows.get(i);
5681c25c
SG
952 if(nw instanceof WindowImpl) {
953 ((WindowImpl)nw).setVisible(false);
0feca163 954 }
5e1a67c9 955 }
5681c25c
SG
956 }
957 }
28b0df6c 958 if(!isNativeValid() && visible) {
d149c554 959 if( 0<getWidth()*getHeight() ) {
5681c25c 960 nativeWindowCreated = createNative();
e228acfc 961 madeVisible = nativeWindowCreated;
5e1a67c9 962 }
51f84125 963 // always flag visible, allowing a retry ..
5e9c02bc 964 WindowImpl.this.visible = true;
5681c25c 965 } else if(WindowImpl.this.visible != visible) {
28b0df6c 966 if(isNativeValid()) {
d149c554 967 setVisibleImpl(visible, getX(), getY(), getWidth(), getHeight());
b6f54d17 968 WindowImpl.this.waitForVisible(visible, false);
5681c25c 969 madeVisible = visible;
b6f54d17
SG
970 } else {
971 WindowImpl.this.visible = true;
5e1a67c9 972 }
5681c25c 973 }
5e1a67c9 974
5681c25c
SG
975 if(null!=lifecycleHook) {
976 lifecycleHook.setVisibleActionPost(visible, nativeWindowCreated);
977 }
978
28b0df6c 979 if(isNativeValid() && visible && null!=childWindows && childWindows.size()>0) {
5681c25c
SG
980 synchronized(childWindowsLock) {
981 for(int i = 0; i < childWindows.size(); i++ ) {
cb4e7318 982 NativeWindow nw = childWindows.get(i);
5681c25c
SG
983 if(nw instanceof WindowImpl) {
984 ((WindowImpl)nw).setVisible(true);
579326db 985 }
0feca163 986 }
5681c25c 987 }
0feca163 988 }
5681c25c 989 if(DEBUG_IMPLEMENTATION) {
d149c554 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);
2c1a870f 991 }
5681c25c 992 } finally {
85d70b7d
SG
993 if(null!=lifecycleHook) {
994 lifecycleHook.resetCounter();
995 }
4b5a0f65 996 _lock.unlock();
5681c25c 997 }
6ff7ba18
SG
998 if( nativeWindowCreated || madeVisible ) {
999 sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener
c9faebb8
SG
1000 }
1001 }
1002 private class VisibleAction implements Runnable {
5681c25c
SG
1003 boolean visible;
1004
c9faebb8 1005 private VisibleAction(boolean visible) {
5681c25c
SG
1006 this.visible = visible;
1007 }
1008
f1ae8ddb 1009 @Override
3747f6a6 1010 public final void run() {
5681c25c 1011 setVisibleActionImpl(visible);
0feca163 1012 }
be59d561 1013 }
c9faebb8 1014
24eab4dc 1015 @Override
852fcbf1 1016 public final void setVisible(boolean wait, boolean visible) {
c9faebb8 1017 if(DEBUG_IMPLEMENTATION) {
d149c554 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));
18bf27fa 1019 }
5e9c02bc 1020 runOnEDTIfAvail(wait, new VisibleAction(visible));
4dd44b98 1021 }
af384deb
SG
1022
1023 @Override
852fcbf1 1024 public final void setVisible(boolean visible) {
4dd44b98 1025 setVisible(true, visible);
18bf27fa 1026 }
5e9c02bc 1027
c9faebb8 1028 private class SetSizeAction implements Runnable {
5742b1fa 1029 int width, height;
2790c756 1030 boolean force;
5742b1fa 1031
205a17de 1032 private SetSizeAction(int w, int h, boolean disregardFS) {
5742b1fa
SG
1033 this.width = w;
1034 this.height = h;
2790c756 1035 this.force = disregardFS;
5742b1fa 1036 }
c9faebb8 1037
f1ae8ddb 1038 @Override
5742b1fa 1039 public final void run() {
4b5a0f65
SG
1040 final RecursiveLock _lock = windowLock;
1041 _lock.lock();
5742b1fa 1042 try {
d149c554 1043 if ( force || ( !isFullscreen() && ( getWidth() != width || getHeight() != height ) ) ) {
c9faebb8 1044 if(DEBUG_IMPLEMENTATION) {
d149c554 1045 System.err.println("Window setSize: START force "+force+", "+getWidth()+"x"+getHeight()+" -> "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible "+visible);
c9faebb8
SG
1046 }
1047 int visibleAction; // 0 nop, 1 invisible, 2 visible (create)
a3cb6bb1 1048 if ( visible && isNativeValid() && ( 0 >= width || 0 >= height ) ) {
c9faebb8
SG
1049 visibleAction=1; // invisible
1050 defineSize(0, 0);
a3cb6bb1 1051 } else if ( visible && !isNativeValid() && 0 < width && 0 < height ) {
c9faebb8
SG
1052 visibleAction = 2; // visible (create)
1053 defineSize(width, height);
1f360ff7 1054 } else if ( visible && isNativeValid() ) {
c9faebb8
SG
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()));
b6f54d17 1058 WindowImpl.this.waitForSize(width, height, false, TIMEOUT_NATIVEWINDOW);
c9faebb8 1059 } else {
1f360ff7 1060 // invisible or invalid w/ 0 size
c9faebb8
SG
1061 visibleAction = 0;
1062 defineSize(width, height);
1063 }
1064 if(DEBUG_IMPLEMENTATION) {
d149c554 1065 System.err.println("Window setSize: END "+getWidth()+"x"+getHeight()+", visibleAction "+visibleAction);
c9faebb8
SG
1066 }
1067 switch(visibleAction) {
1068 case 1: setVisibleActionImpl(false); break;
1069 case 2: setVisibleActionImpl(true); break;
1070 }
51ad6992 1071 }
5742b1fa 1072 } finally {
4b5a0f65 1073 _lock.unlock();
c9faebb8 1074 }
0feca163 1075 }
be59d561 1076 }
c9faebb8 1077
2790c756
SG
1078 private void setSize(final int width, final int height, final boolean force) {
1079 runOnEDTIfAvail(true, new SetSizeAction(width, height, force));
5e9c02bc 1080 }
af384deb 1081 @Override
fb57c652 1082 public final void setSize(final int width, final int height) {
205a17de 1083 runOnEDTIfAvail(true, new SetSizeAction(width, height, false));
5e9c02bc 1084 }
af384deb 1085 @Override
fb57c652 1086 public final void setSurfaceSize(final int pixelWidth, final int pixelHeight) {
98ed02cd 1087 // FIXME HiDPI: Shortcut, may need to adjust if we change scaling methodology
56d60b36 1088 setSize(pixelWidth / getPixelScaleX(), pixelHeight / getPixelScaleY());
fb57c652
SG
1089 }
1090 @Override
1091 public final void setTopLevelSize(final int width, final int height) {
d4670328
SG
1092 setSize(width - getInsets().getTotalWidth(), height - getInsets().getTotalHeight());
1093 }
0feca163 1094
3747f6a6 1095 private class DestroyAction implements Runnable {
f1ae8ddb 1096 @Override
3747f6a6 1097 public final void run() {
5681c25c
SG
1098 boolean animatorPaused = false;
1099 if(null!=lifecycleHook) {
1100 animatorPaused = lifecycleHook.pauseRenderingAction();
1101 }
1102 if(null!=lifecycleHook) {
1103 lifecycleHook.destroyActionPreLock();
1104 }
4b5a0f65
SG
1105 final RecursiveLock _lock = windowLock;
1106 _lock.lock();
0feca163 1107 try {
e1ecd85a 1108 if(DEBUG_IMPLEMENTATION) {
808a9a27 1109 System.err.println("Window DestroyAction() hasScreen "+(null != screen)+", isNativeValid "+isNativeValid()+" - "+getThreadName());
f47230cb 1110 }
5e9c02bc 1111
808a9a27
SG
1112 // send synced destroy-notify notification
1113 sendWindowEvent(WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY);
5e9c02bc 1114
0feca163
SG
1115 // Childs first ..
1116 synchronized(childWindowsLock) {
db4d7d19
SG
1117 if(childWindows.size()>0) {
1118 // avoid ConcurrentModificationException: parent -> child -> parent.removeChild(this)
cb4e7318
SG
1119 @SuppressWarnings("unchecked")
1120 ArrayList<NativeWindow> clonedChildWindows = (ArrayList<NativeWindow>) childWindows.clone();
db4d7d19 1121 while( clonedChildWindows.size() > 0 ) {
cb4e7318 1122 NativeWindow nw = clonedChildWindows.remove(0);
db4d7d19 1123 if(nw instanceof WindowImpl) {
808a9a27 1124 ((WindowImpl)nw).windowDestroyNotify(true);
db4d7d19
SG
1125 } else {
1126 nw.destroy();
1127 }
0feca163
SG
1128 }
1129 }
1130 }
1131
1132 if(null!=lifecycleHook) {
e7778124 1133 // send synced destroy notification for proper cleanup, eg GLWindow/OpenGL
db4d7d19 1134 lifecycleHook.destroyActionInLock();
0feca163
SG
1135 }
1136
01fdd5c5 1137 if( isNativeValid() ) {
84576bc0 1138 screen.removeMonitorModeListener(monitorModeListenerImpl);
01fdd5c5
SG
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
f47230cb 1143 }
01fdd5c5
SG
1144 setGraphicsConfiguration(null);
1145 }
1146 removeScreenReference();
1147 Display dpy = screen.getDisplay();
1148 if(null != dpy) {
8be1fc98 1149 dpy.validateEDTStopped();
f47230cb 1150 }
db4d7d19 1151
e7778124
SG
1152 // send synced destroyed notification
1153 sendWindowEvent(WindowEvent.EVENT_WINDOW_DESTROYED);
1154
0feca163 1155 if(DEBUG_IMPLEMENTATION) {
db4d7d19 1156 System.err.println("Window.destroy() END "+getThreadName()/*+", "+WindowImpl.this*/);
0feca163
SG
1157 }
1158 } finally {
4e0eb391
SG
1159 // update states before release window lock
1160 setWindowHandle(0);
1161 visible = false;
1162 fullscreen = false;
6ebf649d
SG
1163 fullscreenMonitors = null;
1164 fullscreenUseMainMonitor = true;
4e0eb391
SG
1165 hasFocus = false;
1166 parentWindowHandle = 0;
c9faebb8 1167
4b5a0f65 1168 _lock.unlock();
0feca163 1169 }
5681c25c
SG
1170 if(animatorPaused) {
1171 lifecycleHook.resumeRenderingAction();
f47230cb 1172 }
5e9c02bc 1173
28b0df6c 1174 // these refs shall be kept alive - resurrection via setVisible(true)
f47230cb
SG
1175 /**
1176 if(null!=parentWindow && parentWindow instanceof Window) {
1177 ((Window)parentWindow).removeChild(WindowImpl.this);
5e9c02bc 1178 }
f47230cb
SG
1179 childWindows = null;
1180 surfaceUpdatedListeners = null;
1181 mouseListeners = null;
1182 keyListeners = null;
1183 capsRequested = null;
1184 lifecycleHook = null;
5e9c02bc
HH
1185
1186 screen = null;
f47230cb
SG
1187 windowListeners = null;
1188 parentWindow = null;
5e9c02bc 1189 */
0feca163
SG
1190 }
1191 }
5742b1fa 1192 private final DestroyAction destroyAction = new DestroyAction();
0feca163 1193
af384deb 1194 @Override
db4d7d19 1195 public void destroy() {
5e9c02bc 1196 visible = false; // Immediately mark synchronized visibility flag, avoiding possible recreation
28b0df6c 1197 runOnEDTIfAvail(true, destroyAction);
18bf27fa
SG
1198 }
1199
e2506d76 1200 protected void destroy(boolean preserveResources) {
2d32b056
SG
1201 if( null != lifecycleHook ) {
1202 lifecycleHook.preserveGLStateAtDestroy( preserveResources );
e2506d76
SG
1203 }
1204 destroy();
1205 }
5e9c02bc 1206
7e81956d
SG
1207 /**
1208 * @param cWin child window, must not be null
1209 * @param pWin parent window, may be null
5e9c02bc 1210 * @return true if at least one of both window's configurations is offscreen
7e81956d
SG
1211 */
1212 protected static boolean isOffscreenInstance(NativeWindow cWin, NativeWindow pWin) {
1213 boolean ofs = false;
5e9c02bc 1214 final AbstractGraphicsConfiguration cWinCfg = cWin.getGraphicsConfiguration();
4dd44b98
SG
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 }
7e81956d
SG
1223 }
1224 return ofs;
1225 }
5e9c02bc 1226
c9faebb8 1227 private class ReparentAction implements Runnable {
8be1fc98
SG
1228 final NativeWindow newParentWindow;
1229 final int topLevelX, topLevelY;
a00406f2 1230 final int hints;
c9faebb8 1231 ReparentOperation operation;
0feca163 1232
a00406f2 1233 private ReparentAction(NativeWindow newParentWindow, int topLevelX, int topLevelY, int hints) {
0feca163 1234 this.newParentWindow = newParentWindow;
8be1fc98
SG
1235 this.topLevelX = topLevelX;
1236 this.topLevelY = topLevelY;
a00406f2
SG
1237 if( DEBUG_TEST_REPARENT_INCOMPATIBLE ) {
1238 hints |= REPARENT_HINT_FORCE_RECREATION;
1239 }
1240 this.hints = hints;
c9faebb8 1241 this.operation = ReparentOperation.ACTION_INVALID; // ensure it's set
0feca163
SG
1242 }
1243
c9faebb8
SG
1244 private ReparentOperation getOp() {
1245 return operation;
0feca163
SG
1246 }
1247
f1ae8ddb 1248 @Override
3747f6a6 1249 public final void run() {
b4e03a51
SG
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 }
5681c25c
SG
1258 boolean animatorPaused = false;
1259 if(null!=lifecycleHook) {
1260 animatorPaused = lifecycleHook.pauseRenderingAction();
1261 }
3747f6a6
SG
1262 reparent();
1263 if(animatorPaused) {
1264 lifecycleHook.resumeRenderingAction();
1265 }
1266 }
5e9c02bc 1267
3747f6a6 1268 private void reparent() {
18bf27fa 1269 // mirror pos/size so native change notification can get overwritten
8be1fc98
SG
1270 final int oldX = getX();
1271 final int oldY = getY();
d149c554
SG
1272 final int oldWidth = getWidth();
1273 final int oldHeight = getHeight();
8be1fc98
SG
1274 final int x, y;
1275 int width = oldWidth;
1276 int height = oldHeight;
a00406f2
SG
1277
1278 final boolean wasVisible;
1279 final boolean becomesVisible;
1280 final boolean forceDestroyCreate;
5e9c02bc 1281
4b5a0f65
SG
1282 final RecursiveLock _lock = windowLock;
1283 _lock.lock();
0feca163 1284 try {
a00406f2
SG
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;
7e81956d 1292 }
5e9c02bc 1293
0feca163 1294 wasVisible = isVisible();
a00406f2 1295 becomesVisible = wasVisible || 0 != ( REPARENT_HINT_BECOMES_VISIBLE & hints );
0feca163
SG
1296
1297 Window newParentWindowNEWT = null;
1298 if(newParentWindow instanceof Window) {
1299 newParentWindowNEWT = (Window) newParentWindow;
1300 }
1301
1302 long newParentWindowHandle = 0 ;
1303
a00406f2
SG
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+
e217858a 1309 ", DEBUG_TEST_REPARENT_INCOMPATIBLE "+DEBUG_TEST_REPARENT_INCOMPATIBLE+
a00406f2
SG
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) );
0feca163
SG
1314 }
1315
1316 if(null!=newParentWindow) {
8be1fc98 1317 // REPARENT TO CHILD WINDOW
5e9c02bc 1318
18bf27fa
SG
1319 // reset position to 0/0 within parent space
1320 x = 0;
1321 y = 0;
1322
1323 // refit if size is bigger than parent
d149c554
SG
1324 if( width > newParentWindow.getWidth() ) {
1325 width = newParentWindow.getWidth();
18bf27fa 1326 }
d149c554
SG
1327 if( height > newParentWindow.getHeight() ) {
1328 height = newParentWindow.getHeight();
18bf27fa
SG
1329 }
1330
0feca163
SG
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 }
538a4184 1338 // Destroy this window and use parent's Screen.
0feca163 1339 // It may be created properly when the parent is made visible.
a00406f2 1340 destroy( becomesVisible );
0feca163 1341 setScreen( (ScreenImpl) newParentWindowNEWT.getScreen() );
c9faebb8 1342 operation = ReparentOperation.ACTION_NATIVE_CREATION_PENDING;
018c7e86 1343 } else if(newParentWindow != getParent()) {
0feca163
SG
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 {
1f360ff7
SG
1351 final Screen newScreen = NewtFactory.createCompatibleScreen(newParentWindow, screen);
1352 if( screen != newScreen ) {
0feca163 1353 // auto destroy on-the-fly created Screen/Display
0feca163 1354 setScreen( (ScreenImpl) newScreen );
0feca163
SG
1355 }
1356 }
a3cb6bb1 1357 if( 0 < width && 0 < height ) {
c9faebb8 1358 operation = ReparentOperation.ACTION_NATIVE_CREATION;
5e1a67c9 1359 } else {
c9faebb8 1360 operation = ReparentOperation.ACTION_NATIVE_CREATION_PENDING;
5e1a67c9 1361 }
1f360ff7 1362 } else if ( forceDestroyCreate || !NewtFactory.isScreenCompatible(newParentWindow, screen) ) {
5e9c02bc 1363 // Destroy this window, may create a new compatible Screen/Display, while trying to preserve resources if becoming visible again.
a00406f2 1364 destroy( becomesVisible );
0feca163
SG
1365 if(null!=newParentWindowNEWT) {
1366 setScreen( (ScreenImpl) newParentWindowNEWT.getScreen() );
1367 } else {
1f360ff7 1368 setScreen( (ScreenImpl) NewtFactory.createCompatibleScreen(newParentWindow, screen) );
0feca163 1369 }
c9faebb8 1370 operation = ReparentOperation.ACTION_NATIVE_CREATION;
0feca163
SG
1371 } else {
1372 // Mark it for native reparenting
c9faebb8 1373 operation = ReparentOperation.ACTION_NATIVE_REPARENTING;
0feca163
SG
1374 }
1375 } else {
1376 // Case: Parent's native window realized and not changed
c9faebb8 1377 operation = ReparentOperation.ACTION_NOP;
0feca163
SG
1378 }
1379 } else {
8be1fc98
SG
1380 // REPARENT TO TOP-LEVEL WINDOW
1381 if( 0 <= topLevelX && 0 <= topLevelY ) {
1382 x = topLevelX;
1383 y = topLevelY;
1384 } else if( null != parentWindow ) {
18bf27fa
SG
1385 // child -> top
1386 // put client to current parent+child position
1f360ff7 1387 final Point p = getLocationOnScreen(null);
18bf27fa
SG
1388 x = p.getX();
1389 y = p.getY();
8be1fc98
SG
1390 } else {
1391 x = oldX;
1392 y = oldY;
18bf27fa
SG
1393 }
1394
0feca163 1395 // Case: Top Window
4faa65ee 1396 if( 0 == parentWindowHandle ) {
0feca163 1397 // Already Top Window
c9faebb8 1398 operation = ReparentOperation.ACTION_NOP;
46542168 1399 } else if( !isNativeValid() || forceDestroyCreate ) {
538a4184
SG
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.
a00406f2 1402 destroy( becomesVisible );
a3cb6bb1 1403 if( 0 < width && 0 < height ) {
c9faebb8 1404 operation = ReparentOperation.ACTION_NATIVE_CREATION;
5e1a67c9 1405 } else {
c9faebb8 1406 operation = ReparentOperation.ACTION_NATIVE_CREATION_PENDING;
5e1a67c9 1407 }
0feca163
SG
1408 } else {
1409 // Mark it for native reparenting
c9faebb8 1410 operation = ReparentOperation.ACTION_NATIVE_REPARENTING;
0feca163
SG
1411 }
1412 }
1413 parentWindowHandle = newParentWindowHandle;
1414
c9faebb8 1415 if ( ReparentOperation.ACTION_INVALID == operation ) {
0feca163
SG
1416 throw new NativeWindowException("Internal Error: reparentAction not set");
1417 }
5e9c02bc 1418
8be1fc98
SG
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 }
5e9c02bc 1422
c9faebb8 1423 if( ReparentOperation.ACTION_NOP == operation ) {
0feca163
SG
1424 return;
1425 }
1426
58756bbd
SG
1427 if( null == newParentWindow ) {
1428 // CLIENT -> TOP: Reset Parent's Pointer State
1429 setOffscreenPointerIcon(null);
1430 setOffscreenPointerVisible(true, null);
1431 }
1432
0feca163
SG
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
c9faebb8 1442 if( ReparentOperation.ACTION_NATIVE_REPARENTING == operation ) {
1f360ff7 1443 final DisplayImpl display = (DisplayImpl) screen.getDisplay();
18bf27fa 1444 display.dispatchMessagesNative(); // status up2date
0e9935a8 1445
8be1fc98
SG
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);
b6f54d17
SG
1449 WindowImpl.this.waitForVisible(false, false);
1450 // FIXME: Some composite WM behave slacky .. give 'em chance to change state -> invisible,
0e9935a8
SG
1451 // even though we do exactly that (KDE+Composite)
1452 try { Thread.sleep(100); } catch (InterruptedException e) { }
1453 display.dispatchMessagesNative(); // status up2date
0feca163 1454 }
cc39d257
SG
1455
1456 // Lock parentWindow only during reparenting (attempt)
4faa65ee 1457 final NativeWindow parentWindowLocked;
cc39d257
SG
1458 if( null != parentWindow ) {
1459 parentWindowLocked = parentWindow;
18bf27fa 1460 if( NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindowLocked.lockSurface() ) {
0e9935a8 1461 throw new NativeWindowException("Parent surface lock: not ready: "+parentWindowLocked);
cc39d257 1462 }
0e9935a8
SG
1463 // update native handle, locked state
1464 parentWindowHandle = parentWindowLocked.getWindowHandle();
4faa65ee
SG
1465 } else {
1466 parentWindowLocked = null;
cc39d257
SG
1467 }
1468 boolean ok = false;
1469 try {
4faa65ee 1470 ok = reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(FLAG_CHANGE_PARENTING | FLAG_CHANGE_DECORATION, isVisible()));
cc39d257
SG
1471 } finally {
1472 if(null!=parentWindowLocked) {
1473 parentWindowLocked.unlockSurface();
1474 }
1475 }
b6f54d17 1476 definePosition(x, y); // position might not get updated by WM events (SWT parent apparently)
cc39d257 1477
4faa65ee 1478 // set visible again
18bf27fa
SG
1479 if(ok) {
1480 display.dispatchMessagesNative(); // status up2date
1481 if(wasVisible) {
1482 setVisibleImpl(true, x, y, width, height);
6ebf649d 1483 ok = 0 <= WindowImpl.this.waitForVisible(true, false);
e1ecd85a 1484 if(ok) {
904adbe6
SG
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 }
edbee9a1 1489 ok = WindowImpl.this.waitForSize(width, height, false, TIMEOUT_NATIVEWINDOW);
e1ecd85a
SG
1490 }
1491 if(ok) {
2aa8d4bb
SG
1492 if( 0 == parentWindowHandle ) {
1493 // Position mismatch shall not lead to reparent failure
1494 WindowImpl.this.waitForPosition(true, x, y, TIMEOUT_NATIVEWINDOW);
1495 }
9b2bbaf8 1496
8be1fc98 1497 requestFocusInt( 0 == parentWindowHandle /* skipFocusAction if top-level */);
5e9c02bc 1498 display.dispatchMessagesNative(); // status up2date
e1ecd85a 1499 }
18bf27fa
SG
1500 }
1501 }
1502
3b402dad 1503 if(!ok || !wasVisible) {
5e9c02bc 1504 // make size and position persistent manual,
3b402dad 1505 // since we don't have a WM feedback (invisible or recreation)
319fdccb
SG
1506 definePosition(x, y);
1507 defineSize(width, height);
3b402dad 1508 }
5e9c02bc 1509
e1ecd85a 1510 if(!ok) {
538a4184 1511 // native reparent failed -> try creation, while trying to preserve resources if becoming visible again.
0feca163
SG
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 }
a00406f2 1515 destroy( becomesVisible );
c9faebb8 1516 operation = ReparentOperation.ACTION_NATIVE_CREATION ;
58756bbd
SG
1517 } else {
1518 if( null != parentWindow ) {
1519 // TOP -> CLIENT: Setup Parent's Pointer State
1520 setOffscreenPointerIcon(pointerIcon);
1521 setOffscreenPointerVisible(pointerVisible, pointerIcon);
1522 }
0feca163 1523 }
8be1fc98
SG
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);
0feca163 1532 }
5e9c02bc 1533
0feca163 1534 if(DEBUG_IMPLEMENTATION) {
fb57c652
SG
1535 System.err.println("Window.reparent: END-1 ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+
1536 ", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+
1537 ", parentWindow "+ Display.hashCodeNullSafe(parentWindow)+" "+
d149c554 1538 getX()+"/"+getY()+" "+getWidth()+"x"+getHeight());
0feca163
SG
1539 }
1540 } finally {
85d70b7d
SG
1541 if(null!=lifecycleHook) {
1542 lifecycleHook.resetCounter();
1543 }
4b5a0f65 1544 _lock.unlock();
0feca163 1545 }
6ff7ba18 1546 if(wasVisible) {
c9faebb8 1547 switch (operation) {
6ff7ba18
SG
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;
5e9c02bc 1557
d5866a5d 1558 default:
6ff7ba18 1559 }
e1ecd85a
SG
1560 }
1561 if(DEBUG_IMPLEMENTATION) {
fb57c652
SG
1562 System.err.println("Window.reparent: END-X ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+
1563 ", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+
1564 ", parentWindow "+ Display.hashCodeNullSafe(parentWindow)+" "+
d149c554 1565 getX()+"/"+getY()+" "+getWidth()+"x"+getHeight());
0feca163
SG
1566 }
1567 }
1568 }
c9faebb8 1569
3747f6a6 1570 private class ReparentActionRecreate implements Runnable {
f1ae8ddb 1571 @Override
3747f6a6 1572 public final void run() {
4b5a0f65
SG
1573 final RecursiveLock _lock = windowLock;
1574 _lock.lock();
0feca163 1575 try {
0feca163 1576 if(DEBUG_IMPLEMENTATION) {
e96b2d64 1577 System.err.println("Window.reparent: ReparentActionRecreate ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+Display.hashCodeNullSafe(parentWindow));
0feca163 1578 }
8be1fc98
SG
1579 setVisibleActionImpl(true); // native creation
1580 requestFocusInt( 0 == parentWindowHandle /* skipFocusAction if top-level */);
0feca163 1581 } finally {
4b5a0f65 1582 _lock.unlock();
0feca163
SG
1583 }
1584 }
1585 }
5742b1fa 1586 private final ReparentActionRecreate reparentActionRecreate = new ReparentActionRecreate();
0feca163 1587
af384deb 1588 @Override
c9faebb8 1589 public final ReparentOperation reparentWindow(NativeWindow newParent) {
a00406f2 1590 return reparentWindow(newParent, -1, -1, 0);
0feca163
SG
1591 }
1592
8be1fc98 1593 @Override
852fcbf1 1594 public final ReparentOperation reparentWindow(NativeWindow newParent, int x, int y, boolean forceDestroyCreate) {
a00406f2
SG
1595 return reparentWindow(newParent, x, y, forceDestroyCreate ? REPARENT_HINT_FORCE_RECREATION : 0);
1596 }
1597
1598 @Override
852fcbf1 1599 public final ReparentOperation reparentWindow(NativeWindow newParent, int x, int y, int hints) {
a00406f2 1600 final ReparentAction reparentAction = new ReparentAction(newParent, x, y, hints);
c9faebb8
SG
1601 runOnEDTIfAvail(true, reparentAction);
1602 return reparentAction.getOp();
0feca163
SG
1603 }
1604
af384deb 1605 @Override
852fcbf1 1606 public final CapabilitiesChooser setCapabilitiesChooser(CapabilitiesChooser chooser) {
7c159772
SG
1607 CapabilitiesChooser old = this.capabilitiesChooser;
1608 this.capabilitiesChooser = chooser;
1609 return old;
1610 }
1611
af384deb 1612 @Override
e4913066 1613 public final CapabilitiesImmutable getChosenCapabilities() {
bafd9b99 1614 return getGraphicsConfiguration().getChosenCapabilities();
0feca163
SG
1615 }
1616
af384deb 1617 @Override
e4913066 1618 public final CapabilitiesImmutable getRequestedCapabilities() {
29e3b223 1619 return capsRequested;
0feca163
SG
1620 }
1621
c9faebb8 1622 private class DecorationAction implements Runnable {
18bf27fa
SG
1623 boolean undecorated;
1624
c9faebb8
SG
1625 private DecorationAction(boolean undecorated) {
1626 this.undecorated = undecorated;
18bf27fa
SG
1627 }
1628
f1ae8ddb 1629 @Override
3747f6a6 1630 public final void run() {
4b5a0f65
SG
1631 final RecursiveLock _lock = windowLock;
1632 _lock.lock();
c9faebb8
SG
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();
d149c554
SG
1642 final int width = getWidth();
1643 final int height = getHeight();
c9faebb8
SG
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 }
18bf27fa 1651 } finally {
4b5a0f65 1652 _lock.unlock();
0feca163 1653 }
6ff7ba18 1654 sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener
0feca163
SG
1655 }
1656 }
1657
af384deb 1658 @Override
852fcbf1 1659 public final void setUndecorated(boolean value) {
c9faebb8 1660 runOnEDTIfAvail(true, new DecorationAction(value));
18bf27fa
SG
1661 }
1662
af384deb 1663 @Override
d4670328 1664 public final boolean isUndecorated() {
0feca163
SG
1665 return 0 != parentWindowHandle || undecorated || fullscreen ;
1666 }
1667
c9faebb8 1668 private class AlwaysOnTopAction implements Runnable {
a245d530
SG
1669 boolean alwaysOnTop;
1670
c9faebb8
SG
1671 private AlwaysOnTopAction(boolean alwaysOnTop) {
1672 this.alwaysOnTop = alwaysOnTop;
a245d530
SG
1673 }
1674
f1ae8ddb 1675 @Override
a245d530 1676 public final void run() {
4b5a0f65
SG
1677 final RecursiveLock _lock = windowLock;
1678 _lock.lock();
a245d530 1679 try {
c9faebb8
SG
1680 if(WindowImpl.this.alwaysOnTop != alwaysOnTop) {
1681 // set current state
1682 WindowImpl.this.alwaysOnTop = alwaysOnTop;
5e9c02bc 1683
c9faebb8
SG
1684 if( isNativeValid() ) {
1685 // Mirror pos/size so native change notification can get overwritten
1686 final int x = getX();
1687 final int y = getY();
d149c554
SG
1688 final int width = getWidth();
1689 final int height = getHeight();
a245d530 1690
c9faebb8
SG
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 }
a245d530 1697 } finally {
4b5a0f65 1698 _lock.unlock();
a245d530
SG
1699 }
1700 sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener
1701 }
1702 }
1703
af384deb 1704 @Override
a245d530 1705 public final void setAlwaysOnTop(boolean value) {
4cb35d98
SG
1706 if( isFullscreen() ) {
1707 nfs_alwaysOnTop = value;
1708 } else {
1709 runOnEDTIfAvail(true, new AlwaysOnTopAction(value));
1710 }
a245d530 1711 }
5e9c02bc 1712
af384deb 1713 @Override
a245d530 1714 public final boolean isAlwaysOnTop() {
be59d561 1715 return alwaysOnTop;
a245d530 1716 }
5e9c02bc 1717
af384deb 1718 @Override
852fcbf1 1719 public final String getTitle() {
24e0591b
SG
1720 return title;
1721 }
af384deb 1722 @Override
852fcbf1 1723 public final void setTitle(String title) {
24e0591b
SG
1724 if (title == null) {
1725 title = "";
1726 }
1727 this.title = title;
1728 if(0 != getWindowHandle()) {
1729 setTitleImpl(title);
1730 }
1731 }
1732
af384deb 1733 @Override
852fcbf1 1734 public final boolean isPointerVisible() {
24e0591b
SG
1735 return pointerVisible;
1736 }
af384deb 1737 @Override
58756bbd 1738 public final void setPointerVisible(final boolean pointerVisible) {
24e0591b
SG
1739 if(this.pointerVisible != pointerVisible) {
1740 boolean setVal = 0 == getWindowHandle();
1741 if(!setVal) {
58756bbd 1742 setVal = setPointerVisibleIntern(pointerVisible);
24e0591b
SG
1743 }
1744 if(setVal) {
5e9c02bc 1745 this.pointerVisible = pointerVisible;
24e0591b
SG
1746 }
1747 }
1748 }
58756bbd
SG
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
bd98b927 1755 * {@link OffscreenLayerSurface#hideCursor()} or {@link OffscreenLayerSurface#setCursor(PixelRectangle, PointImmutable)}.
58756bbd
SG
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 }
e7ffa68b
SG
1787
1788 @Override
1789 public final PointerIcon getPointerIcon() { return pointerIcon; }
1790
1791 @Override
1792 public final void setPointerIcon(final PointerIcon pi) {
58756bbd
SG
1793 final PointerIconImpl piImpl = (PointerIconImpl)pi;
1794 if( this.pointerIcon != piImpl ) {
e7ffa68b 1795 if( isNativeValid() ) {
fcc0e739
SG
1796 runOnEDTIfAvail(true, new Runnable() {
1797 public void run() {
58756bbd 1798 setPointerIconIntern(piImpl);
fcc0e739 1799 } } );
e7ffa68b 1800 }
58756bbd
SG
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
bd98b927 1810 * {@link OffscreenLayerSurface#setCursor(PixelRectangle, PointImmutable)}
58756bbd
SG
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 ) {
bd98b927 1832 return ols.setCursor(pi, pi.getHotspot());
58756bbd
SG
1833 } else {
1834 return ols.setCursor(null, null); // default
1835 }
1836 } catch (Exception e) {
1837 e.printStackTrace();
1838 }
e7ffa68b 1839 }
58756bbd 1840 return false;
e7ffa68b
SG
1841 }
1842
af384deb 1843 @Override
852fcbf1 1844 public final boolean isPointerConfined() {
24e0591b
SG
1845 return pointerConfined;
1846 }
af384deb 1847 @Override
852fcbf1 1848 public final void confinePointer(boolean confine) {
24e0591b
SG
1849 if(this.pointerConfined != confine) {
1850 boolean setVal = 0 == getWindowHandle();
1851 if(!setVal) {
50dca7ef
SG
1852 if(confine) {
1853 requestFocus();
f9a00b91 1854 warpPointer(getSurfaceWidth()/2, getSurfaceHeight()/2);
50dca7ef 1855 }
24e0591b 1856 setVal = confinePointerImpl(confine);
50dca7ef
SG
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 }
24e0591b
SG
1864 }
1865 if(setVal) {
5e9c02bc 1866 this.pointerConfined = confine;
24e0591b 1867 }
5e9c02bc 1868 }
24e0591b 1869 }
5e9c02bc 1870
af384deb 1871 @Override
852fcbf1 1872 public final void warpPointer(int x, int y) {
24e0591b
SG
1873 if(0 != getWindowHandle()) {
1874 warpPointerImpl(x, y);
1875 }
1876 }
5e9c02bc 1877
af384deb 1878 @Override
d4670328
SG
1879 public final InsetsImmutable getInsets() {
1880 if(isUndecorated()) {
1881 return Insets.getZero();
1882 }
1883 updateInsetsImpl(insets);
1884 return insets;
0feca163 1885 }
5e9c02bc 1886
af384deb 1887 @Override
98ed02cd
SG
1888 public final int getX() {
1889 return x;
0feca163
SG
1890 }
1891
af384deb 1892 @Override
98ed02cd
SG
1893 public final int getY() {
1894 return y;
f9a00b91
SG
1895 }
1896
1897 @Override
d149c554 1898 public final int getWidth() {
fb57c652 1899 return winWidth;
f9a00b91
SG
1900 }
1901
1902 @Override
d149c554 1903 public final int getHeight() {
fb57c652
SG
1904 return winHeight;
1905 }
1906
1907 @Override
98ed02cd
SG
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
fb57c652
SG
1929 public final int[] convertToWindowUnits(final int[] pixelUnitsAndResult) {
1930 pixelUnitsAndResult[0] /= getPixelScaleX();
1931 pixelUnitsAndResult[1] /= getPixelScaleY();
1932 return pixelUnitsAndResult;
f9a00b91
SG
1933 }
1934
1935 @Override
fb57c652
SG
1936 public final int[] convertToPixelUnits(final int[] windowUnitsAndResult) {
1937 windowUnitsAndResult[0] *= getPixelScaleX();
1938 windowUnitsAndResult[1] *= getPixelScaleY();
1939 return windowUnitsAndResult;
f9a00b91
SG
1940 }
1941
98ed02cd 1942 protected final Point convertToWindowUnits(final Point pixelUnitsAndResult) {
fb57c652 1943 return pixelUnitsAndResult.scaleInv(getPixelScaleX(), getPixelScaleY());
0feca163
SG
1944 }
1945
98ed02cd 1946 protected final Point convertToPixelUnits(final Point windowUnitsAndResult) {
fb57c652
SG
1947 return windowUnitsAndResult.scale(getPixelScaleX(), getPixelScaleY());
1948 }
1949
fb57c652 1950 @Override
98ed02cd
SG
1951 public final Rectangle convertToWindowUnits(final Rectangle pixelUnitsAndResult) {
1952 return pixelUnitsAndResult.scaleInv(getPixelScaleX(), getPixelScaleY());
0feca163
SG
1953 }
1954
af384deb 1955 @Override
98ed02cd
SG
1956 public final Rectangle convertToPixelUnits(final Rectangle windowUnitsAndResult) {
1957 return windowUnitsAndResult.scale(getPixelScaleX(), getPixelScaleY());
0feca163
SG
1958 }
1959
98ed02cd 1960 /** HiDPI: We currently base scaling of window units to pixel units on an integer scale factor per component. */
2571ed0b
SG
1961 protected final int getPixelScaleX() {
1962 return hasPixelScale[0];
1963 }
1964
98ed02cd 1965 /** HiDPI: We currently base scaling of window units to pixel units on an integer scale factor per component. */
2571ed0b
SG
1966 protected final int getPixelScaleY() {
1967 return hasPixelScale[1];
1968 }
1969
1970 @Override
1971 public void setSurfaceScale(final int[] pixelScale) {
1972 SurfaceScaleUtils.validateReqPixelScale(reqPixelScale, pixelScale, DEBUG_IMPLEMENTATION ? getClass().getSimpleName() : null);
1973 }
1974
1975 @Override
1976 public final int[] getSurfaceScale(final int[] result) {
1977 System.arraycopy(isNativeValid() ? hasPixelScale : reqPixelScale, 0, result, 0, 2);
1978 return result;
1979 }
98ed02cd 1980
319fdccb 1981 protected final boolean autoPosition() { return autoPosition; }
5e9c02bc 1982
fb57c652 1983 /** Sets the position fields {@link #x} and {@link #y} in window units to the given values and {@link #autoPosition} to false. */
319fdccb
SG
1984 protected final void definePosition(int x, int y) {
1985 if(DEBUG_IMPLEMENTATION) {
1986 System.err.println("definePosition: "+this.x+"/"+this.y+" -> "+x+"/"+y);
1987 // Thread.dumpStack();
1988 }
1989 autoPosition = false;
1990 this.x = x; this.y = y;
1991 }
1992
fb57c652
SG
1993 /**
1994 * Sets the size fields {@link #winWidth} and {@link #winHeight} in window units to the given values
1995 * and {@link #pixWidth} and {@link #pixHeight} in pixel units according to {@link #convertToPixelUnits(int[])}.
1996 */
1997 protected final void defineSize(int winWidth, int winHeight) {
98ed02cd
SG
1998 final int pixWidth = winWidth * getPixelScaleX(); // FIXME HiDPI: Shortcut, may need to adjust if we change scaling methodology
1999 final int pixHeight = winHeight * getPixelScaleY();
319fdccb 2000 if(DEBUG_IMPLEMENTATION) {
fb57c652 2001 System.err.println("defineSize: win["+this.winWidth+"x"+this.winHeight+" -> "+winWidth+"x"+winHeight+
98ed02cd 2002 "], pixel["+this.pixWidth+"x"+this.pixHeight+" -> "+pixWidth+"x"+pixHeight+"]");
319fdccb
SG
2003 // Thread.dumpStack();
2004 }
fb57c652 2005 this.winWidth = winWidth; this.winHeight = winHeight;
98ed02cd 2006 this.pixWidth = pixWidth; this.pixHeight = pixHeight;
319fdccb 2007 }
5e9c02bc 2008
af384deb 2009 @Override
0feca163
SG
2010 public final boolean isVisible() {
2011 return visible;
2012 }
2013
af384deb 2014 @Override
0feca163
SG
2015 public final boolean isFullscreen() {
2016 return fullscreen;
2017 }
2018
0feca163
SG
2019 //----------------------------------------------------------------------
2020 // Window
2021 //
2022
af384deb
SG
2023 @Override
2024 public final Window getDelegatedWindow() {
2025 return this;
2026 }
5e9c02bc 2027
af384deb
SG
2028 //----------------------------------------------------------------------
2029 // WindowImpl
2030 //
2031
0feca163
SG
2032 /**
2033 * If the implementation is capable of detecting a device change
2034 * return true and clear the status/reason of the change.
2035 */
2036 public boolean hasDeviceChanged() {
2037 return false;
2038 }
2039
852fcbf1 2040 public final LifecycleHook getLifecycleHook() {
0feca163
SG
2041 return lifecycleHook;
2042 }
2043
852fcbf1 2044 public final LifecycleHook setLifecycleHook(LifecycleHook hook) {
0feca163
SG
2045 LifecycleHook old = lifecycleHook;
2046 lifecycleHook = hook;
2047 return old;
2048 }
2049
5e9c02bc
HH
2050 /**
2051 * If this Window actually wraps a {@link NativeSurface} from another instance or toolkit,
9d78ea65
SG
2052 * it will return such reference. Otherwise returns null.
2053 */
2054 public NativeSurface getWrappedSurface() {
0feca163
SG
2055 return null;
2056 }
808a9a27
SG
2057
2058 @Override
852fcbf1 2059 public final void setWindowDestroyNotifyAction(Runnable r) {
808a9a27 2060 windowDestroyNotifyAction = r;
0feca163 2061 }
5e9c02bc 2062
0feca163 2063 protected final long getParentWindowHandle() {
4faa65ee 2064 return isFullscreen() ? 0 : parentWindowHandle;
0feca163
SG
2065 }
2066
10c696f7 2067 @Override
852fcbf1 2068 public final String toString() {
10c696f7 2069 StringBuilder sb = new StringBuilder();
0feca163
SG
2070
2071 sb.append(getClass().getName()+"[Config "+config+
fb57c652
SG
2072 ",\n "+screen+
2073 ",\n ParentWindow "+parentWindow+
2074 ",\n ParentWindowHandle "+toHexString(parentWindowHandle)+" ("+(0!=getParentWindowHandle())+")"+
2075 ",\n WindowHandle "+toHexString(getWindowHandle())+
2076 ",\n SurfaceHandle "+toHexString(getSurfaceHandle())+ " (lockedExt window "+windowLock.isLockedByOtherThread()+", surface "+isSurfaceLockedByOtherThread()+")"+
d149c554 2077 ",\n window["+getX()+"/"+getY()+" (auto "+autoPosition()+") "+getWidth()+"x"+getHeight()+"], pixel["+getSurfaceWidth()+"x"+getSurfaceHeight()+
fb57c652
SG
2078 "],\n Visible "+isVisible()+", focus "+hasFocus()+
2079 ",\n Undecorated "+undecorated+" ("+isUndecorated()+")"+
2080 ",\n AlwaysOnTop "+alwaysOnTop+", Fullscreen "+fullscreen+
2081 ",\n WrappedSurface "+getWrappedSurface()+
2082 ",\n ChildWindows "+childWindows.size());
0feca163 2083
ccb7213c
SG
2084 sb.append(", SurfaceUpdatedListeners num "+surfaceUpdatedHelper.size()+" [");
2085 for (int i = 0; i < surfaceUpdatedHelper.size(); i++ ) {
2086 sb.append(surfaceUpdatedHelper.get(i)+", ");
0feca163
SG
2087 }
2088 sb.append("], WindowListeners num "+windowListeners.size()+" [");
77413854
SG
2089 for (int i = 0; i < windowListeners.size(); i++ ) {
2090 sb.append(windowListeners.get(i)+", ");
0feca163
SG
2091 }
2092 sb.append("], MouseListeners num "+mouseListeners.size()+" [");
77413854
SG
2093 for (int i = 0; i < mouseListeners.size(); i++ ) {
2094 sb.append(mouseListeners.get(i)+", ");
0feca163 2095 }
bc72e232
SG
2096 sb.append("], PointerGestures default "+defaultGestureHandlerEnabled+", custom "+pointerGestureHandler.size()+" [");
2097 for (int i = 0; i < pointerGestureHandler.size(); i++ ) {
2098 sb.append(pointerGestureHandler.get(i)+", ");
2099 }
0feca163 2100 sb.append("], KeyListeners num "+keyListeners.size()+" [");
77413854
SG
2101 for (int i = 0; i < keyListeners.size(); i++ ) {
2102 sb.append(keyListeners.get(i)+", ");
0feca163 2103 }
b6f54d17 2104 sb.append("], windowLock "+windowLock+", surfaceLockCount "+surfaceLockCount+"]");
0feca163
SG
2105 return sb.toString();
2106 }
2107
2108 protected final void setWindowHandle(long handle) {
2109 windowHandle = handle;
2110 }
2111
58ebd43a 2112 @Override
852fcbf1 2113 public final void runOnEDTIfAvail(boolean wait, final Runnable task) {
1f360ff7 2114 if( windowLock.isOwner( Thread.currentThread() ) ) {
4a0a5d69
SG
2115 task.run();
2116 } else {
1f360ff7 2117 ( (DisplayImpl) screen.getDisplay() ).runOnEDTIfAvail(wait, task);
0feca163 2118 }
0feca163
SG
2119 }
2120
5742b1fa 2121 private final Runnable requestFocusAction = new Runnable() {
f1ae8ddb 2122 @Override
3747f6a6 2123 public final void run() {
18bf27fa 2124 if(DEBUG_IMPLEMENTATION) {
d8bbddae 2125 System.err.println("Window.RequestFocusAction: force 0 - ("+getThreadName()+"): "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle));
18bf27fa 2126 }
0feca163
SG
2127 WindowImpl.this.requestFocusImpl(false);
2128 }
d8bbddae 2129 };
5742b1fa 2130 private final Runnable requestFocusActionForced = new Runnable() {
f1ae8ddb 2131 @Override
d8bbddae
SG
2132 public final void run() {
2133 if(DEBUG_IMPLEMENTATION) {
2134 System.err.println("Window.RequestFocusAction: force 1 - ("+getThreadName()+"): "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle));
2135 }
2136 WindowImpl.this.requestFocusImpl(true);
2137 }
2138 };
0feca163 2139
58ebd43a 2140 @Override
3db4e89c
SG
2141 public final boolean hasFocus() {
2142 return hasFocus;
2143 }
2144
58ebd43a 2145 @Override
852fcbf1 2146 public final void requestFocus() {
3db4e89c
SG
2147 requestFocus(true);
2148 }
2149
58ebd43a 2150 @Override
852fcbf1 2151 public final void requestFocus(boolean wait) {
a19db300 2152 requestFocus(wait /* wait */, false /* skipFocusAction */, brokenFocusChange /* force */);
d8bbddae 2153 }
5e9c02bc 2154
d8bbddae
SG
2155 private void requestFocus(boolean wait, boolean skipFocusAction, boolean force) {
2156 if( isNativeValid() &&
2157 ( force || !hasFocus() ) &&
2158 ( skipFocusAction || !focusAction() ) ) {
2159 runOnEDTIfAvail(wait, force ? requestFocusActionForced : requestFocusAction);
2641d1cf 2160 }
0feca163 2161 }
5e9c02bc 2162
a19db300
SG
2163 /** Internally forcing request focus on current thread */
2164 private void requestFocusInt(boolean skipFocusAction) {
d8bbddae
SG
2165 if( skipFocusAction || !focusAction() ) {
2166 if(DEBUG_IMPLEMENTATION) {
8be1fc98 2167 System.err.println("Window.RequestFocusInt: forcing - ("+getThreadName()+"): skipFocusAction "+skipFocusAction+", focus "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle));
d8bbddae 2168 }
a19db300 2169 requestFocusImpl(true);
5e9c02bc 2170 }
2641d1cf 2171 }
5e9c02bc 2172
58ebd43a 2173 @Override
852fcbf1 2174 public final void setFocusAction(FocusRunnable focusAction) {
b32c65e2 2175 this.focusAction = focusAction;
0feca163 2176 }
5e9c02bc 2177
2641d1cf 2178 private boolean focusAction() {
0feca163
SG
2179 if(DEBUG_IMPLEMENTATION) {
2180 System.err.println("Window.focusAction() START - "+getThreadName()+", focusAction: "+focusAction+" - windowHandle "+toHexString(getWindowHandle()));
2181 }
2182 boolean res;
2183 if(null!=focusAction) {
2184 res = focusAction.run();
2185 } else {
2186 res = false;
2187 }
2188 if(DEBUG_IMPLEMENTATION) {
2189 System.err.println("Window.focusAction() END - "+getThreadName()+", focusAction: "+focusAction+" - windowHandle "+toHexString(getWindowHandle())+", res: "+res);
2190 }
2191 return res;
2192 }
5e9c02bc 2193
852fcbf1 2194 protected final void setBrokenFocusChange(boolean v) {
a19db300
SG
2195 brokenFocusChange = v;
2196 }
5e9c02bc 2197
58ebd43a 2198 @Override
852fcbf1 2199 public final void setKeyboardFocusHandler(KeyListener l) {
3db4e89c
SG
2200 keyboardFocusHandler = l;
2201 }
5e9c02bc 2202
c9faebb8 2203 private class SetPositionAction implements Runnable {
18bf27fa 2204 int x, y;
0feca163 2205
c9faebb8
SG
2206 private SetPositionAction(int x, int y) {
2207 this.x = x;
2208 this.y = y;
18bf27fa 2209 }
c9faebb8 2210
f1ae8ddb 2211 @Override
3747f6a6 2212 public final void run() {
4b5a0f65
SG
2213 final RecursiveLock _lock = windowLock;
2214 _lock.lock();
18bf27fa
SG
2215 try {
2216 if(DEBUG_IMPLEMENTATION) {
319fdccb 2217 System.err.println("Window setPosition: "+getX()+"/"+getY()+" -> "+x+"/"+y+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle));
18bf27fa 2218 }
c2e6233c 2219 // Let the window be positioned if !fullscreen and position changed or being a child window.
78fcb822 2220 if ( !isFullscreen() && ( getX() != x || getY() != y || null != getParent()) ) {
c9faebb8
SG
2221 if(isNativeValid()) {
2222 // this.x/this.y will be set by sizeChanged, triggered by windowing event system
d149c554 2223 reconfigureWindowImpl(x, y, getWidth(), getHeight(), getReconfigureFlags(0, isVisible()));
2aa8d4bb
SG
2224 if( null == parentWindow ) {
2225 // Wait until custom position is reached within tolerances
2226 waitForPosition(true, x, y, Window.TIMEOUT_NATIVEWINDOW);
2227 }
c9faebb8
SG
2228 } else {
2229 definePosition(x, y); // set pos for createNative(..)
2230 }
2231 }
18bf27fa 2232 } finally {
4b5a0f65 2233 _lock.unlock();
0feca163 2234 }
0feca163
SG
2235 }
2236 }
2237
58ebd43a 2238 @Override
18bf27fa 2239 public void setPosition(int x, int y) {
c9faebb8
SG
2240 autoPosition = false;
2241 runOnEDTIfAvail(true, new SetPositionAction(x, y));
18bf27fa 2242 }
5e9c02bc 2243
58ebd43a 2244 @Override
852fcbf1 2245 public final void setTopLevelPosition(int x, int y) {
d4670328
SG
2246 setPosition(x + getInsets().getLeftWidth(), y + getInsets().getTopHeight());
2247 }
5e9c02bc 2248
c9faebb8 2249 private class FullScreenAction implements Runnable {
0f1ddc82 2250 boolean _fullscreen;
18bf27fa 2251
5e9c02bc 2252 private boolean init(boolean fullscreen) {
3424091e 2253 if(isNativeValid()) {
0f1ddc82 2254 this._fullscreen = fullscreen;
205a17de 2255 return isFullscreen() != fullscreen;
3424091e
SG
2256 } else {
2257 WindowImpl.this.fullscreen = fullscreen; // set current state for createNative(..)
2258 return false;
2259 }
5e9c02bc 2260 }
0f1ddc82 2261 public boolean fsOn() { return _fullscreen; }
18bf27fa 2262
f1ae8ddb 2263 @Override
3747f6a6 2264 public final void run() {
4b5a0f65
SG
2265 final RecursiveLock _lock = windowLock;
2266 _lock.lock();
ded0a8fb 2267 blockInsetsChange = true;
18bf27fa 2268 try {
929cae9a
SG
2269 final int oldX = getX();
2270 final int oldY = getY();
d149c554
SG
2271 final int oldWidth = getWidth();
2272 final int oldHeight = getHeight();
5e9c02bc 2273
d8bbddae 2274 int x,y,w,h;
5e9c02bc 2275
56d60b36
SG
2276 final RectangleImmutable sviewport = screen.getViewportInWindowUnits(); // window units
2277 final RectangleImmutable viewport; // window units
6ebf649d 2278 final int fs_span_flag;
4cb35d98 2279 final boolean alwaysOnTopChange;
0f1ddc82 2280 if(_fullscreen) {
205a17de
SG
2281 if( null == fullscreenMonitors ) {
2282 if( fullscreenUseMainMonitor ) {
2283 fullscreenMonitors = new ArrayList<MonitorDevice>();
2284 fullscreenMonitors.add( getMainMonitor() );
6ebf649d 2285 } else {
205a17de 2286 fullscreenMonitors = getScreen().getMonitorDevices();
6ebf649d
SG
2287 }
2288 }
56d60b36
SG
2289 {
2290 final Rectangle viewportInWindowUnits = new Rectangle();
2291 MonitorDevice.unionOfViewports(null, viewportInWindowUnits, fullscreenMonitors);
2292 viewport = viewportInWindowUnits;
2293 }
fa6e868a
SG
2294 if( isReconfigureFlagSupported(FLAG_IS_FULLSCREEN_SPAN) &&
2295 ( fullscreenMonitors.size() > 1 || sviewport.compareTo(viewport) > 0 ) ) {
fa6e868a
SG
2296 fs_span_flag = FLAG_IS_FULLSCREEN_SPAN;
2297 } else {
fa6e868a
SG
2298 fs_span_flag = 0;
2299 }
929cae9a
SG
2300 nfs_x = oldX;
2301 nfs_y = oldY;
2302 nfs_width = oldWidth;
2303 nfs_height = oldHeight;
4cb35d98 2304 nfs_alwaysOnTop = alwaysOnTop;
5e9c02bc 2305 x = viewport.getX();
6ebf649d
SG
2306 y = viewport.getY();
2307 w = viewport.getWidth();
2308 h = viewport.getHeight();
4cb35d98
SG
2309 alwaysOnTop = false;
2310 alwaysOnTopChange = nfs_alwaysOnTop != alwaysOnTop;
d8bbddae 2311 } else {
205a17de
SG
2312 fullscreenUseMainMonitor = true;
2313 fullscreenMonitors = null;
6ebf649d
SG
2314 fs_span_flag = 0;
2315 viewport = null;
d8bbddae
SG
2316 x = nfs_x;
2317 y = nfs_y;
2318 w = nfs_width;
2319 h = nfs_height;
4cb35d98
SG
2320 alwaysOnTopChange = nfs_alwaysOnTop != alwaysOnTop;
2321 alwaysOnTop = nfs_alwaysOnTop;
5e9c02bc 2322
d8bbddae
SG
2323 if(null!=parentWindow) {
2324 // reset position to 0/0 within parent space
2325 x = 0;
2326 y = 0;
5e9c02bc 2327
d8bbddae 2328 // refit if size is bigger than parent
d149c554
SG
2329 if( w > parentWindow.getWidth() ) {
2330 w = parentWindow.getWidth();
d8bbddae 2331 }
d149c554
SG
2332 if( h > parentWindow.getHeight() ) {
2333 h = parentWindow.getHeight();
e1ecd85a 2334 }
18bf27fa 2335 }
d8bbddae 2336 }
0f1ddc82
SG
2337
2338 final DisplayImpl display = (DisplayImpl) screen.getDisplay();
2339 display.dispatchMessagesNative(); // status up2date
2340 final boolean wasVisible = isVisible();
2341 final boolean tempInvisible = !_fullscreen && wasVisible && NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(true);
2342
d8bbddae 2343 if(DEBUG_IMPLEMENTATION) {
0f1ddc82 2344 System.err.println("Window fs: "+_fullscreen+" "+x+"/"+y+" "+w+"x"+h+", "+isUndecorated()+
98ed02cd 2345 ", virtl-screenSize: "+sviewport+" [wu], monitorsViewport "+viewport+" [wu]"+
4cb35d98
SG
2346 ", spanning "+(0!=fs_span_flag)+
2347 ", alwaysOnTop "+alwaysOnTop+(alwaysOnTopChange?"*":"")+
0f1ddc82
SG
2348 ", wasVisible "+wasVisible+", tempInvisible "+tempInvisible+
2349 ", hasParent "+(null!=parentWindow)+
4cb35d98 2350 " @ "+Thread.currentThread().getName());
d8bbddae 2351 }
18bf27fa 2352
0f1ddc82
SG
2353 // fullscreen off: !visible first (fixes X11 unsuccessful return to parent window _and_ wrong window size propagation)
2354 if( tempInvisible ) {
2355 setVisibleImpl(false, oldX, oldY, oldWidth, oldHeight);
2356 WindowImpl.this.waitForVisible(false, false);
2357 try { Thread.sleep(100); } catch (InterruptedException e) { }
2358 display.dispatchMessagesNative(); // status up2date
2359 }
5e9c02bc 2360
d8bbddae
SG
2361 // Lock parentWindow only during reparenting (attempt)
2362 final NativeWindow parentWindowLocked;
2363 if( null != parentWindow ) {
2364 parentWindowLocked = parentWindow;
2365 if( NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindowLocked.lockSurface() ) {
2366 throw new NativeWindowException("Parent surface lock: not ready: "+parentWindow);
de0fe22e 2367 }
d8bbddae
SG
2368 } else {
2369 parentWindowLocked = null;
2370 }
2371 try {
0f1ddc82 2372 if(alwaysOnTopChange && _fullscreen) {
4cb35d98 2373 // Enter fullscreen - Disable alwaysOnTop
0f1ddc82 2374 reconfigureWindowImpl(oldX, oldY, oldWidth, oldHeight, getReconfigureFlags(FLAG_CHANGE_ALWAYSONTOP, isVisible()));
4cb35d98 2375 }
0f1ddc82
SG
2376
2377 WindowImpl.this.fullscreen = _fullscreen;
5e9c02bc
HH
2378 reconfigureWindowImpl(x, y, w, h,
2379 getReconfigureFlags( ( ( null != parentWindowLocked ) ? FLAG_CHANGE_PARENTING : 0 ) |
929cae9a 2380 fs_span_flag | FLAG_CHANGE_FULLSCREEN | FLAG_CHANGE_DECORATION, isVisible()) );
0f1ddc82 2381 if(alwaysOnTopChange && !_fullscreen) {
4cb35d98
SG
2382 // Leave fullscreen - Restore alwaysOnTop
2383 reconfigureWindowImpl(x, y, w, h, getReconfigureFlags(FLAG_CHANGE_ALWAYSONTOP, isVisible()));
2384 }
d8bbddae
SG
2385 } finally {
2386 if(null!=parentWindowLocked) {
2387 parentWindowLocked.unlockSurface();
de0fe22e 2388 }
d8bbddae
SG
2389 }
2390 display.dispatchMessagesNative(); // status up2date
5e9c02bc 2391
d8bbddae 2392 if(wasVisible) {
ded0a8fb
SG
2393 if( NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(true) ) {
2394 // Give sluggy WM's (e.g. Unity) a chance to properly restore window ..
2395 try { Thread.sleep(100); } catch (InterruptedException e) { }
2396 display.dispatchMessagesNative(); // status up2date
2397 }
d8bbddae 2398 setVisibleImpl(true, x, y, w, h);
929cae9a
SG
2399 boolean ok = 0 <= WindowImpl.this.waitForVisible(true, false);
2400 if(ok) {
2401 ok = WindowImpl.this.waitForSize(w, h, false, TIMEOUT_NATIVEWINDOW);
2402 }
2aa8d4bb 2403 if(ok && !_fullscreen && null == parentWindow) {
9b2bbaf8
SG
2404 // Position mismatch shall not lead to fullscreen failure
2405 WindowImpl.this.waitForPosition(true, x, y, TIMEOUT_NATIVEWINDOW);
e96b2d64 2406 }
929cae9a 2407 if(ok) {
0f1ddc82 2408 requestFocusInt(_fullscreen /* skipFocusAction if fullscreen */);
5e9c02bc 2409 display.dispatchMessagesNative(); // status up2date
929cae9a 2410 }
d8bbddae 2411 if(DEBUG_IMPLEMENTATION) {
929cae9a 2412 System.err.println("Window fs done: ok " + ok + ", " + WindowImpl.this);
0feca163 2413 }
0feca163 2414 }
18bf27fa 2415 } finally {
ded0a8fb 2416 blockInsetsChange = false;
4b5a0f65 2417 _lock.unlock();
0feca163 2418 }
6ff7ba18 2419 sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener
0feca163 2420 }
18bf27fa 2421 }
5e9c02bc 2422 private final FullScreenAction fullScreenAction = new FullScreenAction();
18bf27fa 2423
58ebd43a 2424 @Override
18bf27fa 2425 public boolean setFullscreen(boolean fullscreen) {
6ebf649d
SG
2426 return setFullscreenImpl(fullscreen, true, null);
2427 }
5e9c02bc 2428
6ebf649d
SG
2429 @Override
2430 public boolean setFullscreen(List<MonitorDevice> monitors) {
2431 return setFullscreenImpl(true, false, monitors);
2432 }
5e9c02bc 2433
6ebf649d 2434 private boolean setFullscreenImpl(boolean fullscreen, boolean useMainMonitor, List<MonitorDevice> monitors) {
d8bbddae 2435 synchronized(fullScreenAction) {
205a17de
SG
2436 fullscreenMonitors = monitors;
2437 fullscreenUseMainMonitor = useMainMonitor;
2438 if( fullScreenAction.init(fullscreen) ) {
a00406f2 2439 if( fullScreenAction.fsOn() && isOffscreenInstance(WindowImpl.this, parentWindow) ) {
d8bbddae
SG
2440 // enable fullscreen on offscreen instance
2441 if(null != parentWindow) {
2442 nfs_parent = parentWindow;
45525950 2443 reparentWindow(null, -1, -1, REPARENT_HINT_FORCE_RECREATION | REPARENT_HINT_BECOMES_VISIBLE);
d8bbddae
SG
2444 } else {
2445 throw new InternalError("Offscreen instance w/o parent unhandled");
2446 }
2447 }
5e9c02bc 2448
d8bbddae 2449 runOnEDTIfAvail(true, fullScreenAction);
5e9c02bc 2450
3424091e 2451 if(!fullScreenAction.fsOn() && null != nfs_parent) {
d8bbddae 2452 // disable fullscreen on offscreen instance
45525950 2453 reparentWindow(nfs_parent, -1, -1, REPARENT_HINT_FORCE_RECREATION | REPARENT_HINT_BECOMES_VISIBLE);
d8bbddae
SG
2454 nfs_parent = null;
2455 }
d8bbddae 2456 }
5e9c02bc 2457 return this.fullscreen;
7c83b3d2 2458 }
0feca163 2459 }
5e9c02bc 2460
2790c756
SG
2461 /** Notify WindowDriver about the finished monitor mode change. */
2462 protected void monitorModeChanged(MonitorEvent me, boolean success) {
2463 }
2464
84576bc0 2465 private class MonitorModeListenerImpl implements MonitorModeListener {
2aa29677 2466 boolean animatorPaused = false;
2790c756 2467 boolean hidden = false;
205a17de 2468 boolean hadFocus = false;
fa6e868a
SG
2469 boolean fullscreenPaused = false;
2470 List<MonitorDevice> _fullscreenMonitors = null;
2471 boolean _fullscreenUseMainMonitor = true;
a0e9d6c8 2472
f1ae8ddb 2473 @Override
6ebf649d 2474 public void monitorModeChangeNotify(MonitorEvent me) {
205a17de 2475 hadFocus = hasFocus();
2790c756
SG
2476 final boolean isOSX = NativeWindowFactory.TYPE_MACOSX == NativeWindowFactory.getNativeWindowType(true);
2477 final boolean quirkFSPause = fullscreen && isReconfigureFlagSupported(FLAG_IS_FULLSCREEN_SPAN);
2478 final boolean quirkHide = !quirkFSPause && !fullscreen && isVisible() && isOSX;
e1ecd85a 2479 if(DEBUG_IMPLEMENTATION) {
2790c756 2480 System.err.println("Window.monitorModeChangeNotify: hadFocus "+hadFocus+", qFSPause "+quirkFSPause+", qHide "+quirkHide+", "+me+" @ "+Thread.currentThread().getName());
2aa29677 2481 }
a34d04ad 2482
2aa29677
SG
2483 if(null!=lifecycleHook) {
2484 animatorPaused = lifecycleHook.pauseRenderingAction();
2485 }
2790c756 2486 if( quirkFSPause ) {
fa6e868a
SG
2487 if(DEBUG_IMPLEMENTATION) {
2488 System.err.println("Window.monitorModeChangeNotify: FS Pause");
2489 }
2490 fullscreenPaused = true;
2491 _fullscreenMonitors = fullscreenMonitors;
2492 _fullscreenUseMainMonitor = fullscreenUseMainMonitor;
2493 setFullscreenImpl(false, true, null);
2494 }
2790c756
SG
2495 if( quirkHide ) {
2496 // hiding & showing the window around mode-change solves issues w/ OSX,
2497 // where the content would be black until a resize.
2498 hidden = true;
2499 WindowImpl.this.setVisible(false);
2500 }
a34d04ad 2501 }
a34d04ad 2502
f1ae8ddb 2503 @Override
6ebf649d 2504 public void monitorModeChanged(MonitorEvent me, boolean success) {
2790c756
SG
2505 if(!animatorPaused && success && null!=lifecycleHook) {
2506 // Didn't pass above notify method. probably detected screen change after it happened.
2507 animatorPaused = lifecycleHook.pauseRenderingAction();
2508 }
e1ecd85a 2509 if(DEBUG_IMPLEMENTATION) {
2790c756
SG
2510 System.err.println("Window.monitorModeChanged.0: success: "+success+", hadFocus "+hadFocus+", animPaused "+animatorPaused+
2511 ", hidden "+hidden+", FS "+fullscreen+", FS-paused "+fullscreenPaused+
2512 " @ "+Thread.currentThread().getName());
2513 System.err.println("Window.monitorModeChanged.0: "+getScreen());
2514 System.err.println("Window.monitorModeChanged.0: "+me);
a34d04ad 2515 }
2790c756
SG
2516 WindowImpl.this.monitorModeChanged(me, success);
2517
2518 if( success && !fullscreen && !fullscreenPaused ) {
2519 // Simply move/resize window to fit in virtual screen if required
2520 final RectangleImmutable viewport = screen.getViewportInWindowUnits();
2521 if( viewport.getWidth() > 0 && viewport.getHeight() > 0 ) { // failsafe
d149c554 2522 final RectangleImmutable rect = new Rectangle(getX(), getY(), getWidth(), getHeight());
2790c756 2523 final RectangleImmutable isect = viewport.intersection(rect);
d149c554
SG
2524 if ( getHeight() > isect.getHeight() ||
2525 getWidth() > isect.getWidth() ) {
205a17de 2526 if(DEBUG_IMPLEMENTATION) {
2790c756
SG
2527 System.err.println("Window.monitorModeChanged.1: Non-FS - Fit window "+rect+" into screen viewport "+viewport+
2528 ", due to minimal intersection "+isect);
205a17de
SG
2529 }
2530 definePosition(viewport.getX(), viewport.getY()); // set pos for setVisible(..) or createNative(..) - reduce EDT roundtrip
2790c756
SG
2531 setSize(viewport.getWidth(), viewport.getHeight(), true /* force */);
2532 }
2533 }
2534 } else if( fullscreenPaused ) {
2535 if(DEBUG_IMPLEMENTATION) {
2536 System.err.println("Window.monitorModeChanged.2: FS Restore");
2537 }
2538 setFullscreenImpl(true, _fullscreenUseMainMonitor, _fullscreenMonitors);
2539 fullscreenPaused = false;
2540 _fullscreenMonitors = null;
2541 _fullscreenUseMainMonitor = true;
2542 } else if( success && fullscreen && null != fullscreenMonitors ) {
2543 // If changed monitor is part of this fullscreen mode, reset size! (Bug 771)
2544 final MonitorDevice md = me.getMonitor();
2545 if( fullscreenMonitors.contains(md) ) {
2546 final Rectangle viewportInWindowUnits = new Rectangle();
2547 MonitorDevice.unionOfViewports(null, viewportInWindowUnits, fullscreenMonitors);
2548 if(DEBUG_IMPLEMENTATION) {
2549 final RectangleImmutable winBounds = WindowImpl.this.getBounds();
2550 System.err.println("Window.monitorModeChanged.3: FS Monitor Match: Fit window "+winBounds+" into new viewport union "+viewportInWindowUnits+" [window], provoked by "+md);
205a17de 2551 }
2790c756
SG
2552 definePosition(viewportInWindowUnits.getX(), viewportInWindowUnits.getY()); // set pos for setVisible(..) or createNative(..) - reduce EDT roundtrip
2553 setSize(viewportInWindowUnits.getWidth(), viewportInWindowUnits.getHeight(), true /* force */);
2aa29677
SG
2554 }
2555 }
2790c756
SG
2556 if( hidden ) {
2557 WindowImpl.this.setVisible(true);
2558 hidden = false;
2559 }
2560 sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener
2aa29677
SG
2561 if(animatorPaused) {
2562 lifecycleHook.resumeRenderingAction();
2563 }
205a17de
SG
2564 if( hadFocus ) {
2565 requestFocus(true);
2566 }
2790c756
SG
2567 if(DEBUG_IMPLEMENTATION) {
2568 System.err.println("Window.monitorModeChanged.X: @ "+Thread.currentThread().getName()+", this: "+WindowImpl.this);
2569 }
a34d04ad
SG
2570 }
2571 }
84576bc0 2572 private final MonitorModeListenerImpl monitorModeListenerImpl = new MonitorModeListenerImpl();
5742b1fa
SG
2573
2574
0feca163
SG
2575 //----------------------------------------------------------------------
2576 // Child Window Management
5e9c02bc 2577 //
0feca163 2578
58ebd43a 2579 @Override
cb4e7318 2580 public final boolean removeChild(NativeWindow win) {
0feca163 2581 synchronized(childWindowsLock) {
cb4e7318 2582 return childWindows.remove(win);
0feca163
SG
2583 }
2584 }
2585
58ebd43a 2586 @Override
cb4e7318 2587 public final boolean addChild(NativeWindow win) {
0feca163 2588 if (win == null) {
cb4e7318 2589 return false;
0feca163
SG
2590 }
2591 synchronized(childWindowsLock) {
cb4e7318 2592 return childWindows.add(win);
0feca163
SG
2593 }
2594 }
2595
2596 //----------------------------------------------------------------------
2597 // Generic Event Support
2598 //
2599 private void doEvent(boolean enqueue, boolean wait, com.jogamp.newt.event.NEWTEvent event) {
2600 boolean done = false;
2601
2602 if(!enqueue) {
2603 done = consumeEvent(event);
2604 wait = done; // don't wait if event can't be consumed now
2605 }
2606
2607 if(!done) {
2608 enqueueEvent(wait, event);
2609 }
2610 }
2611
58ebd43a 2612 @Override
852fcbf1 2613 public final void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event) {
28b0df6c 2614 if(isNativeValid()) {
1f360ff7 2615 ((DisplayImpl)screen.getDisplay()).enqueueEvent(wait, event);
0feca163
SG
2616 }
2617 }
2618
58ebd43a 2619 @Override
a90bf31f 2620 public final boolean consumeEvent(NEWTEvent e) {
0feca163
SG
2621 switch(e.getEventType()) {
2622 // special repaint treatment
2623 case WindowEvent.EVENT_WINDOW_REPAINT:
e587f2a7 2624 // queue repaint event in case window is locked, ie in operation
4b5a0f65 2625 if( null != windowLock.getOwner() ) {
0feca163
SG
2626 // make sure only one repaint event is queued
2627 if(!repaintQueued) {
2628 repaintQueued=true;
d8bbddae 2629 final boolean discardTO = QUEUED_EVENT_TO <= System.currentTimeMillis()-e.getWhen();
0feca163 2630 if(DEBUG_IMPLEMENTATION) {
1f360ff7 2631 System.err.println("Window.consumeEvent: REPAINT "+Thread.currentThread().getName()+" - queued "+e+", discard-to "+discardTO);
472a9c60 2632 // Thread.dumpStack();
5e9c02bc 2633 }
d8bbddae 2634 return discardTO; // discardTO:=true -> consumed
0feca163
SG
2635 }
2636 return true;
2637 }
2638 repaintQueued=false; // no repaint event queued
2639 break;