Commit | Line | Data |
---|---|---|
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 | 35 | package jogamp.newt; |
0feca163 | 36 | |
99479bf3 | 37 | import java.lang.ref.WeakReference; |
09fc0654 | 38 | import java.lang.reflect.Method; |
58756bbd SG |
39 | import java.util.ArrayList; |
40 | import java.util.List; | |
41 | ||
42 | import javax.media.nativewindow.AbstractGraphicsConfiguration; | |
43 | import javax.media.nativewindow.AbstractGraphicsDevice; | |
44 | import javax.media.nativewindow.CapabilitiesChooser; | |
45 | import javax.media.nativewindow.CapabilitiesImmutable; | |
46 | import javax.media.nativewindow.NativeSurface; | |
47 | import javax.media.nativewindow.NativeWindow; | |
48 | import javax.media.nativewindow.NativeWindowException; | |
49 | import javax.media.nativewindow.NativeWindowFactory; | |
50 | import javax.media.nativewindow.OffscreenLayerSurface; | |
2571ed0b | 51 | import javax.media.nativewindow.ScalableSurface; |
58756bbd SG |
52 | import javax.media.nativewindow.SurfaceUpdatedListener; |
53 | import javax.media.nativewindow.WindowClosingProtocol; | |
54 | import javax.media.nativewindow.util.DimensionImmutable; | |
55 | import javax.media.nativewindow.util.Insets; | |
56 | import javax.media.nativewindow.util.InsetsImmutable; | |
bd98b927 | 57 | import javax.media.nativewindow.util.PixelRectangle; |
58756bbd SG |
58 | import javax.media.nativewindow.util.Point; |
59 | import javax.media.nativewindow.util.PointImmutable; | |
60 | import javax.media.nativewindow.util.Rectangle; | |
61 | import javax.media.nativewindow.util.RectangleImmutable; | |
62 | ||
2571ed0b | 63 | import jogamp.nativewindow.SurfaceScaleUtils; |
58756bbd | 64 | import jogamp.nativewindow.SurfaceUpdatedHelper; |
09fc0654 | 65 | |
8815245e | 66 | import com.jogamp.common.util.ArrayHashSet; |
2f9c77a3 | 67 | import com.jogamp.common.util.IntBitfield; |
a9e946d3 | 68 | import com.jogamp.common.util.ReflectionUtil; |
58756bbd SG |
69 | import com.jogamp.common.util.locks.LockFactory; |
70 | import com.jogamp.common.util.locks.RecursiveLock; | |
71 | import com.jogamp.newt.Display; | |
e7ffa68b | 72 | import com.jogamp.newt.Display.PointerIcon; |
6ebf649d | 73 | import com.jogamp.newt.MonitorDevice; |
0feca163 | 74 | import com.jogamp.newt.NewtFactory; |
0feca163 SG |
75 | import com.jogamp.newt.Screen; |
76 | import com.jogamp.newt.Window; | |
bc72e232 SG |
77 | import com.jogamp.newt.event.DoubleTapScrollGesture; |
78 | import com.jogamp.newt.event.GestureHandler; | |
bdf1876f | 79 | import com.jogamp.newt.event.InputEvent; |
a9e946d3 SG |
80 | import com.jogamp.newt.event.KeyEvent; |
81 | import com.jogamp.newt.event.KeyListener; | |
6ebf649d | 82 | import com.jogamp.newt.event.MonitorEvent; |
58756bbd | 83 | import com.jogamp.newt.event.MonitorModeListener; |
a9e946d3 | 84 | import com.jogamp.newt.event.MouseEvent; |
58756bbd | 85 | import com.jogamp.newt.event.MouseEvent.PointerType; |
a9e946d3 SG |
86 | import com.jogamp.newt.event.MouseListener; |
87 | import com.jogamp.newt.event.NEWTEvent; | |
88 | import com.jogamp.newt.event.NEWTEventConsumer; | |
a9e946d3 SG |
89 | import com.jogamp.newt.event.WindowEvent; |
90 | import com.jogamp.newt.event.WindowListener; | |
91 | import com.jogamp.newt.event.WindowUpdateEvent; | |
ccb7213c | 92 | |
2aa29677 | 93 | public 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; |