2 * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved.
3 Copyright (c) 2010 JogAmp Community. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * - Redistribution of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
37 import java.lang.ref.WeakReference;
38 import java.lang.reflect.Method;
39 import java.util.ArrayList;
40 import java.util.List;
42 import javax.media.nativewindow.AbstractGraphicsConfiguration;
43 import javax.media.nativewindow.AbstractGraphicsDevice;
44 import javax.media.nativewindow.CapabilitiesChooser;
45 import javax.media.nativewindow.CapabilitiesImmutable;
46 import javax.media.nativewindow.NativeSurface;
47 import javax.media.nativewindow.NativeWindow;
48 import javax.media.nativewindow.NativeWindowException;
49 import javax.media.nativewindow.NativeWindowFactory;
50 import javax.media.nativewindow.OffscreenLayerSurface;
51 import javax.media.nativewindow.ScalableSurface;
52 import javax.media.nativewindow.SurfaceUpdatedListener;
53 import javax.media.nativewindow.WindowClosingProtocol;
54 import javax.media.nativewindow.util.DimensionImmutable;
55 import javax.media.nativewindow.util.Insets;
56 import javax.media.nativewindow.util.InsetsImmutable;
57 import javax.media.nativewindow.util.PixelRectangle;
58 import javax.media.nativewindow.util.Point;
59 import javax.media.nativewindow.util.PointImmutable;
60 import javax.media.nativewindow.util.Rectangle;
61 import javax.media.nativewindow.util.RectangleImmutable;
63 import jogamp.nativewindow.SurfaceScaleUtils;
64 import jogamp.nativewindow.SurfaceUpdatedHelper;
66 import com.jogamp.common.util.ArrayHashSet;
67 import com.jogamp.common.util.IntBitfield;
68 import com.jogamp.common.util.ReflectionUtil;
69 import com.jogamp.common.util.locks.LockFactory;
70 import com.jogamp.common.util.locks.RecursiveLock;
71 import com.jogamp.newt.Display;
72 import com.jogamp.newt.Display.PointerIcon;
73 import com.jogamp.newt.MonitorDevice;
74 import com.jogamp.newt.NewtFactory;
75 import com.jogamp.newt.Screen;
76 import com.jogamp.newt.Window;
77 import com.jogamp.newt.event.DoubleTapScrollGesture;
78 import com.jogamp.newt.event.GestureHandler;
79 import com.jogamp.newt.event.InputEvent;
80 import com.jogamp.newt.event.KeyEvent;
81 import com.jogamp.newt.event.KeyListener;
82 import com.jogamp.newt.event.MonitorEvent;
83 import com.jogamp.newt.event.MonitorModeListener;
84 import com.jogamp.newt.event.MouseEvent;
85 import com.jogamp.newt.event.MouseEvent.PointerType;
86 import com.jogamp.newt.event.MouseListener;
87 import com.jogamp.newt.event.NEWTEvent;
88 import com.jogamp.newt.event.NEWTEventConsumer;
89 import com.jogamp.newt.event.WindowEvent;
90 import com.jogamp.newt.event.WindowListener;
91 import com.jogamp.newt.event.WindowUpdateEvent;
93 public abstract class WindowImpl implements Window, NEWTEventConsumer
95 public static final boolean DEBUG_TEST_REPARENT_INCOMPATIBLE;
98 Debug.initSingleton();
99 DEBUG_TEST_REPARENT_INCOMPATIBLE = Debug.isPropertyDefined("newt.test.Window.reparent.incompatible", true);
101 ScreenImpl.initSingleton();
104 protected static final ArrayList<WeakReference<WindowImpl>> windowList = new ArrayList<WeakReference<WindowImpl>>();
106 /** Maybe utilized at a shutdown hook, impl. does not block. */
107 public static final void shutdownAll() {
108 final int wCount = windowList.size();
109 if(DEBUG_IMPLEMENTATION) {
110 System.err.println("Window.shutdownAll "+wCount+" instances, on thread "+getThreadName());
112 for(int i=0; i<wCount && windowList.size()>0; i++) { // be safe ..
113 final WindowImpl w = windowList.remove(0).get();
114 if(DEBUG_IMPLEMENTATION) {
115 final long wh = null != w ? w.getWindowHandle() : 0;
116 System.err.println("Window.shutdownAll["+(i+1)+"/"+wCount+"]: "+toHexString(wh)+", GCed "+(null==w));
123 private static void addWindow2List(WindowImpl window) {
124 synchronized(windowList) {
127 while( i < windowList.size() ) {
128 if( null == windowList.get(i).get() ) {
130 windowList.remove(i);
135 windowList.add(new WeakReference<WindowImpl>(window));
136 if(DEBUG_IMPLEMENTATION) {
137 System.err.println("Window.addWindow2List: GCed "+gced+", size "+windowList.size());
142 /** Timeout of queued events (repaint and resize) */
143 static final long QUEUED_EVENT_TO = 1200; // ms
145 private static final PointerType[] constMousePointerTypes = new PointerType[] { PointerType.Mouse };
148 // Volatile: Multithread Mutable Access
150 private volatile long windowHandle = 0; // lifecycle critical
151 private volatile boolean visible = false; // lifecycle critical
152 private volatile boolean hasFocus = false;
153 private volatile int pixWidth = 128, pixHeight = 128; // client-area size w/o insets in pixel units, default: may be overwritten by user
154 private volatile int winWidth = 128, winHeight = 128; // client-area size w/o insets in window units, default: may be overwritten by user
155 protected int[] hasPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
156 protected int[] reqPixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE };
158 private volatile int x = 64, y = 64; // client-area pos w/o insets in window units
159 private volatile Insets insets = new Insets(); // insets of decoration (if top-level && decorated)
160 private boolean blockInsetsChange = false; // block insets change (from same thread)
162 private final RecursiveLock windowLock = LockFactory.createRecursiveLock(); // Window instance wide lock
163 private int surfaceLockCount = 0; // surface lock recursion count
165 private ScreenImpl screen; // never null after create - may change reference though (reparent)
166 private boolean screenReferenceAdded = false;
167 private NativeWindow parentWindow = null;
168 private long parentWindowHandle = 0;
169 private AbstractGraphicsConfiguration config = null; // control access due to delegation
170 protected CapabilitiesImmutable capsRequested = null;
171 protected CapabilitiesChooser capabilitiesChooser = null; // default null -> default
172 private boolean fullscreen = false, brokenFocusChange = false;
173 private List<MonitorDevice> fullscreenMonitors = null;
174 private boolean fullscreenUseMainMonitor = true;
175 private boolean autoPosition = true; // default: true (allow WM to choose top-level position, if not set by user)
177 private int nfs_width, nfs_height, nfs_x, nfs_y; // non fullscreen client-area size/pos w/o insets
178 private boolean nfs_alwaysOnTop; // non fullscreen alwaysOnTop setting
179 private NativeWindow nfs_parent = null; // non fullscreen parent, in case explicit reparenting is performed (offscreen)
180 private String title = "Newt Window";
181 private boolean undecorated = false;
182 private boolean alwaysOnTop = false;
183 private PointerIconImpl pointerIcon = null;
184 private boolean pointerVisible = true;
185 private boolean pointerConfined = false;
186 private LifecycleHook lifecycleHook = null;
188 private Runnable windowDestroyNotifyAction = null;
190 private FocusRunnable focusAction = null;
191 private KeyListener keyboardFocusHandler = null;
193 private final SurfaceUpdatedHelper surfaceUpdatedHelper = new SurfaceUpdatedHelper();
195 private final Object childWindowsLock = new Object();
196 private final ArrayList<NativeWindow> childWindows = new ArrayList<NativeWindow>();
198 private ArrayList<MouseListener> mouseListeners = new ArrayList<MouseListener>();
200 /** from event passing: {@link WindowImpl#consumePointerEvent(MouseEvent)}. */
201 private static class PointerState0 {
202 /** Pointer entered window - is inside the window (may be synthetic) */
203 boolean insideSurface = false;
204 /** Mouse EXIT has been sent (only for MOUSE type enter/exit)*/
205 boolean exitSent = false;
207 /** last time when a pointer button was pressed */
208 long lastButtonPressTime = 0;
210 /** Pointer in dragging mode */
211 boolean dragging = false;
214 lastButtonPressTime = 0;
216 public String toString() { return "PState0[inside "+insideSurface+", exitSent "+exitSent+", lastPress "+lastButtonPressTime+", dragging "+dragging+"]"; }
218 private final PointerState0 pState0 = new PointerState0();
220 /** from direct input: {@link WindowImpl#doPointerEvent(boolean, boolean, int[], short, int, int, boolean, short[], int[], int[], float[], float, float[], float)}. */
221 private static class PointerState1 extends PointerState0 {
222 /** Current pressed mouse button number */
223 short buttonPressed = (short)0;
224 /** Current pressed mouse button modifier mask */
225 int buttonPressedMask = 0;
226 /** Last mouse button click count */
227 short lastButtonClickCount = (short)0;
230 final void clearButton() {
232 lastButtonClickCount = (short)0;
233 if( !dragging || 0 == buttonPressedMask ) {
235 buttonPressedMask = 0;
240 /** Last pointer-move position for 8 touch-down pointers */
241 final Point[] movePositions = new Point[] {
242 new Point(), new Point(), new Point(), new Point(),
243 new Point(), new Point(), new Point(), new Point() };
244 final Point getMovePosition(int id) {
245 if( 0 <= id && id < movePositions.length ) {
246 return movePositions[id];
250 public final String toString() { return "PState1[inside "+insideSurface+", exitSent "+exitSent+", lastPress "+lastButtonPressTime+
251 ", pressed [button "+buttonPressed+", mask "+buttonPressedMask+", dragging "+dragging+", clickCount "+lastButtonClickCount+"]"; }
253 private final PointerState1 pState1 = new PointerState1();
255 /** Pointer names -> pointer ID (consecutive index, starting w/ 0) */
256 private final ArrayHashSet<Integer> pName2pID = new ArrayHashSet<Integer>();
258 private boolean defaultGestureHandlerEnabled = true;
259 private DoubleTapScrollGesture gesture2PtrTouchScroll = null;
260 private ArrayList<GestureHandler> pointerGestureHandler = new ArrayList<GestureHandler>();
262 private ArrayList<GestureHandler.GestureListener> gestureListeners = new ArrayList<GestureHandler.GestureListener>();
264 private ArrayList<KeyListener> keyListeners = new ArrayList<KeyListener>();
266 private ArrayList<WindowListener> windowListeners = new ArrayList<WindowListener>();
267 private boolean repaintQueued = false;
270 // Construction Methods
273 private static Class<?> getWindowClass(String type)
274 throws ClassNotFoundException
276 final Class<?> windowClass = NewtFactory.getCustomClass(type, "WindowDriver");
277 if(null==windowClass) {
278 throw new ClassNotFoundException("Failed to find NEWT Window Class <"+type+".WindowDriver>");
283 public static WindowImpl create(NativeWindow parentWindow, long parentWindowHandle, Screen screen, CapabilitiesImmutable caps) {
285 Class<?> windowClass;
286 if(caps.isOnscreen()) {
287 windowClass = getWindowClass(screen.getDisplay().getType());
289 windowClass = OffscreenWindow.class;
291 WindowImpl window = (WindowImpl) windowClass.newInstance();
292 window.parentWindow = parentWindow;
293 window.parentWindowHandle = parentWindowHandle;
294 window.screen = (ScreenImpl) screen;
295 window.capsRequested = (CapabilitiesImmutable) caps.cloneMutable();
296 window.instantiationFinished();
297 addWindow2List(window);
299 } catch (Throwable t) {
301 throw new NativeWindowException(t);
305 public static WindowImpl create(Object[] cstrArguments, Screen screen, CapabilitiesImmutable caps) {
307 Class<?> windowClass = getWindowClass(screen.getDisplay().getType());
308 Class<?>[] cstrArgumentTypes = getCustomConstructorArgumentTypes(windowClass);
309 if(null==cstrArgumentTypes) {
310 throw new NativeWindowException("WindowClass "+windowClass+" doesn't support custom arguments in constructor");
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));
316 WindowImpl window = (WindowImpl) ReflectionUtil.createInstance( windowClass, cstrArgumentTypes, cstrArguments ) ;
317 window.screen = (ScreenImpl) screen;
318 window.capsRequested = (CapabilitiesImmutable) caps.cloneMutable();
319 window.instantiationFinished();
320 addWindow2List(window);
322 } catch (Throwable t) {
323 throw new NativeWindowException(t);
327 /** Fast invalidation of instance w/o any blocking function call. */
328 private final void shutdown() {
329 if(null!=lifecycleHook) {
330 lifecycleHook.shutdownRenderingAction();
335 fullscreenMonitors = null;
336 fullscreenUseMainMonitor = true;
338 parentWindowHandle = 0;
341 protected final void setGraphicsConfiguration(AbstractGraphicsConfiguration cfg) {
345 public static interface LifecycleHook {
347 * Reset of internal state counter, ie totalFrames, etc.
348 * Called from EDT while window is locked.
350 public abstract void resetCounter();
353 * Invoked after Window setVisible,
354 * allows allocating resources depending on the native Window.
355 * Called from EDT while window is locked.
357 void setVisibleActionPost(boolean visible, boolean nativeWindowCreated);
360 * Notifies the receiver to preserve resources (GL, ..)
361 * for the next destroy*() calls (only), if supported and if <code>value</code> is <code>true</code>, otherwise clears preservation flag.
362 * @param value <code>true</code> to set the one-shot preservation if supported, otherwise clears it.
364 void preserveGLStateAtDestroy(boolean value);
367 * Invoked before Window destroy action,
368 * allows releasing of resources depending on the native Window.<br>
369 * Surface not locked yet.<br>
370 * Called not necessarily from EDT.
372 void destroyActionPreLock();
375 * Invoked before Window destroy action,
376 * allows releasing of resources depending on the native Window.<br>
377 * Surface locked.<br>
378 * Called from EDT while window is locked.
380 void destroyActionInLock();
383 * Invoked for expensive modifications, ie while reparenting and MonitorMode change.<br>
384 * No lock is hold when invoked.<br>
386 * @return true is paused, otherwise false. If true {@link #resumeRenderingAction()} shall be issued.
388 * @see #resumeRenderingAction()
390 boolean pauseRenderingAction();
393 * Invoked for expensive modifications, ie while reparenting and MonitorMode change.
394 * No lock is hold when invoked.<br>
396 * @see #pauseRenderingAction()
398 void resumeRenderingAction();
401 * Shutdown rendering action (thread) abnormally.
403 * Should be called only at shutdown, if necessary.
406 void shutdownRenderingAction();
409 private boolean createNative() {
411 if(DEBUG_IMPLEMENTATION) {
412 tStart = System.nanoTime();
413 System.err.println("Window.createNative() START ("+getThreadName()+", "+this+")");
418 if( null != parentWindow &&
419 NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindow.lockSurface() ) {
420 throw new NativeWindowException("Parent surface lock: not ready: "+parentWindow);
423 final boolean hasParent = null != parentWindow || 0 != this.parentWindowHandle;
425 // child window: position defaults to 0/0, no auto position, no negative position
426 if( hasParent && ( autoPosition || 0>getX() || 0>getY() ) ) {
427 definePosition(0, 0);
429 boolean postParentlockFocus = false;
431 if(validateParentWindowHandle()) {
432 if( !screenReferenceAdded ) {
433 screen.addReference();
434 screenReferenceAdded = true;
436 if(canCreateNativeImpl()) {
438 final boolean usePosition;
448 final long t0 = System.currentTimeMillis();
450 screen.addMonitorModeListener(monitorModeListenerImpl);
452 setPointerIconIntern(pointerIcon);
453 setPointerVisibleIntern(pointerVisible);
454 confinePointerImpl(pointerConfined);
455 setKeyboardVisible(keyboardVisible);
456 final long remainingV = waitForVisible(true, false);
457 if( 0 <= remainingV ) {
459 synchronized(fullScreenAction) {
460 fullscreen = false; // trigger a state change
461 fullScreenAction.init(true);
462 fullScreenAction.run();
464 } else if ( !hasParent ) {
465 // Wait until position is reached within tolerances, either auto-position or custom position.
466 waitForPosition(usePosition, wX, wY, Window.TIMEOUT_NATIVEWINDOW);
468 if (DEBUG_IMPLEMENTATION) {
469 System.err.println("Window.createNative(): elapsed "+(System.currentTimeMillis()-t0)+" ms");
471 postParentlockFocus = true;
476 if(null!=parentWindow) {
477 parentWindow.unlockSurface();
480 if(postParentlockFocus) {
481 // harmonize focus behavior for all platforms: focus on creation
482 requestFocusInt(isFullscreen() /* skipFocusAction if fullscreen */);
483 ((DisplayImpl) screen.getDisplay()).dispatchMessagesNative(); // status up2date
485 if(DEBUG_IMPLEMENTATION) {
486 System.err.println("Window.createNative() END ("+getThreadName()+", "+this+") total "+ (System.nanoTime()-tStart)/1e6 +"ms");
488 return isNativeValid() ;
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();
501 private boolean validateParentWindowHandle() {
502 if(null!=parentWindow) {
503 parentWindowHandle = getNativeWindowHandle(parentWindow);
504 return 0 != parentWindowHandle ;
509 private static long getNativeWindowHandle(NativeWindow nativeWindow) {
511 if(null!=nativeWindow) {
512 boolean wasLocked = false;
513 if( NativeSurface.LOCK_SURFACE_NOT_READY < nativeWindow.lockSurface() ) {
516 handle = nativeWindow.getWindowHandle();
518 throw new NativeWindowException("Parent native window handle is NULL, after succesful locking: "+nativeWindow);
520 } catch (NativeWindowException nwe) {
521 if(DEBUG_IMPLEMENTATION) {
522 System.err.println("Window.getNativeWindowHandle: not successful yet: "+nwe);
525 nativeWindow.unlockSurface();
528 if(DEBUG_IMPLEMENTATION) {
529 System.err.println("Window.getNativeWindowHandle: locked "+wasLocked+", "+nativeWindow);
536 //----------------------------------------------------------------------
537 // NativeSurface: Native implementation
540 protected int lockSurfaceImpl() { return LOCK_SUCCESS; }
542 protected void unlockSurfaceImpl() { }
544 //----------------------------------------------------------------------
545 // WindowClosingProtocol implementation
547 private final Object closingListenerLock = new Object();
548 private WindowClosingMode defaultCloseOperation = WindowClosingMode.DISPOSE_ON_CLOSE;
551 public final WindowClosingMode getDefaultCloseOperation() {
552 synchronized (closingListenerLock) {
553 return defaultCloseOperation;
558 public final WindowClosingMode setDefaultCloseOperation(WindowClosingMode op) {
559 synchronized (closingListenerLock) {
560 WindowClosingMode _op = defaultCloseOperation;
561 defaultCloseOperation = op;
566 //----------------------------------------------------------------------
567 // Window: Native implementation
571 * Notifies the driver impl. that the instantiation is finished,
572 * ie. instance created and all fields set.
574 protected void instantiationFinished() {
578 protected boolean canCreateNativeImpl() {
579 return true; // default: always able to be created
583 * The native implementation must set the native windowHandle.<br>
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.
591 * The implementation should invoke the referenced java state callbacks
592 * to notify this Java object of state changes.</p>
594 * @see #windowDestroyNotify(boolean)
595 * @see #focusChanged(boolean, boolean)
596 * @see #visibleChanged(boolean, boolean)
597 * @see #sizeChanged(int,int)
598 * @see #positionChanged(boolean,int, int)
599 * @see #windowDestroyNotify(boolean)
601 protected abstract void createNativeImpl();
603 protected abstract void closeNativeImpl();
606 * Async request which shall be performed within {@link #TIMEOUT_NATIVEWINDOW}.
608 * If if <code>force == false</code> the native implementation
609 * may only request focus if not yet owner.</p>
611 * {@link #focusChanged(boolean, boolean)} should be called
612 * to notify about the focus traversal.
615 * @param force if true, bypass {@link #focusChanged(boolean, boolean)} and force focus request
617 protected abstract void requestFocusImpl(boolean force);
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;
625 public static final int FLAG_HAS_PARENT = 1 << 8;
626 public static final int FLAG_IS_UNDECORATED = 1 << 9;
627 public static final int FLAG_IS_FULLSCREEN = 1 << 10;
628 public static final int FLAG_IS_FULLSCREEN_SPAN = 1 << 11;
629 public static final int FLAG_IS_ALWAYSONTOP = 1 << 12;
630 public static final int FLAG_IS_VISIBLE = 1 << 13;
633 * The native implementation should invoke the referenced java state callbacks
634 * to notify this Java object of state changes.
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>
641 * @param x client-area position in window units, or <0 if unchanged
642 * @param y client-area position in window units, or <0 if unchanged
643 * @param width client-area size in window units, or <=0 if unchanged
644 * @param height client-area size in window units, or <=0 if unchanged
645 * @param flags bitfield of change and status flags
647 * @see #sizeChanged(int,int)
648 * @see #positionChanged(boolean,int, int)
650 protected abstract boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags);
653 * Tests whether a single reconfigure flag is supported by implementation.
655 * Default is all but {@link #FLAG_IS_FULLSCREEN_SPAN}
658 protected boolean isReconfigureFlagSupported(int changeFlags) {
659 return 0 == ( changeFlags & FLAG_IS_FULLSCREEN_SPAN );
662 protected int getReconfigureFlags(int changeFlags, boolean visible) {
663 return changeFlags |= ( ( 0 != getParentWindowHandle() ) ? FLAG_HAS_PARENT : 0 ) |
664 ( isUndecorated() ? FLAG_IS_UNDECORATED : 0 ) |
665 ( isFullscreen() ? FLAG_IS_FULLSCREEN : 0 ) |
666 ( isAlwaysOnTop() ? FLAG_IS_ALWAYSONTOP : 0 ) |
667 ( visible ? FLAG_IS_VISIBLE : 0 ) ;
669 protected static String getReconfigureFlagsAsString(StringBuilder sb, int flags) {
670 if(null == sb) { sb = new StringBuilder(); }
673 if( 0 != ( FLAG_CHANGE_PARENTING & flags) ) {
676 sb.append("PARENT ");
677 sb.append(0 != ( FLAG_HAS_PARENT & flags));
680 if( 0 != ( FLAG_CHANGE_FULLSCREEN & flags) ) {
684 sb.append(0 != ( FLAG_IS_FULLSCREEN & flags));
686 sb.append(0 != ( FLAG_IS_FULLSCREEN_SPAN & flags));
689 if( 0 != ( FLAG_CHANGE_DECORATION & flags) ) {
692 sb.append("UNDECOR ");
693 sb.append(0 != ( FLAG_IS_UNDECORATED & flags));
696 if( 0 != ( FLAG_CHANGE_ALWAYSONTOP & flags) ) {
699 sb.append("ALWAYSONTOP ");
700 sb.append(0 != ( FLAG_IS_ALWAYSONTOP & flags));
703 if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) {
706 sb.append("VISIBLE ");
707 sb.append(0 != ( FLAG_IS_VISIBLE & flags));
710 return sb.toString();
713 protected void setTitleImpl(String title) {}
716 * Translates the given window client-area coordinates with top-left origin
717 * to screen coordinates in window units.
719 * Since the position reflects the client area, it does not include the insets.
722 * May return <code>null</code>, in which case the caller shall traverse through the NativeWindow tree
723 * as demonstrated in {@link #getLocationOnScreen(javax.media.nativewindow.util.Point)}.
726 * @return if not null, the screen location of the given coordinates
728 protected abstract Point getLocationOnScreenImpl(int x, int y);
731 * Triggered by user via {@link #getInsets()}.<br>
732 * Implementations may implement this hook to update the insets.<br>
733 * However, they may prefer the event driven path via {@link #insetsChanged(boolean, int, int, int, int)}.
736 * @see #insetsChanged(boolean, int, int, int, int)
738 protected abstract void updateInsetsImpl(Insets insets);
740 protected boolean setPointerVisibleImpl(boolean pointerVisible) { return false; }
741 protected boolean confinePointerImpl(boolean confine) { return false; }
742 protected void warpPointerImpl(int x, int y) { }
743 protected void setPointerIconImpl(final PointerIconImpl pi) { }
745 //----------------------------------------------------------------------
750 public final int lockSurface() throws NativeWindowException, RuntimeException {
751 final RecursiveLock _wlock = windowLock;
754 int res = ( 1 == surfaceLockCount ) ? LOCK_SURFACE_NOT_READY : LOCK_SUCCESS; // new lock ?
756 if ( LOCK_SURFACE_NOT_READY == res ) {
758 if( isNativeValid() ) {
759 final AbstractGraphicsDevice adevice = getGraphicsConfiguration().getScreen().getDevice();
762 res = lockSurfaceImpl();
764 if (LOCK_SURFACE_NOT_READY >= res) {
770 if (LOCK_SURFACE_NOT_READY >= res) {
780 public final void unlockSurface() {
781 final RecursiveLock _wlock = windowLock;
782 _wlock.validateLocked();
784 if ( 1 == surfaceLockCount ) {
785 final AbstractGraphicsDevice adevice = getGraphicsConfiguration().getScreen().getDevice();
797 public final boolean isSurfaceLockedByOtherThread() {
798 return windowLock.isLockedByOtherThread();
802 public final Thread getSurfaceLockOwner() {
803 return windowLock.getOwner();
806 public final RecursiveLock getLock() {
811 public long getSurfaceHandle() {
812 return windowHandle; // default: return window handle
816 public boolean surfaceSwap() {
821 public final void addSurfaceUpdatedListener(SurfaceUpdatedListener l) {
822 surfaceUpdatedHelper.addSurfaceUpdatedListener(l);
826 public final void addSurfaceUpdatedListener(int index, SurfaceUpdatedListener l) throws IndexOutOfBoundsException {
827 surfaceUpdatedHelper.addSurfaceUpdatedListener(index, l);
831 public final void removeSurfaceUpdatedListener(SurfaceUpdatedListener l) {
832 surfaceUpdatedHelper.removeSurfaceUpdatedListener(l);
836 public final void surfaceUpdated(Object updater, NativeSurface ns, long when) {
837 surfaceUpdatedHelper.surfaceUpdated(updater, ns, when);
841 public final AbstractGraphicsConfiguration getGraphicsConfiguration() {
842 return config.getNativeGraphicsConfiguration();
846 public final long getDisplayHandle() {
847 return config.getNativeGraphicsConfiguration().getScreen().getDevice().getHandle();
851 public final int getScreenIndex() {
852 return screen.getIndex();
855 //----------------------------------------------------------------------
859 // public final void destroy() - see below
862 public final NativeSurface getNativeSurface() { return this; }
865 public final NativeWindow getParent() {
870 public final long getWindowHandle() {
875 public Point getLocationOnScreen(Point storage) {
876 if(isNativeValid()) {
878 final RecursiveLock _lock = windowLock;
881 d = getLocationOnScreenImpl(0, 0);
887 storage.translate(d.getX(),d.getY());
892 // fall through intended ..
896 storage.translate(getX(),getY());
898 storage = new Point(getX(),getY());
900 if(null!=parentWindow) {
901 // traverse through parent list ..
902 parentWindow.getLocationOnScreen(storage);
907 //----------------------------------------------------------------------
912 public final boolean isNativeValid() {
913 return 0 != windowHandle ;
917 public final Screen getScreen() {
921 protected void setScreen(ScreenImpl newScreen) { // never null !
922 removeScreenReference();
927 public final MonitorDevice getMainMonitor() {
928 return screen.getMainMonitor( getBounds() );
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
938 protected final void setVisibleImpl(boolean visible, int x, int y, int width, int height) {
939 reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(FLAG_CHANGE_VISIBILITY, visible));
941 final void setVisibleActionImpl(boolean visible) {
942 boolean nativeWindowCreated = false;
943 boolean madeVisible = false;
945 final RecursiveLock _lock = windowLock;
948 if(!visible && null!=childWindows && childWindows.size()>0) {
949 synchronized(childWindowsLock) {
950 for(int i = 0; i < childWindows.size(); i++ ) {
951 NativeWindow nw = childWindows.get(i);
952 if(nw instanceof WindowImpl) {
953 ((WindowImpl)nw).setVisible(false);
958 if(!isNativeValid() && visible) {
959 if( 0<getWidth()*getHeight() ) {
960 nativeWindowCreated = createNative();
961 madeVisible = nativeWindowCreated;
963 // always flag visible, allowing a retry ..
964 WindowImpl.this.visible = true;
965 } else if(WindowImpl.this.visible != visible) {
966 if(isNativeValid()) {
967 setVisibleImpl(visible, getX(), getY(), getWidth(), getHeight());
968 WindowImpl.this.waitForVisible(visible, false);
969 madeVisible = visible;
971 WindowImpl.this.visible = true;
975 if(null!=lifecycleHook) {
976 lifecycleHook.setVisibleActionPost(visible, nativeWindowCreated);
979 if(isNativeValid() && visible && null!=childWindows && childWindows.size()>0) {
980 synchronized(childWindowsLock) {
981 for(int i = 0; i < childWindows.size(); i++ ) {
982 NativeWindow nw = childWindows.get(i);
983 if(nw instanceof WindowImpl) {
984 ((WindowImpl)nw).setVisible(true);
989 if(DEBUG_IMPLEMENTATION) {
990 System.err.println("Window setVisible: END ("+getThreadName()+") "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible: "+WindowImpl.this.visible+", nativeWindowCreated: "+nativeWindowCreated+", madeVisible: "+madeVisible);
993 if(null!=lifecycleHook) {
994 lifecycleHook.resetCounter();
998 if( nativeWindowCreated || madeVisible ) {
999 sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener
1002 private class VisibleAction implements Runnable {
1005 private VisibleAction(boolean visible) {
1006 this.visible = visible;
1010 public final void run() {
1011 setVisibleActionImpl(visible);
1016 public final void setVisible(boolean wait, boolean visible) {
1017 if(DEBUG_IMPLEMENTATION) {
1018 System.err.println("Window setVisible: START ("+getThreadName()+") "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible: "+this.visible+" -> "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+(null!=parentWindow));
1020 runOnEDTIfAvail(wait, new VisibleAction(visible));
1024 public final void setVisible(boolean visible) {
1025 setVisible(true, visible);
1028 private class SetSizeAction implements Runnable {
1032 private SetSizeAction(int w, int h, boolean disregardFS) {
1035 this.force = disregardFS;
1039 public final void run() {
1040 final RecursiveLock _lock = windowLock;
1043 if ( force || ( !isFullscreen() && ( getWidth() != width || getHeight() != height ) ) ) {
1044 if(DEBUG_IMPLEMENTATION) {
1045 System.err.println("Window setSize: START force "+force+", "+getWidth()+"x"+getHeight()+" -> "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible "+visible);
1047 int visibleAction; // 0 nop, 1 invisible, 2 visible (create)
1048 if ( visible && isNativeValid() && ( 0 >= width || 0 >= height ) ) {
1049 visibleAction=1; // invisible
1051 } else if ( visible && !isNativeValid() && 0 < width && 0 < height ) {
1052 visibleAction = 2; // visible (create)
1053 defineSize(width, height);
1054 } else if ( visible && isNativeValid() ) {
1056 // this width/height will be set by windowChanged, called by the native implementation
1057 reconfigureWindowImpl(getX(), getY(), width, height, getReconfigureFlags(0, isVisible()));
1058 WindowImpl.this.waitForSize(width, height, false, TIMEOUT_NATIVEWINDOW);
1060 // invisible or invalid w/ 0 size
1062 defineSize(width, height);
1064 if(DEBUG_IMPLEMENTATION) {
1065 System.err.println("Window setSize: END "+getWidth()+"x"+getHeight()+", visibleAction "+visibleAction);
1067 switch(visibleAction) {
1068 case 1: setVisibleActionImpl(false); break;
1069 case 2: setVisibleActionImpl(true); break;
1078 private void setSize(final int width, final int height, final boolean force) {
1079 runOnEDTIfAvail(true, new SetSizeAction(width, height, force));
1082 public final void setSize(final int width, final int height) {
1083 runOnEDTIfAvail(true, new SetSizeAction(width, height, false));
1086 public final void setSurfaceSize(final int pixelWidth, final int pixelHeight) {
1087 // FIXME HiDPI: Shortcut, may need to adjust if we change scaling methodology
1088 setSize(pixelWidth / getPixelScaleX(), pixelHeight / getPixelScaleY());
1091 public final void setTopLevelSize(final int width, final int height) {
1092 setSize(width - getInsets().getTotalWidth(), height - getInsets().getTotalHeight());
1095 private class DestroyAction implements Runnable {
1097 public final void run() {
1098 boolean animatorPaused = false;
1099 if(null!=lifecycleHook) {
1100 animatorPaused = lifecycleHook.pauseRenderingAction();
1102 if(null!=lifecycleHook) {
1103 lifecycleHook.destroyActionPreLock();
1105 final RecursiveLock _lock = windowLock;
1108 if(DEBUG_IMPLEMENTATION) {
1109 System.err.println("Window DestroyAction() hasScreen "+(null != screen)+", isNativeValid "+isNativeValid()+" - "+getThreadName());
1112 // send synced destroy-notify notification
1113 sendWindowEvent(WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY);
1116 synchronized(childWindowsLock) {
1117 if(childWindows.size()>0) {
1118 // avoid ConcurrentModificationException: parent -> child -> parent.removeChild(this)
1119 @SuppressWarnings("unchecked")
1120 ArrayList<NativeWindow> clonedChildWindows = (ArrayList<NativeWindow>) childWindows.clone();
1121 while( clonedChildWindows.size() > 0 ) {
1122 NativeWindow nw = clonedChildWindows.remove(0);
1123 if(nw instanceof WindowImpl) {
1124 ((WindowImpl)nw).windowDestroyNotify(true);
1132 if(null!=lifecycleHook) {
1133 // send synced destroy notification for proper cleanup, eg GLWindow/OpenGL
1134 lifecycleHook.destroyActionInLock();
1137 if( isNativeValid() ) {
1138 screen.removeMonitorModeListener(monitorModeListenerImpl);
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
1144 setGraphicsConfiguration(null);
1146 removeScreenReference();
1147 Display dpy = screen.getDisplay();
1149 dpy.validateEDTStopped();
1152 // send synced destroyed notification
1153 sendWindowEvent(WindowEvent.EVENT_WINDOW_DESTROYED);
1155 if(DEBUG_IMPLEMENTATION) {
1156 System.err.println("Window.destroy() END "+getThreadName()/*+", "+WindowImpl.this*/);
1159 // update states before release window lock
1163 fullscreenMonitors = null;
1164 fullscreenUseMainMonitor = true;
1166 parentWindowHandle = 0;
1170 if(animatorPaused) {
1171 lifecycleHook.resumeRenderingAction();
1174 // these refs shall be kept alive - resurrection via setVisible(true)
1176 if(null!=parentWindow && parentWindow instanceof Window) {
1177 ((Window)parentWindow).removeChild(WindowImpl.this);
1179 childWindows = null;
1180 surfaceUpdatedListeners = null;
1181 mouseListeners = null;
1182 keyListeners = null;
1183 capsRequested = null;
1184 lifecycleHook = null;
1187 windowListeners = null;
1188 parentWindow = null;
1192 private final DestroyAction destroyAction = new DestroyAction();
1195 public void destroy() {
1196 visible = false; // Immediately mark synchronized visibility flag, avoiding possible recreation
1197 runOnEDTIfAvail(true, destroyAction);
1200 protected void destroy(boolean preserveResources) {
1201 if( null != lifecycleHook ) {
1202 lifecycleHook.preserveGLStateAtDestroy( preserveResources );
1208 * @param cWin child window, must not be null
1209 * @param pWin parent window, may be null
1210 * @return true if at least one of both window's configurations is offscreen
1212 protected static boolean isOffscreenInstance(NativeWindow cWin, NativeWindow pWin) {
1213 boolean ofs = false;
1214 final AbstractGraphicsConfiguration cWinCfg = cWin.getGraphicsConfiguration();
1215 if( null != cWinCfg ) {
1216 ofs = !cWinCfg.getChosenCapabilities().isOnscreen();
1218 if( !ofs && null != pWin ) {
1219 final AbstractGraphicsConfiguration pWinCfg = pWin.getGraphicsConfiguration();
1220 if( null != pWinCfg ) {
1221 ofs = !pWinCfg.getChosenCapabilities().isOnscreen();
1227 private class ReparentAction implements Runnable {
1228 final NativeWindow newParentWindow;
1229 final int topLevelX, topLevelY;
1231 ReparentOperation operation;
1233 private ReparentAction(NativeWindow newParentWindow, int topLevelX, int topLevelY, int hints) {
1234 this.newParentWindow = newParentWindow;
1235 this.topLevelX = topLevelX;
1236 this.topLevelY = topLevelY;
1237 if( DEBUG_TEST_REPARENT_INCOMPATIBLE ) {
1238 hints |= REPARENT_HINT_FORCE_RECREATION;
1241 this.operation = ReparentOperation.ACTION_INVALID; // ensure it's set
1244 private ReparentOperation getOp() {
1249 public final void run() {
1250 if( WindowImpl.this.isFullscreen() ) {
1251 // Bug 924: Ignore reparent when in fullscreen - otherwise may confuse WM
1252 if( DEBUG_IMPLEMENTATION) {
1253 System.err.println("Window.reparent: NOP (in fullscreen, "+getThreadName()+") valid "+isNativeValid()+
1254 ", windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle));
1258 boolean animatorPaused = false;
1259 if(null!=lifecycleHook) {
1260 animatorPaused = lifecycleHook.pauseRenderingAction();
1263 if(animatorPaused) {
1264 lifecycleHook.resumeRenderingAction();
1268 private void reparent() {
1269 // mirror pos/size so native change notification can get overwritten
1270 final int oldX = getX();
1271 final int oldY = getY();
1272 final int oldWidth = getWidth();
1273 final int oldHeight = getHeight();
1275 int width = oldWidth;
1276 int height = oldHeight;
1278 final boolean wasVisible;
1279 final boolean becomesVisible;
1280 final boolean forceDestroyCreate;
1282 final RecursiveLock _lock = windowLock;
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);
1291 forceDestroyCreate = v;
1294 wasVisible = isVisible();
1295 becomesVisible = wasVisible || 0 != ( REPARENT_HINT_BECOMES_VISIBLE & hints );
1297 Window newParentWindowNEWT = null;
1298 if(newParentWindow instanceof Window) {
1299 newParentWindowNEWT = (Window) newParentWindow;
1302 long newParentWindowHandle = 0 ;
1304 if( DEBUG_IMPLEMENTATION) {
1305 System.err.println("Window.reparent: START ("+getThreadName()+") valid "+isNativeValid()+
1306 ", windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)+
1307 ", visible "+wasVisible+", becomesVisible "+becomesVisible+
1308 ", forceDestroyCreate "+forceDestroyCreate+
1309 ", DEBUG_TEST_REPARENT_INCOMPATIBLE "+DEBUG_TEST_REPARENT_INCOMPATIBLE+
1310 ", HINT_FORCE_RECREATION "+( 0 != ( REPARENT_HINT_FORCE_RECREATION & hints ) )+
1311 ", HINT_BECOMES_VISIBLE "+( 0 != ( REPARENT_HINT_BECOMES_VISIBLE & hints ) ) +
1312 ", old parentWindow: "+Display.hashCodeNullSafe(parentWindow)+
1313 ", new parentWindow: "+Display.hashCodeNullSafe(newParentWindow) );
1316 if(null!=newParentWindow) {
1317 // REPARENT TO CHILD WINDOW
1319 // reset position to 0/0 within parent space
1323 // refit if size is bigger than parent
1324 if( width > newParentWindow.getWidth() ) {
1325 width = newParentWindow.getWidth();
1327 if( height > newParentWindow.getHeight() ) {
1328 height = newParentWindow.getHeight();
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);
1338 // Destroy this window and use parent's Screen.
1339 // It may be created properly when the parent is made visible.
1340 destroy( becomesVisible );
1341 setScreen( (ScreenImpl) newParentWindowNEWT.getScreen() );
1342 operation = ReparentOperation.ACTION_NATIVE_CREATION_PENDING;
1343 } else if(newParentWindow != getParent()) {
1344 // Case: Parent's native window realized and changed
1345 if( !isNativeValid() ) {
1346 // May create a new compatible Screen/Display and
1347 // mark it for creation.
1348 if(null!=newParentWindowNEWT) {
1349 setScreen( (ScreenImpl) newParentWindowNEWT.getScreen() );
1351 final Screen newScreen = NewtFactory.createCompatibleScreen(newParentWindow, screen);
1352 if( screen != newScreen ) {
1353 // auto destroy on-the-fly created Screen/Display
1354 setScreen( (ScreenImpl) newScreen );
1357 if( 0 < width && 0 < height ) {
1358 operation = ReparentOperation.ACTION_NATIVE_CREATION;
1360 operation = ReparentOperation.ACTION_NATIVE_CREATION_PENDING;
1362 } else if ( forceDestroyCreate || !NewtFactory.isScreenCompatible(newParentWindow, screen) ) {
1363 // Destroy this window, may create a new compatible Screen/Display, while trying to preserve resources if becoming visible again.
1364 destroy( becomesVisible );
1365 if(null!=newParentWindowNEWT) {
1366 setScreen( (ScreenImpl) newParentWindowNEWT.getScreen() );
1368 setScreen( (ScreenImpl) NewtFactory.createCompatibleScreen(newParentWindow, screen) );
1370 operation = ReparentOperation.ACTION_NATIVE_CREATION;
1372 // Mark it for native reparenting
1373 operation = ReparentOperation.ACTION_NATIVE_REPARENTING;
1376 // Case: Parent's native window realized and not changed
1377 operation = ReparentOperation.ACTION_NOP;
1380 // REPARENT TO TOP-LEVEL WINDOW
1381 if( 0 <= topLevelX && 0 <= topLevelY ) {
1384 } else if( null != parentWindow ) {
1386 // put client to current parent+child position
1387 final Point p = getLocationOnScreen(null);
1396 if( 0 == parentWindowHandle ) {
1397 // Already Top Window
1398 operation = ReparentOperation.ACTION_NOP;
1399 } else if( !isNativeValid() || forceDestroyCreate ) {
1400 // Destroy this window and mark it for [pending] creation.
1401 // If isNativeValid() and becoming visible again - try to preserve resources, i.e. b/c on-/offscreen switch.
1402 destroy( becomesVisible );
1403 if( 0 < width && 0 < height ) {
1404 operation = ReparentOperation.ACTION_NATIVE_CREATION;
1406 operation = ReparentOperation.ACTION_NATIVE_CREATION_PENDING;
1409 // Mark it for native reparenting
1410 operation = ReparentOperation.ACTION_NATIVE_REPARENTING;
1413 parentWindowHandle = newParentWindowHandle;
1415 if ( ReparentOperation.ACTION_INVALID == operation ) {
1416 throw new NativeWindowException("Internal Error: reparentAction not set");
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);
1423 if( ReparentOperation.ACTION_NOP == operation ) {
1427 if( null == newParentWindow ) {
1428 // CLIENT -> TOP: Reset Parent's Pointer State
1429 setOffscreenPointerIcon(null);
1430 setOffscreenPointerVisible(true, null);
1433 // rearrange window tree
1434 if(null!=parentWindow && parentWindow instanceof Window) {
1435 ((Window)parentWindow).removeChild(WindowImpl.this);
1437 parentWindow = newParentWindow;
1438 if(parentWindow instanceof Window) {
1439 ((Window)parentWindow).addChild(WindowImpl.this);
1442 if( ReparentOperation.ACTION_NATIVE_REPARENTING == operation ) {
1443 final DisplayImpl display = (DisplayImpl) screen.getDisplay();
1444 display.dispatchMessagesNative(); // status up2date
1446 // TOP -> CLIENT: !visible first (fixes X11 unsuccessful return to parent window)
1447 if( null != parentWindow && wasVisible && NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(true) ) {
1448 setVisibleImpl(false, oldX, oldY, oldWidth, oldHeight);
1449 WindowImpl.this.waitForVisible(false, false);
1450 // FIXME: Some composite WM behave slacky .. give 'em chance to change state -> invisible,
1451 // even though we do exactly that (KDE+Composite)
1452 try { Thread.sleep(100); } catch (InterruptedException e) { }
1453 display.dispatchMessagesNative(); // status up2date
1456 // Lock parentWindow only during reparenting (attempt)
1457 final NativeWindow parentWindowLocked;
1458 if( null != parentWindow ) {
1459 parentWindowLocked = parentWindow;
1460 if( NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindowLocked.lockSurface() ) {
1461 throw new NativeWindowException("Parent surface lock: not ready: "+parentWindowLocked);
1463 // update native handle, locked state
1464 parentWindowHandle = parentWindowLocked.getWindowHandle();
1466 parentWindowLocked = null;
1470 ok = reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(FLAG_CHANGE_PARENTING | FLAG_CHANGE_DECORATION, isVisible()));
1472 if(null!=parentWindowLocked) {
1473 parentWindowLocked.unlockSurface();
1476 definePosition(x, y); // position might not get updated by WM events (SWT parent apparently)
1478 // set visible again
1480 display.dispatchMessagesNative(); // status up2date
1482 setVisibleImpl(true, x, y, width, height);
1483 ok = 0 <= WindowImpl.this.waitForVisible(true, false);
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()));
1489 ok = WindowImpl.this.waitForSize(width, height, false, TIMEOUT_NATIVEWINDOW);
1492 if( 0 == parentWindowHandle ) {
1493 // Position mismatch shall not lead to reparent failure
1494 WindowImpl.this.waitForPosition(true, x, y, TIMEOUT_NATIVEWINDOW);
1497 requestFocusInt( 0 == parentWindowHandle /* skipFocusAction if top-level */);
1498 display.dispatchMessagesNative(); // status up2date
1503 if(!ok || !wasVisible) {
1504 // make size and position persistent manual,
1505 // since we don't have a WM feedback (invisible or recreation)
1506 definePosition(x, y);
1507 defineSize(width, height);
1511 // native reparent failed -> try creation, while trying to preserve resources if becoming visible again.
1512 if(DEBUG_IMPLEMENTATION) {
1513 System.err.println("Window.reparent: native reparenting failed ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)+" -> "+toHexString(newParentWindowHandle)+" - Trying recreation");
1515 destroy( becomesVisible );
1516 operation = ReparentOperation.ACTION_NATIVE_CREATION ;
1518 if( null != parentWindow ) {
1519 // TOP -> CLIENT: Setup Parent's Pointer State
1520 setOffscreenPointerIcon(pointerIcon);
1521 setOffscreenPointerVisible(pointerVisible, pointerIcon);
1526 // ACTION_NATIVE_CREATION
1527 // ACTION_NATIVE_CREATION_PENDING;
1529 // make size and position persistent for proper [re]creation
1530 definePosition(x, y);
1531 defineSize(width, height);
1534 if(DEBUG_IMPLEMENTATION) {
1535 System.err.println("Window.reparent: END-1 ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+
1536 ", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+
1537 ", parentWindow "+ Display.hashCodeNullSafe(parentWindow)+" "+
1538 getX()+"/"+getY()+" "+getWidth()+"x"+getHeight());
1541 if(null!=lifecycleHook) {
1542 lifecycleHook.resetCounter();
1547 switch (operation) {
1548 case ACTION_NATIVE_REPARENTING:
1549 // trigger a resize/relayout and repaint to listener
1550 sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED);
1553 case ACTION_NATIVE_CREATION:
1554 // This may run on the new Display/Screen connection, hence a new EDT task
1555 runOnEDTIfAvail(true, reparentActionRecreate);
1561 if(DEBUG_IMPLEMENTATION) {
1562 System.err.println("Window.reparent: END-X ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+
1563 ", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+
1564 ", parentWindow "+ Display.hashCodeNullSafe(parentWindow)+" "+
1565 getX()+"/"+getY()+" "+getWidth()+"x"+getHeight());
1570 private class ReparentActionRecreate implements Runnable {
1572 public final void run() {
1573 final RecursiveLock _lock = windowLock;
1576 if(DEBUG_IMPLEMENTATION) {
1577 System.err.println("Window.reparent: ReparentActionRecreate ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+Display.hashCodeNullSafe(parentWindow));
1579 setVisibleActionImpl(true); // native creation
1580 requestFocusInt( 0 == parentWindowHandle /* skipFocusAction if top-level */);
1586 private final ReparentActionRecreate reparentActionRecreate = new ReparentActionRecreate();
1589 public final ReparentOperation reparentWindow(NativeWindow newParent) {
1590 return reparentWindow(newParent, -1, -1, 0);
1594 public final ReparentOperation reparentWindow(NativeWindow newParent, int x, int y, boolean forceDestroyCreate) {
1595 return reparentWindow(newParent, x, y, forceDestroyCreate ? REPARENT_HINT_FORCE_RECREATION : 0);
1599 public final ReparentOperation reparentWindow(NativeWindow newParent, int x, int y, int hints) {
1600 final ReparentAction reparentAction = new ReparentAction(newParent, x, y, hints);
1601 runOnEDTIfAvail(true, reparentAction);
1602 return reparentAction.getOp();
1606 public final CapabilitiesChooser setCapabilitiesChooser(CapabilitiesChooser chooser) {
1607 CapabilitiesChooser old = this.capabilitiesChooser;
1608 this.capabilitiesChooser = chooser;
1613 public final CapabilitiesImmutable getChosenCapabilities() {
1614 return getGraphicsConfiguration().getChosenCapabilities();
1618 public final CapabilitiesImmutable getRequestedCapabilities() {
1619 return capsRequested;
1622 private class DecorationAction implements Runnable {
1623 boolean undecorated;
1625 private DecorationAction(boolean undecorated) {
1626 this.undecorated = undecorated;
1630 public final void run() {
1631 final RecursiveLock _lock = windowLock;
1634 if(WindowImpl.this.undecorated != undecorated) {
1635 // set current state
1636 WindowImpl.this.undecorated = undecorated;
1638 if( isNativeValid() && !isFullscreen() ) {
1639 // Mirror pos/size so native change notification can get overwritten
1640 final int x = getX();
1641 final int y = getY();
1642 final int width = getWidth();
1643 final int height = getHeight();
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
1654 sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener
1659 public final void setUndecorated(boolean value) {
1660 runOnEDTIfAvail(true, new DecorationAction(value));
1664 public final boolean isUndecorated() {
1665 return 0 != parentWindowHandle || undecorated || fullscreen ;
1668 private class AlwaysOnTopAction implements Runnable {
1669 boolean alwaysOnTop;
1671 private AlwaysOnTopAction(boolean alwaysOnTop) {
1672 this.alwaysOnTop = alwaysOnTop;
1676 public final void run() {
1677 final RecursiveLock _lock = windowLock;
1680 if(WindowImpl.this.alwaysOnTop != alwaysOnTop) {
1681 // set current state
1682 WindowImpl.this.alwaysOnTop = alwaysOnTop;
1684 if( isNativeValid() ) {
1685 // Mirror pos/size so native change notification can get overwritten
1686 final int x = getX();
1687 final int y = getY();
1688 final int width = getWidth();
1689 final int height = getHeight();
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
1700 sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener
1705 public final void setAlwaysOnTop(boolean value) {
1706 if( isFullscreen() ) {
1707 nfs_alwaysOnTop = value;
1709 runOnEDTIfAvail(true, new AlwaysOnTopAction(value));
1714 public final boolean isAlwaysOnTop() {
1719 public final String getTitle() {
1723 public final void setTitle(String title) {
1724 if (title == null) {
1728 if(0 != getWindowHandle()) {
1729 setTitleImpl(title);
1734 public final boolean isPointerVisible() {
1735 return pointerVisible;
1738 public final void setPointerVisible(final boolean pointerVisible) {
1739 if(this.pointerVisible != pointerVisible) {
1740 boolean setVal = 0 == getWindowHandle();
1742 setVal = setPointerVisibleIntern(pointerVisible);
1745 this.pointerVisible = pointerVisible;
1749 private boolean setPointerVisibleIntern(final boolean pointerVisible) {
1750 boolean res = setOffscreenPointerVisible(pointerVisible, pointerIcon);
1751 return setPointerVisibleImpl(pointerVisible) || res; // accept onscreen or offscreen positive result!
1754 * Helper method to delegate {@link #setPointerVisibleImpl(boolean)} to
1755 * {@link OffscreenLayerSurface#hideCursor()} or {@link OffscreenLayerSurface#setCursor(PixelRectangle, PointImmutable)}.
1757 * Note: JAWTWindow is an OffscreenLayerSurface.
1760 * Performing OffscreenLayerSurface's setCursor(..)/hideCursor(), if available,
1761 * gives same behavior on all platforms.
1764 * If visible, implementation invokes {@link #setOffscreenPointerIcon(OffscreenLayerSurface, PointerIconImpl)} using the
1765 * given <code>defaultPointerIcon</code>, otherwise {@link OffscreenLayerSurface#hideCursor()} is invoked.
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.
1771 private boolean setOffscreenPointerVisible(final boolean pointerVisible, final PointerIconImpl defaultPointerIcon) {
1772 if( pointerVisible ) {
1773 return setOffscreenPointerIcon(defaultPointerIcon);
1775 final NativeWindow parent = getParent();
1776 if( parent instanceof OffscreenLayerSurface ) {
1777 final OffscreenLayerSurface ols = (OffscreenLayerSurface) parent;
1779 return ols.hideCursor();
1780 } catch (Exception e) {
1781 e.printStackTrace();
1789 public final PointerIcon getPointerIcon() { return pointerIcon; }
1792 public final void setPointerIcon(final PointerIcon pi) {
1793 final PointerIconImpl piImpl = (PointerIconImpl)pi;
1794 if( this.pointerIcon != piImpl ) {
1795 if( isNativeValid() ) {
1796 runOnEDTIfAvail(true, new Runnable() {
1798 setPointerIconIntern(piImpl);
1801 this.pointerIcon = piImpl;
1804 private void setPointerIconIntern(final PointerIconImpl pi) {
1805 setOffscreenPointerIcon(pi);
1806 setPointerIconImpl(pi);
1809 * Helper method to delegate {@link #setPointerIconIntern(PointerIconImpl)} to
1810 * {@link OffscreenLayerSurface#setCursor(PixelRectangle, PointImmutable)}
1812 * Note: JAWTWindow is an OffscreenLayerSurface.
1815 * Performing OffscreenLayerSurface's setCursor(..), if available,
1816 * gives same behavior on all platforms.
1819 * Workaround for AWT/Windows bug within browser,
1820 * where the PointerIcon gets periodically overridden
1821 * by the AWT Component's icon.
1823 * @param ols the {@link OffscreenLayerSurface} instance, if null method does nothing.
1824 * @param pi the {@link PointerIconImpl} instance, if null PointerIcon gets reset.
1826 private boolean setOffscreenPointerIcon(final PointerIconImpl pi) {
1827 final NativeWindow parent = getParent();
1828 if( parent instanceof OffscreenLayerSurface ) {
1829 final OffscreenLayerSurface ols = (OffscreenLayerSurface) parent;
1832 return ols.setCursor(pi, pi.getHotspot());
1834 return ols.setCursor(null, null); // default
1836 } catch (Exception e) {
1837 e.printStackTrace();
1844 public final boolean isPointerConfined() {
1845 return pointerConfined;
1848 public final void confinePointer(boolean confine) {
1849 if(this.pointerConfined != confine) {
1850 boolean setVal = 0 == getWindowHandle();
1854 warpPointer(getSurfaceWidth()/2, getSurfaceHeight()/2);
1856 setVal = confinePointerImpl(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
1861 Thread.sleep(3 * screen.getDisplay().getEDTUtil().getPollPeriod());
1862 } catch (InterruptedException e) { }
1866 this.pointerConfined = confine;
1872 public final void warpPointer(int x, int y) {
1873 if(0 != getWindowHandle()) {
1874 warpPointerImpl(x, y);
1879 public final InsetsImmutable getInsets() {
1880 if(isUndecorated()) {
1881 return Insets.getZero();
1883 updateInsetsImpl(insets);
1888 public final int getX() {
1893 public final int getY() {
1898 public final int getWidth() {
1903 public final int getHeight() {
1908 public final Rectangle getBounds() {
1909 return new Rectangle(x, y, winWidth, winHeight);
1913 public final int getSurfaceWidth() {
1918 public final int getSurfaceHeight() {
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);
1929 public final int[] convertToWindowUnits(final int[] pixelUnitsAndResult) {
1930 pixelUnitsAndResult[0] /= getPixelScaleX();
1931 pixelUnitsAndResult[1] /= getPixelScaleY();
1932 return pixelUnitsAndResult;
1936 public final int[] convertToPixelUnits(final int[] windowUnitsAndResult) {
1937 windowUnitsAndResult[0] *= getPixelScaleX();
1938 windowUnitsAndResult[1] *= getPixelScaleY();
1939 return windowUnitsAndResult;
1942 protected final Point convertToWindowUnits(final Point pixelUnitsAndResult) {
1943 return pixelUnitsAndResult.scaleInv(getPixelScaleX(), getPixelScaleY());
1946 protected final Point convertToPixelUnits(final Point windowUnitsAndResult) {
1947 return windowUnitsAndResult.scale(getPixelScaleX(), getPixelScaleY());
1951 public final Rectangle convertToWindowUnits(final Rectangle pixelUnitsAndResult) {
1952 return pixelUnitsAndResult.scaleInv(getPixelScaleX(), getPixelScaleY());
1956 public final Rectangle convertToPixelUnits(final Rectangle windowUnitsAndResult) {
1957 return windowUnitsAndResult.scale(getPixelScaleX(), getPixelScaleY());
1960 /** HiDPI: We currently base scaling of window units to pixel units on an integer scale factor per component. */
1961 protected final int getPixelScaleX() {
1962 return hasPixelScale[0];
1965 /** HiDPI: We currently base scaling of window units to pixel units on an integer scale factor per component. */
1966 protected final int getPixelScaleY() {
1967 return hasPixelScale[1];
1971 public void setSurfaceScale(final int[] pixelScale) {
1972 SurfaceScaleUtils.validateReqPixelScale(reqPixelScale, pixelScale, DEBUG_IMPLEMENTATION ? getClass().getSimpleName() : null);
1976 public final int[] getRequestedSurfaceScale(final int[] result) {
1977 System.arraycopy(reqPixelScale, 0, result, 0, 2);
1982 public final int[] getCurrentSurfaceScale(final int[] result) {
1983 System.arraycopy(hasPixelScale, 0, result, 0, 2);
1987 protected final boolean autoPosition() { return autoPosition; }
1989 /** Sets the position fields {@link #x} and {@link #y} in window units to the given values and {@link #autoPosition} to false. */
1990 protected final void definePosition(int x, int y) {
1991 if(DEBUG_IMPLEMENTATION) {
1992 System.err.println("definePosition: "+this.x+"/"+this.y+" -> "+x+"/"+y);
1993 // Thread.dumpStack();
1995 autoPosition = false;
1996 this.x = x; this.y = y;
2000 * Sets the size fields {@link #winWidth} and {@link #winHeight} in window units to the given values
2001 * and {@link #pixWidth} and {@link #pixHeight} in pixel units according to {@link #convertToPixelUnits(int[])}.
2003 protected final void defineSize(int winWidth, int winHeight) {
2004 final int pixWidth = winWidth * getPixelScaleX(); // FIXME HiDPI: Shortcut, may need to adjust if we change scaling methodology
2005 final int pixHeight = winHeight * getPixelScaleY();
2006 if(DEBUG_IMPLEMENTATION) {
2007 System.err.println("defineSize: win["+this.winWidth+"x"+this.winHeight+" -> "+winWidth+"x"+winHeight+
2008 "], pixel["+this.pixWidth+"x"+this.pixHeight+" -> "+pixWidth+"x"+pixHeight+"]");
2009 // Thread.dumpStack();
2011 this.winWidth = winWidth; this.winHeight = winHeight;
2012 this.pixWidth = pixWidth; this.pixHeight = pixHeight;
2016 public final boolean isVisible() {
2021 public final boolean isFullscreen() {
2025 //----------------------------------------------------------------------
2030 public final Window getDelegatedWindow() {
2034 //----------------------------------------------------------------------
2039 * If the implementation is capable of detecting a device change
2040 * return true and clear the status/reason of the change.
2042 public boolean hasDeviceChanged() {
2046 public final LifecycleHook getLifecycleHook() {
2047 return lifecycleHook;
2050 public final LifecycleHook setLifecycleHook(LifecycleHook hook) {
2051 LifecycleHook old = lifecycleHook;
2052 lifecycleHook = hook;
2057 * If this Window actually wraps a {@link NativeSurface} from another instance or toolkit,
2058 * it will return such reference. Otherwise returns null.
2060 public NativeSurface getWrappedSurface() {
2065 public final void setWindowDestroyNotifyAction(Runnable r) {
2066 windowDestroyNotifyAction = r;
2069 protected final long getParentWindowHandle() {
2070 return isFullscreen() ? 0 : parentWindowHandle;
2074 public final String toString() {
2075 StringBuilder sb = new StringBuilder();
2077 sb.append(getClass().getName()+"[Config "+config+
2079 ",\n ParentWindow "+parentWindow+
2080 ",\n ParentWindowHandle "+toHexString(parentWindowHandle)+" ("+(0!=getParentWindowHandle())+")"+
2081 ",\n WindowHandle "+toHexString(getWindowHandle())+
2082 ",\n SurfaceHandle "+toHexString(getSurfaceHandle())+ " (lockedExt window "+windowLock.isLockedByOtherThread()+", surface "+isSurfaceLockedByOtherThread()+")"+
2083 ",\n window["+getX()+"/"+getY()+" (auto "+autoPosition()+") "+getWidth()+"x"+getHeight()+"], pixel["+getSurfaceWidth()+"x"+getSurfaceHeight()+
2084 "],\n Visible "+isVisible()+", focus "+hasFocus()+
2085 ",\n Undecorated "+undecorated+" ("+isUndecorated()+")"+
2086 ",\n AlwaysOnTop "+alwaysOnTop+", Fullscreen "+fullscreen+
2087 ",\n WrappedSurface "+getWrappedSurface()+
2088 ",\n ChildWindows "+childWindows.size());
2090 sb.append(", SurfaceUpdatedListeners num "+surfaceUpdatedHelper.size()+" [");
2091 for (int i = 0; i < surfaceUpdatedHelper.size(); i++ ) {
2092 sb.append(surfaceUpdatedHelper.get(i)+", ");
2094 sb.append("], WindowListeners num "+windowListeners.size()+" [");
2095 for (int i = 0; i < windowListeners.size(); i++ ) {
2096 sb.append(windowListeners.get(i)+", ");
2098 sb.append("], MouseListeners num "+mouseListeners.size()+" [");
2099 for (int i = 0; i < mouseListeners.size(); i++ ) {
2100 sb.append(mouseListeners.get(i)+", ");
2102 sb.append("], PointerGestures default "+defaultGestureHandlerEnabled+", custom "+pointerGestureHandler.size()+" [");
2103 for (int i = 0; i < pointerGestureHandler.size(); i++ ) {
2104 sb.append(pointerGestureHandler.get(i)+", ");
2106 sb.append("], KeyListeners num "+keyListeners.size()+" [");
2107 for (int i = 0; i < keyListeners.size(); i++ ) {
2108 sb.append(keyListeners.get(i)+", ");
2110 sb.append("], windowLock "+windowLock+", surfaceLockCount "+surfaceLockCount+"]");
2111 return sb.toString();
2114 protected final void setWindowHandle(long handle) {
2115 windowHandle = handle;
2119 public final void runOnEDTIfAvail(boolean wait, final Runnable task) {
2120 if( windowLock.isOwner( Thread.currentThread() ) ) {
2123 ( (DisplayImpl) screen.getDisplay() ).runOnEDTIfAvail(wait, task);
2127 private final Runnable requestFocusAction = new Runnable() {
2129 public final void run() {
2130 if(DEBUG_IMPLEMENTATION) {
2131 System.err.println("Window.RequestFocusAction: force 0 - ("+getThreadName()+"): "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle));
2133 WindowImpl.this.requestFocusImpl(false);
2136 private final Runnable requestFocusActionForced = new Runnable() {
2138 public final void run() {
2139 if(DEBUG_IMPLEMENTATION) {
2140 System.err.println("Window.RequestFocusAction: force 1 - ("+getThreadName()+"): "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle));
2142 WindowImpl.this.requestFocusImpl(true);
2147 public final boolean hasFocus() {
2152 public final void requestFocus() {
2157 public final void requestFocus(boolean wait) {
2158 requestFocus(wait /* wait */, false /* skipFocusAction */, brokenFocusChange /* force */);
2161 private void requestFocus(boolean wait, boolean skipFocusAction, boolean force) {
2162 if( isNativeValid() &&
2163 ( force || !hasFocus() ) &&
2164 ( skipFocusAction || !focusAction() ) ) {
2165 runOnEDTIfAvail(wait, force ? requestFocusActionForced : requestFocusAction);
2169 /** Internally forcing request focus on current thread */
2170 private void requestFocusInt(boolean skipFocusAction) {
2171 if( skipFocusAction || !focusAction() ) {
2172 if(DEBUG_IMPLEMENTATION) {
2173 System.err.println("Window.RequestFocusInt: forcing - ("+getThreadName()+"): skipFocusAction "+skipFocusAction+", focus "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle));
2175 requestFocusImpl(true);
2180 public final void setFocusAction(FocusRunnable focusAction) {
2181 this.focusAction = focusAction;
2184 private boolean focusAction() {
2185 if(DEBUG_IMPLEMENTATION) {
2186 System.err.println("Window.focusAction() START - "+getThreadName()+", focusAction: "+focusAction+" - windowHandle "+toHexString(getWindowHandle()));
2189 if(null!=focusAction) {
2190 res = focusAction.run();
2194 if(DEBUG_IMPLEMENTATION) {
2195 System.err.println("Window.focusAction() END - "+getThreadName()+", focusAction: "+focusAction+" - windowHandle "+toHexString(getWindowHandle())+", res: "+res);
2200 protected final void setBrokenFocusChange(boolean v) {
2201 brokenFocusChange = v;
2205 public final void setKeyboardFocusHandler(KeyListener l) {
2206 keyboardFocusHandler = l;
2209 private class SetPositionAction implements Runnable {
2212 private SetPositionAction(int x, int y) {
2218 public final void run() {
2219 final RecursiveLock _lock = windowLock;
2222 if(DEBUG_IMPLEMENTATION) {
2223 System.err.println("Window setPosition: "+getX()+"/"+getY()+" -> "+x+"/"+y+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle));
2225 // Let the window be positioned if !fullscreen and position changed or being a child window.
2226 if ( !isFullscreen() && ( getX() != x || getY() != y || null != getParent()) ) {
2227 if(isNativeValid()) {
2228 // this.x/this.y will be set by sizeChanged, triggered by windowing event system
2229 reconfigureWindowImpl(x, y, getWidth(), getHeight(), getReconfigureFlags(0, isVisible()));
2230 if( null == parentWindow ) {
2231 // Wait until custom position is reached within tolerances
2232 waitForPosition(true, x, y, Window.TIMEOUT_NATIVEWINDOW);
2235 definePosition(x, y); // set pos for createNative(..)
2245 public void setPosition(int x, int y) {
2246 autoPosition = false;
2247 runOnEDTIfAvail(true, new SetPositionAction(x, y));
2251 public final void setTopLevelPosition(int x, int y) {
2252 setPosition(x + getInsets().getLeftWidth(), y + getInsets().getTopHeight());
2255 private class FullScreenAction implements Runnable {
2256 boolean _fullscreen;
2258 private boolean init(boolean fullscreen) {
2259 if(isNativeValid()) {
2260 this._fullscreen = fullscreen;
2261 return isFullscreen() != fullscreen;
2263 WindowImpl.this.fullscreen = fullscreen; // set current state for createNative(..)
2267 public boolean fsOn() { return _fullscreen; }
2270 public final void run() {
2271 final RecursiveLock _lock = windowLock;
2273 blockInsetsChange = true;
2275 final int oldX = getX();
2276 final int oldY = getY();
2277 final int oldWidth = getWidth();
2278 final int oldHeight = getHeight();
2282 final RectangleImmutable sviewport = screen.getViewportInWindowUnits(); // window units
2283 final RectangleImmutable viewport; // window units
2284 final int fs_span_flag;
2285 final boolean alwaysOnTopChange;
2287 if( null == fullscreenMonitors ) {
2288 if( fullscreenUseMainMonitor ) {
2289 fullscreenMonitors = new ArrayList<MonitorDevice>();
2290 fullscreenMonitors.add( getMainMonitor() );
2292 fullscreenMonitors = getScreen().getMonitorDevices();
2296 final Rectangle viewportInWindowUnits = new Rectangle();
2297 MonitorDevice.unionOfViewports(null, viewportInWindowUnits, fullscreenMonitors);
2298 viewport = viewportInWindowUnits;
2300 if( isReconfigureFlagSupported(FLAG_IS_FULLSCREEN_SPAN) &&
2301 ( fullscreenMonitors.size() > 1 || sviewport.compareTo(viewport) > 0 ) ) {
2302 fs_span_flag = FLAG_IS_FULLSCREEN_SPAN;
2308 nfs_width = oldWidth;
2309 nfs_height = oldHeight;
2310 nfs_alwaysOnTop = alwaysOnTop;
2311 x = viewport.getX();
2312 y = viewport.getY();
2313 w = viewport.getWidth();
2314 h = viewport.getHeight();
2315 alwaysOnTop = false;
2316 alwaysOnTopChange = nfs_alwaysOnTop != alwaysOnTop;
2318 fullscreenUseMainMonitor = true;
2319 fullscreenMonitors = null;
2326 alwaysOnTopChange = nfs_alwaysOnTop != alwaysOnTop;
2327 alwaysOnTop = nfs_alwaysOnTop;
2329 if(null!=parentWindow) {
2330 // reset position to 0/0 within parent space
2334 // refit if size is bigger than parent
2335 if( w > parentWindow.getWidth() ) {
2336 w = parentWindow.getWidth();
2338 if( h > parentWindow.getHeight() ) {
2339 h = parentWindow.getHeight();
2344 final DisplayImpl display = (DisplayImpl) screen.getDisplay();
2345 display.dispatchMessagesNative(); // status up2date
2346 final boolean wasVisible = isVisible();
2347 final boolean tempInvisible = !_fullscreen && wasVisible && NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(true);
2349 if(DEBUG_IMPLEMENTATION) {
2350 System.err.println("Window fs: "+_fullscreen+" "+x+"/"+y+" "+w+"x"+h+", "+isUndecorated()+
2351 ", virtl-screenSize: "+sviewport+" [wu], monitorsViewport "+viewport+" [wu]"+
2352 ", spanning "+(0!=fs_span_flag)+
2353 ", alwaysOnTop "+alwaysOnTop+(alwaysOnTopChange?"*":"")+
2354 ", wasVisible "+wasVisible+", tempInvisible "+tempInvisible+
2355 ", hasParent "+(null!=parentWindow)+
2356 " @ "+Thread.currentThread().getName());
2359 // fullscreen off: !visible first (fixes X11 unsuccessful return to parent window _and_ wrong window size propagation)
2360 if( tempInvisible ) {
2361 setVisibleImpl(false, oldX, oldY, oldWidth, oldHeight);
2362 WindowImpl.this.waitForVisible(false, false);
2363 try { Thread.sleep(100); } catch (InterruptedException e) { }
2364 display.dispatchMessagesNative(); // status up2date
2367 // Lock parentWindow only during reparenting (attempt)
2368 final NativeWindow parentWindowLocked;
2369 if( null != parentWindow ) {
2370 parentWindowLocked = parentWindow;
2371 if( NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindowLocked.lockSurface() ) {
2372 throw new NativeWindowException("Parent surface lock: not ready: "+parentWindow);
2375 parentWindowLocked = null;
2378 if(alwaysOnTopChange && _fullscreen) {
2379 // Enter fullscreen - Disable alwaysOnTop
2380 reconfigureWindowImpl(oldX, oldY, oldWidth, oldHeight, getReconfigureFlags(FLAG_CHANGE_ALWAYSONTOP, isVisible()));
2383 WindowImpl.this.fullscreen = _fullscreen;
2384 reconfigureWindowImpl(x, y, w, h,
2385 getReconfigureFlags( ( ( null != parentWindowLocked ) ? FLAG_CHANGE_PARENTING : 0 ) |
2386 fs_span_flag | FLAG_CHANGE_FULLSCREEN | FLAG_CHANGE_DECORATION, isVisible()) );
2387 if(alwaysOnTopChange && !_fullscreen) {
2388 // Leave fullscreen - Restore alwaysOnTop
2389 reconfigureWindowImpl(x, y, w, h, getReconfigureFlags(FLAG_CHANGE_ALWAYSONTOP, isVisible()));
2392 if(null!=parentWindowLocked) {
2393 parentWindowLocked.unlockSurface();
2396 display.dispatchMessagesNative(); // status up2date
2399 if( NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(true) ) {
2400 // Give sluggy WM's (e.g. Unity) a chance to properly restore window ..
2401 try { Thread.sleep(100); } catch (InterruptedException e) { }
2402 display.dispatchMessagesNative(); // status up2date
2404 setVisibleImpl(true, x, y, w, h);
2405 boolean ok = 0 <= WindowImpl.this.waitForVisible(true, false);
2407 ok = WindowImpl.this.waitForSize(w, h, false, TIMEOUT_NATIVEWINDOW);
2409 if(ok && !_fullscreen && null == parentWindow) {
2410 // Position mismatch shall not lead to fullscreen failure
2411 WindowImpl.this.waitForPosition(true, x, y, TIMEOUT_NATIVEWINDOW);
2414 requestFocusInt(_fullscreen /* skipFocusAction if fullscreen */);
2415 display.dispatchMessagesNative(); // status up2date
2417 if(DEBUG_IMPLEMENTATION) {
2418 System.err.println("Window fs done: ok " + ok + ", " + WindowImpl.this);
2422 blockInsetsChange = false;
2425 sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener
2428 private final FullScreenAction fullScreenAction = new FullScreenAction();
2431 public boolean setFullscreen(boolean fullscreen) {
2432 return setFullscreenImpl(fullscreen, true, null);
2436 public boolean setFullscreen(List<MonitorDevice> monitors) {
2437 return setFullscreenImpl(true, false, monitors);
2440 private boolean setFullscreenImpl(boolean fullscreen, boolean useMainMonitor, List<MonitorDevice> monitors) {
2441 synchronized(fullScreenAction) {
2442 fullscreenMonitors = monitors;
2443 fullscreenUseMainMonitor = useMainMonitor;
2444 if( fullScreenAction.init(fullscreen) ) {
2445 if( fullScreenAction.fsOn() && isOffscreenInstance(WindowImpl.this, parentWindow) ) {
2446 // enable fullscreen on offscreen instance
2447 if(null != parentWindow) {
2448 nfs_parent = parentWindow;
2449 reparentWindow(null, -1, -1, REPARENT_HINT_FORCE_RECREATION | REPARENT_HINT_BECOMES_VISIBLE);
2451 throw new InternalError("Offscreen instance w/o parent unhandled");
2455 runOnEDTIfAvail(true, fullScreenAction);
2457 if(!fullScreenAction.fsOn() && null != nfs_parent) {
2458 // disable fullscreen on offscreen instance
2459 reparentWindow(nfs_parent, -1, -1, REPARENT_HINT_FORCE_RECREATION | REPARENT_HINT_BECOMES_VISIBLE);
2463 return this.fullscreen;
2467 /** Notify WindowDriver about the finished monitor mode change. */
2468 protected void monitorModeChanged(MonitorEvent me, boolean success) {
2471 private class MonitorModeListenerImpl implements MonitorModeListener {
2472 boolean animatorPaused = false;
2473 boolean hidden = false;
2474 boolean hadFocus = false;
2475 boolean fullscreenPaused = false;
2476 List<MonitorDevice> _fullscreenMonitors = null;
2477 boolean _fullscreenUseMainMonitor = true;
2480 public void monitorModeChangeNotify(MonitorEvent me) {
2481 hadFocus = hasFocus();
2482 final boolean isOSX = NativeWindowFactory.TYPE_MACOSX == NativeWindowFactory.getNativeWindowType(true);
2483 final boolean quirkFSPause = fullscreen && isReconfigureFlagSupported(FLAG_IS_FULLSCREEN_SPAN);
2484 final boolean quirkHide = !quirkFSPause && !fullscreen && isVisible() && isOSX;
2485 if(DEBUG_IMPLEMENTATION) {
2486 System.err.println("Window.monitorModeChangeNotify: hadFocus "+hadFocus+", qFSPause "+quirkFSPause+", qHide "+quirkHide+", "+me+" @ "+Thread.currentThread().getName());
2489 if(null!=lifecycleHook) {
2490 animatorPaused = lifecycleHook.pauseRenderingAction();
2492 if( quirkFSPause ) {
2493 if(DEBUG_IMPLEMENTATION) {
2494 System.err.println("Window.monitorModeChangeNotify: FS Pause");
2496 fullscreenPaused = true;
2497 _fullscreenMonitors = fullscreenMonitors;
2498 _fullscreenUseMainMonitor = fullscreenUseMainMonitor;
2499 setFullscreenImpl(false, true, null);
2502 // hiding & showing the window around mode-change solves issues w/ OSX,
2503 // where the content would be black until a resize.
2505 WindowImpl.this.setVisible(false);
2510 public void monitorModeChanged(MonitorEvent me, boolean success) {
2511 if(!animatorPaused && success && null!=lifecycleHook) {
2512 // Didn't pass above notify method. probably detected screen change after it happened.
2513 animatorPaused = lifecycleHook.pauseRenderingAction();
2515 if(DEBUG_IMPLEMENTATION) {
2516 System.err.println("Window.monitorModeChanged.0: success: "+success+", hadFocus "+hadFocus+", animPaused "+animatorPaused+
2517 ", hidden "+hidden+", FS "+fullscreen+", FS-paused "+fullscreenPaused+
2518 " @ "+Thread.currentThread().getName());
2519 System.err.println("Window.monitorModeChanged.0: "+getScreen());
2520 System.err.println("Window.monitorModeChanged.0: "+me);
2522 WindowImpl.this.monitorModeChanged(me, success);
2524 if( success && !fullscreen && !fullscreenPaused ) {
2525 // Simply move/resize window to fit in virtual screen if required
2526 final RectangleImmutable viewport = screen.getViewportInWindowUnits();
2527 if( viewport.getWidth() > 0 && viewport.getHeight() > 0 ) { // failsafe
2528 final RectangleImmutable rect = new Rectangle(getX(), getY(), getWidth(), getHeight());
2529 final RectangleImmutable isect = viewport.intersection(rect);
2530 if ( getHeight() > isect.getHeight() ||
2531 getWidth() > isect.getWidth() ) {
2532 if(DEBUG_IMPLEMENTATION) {
2533 System.err.println("Window.monitorModeChanged.1: Non-FS - Fit window "+rect+" into screen viewport "+viewport+
2534 ", due to minimal intersection "+isect);
2536 definePosition(viewport.getX(), viewport.getY()); // set pos for setVisible(..) or createNative(..) - reduce EDT roundtrip
2537 setSize(viewport.getWidth(), viewport.getHeight(), true /* force */);
2540 } else if( fullscreenPaused ) {
2541 if(DEBUG_IMPLEMENTATION) {
2542 System.err.println("Window.monitorModeChanged.2: FS Restore");
2544 setFullscreenImpl(true, _fullscreenUseMainMonitor, _fullscreenMonitors);
2545 fullscreenPaused = false;
2546 _fullscreenMonitors = null;
2547 _fullscreenUseMainMonitor = true;
2548 } else if( success && fullscreen && null != fullscreenMonitors ) {
2549 // If changed monitor is part of this fullscreen mode, reset size! (Bug 771)
2550 final MonitorDevice md = me.getMonitor();
2551 if( fullscreenMonitors.contains(md) ) {
2552 final Rectangle viewportInWindowUnits = new Rectangle();
2553 MonitorDevice.unionOfViewports(null, viewportInWindowUnits, fullscreenMonitors);
2554 if(DEBUG_IMPLEMENTATION) {
2555 final RectangleImmutable winBounds = WindowImpl.this.getBounds();
2556 System.err.println("Window.monitorModeChanged.3: FS Monitor Match: Fit window "+winBounds+" into new viewport union "+viewportInWindowUnits+" [window], provoked by "+md);
2558 definePosition(viewportInWindowUnits.getX(), viewportInWindowUnits.getY()); // set pos for setVisible(..) or createNative(..) - reduce EDT roundtrip
2559 setSize(viewportInWindowUnits.getWidth(), viewportInWindowUnits.getHeight(), true /* force */);
2563 WindowImpl.this.setVisible(true);
2566 sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener
2567 if(animatorPaused) {
2568 lifecycleHook.resumeRenderingAction();
2573 if(DEBUG_IMPLEMENTATION) {
2574 System.err.println("Window.monitorModeChanged.X: @ "+Thread.currentThread().getName()+", this: "+WindowImpl.this);
2578 private final MonitorModeListenerImpl monitorModeListenerImpl = new MonitorModeListenerImpl();
2581 //----------------------------------------------------------------------
2582 // Child Window Management
2586 public final boolean removeChild(NativeWindow win) {
2587 synchronized(childWindowsLock) {
2588 return childWindows.remove(win);
2593 public final boolean addChild(NativeWindow win) {
2597 synchronized(childWindowsLock) {
2598 return childWindows.add(win);
2602 //----------------------------------------------------------------------
2603 // Generic Event Support
2605 private void doEvent(boolean enqueue, boolean wait, com.jogamp.newt.event.NEWTEvent event) {
2606 boolean done = false;
2609 done = consumeEvent(event);
2610 wait = done; // don't wait if event can't be consumed now
2614 enqueueEvent(wait, event);
2619 public final void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event) {
2620 if(isNativeValid()) {
2621 ((DisplayImpl)screen.getDisplay()).enqueueEvent(wait, event);
2626 public final boolean consumeEvent(NEWTEvent e) {
2627 switch(e.getEventType()) {
2628 // special repaint treatment
2629 case WindowEvent.EVENT_WINDOW_REPAINT:
2630 // queue repaint event in case window is locked, ie in operation
2631 if( null != windowLock.getOwner() ) {
2632 // make sure only one repaint event is queued
2633 if(!repaintQueued) {
2635 final boolean discardTO = QUEUED_EVENT_TO <= System.currentTimeMillis()-e.getWhen();
2636 if(DEBUG_IMPLEMENTATION) {
2637 System.err.println("Window.consumeEvent: REPAINT "+Thread.currentThread().getName()+" - queued "+e+", discard-to "+discardTO);
2638 // Thread.dumpStack();
2640 return discardTO; // discardTO:=true -> consumed
2644 repaintQueued=false; // no repaint event queued
2648 case WindowEvent.EVENT_WINDOW_RESIZED:
2649 // queue event in case window is locked, ie in operation
2650 if( null != windowLock.getOwner() ) {
2651 final boolean discardTO = QUEUED_EVENT_TO <= System.currentTimeMillis()-e.getWhen();
2652 if(DEBUG_IMPLEMENTATION) {
2653 System.err.println("Window.consumeEvent: RESIZED "+Thread.currentThread().getName()+" - queued "+e+", discard-to "+discardTO);
2654 // Thread.dumpStack();
2656 return discardTO; // discardTO:=true -> consumed
2662 if(e instanceof WindowEvent) {
2663 consumeWindowEvent((WindowEvent)e);
2664 } else if(e instanceof KeyEvent) {
2665 consumeKeyEvent((KeyEvent)e);
2666 } else if(e instanceof MouseEvent) {
2667 consumePointerEvent((MouseEvent)e);
2669 throw new NativeWindowException("Unexpected NEWTEvent type " + e);
2675 // MouseListener/Event Support
2679 // Native MouseEvents pre-processed to be enqueued or consumed directly
2682 public final void sendMouseEvent(final short eventType, final int modifiers,
2683 final int x, final int y, final short button, final float rotation) {
2684 doMouseEvent(false, false, eventType, modifiers, x, y, button, MouseEvent.getRotationXYZ(rotation, modifiers), 1f);
2686 public final void enqueueMouseEvent(final boolean wait, final short eventType, final int modifiers,
2687 final int x, final int y, final short button, final float rotation) {
2688 doMouseEvent(true, wait, eventType, modifiers, x, y, button, MouseEvent.getRotationXYZ(rotation, modifiers), 1f);
2690 protected final void doMouseEvent(final boolean enqueue, final boolean wait, final short eventType, final int modifiers,
2691 final int x, final int y, final short button, final float rotation) {
2692 doMouseEvent(enqueue, wait, eventType, modifiers, x, y, button, MouseEvent.getRotationXYZ(rotation, modifiers), 1f);
2695 public final void sendMouseEvent(final short eventType, final int modifiers,
2696 final int x, final int y, final short button, final float[] rotationXYZ, final float rotationScale) {
2697 doMouseEvent(false, false, eventType, modifiers, x, y, button, rotationXYZ, rotationScale);
2699 public final void enqueueMouseEvent(final boolean wait, final short eventType, final int modifiers,
2700 final int x, final int y, final short button, final float[] rotationXYZ, final float rotationScale) {
2701 doMouseEvent(true, wait, eventType, modifiers, x, y, button, rotationXYZ, rotationScale);
2705 * Send mouse event (one-pointer) either to be directly consumed or to be enqueued
2707 * @param enqueue if true, event will be {@link #enqueueEvent(boolean, NEWTEvent) enqueued},
2708 * otherwise {@link #consumeEvent(NEWTEvent) consumed} directly.
2709 * @param wait if true wait until {@link #consumeEvent(NEWTEvent) consumed}.
2711 protected void doMouseEvent(final boolean enqueue, final boolean wait, final short eventType, final int modifiers,
2712 final int x, final int y, final short button, final float[] rotationXYZ, final float rotationScale) {
2713 if( 0 > button || button > MouseEvent.BUTTON_COUNT ) {
2714 throw new NativeWindowException("Invalid mouse button number" + button);
2716 doPointerEvent(enqueue, wait, constMousePointerTypes, eventType, modifiers,
2717 0 /*actionIdx*/, new short[] { (short)0 }, button,
2718 new int[]{x}, new int[]{y}, new float[]{0f} /*pressure*/,
2719 1f /*maxPressure*/, rotationXYZ, rotationScale);
2723 * Send multiple-pointer event either to be directly consumed or to be enqueued
2725 * The index for the element of multiple-pointer arrays represents the pointer which triggered the event
2726 * is passed via <i>actionIdx</i>.
2729 * The given pointer names, <code>pNames</code>, are mapped to consecutive pointer IDs starting w/ 0
2730 * using a hash-map if <code>normalPNames</code> is <code>false</code>.
2731 * Otherwise a simple <code>int</code> to <code>short</code> type cast is performed.
2734 * See {@link #doPointerEvent(boolean, boolean, PointerType[], short, int, int, short[], short, int[], int[], float[], float, float[], float)}
2738 * @param enqueue if true, event will be {@link #enqueueEvent(boolean, NEWTEvent) enqueued},
2739 * otherwise {@link #consumeEvent(NEWTEvent) consumed} directly.
2740 * @param wait if true wait until {@link #consumeEvent(NEWTEvent) consumed}.
2741 * @param pTypes {@link MouseEvent.PointerType} for each pointer (multiple pointer)
2744 * @param actionIdx index of multiple-pointer arrays representing the pointer which triggered the event
2745 * @param normalPNames see pName below.
2746 * @param pNames Pointer name for each pointer (multiple pointer).
2747 * We assume consecutive pointer names starting w/ 0 if <code>normalPIDs</code> is <code>true</code>.
2748 * Otherwise we hash-map the values during state pressed to retrieve the normal ID.
2749 * @param pX X-axis for each pointer (multiple pointer)
2750 * @param pY Y-axis for each pointer (multiple pointer)
2751 * @param pPressure Pressure for each pointer (multiple pointer)
2752 * @param maxPressure Maximum pointer pressure for all pointer
2754 public final void doPointerEvent(final boolean enqueue, final boolean wait,
2755 final PointerType[] pTypes, final short eventType, final int modifiers,
2756 final int actionIdx, final boolean normalPNames, final int[] pNames,
2757 final int[] pX, final int[] pY, final float[] pPressure,
2758 float maxPressure, final float[] rotationXYZ, final float rotationScale) {
2759 final int pCount = pNames.length;
2760 final short[] pIDs = new short[pCount];
2761 for(int i=0; i<pCount; i++) {
2762 if( !normalPNames ) {
2763 // hash map int name -> short idx
2764 final int sz0 = pName2pID.size();
2765 final Integer pNameI1 = pName2pID.getOrAdd(Integer.valueOf(pNames[i]));
2766 final short pID = (short)pName2pID.indexOf(pNameI1);
2768 if(DEBUG_MOUSE_EVENT) {
2769 final int sz1 = pName2pID.size();
2771 System.err.println("PointerName2ID[sz "+sz1+"]: Map "+pNameI1+" == "+pID);
2774 if( MouseEvent.EVENT_MOUSE_RELEASED == eventType ) {
2775 pName2pID.remove(pNameI1);
2776 if(DEBUG_MOUSE_EVENT) {
2777 System.err.println("PointerName2ID[sz "+pName2pID.size()+"]: Unmap "+pNameI1+" == "+pID);
2782 pIDs[i] = (short)pNames[i];
2785 final short button = 0 < pCount ? (short) ( pIDs[0] + 1 ) : (short)0;
2786 doPointerEvent(enqueue, wait, pTypes, eventType, modifiers, actionIdx, pIDs, button,
2787 pX, pY, pPressure, maxPressure, rotationXYZ, rotationScale);
2791 * Send multiple-pointer event either to be directly consumed or to be enqueued.
2793 * Pointer/Mouse Processing Pass 1 (Pass 2 is performed in {@link #consumePointerEvent(MouseEvent)}.
2796 * Usually directly called by event source to enqueue and process event.
2799 * The index for the element of multiple-pointer arrays represents the pointer which triggered the event
2800 * is passed via <i>actionIdx</i>.
2804 * <li>Determine ENTERED/EXITED state</li>
2805 * <li>Remove redundant move/drag events</li>
2806 * <li>Reset states if applicable</li>
2807 * <li>Drop exterior events</li>
2808 * <li>Determine CLICK COUNT</li>
2809 * <li>Ignore sent CLICKED</li>
2810 * <li>Track buttonPressed incl. buttonPressedMask</li>
2811 * <li>Synthesize DRAGGED event (from MOVED if pointer is pressed)</li>
2815 * @param enqueue if true, event will be {@link #enqueueEvent(boolean, NEWTEvent) enqueued},
2816 * otherwise {@link #consumeEvent(NEWTEvent) consumed} directly.
2817 * @param wait if true wait until {@link #consumeEvent(NEWTEvent) consumed}.
2818 * @param pTypes {@link MouseEvent.PointerType} for each pointer (multiple pointer)
2821 * @param pActionIdx index of multiple-pointer arrays representing the pointer which triggered the event
2822 * @param pID Pointer ID for each pointer (multiple pointer). We assume consecutive pointerIDs starting w/ 0.
2823 * @param button Corresponding mouse-button, a button of 0 denotes no activity, i.e. {@link PointerType#Mouse} move.
2824 * @param pX X-axis for each pointer (multiple pointer)
2825 * @param pY Y-axis for each pointer (multiple pointer)
2826 * @param pPressure Pressure for each pointer (multiple pointer)
2827 * @param maxPressure Maximum pointer pressure for all pointer
2829 public final void doPointerEvent(final boolean enqueue, final boolean wait,
2830 final PointerType[] pTypes, final short eventType, int modifiers,
2831 final int pActionIdx, final short[] pID, final short buttonIn, final int[] pX, final int[] pY,
2832 final float[] pPressure, final float maxPressure, final float[] rotationXYZ, final float rotationScale) {
2833 final long when = System.currentTimeMillis();
2834 final int pCount = pTypes.length;
2836 if( 0 > pActionIdx || pActionIdx >= pCount) {
2837 throw new IllegalArgumentException("actionIdx out of bounds [0.."+(pCount-1)+"]");
2839 if( 0 < pActionIdx ) {
2840 // swap values to make idx 0 the triggering pointer
2842 final PointerType aType = pTypes[pActionIdx];
2843 pTypes[pActionIdx] = pTypes[0];
2847 final short s = pID[pActionIdx];
2848 pID[pActionIdx] = pID[0];
2852 int s = pX[pActionIdx];
2853 pX[pActionIdx] = pX[0];
2856 pY[pActionIdx] = pY[0];
2860 final float aPress = pPressure[pActionIdx];
2861 pPressure[pActionIdx] = pPressure[0];
2862 pPressure[0] = aPress;
2868 if( 0 <= buttonIn && buttonIn <= com.jogamp.newt.event.MouseEvent.BUTTON_COUNT ) { // we allow button==0 for no button, i.e. mouse-ptr move
2871 button = com.jogamp.newt.event.MouseEvent.BUTTON1;
2876 // - Determine ENTERED/EXITED state
2877 // - Remove redundant move/drag events
2878 // - Reset states if applicable
2882 final boolean insideSurface = x >= 0 && y >= 0 && x < getSurfaceWidth() && y < getSurfaceHeight();
2883 final Point movePositionP0 = pState1.getMovePosition(pID[0]);
2884 switch( eventType ) {
2885 case MouseEvent.EVENT_MOUSE_EXITED:
2886 if( pState1.dragging ) {
2887 // Drop mouse EXIT if dragging, i.e. due to exterior dragging outside of window.
2888 // NOTE-1: X11 produces the 'premature' EXIT, however it also produces 'EXIT' after exterior dragging!
2889 // NOTE-2: consumePointerEvent(MouseEvent) will synthesize a missing EXIT event!
2890 if(DEBUG_MOUSE_EVENT) {
2891 System.err.println("doPointerEvent: drop "+MouseEvent.getEventTypeString(eventType)+" due to dragging: "+pState1);
2895 if( null != movePositionP0 ) {
2896 if( x==-1 && y==-1 ) {
2897 x = movePositionP0.getX();
2898 y = movePositionP0.getY();
2900 movePositionP0.set(0, 0);
2902 // Fall through intended!
2904 case MouseEvent.EVENT_MOUSE_ENTERED:
2905 if( eventType == MouseEvent.EVENT_MOUSE_ENTERED ) {
2906 pState1.insideSurface = true;
2907 pState1.exitSent = false;
2909 pState1.insideSurface = false;
2910 pState1.exitSent = true;
2912 pState1.clearButton();
2913 if( pTypes[0] != PointerType.Mouse ) {
2914 // Drop !MOUSE ENTER/EXIT Events - Safeguard for non compliant implementations only.
2915 if(DEBUG_MOUSE_EVENT) {
2916 System.err.println("doPointerEvent: drop "+MouseEvent.getEventTypeString(eventType)+" due to !Mouse but "+pTypes[0]+": "+pState1);
2920 // clip coordinates to window dimension
2921 x = Math.min(Math.max(x, 0), getSurfaceWidth()-1);
2922 y = Math.min(Math.max(y, 0), getSurfaceHeight()-1);
2925 case MouseEvent.EVENT_MOUSE_MOVED:
2926 case MouseEvent.EVENT_MOUSE_DRAGGED:
2927 if( null != movePositionP0 ) {
2928 if( movePositionP0.getX() == x && movePositionP0.getY() == y ) {
2929 // Drop same position
2930 if(DEBUG_MOUSE_EVENT) {
2931 System.err.println("doPointerEvent: drop "+MouseEvent.getEventTypeString(eventType)+" w/ same position: "+movePositionP0+", "+pState1);
2935 movePositionP0.set(x, y);
2938 // Fall through intended !
2941 if( pState1.insideSurface != insideSurface ) {
2943 pState1.insideSurface = insideSurface;
2944 if( insideSurface ) {
2945 pState1.exitSent = false;
2947 pState1.clearButton();
2952 // Drop exterior events if not dragging pointer and not EXIT event
2953 // Safeguard for non compliant implementations!
2955 if( !pState1.dragging && !insideSurface && MouseEvent.EVENT_MOUSE_EXITED != eventType ) {
2956 if(DEBUG_MOUSE_EVENT) {
2957 System.err.println("doPointerEvent: drop: "+MouseEvent.getEventTypeString(eventType)+
2958 ", mod "+modifiers+", pos "+x+"/"+y+", button "+button+", lastMousePosition: "+movePositionP0+", insideWindow "+insideSurface+", "+pState1);
2960 return; // .. invalid ..
2962 if(DEBUG_MOUSE_EVENT) {
2963 System.err.println("doPointerEvent: enqueue "+enqueue+", wait "+wait+", "+MouseEvent.getEventTypeString(eventType)+
2964 ", mod "+modifiers+", pos "+x+"/"+y+", button "+button+", lastMousePosition: "+movePositionP0+", "+pState1);
2967 final int buttonMask = InputEvent.getButtonMask(button);
2968 modifiers |= buttonMask; // Always add current button to modifier mask (Bug 571)
2969 modifiers |= pState1.buttonPressedMask; // Always add currently pressed mouse buttons to modifier mask
2971 if( isPointerConfined() ) {
2972 modifiers |= InputEvent.CONFINED_MASK;
2974 if( !isPointerVisible() ) {
2975 modifiers |= InputEvent.INVISIBLE_MASK;
2982 // - Determine CLICK COUNT
2983 // - Ignore sent CLICKED
2984 // - Track buttonPressed incl. buttonPressedMask
2985 // - Synthesize DRAGGED event (from MOVED if pointer is pressed)
2988 switch( eventType ) {
2989 case MouseEvent.EVENT_MOUSE_CLICKED:
2993 case MouseEvent.EVENT_MOUSE_PRESSED:
2994 if( 0 >= pPressure[0] ) {
2995 pPressure[0] = maxPressure;
2997 pState1.buttonPressedMask |= buttonMask;
2999 if( when - pState1.lastButtonPressTime < MouseEvent.getClickTimeout() ) {
3000 pState1.lastButtonClickCount++;
3002 pState1.lastButtonClickCount=(short)1;
3004 pState1.lastButtonPressTime = when;
3005 pState1.buttonPressed = button;
3006 e = new MouseEvent(eventType, this, when, modifiers, pTypes, pID,
3007 pX, pY, pPressure, maxPressure, button, pState1.lastButtonClickCount, rotationXYZ, rotationScale);
3009 e = new MouseEvent(eventType, this, when, modifiers, pTypes, pID,
3010 pX, pY, pPressure, maxPressure, button, (short)1, rotationXYZ, rotationScale);
3013 case MouseEvent.EVENT_MOUSE_RELEASED:
3014 pState1.buttonPressedMask &= ~buttonMask;
3016 e = new MouseEvent(eventType, this, when, modifiers, pTypes, pID,
3017 pX, pY, pPressure, maxPressure, button, pState1.lastButtonClickCount, rotationXYZ, rotationScale);
3018 if( when - pState1.lastButtonPressTime >= MouseEvent.getClickTimeout() ) {
3019 pState1.lastButtonClickCount = (short)0;
3020 pState1.lastButtonPressTime = 0;
3022 pState1.buttonPressed = 0;
3023 pState1.dragging = false;
3025 e = new MouseEvent(eventType, this, when, modifiers, pTypes, pID,
3026 pX, pY, pPressure, maxPressure, button, (short)1, rotationXYZ, rotationScale);
3027 if( 0 == pState1.buttonPressedMask ) {
3028 pState1.clearButton();