diff options
Diffstat (limited to 'src/newt')
16 files changed, 748 insertions, 434 deletions
diff --git a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java index a71c6106d..413dd2fe9 100644 --- a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java +++ b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java @@ -136,20 +136,32 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto public boolean isApplet() { return jawtWindow.isApplet(); } + + boolean isParent() { + return null!=newtChild && jawtWindow == newtChild.getParent(); + } + + boolean isFullscreen() { + return null != newtChild && newtChild.isFullscreen(); + } class FocusAction implements Window.FocusRunnable { public boolean run() { + final boolean isParent = isParent(); + final boolean isFullscreen = isFullscreen(); if(DEBUG) { - System.err.println("NewtCanvasAWT.FocusAction: "+Display.getThreadName()+", isOnscreen "+isOnscreen+", hasFocus "+hasFocus()); + System.err.println("NewtCanvasAWT.FocusAction: "+Display.getThreadName()+", isOnscreen "+isOnscreen+", hasFocus "+hasFocus()+", isParent "+isParent+", isFS "+isFullscreen); } - // Newt-EDT -> AWT-EDT may freeze Window's native peer requestFocus. - if(!hasFocus()) { - // Acquire the AWT focus 1st for proper AWT traversal - NewtCanvasAWT.super.requestFocus(); - } - if(isOnscreen) { - // Remove the AWT focus in favor of the native NEWT focus - KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner(); + if(isParent && !isFullscreen) { + // Newt-EDT -> AWT-EDT may freeze Window's native peer requestFocus. + if(!hasFocus()) { + // Acquire the AWT focus 1st for proper AWT traversal + NewtCanvasAWT.super.requestFocus(); + } + if(isOnscreen) { + // Remove the AWT focus in favor of the native NEWT focus + KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner(); + } } return false; // NEWT shall proceed requesting the native focus } @@ -159,7 +171,9 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto WindowListener clearAWTMenusOnNewtFocus = new WindowAdapter() { @Override public void windowGainedFocus(WindowEvent arg0) { - MenuSelectionManager.defaultManager().clearSelectedPath(); + if( isParent() && !isFullscreen() ) { + MenuSelectionManager.defaultManager().clearSelectedPath(); + } } }; @@ -167,10 +181,14 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto boolean suppress = false; public void keyPressed(KeyEvent e) { - handleKey(e, false); + if( isParent() && !isFullscreen() ) { + handleKey(e, false); + } } public void keyReleased(KeyEvent e) { - handleKey(e, true); + if( isParent() && !isFullscreen() ) { + handleKey(e, true); + } } public void keyTyped(KeyEvent e) { if(suppress) { @@ -217,21 +235,25 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto public void propertyChange(PropertyChangeEvent evt) { final Object oldF = evt.getOldValue(); final Object newF = evt.getNewValue(); + final boolean isParent = isParent(); + final boolean isFullscreen = isFullscreen(); if(DEBUG) { - System.err.println("NewtCanvasAWT.FocusProperty: "+evt.getPropertyName()+", src "+evt.getSource()+", "+oldF+" -> "+newF); + System.err.println("NewtCanvasAWT.FocusProperty: "+evt.getPropertyName()+", src "+evt.getSource()+", "+oldF+" -> "+newF+", isParent "+isParent+", isFS "+isFullscreen); } - if(oldF == NewtCanvasAWT.this && newF == null) { - // focus traversal to NEWT - NOP - if(DEBUG) { - System.err.println("NewtCanvasAWT.FocusProperty: NEWT focus traversal"); - } - } else if(null != newF && newF != NewtCanvasAWT.this) { - // focus traversal to another AWT component - if(DEBUG) { - System.err.println("NewtCanvasAWT.FocusProperty: lost focus - clear focus"); - } - if(newtChild.getDelegatedWindow() instanceof DriverClearFocus) { - ((DriverClearFocus)newtChild.getDelegatedWindow()).clearFocus(); + if(isParent && !isFullscreen) { + if(oldF == NewtCanvasAWT.this && newF == null) { + // focus traversal to NEWT - NOP + if(DEBUG) { + System.err.println("NewtCanvasAWT.FocusProperty: NEWT focus traversal"); + } + } else if(null != newF && newF != NewtCanvasAWT.this) { + // focus traversal to another AWT component + if(DEBUG) { + System.err.println("NewtCanvasAWT.FocusProperty: lost focus - clear focus"); + } + if(newtChild.getDelegatedWindow() instanceof DriverClearFocus) { + ((DriverClearFocus)newtChild.getDelegatedWindow()).clearFocus(); + } } } } @@ -294,7 +316,7 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto throw new InternalError("XXX"); } isOnscreen = jawtWindow.getGraphicsConfiguration().getChosenCapabilities().isOnscreen(); - awtAdapter = new AWTParentWindowAdapter(newtChild).addTo(this); + awtAdapter = new AWTParentWindowAdapter(jawtWindow, newtChild).addTo(this); newtChild.addWindowListener(clearAWTMenusOnNewtFocus); newtChild.setFocusAction(focusAction); // enable AWT focus traversal newtChildCloseOp = newtChild.setDefaultCloseOperation(WindowClosingProtocol.DO_NOTHING_ON_CLOSE); diff --git a/src/newt/classes/jogamp/newt/WindowImpl.java b/src/newt/classes/jogamp/newt/WindowImpl.java index 6564857e4..29056ee04 100644 --- a/src/newt/classes/jogamp/newt/WindowImpl.java +++ b/src/newt/classes/jogamp/newt/WindowImpl.java @@ -78,6 +78,9 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer { public static final boolean DEBUG_TEST_REPARENT_INCOMPATIBLE = Debug.isPropertyDefined("newt.test.Window.reparent.incompatible", true); + /** Timeout of queued events (repaint and resize) */ + static final long QUEUED_EVENT_TO = 1200; // ms + private volatile long windowHandle = 0; // lifecycle critical private volatile boolean visible = false; // lifecycle critical private RecursiveLock windowLock = LockFactory.createRecursiveLock(); // Window instance wide lock @@ -90,16 +93,17 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private AbstractGraphicsConfiguration config = null; // control access due to delegation protected CapabilitiesImmutable capsRequested = null; protected CapabilitiesChooser capabilitiesChooser = null; // default null -> default - protected boolean fullscreen = false, hasFocus = false; - protected int width = 128, height = 128; // client-area size w/o insets, default: may be overwritten by user - protected int x = 64, y = 64; // client-area pos w/o insets - protected boolean autoPosition = true; // default: true (allow WM to choose if not set by user) - protected Insets insets = new Insets(); // insets of decoration (if top-level && decorated) + private boolean fullscreen = false, hasFocus = false; + private int width = 128, height = 128; // client-area size w/o insets, default: may be overwritten by user + private int x = 64, y = 64; // client-area pos w/o insets + private boolean autoPosition = true; // default: true (allow WM to choose top-level position, if not set by user) + private Insets insets = new Insets(); // insets of decoration (if top-level && decorated) - protected int nfs_width, nfs_height, nfs_x, nfs_y; // non fullscreen client-area size/pos w/o insets - protected String title = "Newt Window"; - protected boolean undecorated = false; - protected boolean alwaysOnTop = false; + private int nfs_width, nfs_height, nfs_x, nfs_y; // non fullscreen client-area size/pos w/o insets + private NativeWindow nfs_parent = null; // non fullscreen parent, in case explicit reparenting is performed (offscreen) + private String title = "Newt Window"; + private boolean undecorated = false; + private boolean alwaysOnTop = false; private boolean pointerVisible = true; private boolean pointerConfined = false; private LifecycleHook lifecycleHook = null; @@ -109,7 +113,6 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private ReparentActionRecreate reparentActionRecreate = new ReparentActionRecreate(); - private RequestFocusAction requestFocusAction = new RequestFocusAction(); private FocusRunnable focusAction = null; private KeyListener keyboardFocusHandler = null; @@ -122,8 +125,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private int mouseButtonPressed = 0; // current pressed mouse button number private long lastMousePressed = 0; // last time when a mouse button was pressed private int lastMouseClickCount = 0; // last mouse button click count - protected boolean mouseInWindow = false;// mouse entered window - is inside the window (may be synthetic) - protected Point lastMousePosition = new Point(); + private boolean mouseInWindow = false;// mouse entered window - is inside the window (may be synthetic) + private Point lastMousePosition = new Point(); private ArrayList<KeyListener> keyListeners = new ArrayList<KeyListener>(); @@ -276,10 +279,11 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if( null != parentWindow && NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindow.lockSurface() ) { throw new NativeWindowException("Parent surface lock: not ready: "+parentWindow); - } - if( ( 0>x || 0>y ) && null != parentWindow ) { - // min. child window position is 0/0 - x = 0; y = 0; + } + + // child window: position defaults to 0/0, no auto position, no negative position + if( null != parentWindow && ( autoPosition || 0>getX() || 0>getY() ) ) { + definePosition(0, 0); } try { if(validateParentWindowHandle()) { @@ -296,10 +300,15 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer confinePointerImpl(pointerConfined); if(waitForVisible(true, false)) { if(isFullscreen()) { - fullscreen = false; - FullScreenActionImpl fsa = new FullScreenActionImpl(true); - fsa.run(); + synchronized(fullScreenAction) { + fullscreen = false; // trigger a state change + fullScreenAction.init(true); + fullScreenAction.run(); + } } + // harmonize focus behavior for all platforms: focus on creation + requestFocusInt(isFullscreen() /* skipFocusAction */, true/* force */); + ((DisplayImpl) screen.getDisplay()).dispatchMessagesNative(); // status up2date } } } @@ -713,7 +722,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } if(!isNativeValid() && visible) { - if( 0<width*height ) { + if( 0<getWidth()*getHeight() ) { nativeWindowCreated = createNative(); madeVisible = nativeWindowCreated; } @@ -721,7 +730,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer WindowImpl.this.visible = true; } else if(WindowImpl.this.visible != visible) { if(isNativeValid()) { - setVisibleImpl(visible, x, y, width, height); + setVisibleImpl(visible, getX(), getY(), getWidth(), getHeight()); WindowImpl.this.waitForVisible(visible, true); madeVisible = visible; } @@ -742,7 +751,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } if(DEBUG_IMPLEMENTATION) { - System.err.println("Window setVisible: END ("+getThreadName()+") "+x+"/"+y+" "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible: "+WindowImpl.this.visible+", nativeWindowCreated: "+nativeWindowCreated+", madeVisible: "+madeVisible); + 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); } } finally { windowLock.unlock(); @@ -764,7 +773,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } public void setVisible(boolean visible) { if(DEBUG_IMPLEMENTATION) { - System.err.println("Window setVisible: START ("+getThreadName()+") "+x+"/"+y+" "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible: "+this.visible+" -> "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+(null!=parentWindow)); + 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)); Thread.dumpStack(); } runOnEDTIfAvail(true, new VisibleAction(visible)); @@ -775,10 +784,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer windowLock.lock(); try { int visibleAction = 0; // 1 invisible, 2 visible (create) - if ( !fullscreen && ( width != WindowImpl.this.width || WindowImpl.this.height != height ) ) { + if ( !fullscreen && ( getWidth() != width || getHeight() != height ) ) { recreate = isNativeValid() && !getGraphicsConfiguration().getChosenCapabilities().isOnscreen(); if(DEBUG_IMPLEMENTATION) { - System.err.println("Window setSize: START "+WindowImpl.this.width+"x"+WindowImpl.this.height+" -> "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible "+visible+", recreate "+recreate); + System.err.println("Window setSize: START "+getWidth()+"x"+getHeight()+" -> "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible "+visible+", recreate "+recreate); } if(recreate) { // will trigger visibleAction:=2 -> create if wasVisible @@ -789,21 +798,18 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } if ( isNativeValid() && 0>=width*height && visible ) { visibleAction=1; // invisible - WindowImpl.this.width = 0; - WindowImpl.this.height = 0; + defineSize(0, 0); } else if ( !isNativeValid() && 0<width*height && visible ) { visibleAction = 2; // visible (create) - WindowImpl.this.width = width; - WindowImpl.this.height = height; + defineSize(width, height); } else if ( isNativeValid() ) { // this width/height will be set by windowChanged, called by the native implementation - reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(0, isVisible())); + reconfigureWindowImpl(getX(), getY(), width, height, getReconfigureFlags(0, isVisible())); } else { - WindowImpl.this.width = width; - WindowImpl.this.height = height; + defineSize(width, height); } if(DEBUG_IMPLEMENTATION) { - System.err.println("Window setSize: END "+WindowImpl.this.width+"x"+WindowImpl.this.height+", visibleAction "+visibleAction); + System.err.println("Window setSize: END "+getWidth()+"x"+getHeight()+", visibleAction "+visibleAction); } switch(visibleAction) { case 1: setVisibleActionImpl(false); break; @@ -976,10 +982,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private void reparent() { // mirror pos/size so native change notification can get overwritten - int x = WindowImpl.this.x; - int y = WindowImpl.this.y; - int width = WindowImpl.this.width; - int height = WindowImpl.this.height; + int x = getX(); + int y = getY(); + int width = getWidth(); + int height = getHeight(); boolean wasVisible; windowLock.lock(); @@ -1122,10 +1128,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if( ACTION_NATIVE_CREATION_PENDING == reparentAction ) { // make size and position persistent for proper recreation - WindowImpl.this.x = x; - WindowImpl.this.y = y; - WindowImpl.this.width = width; - WindowImpl.this.height = height; + definePosition(x, y); + defineSize(width, height); return; } @@ -1174,7 +1178,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer ok = WindowImpl.this.waitForSize(width, height, false, TIMEOUT_NATIVEWINDOW); } if(ok) { - requestFocusInt(true); + requestFocusInt(false /* skipFocusAction */, true/* force */); display.dispatchMessagesNative(); // status up2date } } @@ -1183,10 +1187,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(!ok || !wasVisible) { // make size and position persistent manual, // since we don't have a WM feedback (invisible or recreation) - WindowImpl.this.x = x; - WindowImpl.this.y = y; - WindowImpl.this.width = width; - WindowImpl.this.height = height; + definePosition(x, y); + defineSize(width, height); } if(!ok) { @@ -1284,10 +1286,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // Change decoration on active window // Mirror pos/size so native change notification can get overwritten - final int x = WindowImpl.this.x; - final int y = WindowImpl.this.y; - final int width = WindowImpl.this.width; - final int height = WindowImpl.this.height; + final int x = getX(); + final int y = getY(); + final int width = getWidth(); + final int height = getHeight(); if( isNativeValid() ) { DisplayImpl display = (DisplayImpl) screen.getDisplay(); @@ -1333,10 +1335,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // Change decoration on active window // Mirror pos/size so native change notification can get overwritten - final int x = WindowImpl.this.x; - final int y = WindowImpl.this.y; - final int width = WindowImpl.this.width; - final int height = WindowImpl.this.height; + final int x = getX(); + final int y = getY(); + final int width = getWidth(); + final int height = getHeight(); if( isNativeValid() ) { DisplayImpl display = (DisplayImpl) screen.getDisplay(); @@ -1398,7 +1400,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(!setVal) { if(confine) { requestFocus(); - warpPointer(width/2, height/2); + warpPointer(getWidth()/2, getHeight()/2); } setVal = confinePointerImpl(confine); if(confine) { @@ -1445,6 +1447,27 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer return y; } + protected final boolean autoPosition() { return autoPosition; } + + /** Sets the position fields {@link #x} and {@link #y} to the given values and {@link #autoPosition} to false. */ + protected final void definePosition(int x, int y) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("definePosition: "+this.x+"/"+this.y+" -> "+x+"/"+y); + // Thread.dumpStack(); + } + autoPosition = false; + this.x = x; this.y = y; + } + + /** Sets the size fields {@link #width} and {@link #height} to the given values. */ + protected final void defineSize(int width, int height) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("defineSize: "+this.width+"x"+this.height+" -> "+width+"x"+height); + // Thread.dumpStack(); + } + this.width = width; this.height = height; + } + public final boolean isVisible() { return visible; } @@ -1519,8 +1542,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer "\n, ParentWindowHandle "+toHexString(parentWindowHandle)+" ("+(0!=getParentWindowHandle())+")"+ "\n, WindowHandle "+toHexString(getWindowHandle())+ "\n, SurfaceHandle "+toHexString(getSurfaceHandle())+ " (lockedExt window "+isWindowLockedByOtherThread()+", surface "+isSurfaceLockedByOtherThread()+")"+ - "\n, Pos "+getX()+"/"+getY()+", size "+getWidth()+"x"+getHeight()+ - "\n, Visible "+isVisible()+ + "\n, Pos "+getX()+"/"+getY()+" (auto "+autoPosition()+"), size "+getWidth()+"x"+getHeight()+ + "\n, Visible "+isVisible()+", focus "+hasFocus()+ "\n, Undecorated "+undecorated+" ("+isUndecorated()+")"+ "\n, AlwaysOnTop "+alwaysOnTop+", Fullscreen "+fullscreen+ "\n, WrappedWindow "+getWrappedWindow()+ @@ -1560,14 +1583,22 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer d.runOnEDTIfAvail(wait, task); } - private class RequestFocusAction implements Runnable { + private Runnable requestFocusAction = new Runnable() { public final void run() { if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.RequestFocusAction: ("+getThreadName()+"): "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); + System.err.println("Window.RequestFocusAction: force 0 - ("+getThreadName()+"): "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); } WindowImpl.this.requestFocusImpl(false); } - } + }; + private Runnable requestFocusActionForced = new Runnable() { + public final void run() { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.RequestFocusAction: force 1 - ("+getThreadName()+"): "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); + } + WindowImpl.this.requestFocusImpl(true); + } + }; public final boolean hasFocus() { return hasFocus; @@ -1578,14 +1609,23 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } public void requestFocus(boolean wait) { - if(isNativeValid() && !focusAction()) { - runOnEDTIfAvail(wait, requestFocusAction); + requestFocus(wait /* wait */, false /* skipFocusAction */, false /* force */); + } + + private void requestFocus(boolean wait, boolean skipFocusAction, boolean force) { + if( isNativeValid() && + ( force || !hasFocus() ) && + ( skipFocusAction || !focusAction() ) ) { + runOnEDTIfAvail(wait, force ? requestFocusActionForced : requestFocusAction); } } /** Internal request focus on current thread */ - private void requestFocusInt(boolean force) { - if(!focusAction()) { + private void requestFocusInt(boolean skipFocusAction, boolean force) { + if( skipFocusAction || !focusAction() ) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.RequestFocusInt: force "+force+" - ("+getThreadName()+"): "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); + } requestFocusImpl(force); } } @@ -1625,16 +1665,15 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer windowLock.lock(); try { if(DEBUG_IMPLEMENTATION) { - System.err.println("Window setPosition: "+WindowImpl.this.x+"/"+WindowImpl.this.y+" -> "+x+"/"+y+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)); + System.err.println("Window setPosition: "+getX()+"/"+getY()+" -> "+x+"/"+y+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)); } - if ( WindowImpl.this.x != x || WindowImpl.this.y != y ) { + if ( getX() != x || getY() != y ) { if(!fullscreen) { if(0!=windowHandle) { // this.x/this.y will be set by sizeChanged, triggered by windowing event system - reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(0, isVisible())); + reconfigureWindowImpl(x, y, getWidth(), getHeight(), getReconfigureFlags(0, isVisible())); } else { - WindowImpl.this.x = x; - WindowImpl.this.y = y; + definePosition(x, y); } } } @@ -1652,110 +1691,135 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer public void setTopLevelPosition(int x, int y) { setPosition(x + getInsets().getLeftWidth(), y + getInsets().getTopHeight()); } - + private class FullScreenActionImpl implements Runnable { boolean fullscreen; + boolean nativeFullscreenChange; - private FullScreenActionImpl (boolean fullscreen) { + private FullScreenActionImpl() { } + + public void init(boolean fullscreen) { this.fullscreen = fullscreen; - } + this.nativeFullscreenChange = isNativeValid() && isFullscreen() != fullscreen ; + } + public boolean nativeFullscreenChange() { return nativeFullscreenChange; } + public boolean nativeFullscreenOn() { return nativeFullscreenChange && fullscreen; } + public boolean nativeFullscreenOff() { return nativeFullscreenChange && !fullscreen; } public final void run() { windowLock.lock(); try { - if(WindowImpl.this.fullscreen != fullscreen) { - final boolean nativeFullscreenChange = isNativeValid() && - isFullscreen() != fullscreen ; - // set current state WindowImpl.this.fullscreen = fullscreen; - if( nativeFullscreenChange ) { - int x,y,w,h; + int x,y,w,h; + + if(fullscreen) { + nfs_x = getX(); + nfs_y = getY(); + nfs_width = getWidth(); + nfs_height = getHeight(); + x = screen.getX(); + y = screen.getY(); + w = screen.getWidth(); + h = screen.getHeight(); + } else { + x = nfs_x; + y = nfs_y; + w = nfs_width; + h = nfs_height; - if(fullscreen) { - nfs_x = WindowImpl.this.x; - nfs_y = WindowImpl.this.y; - nfs_width = WindowImpl.this.width; - nfs_height = WindowImpl.this.height; - x = screen.getX(); - y = screen.getY(); - w = screen.getWidth(); - h = screen.getHeight(); - } else { - x = nfs_x; - y = nfs_y; - w = nfs_width; - h = nfs_height; - - if(null!=parentWindow) { - // reset position to 0/0 within parent space - x = 0; - y = 0; - - // refit if size is bigger than parent - if( w > parentWindow.getWidth() ) { - w = parentWindow.getWidth(); - } - if( h > parentWindow.getHeight() ) { - h = parentWindow.getHeight(); - } + if(null!=parentWindow) { + // reset position to 0/0 within parent space + x = 0; + y = 0; + + // refit if size is bigger than parent + if( w > parentWindow.getWidth() ) { + w = parentWindow.getWidth(); + } + if( h > parentWindow.getHeight() ) { + h = parentWindow.getHeight(); } } - if(DEBUG_IMPLEMENTATION) { - System.err.println("Window fs: "+fullscreen+" "+x+"/"+y+" "+w+"x"+h+", "+isUndecorated()+", "+screen); - } + } + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window fs: "+fullscreen+" "+x+"/"+y+" "+w+"x"+h+", "+isUndecorated()+", "+screen); + } - DisplayImpl display = (DisplayImpl) screen.getDisplay(); - display.dispatchMessagesNative(); // status up2date - boolean wasVisible = isVisible(); - - // Lock parentWindow only during reparenting (attempt) - final NativeWindow parentWindowLocked; - if( null != parentWindow ) { - parentWindowLocked = parentWindow; - if( NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindowLocked.lockSurface() ) { - throw new NativeWindowException("Parent surface lock: not ready: "+parentWindow); - } - } else { - parentWindowLocked = null; + DisplayImpl display = (DisplayImpl) screen.getDisplay(); + display.dispatchMessagesNative(); // status up2date + boolean wasVisible = isVisible(); + + // Lock parentWindow only during reparenting (attempt) + final NativeWindow parentWindowLocked; + if( null != parentWindow ) { + parentWindowLocked = parentWindow; + if( NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindowLocked.lockSurface() ) { + throw new NativeWindowException("Parent surface lock: not ready: "+parentWindow); } - try { - reconfigureWindowImpl(x, y, w, h, - getReconfigureFlags( ( ( null != parentWindowLocked ) ? FLAG_CHANGE_PARENTING : 0 ) | - FLAG_CHANGE_FULLSCREEN | FLAG_CHANGE_DECORATION, wasVisible) ); - } finally { - if(null!=parentWindowLocked) { - parentWindowLocked.unlockSurface(); - } + } else { + parentWindowLocked = null; + } + try { + reconfigureWindowImpl(x, y, w, h, + getReconfigureFlags( ( ( null != parentWindowLocked ) ? FLAG_CHANGE_PARENTING : 0 ) | + FLAG_CHANGE_FULLSCREEN | FLAG_CHANGE_DECORATION, wasVisible) ); + } finally { + if(null!=parentWindowLocked) { + parentWindowLocked.unlockSurface(); } - display.dispatchMessagesNative(); // status up2date + } + display.dispatchMessagesNative(); // status up2date + + if(wasVisible) { + setVisibleImpl(true, x, y, w, h); + WindowImpl.this.waitForVisible(true, false); + display.dispatchMessagesNative(); // status up2date + WindowImpl.this.waitForSize(w, h, false, TIMEOUT_NATIVEWINDOW); + display.dispatchMessagesNative(); // status up2date - if(wasVisible) { - setVisibleImpl(true, x, y, w, h); - WindowImpl.this.waitForVisible(true, false); - display.dispatchMessagesNative(); // status up2date - WindowImpl.this.waitForSize(w, h, false, TIMEOUT_NATIVEWINDOW); - display.dispatchMessagesNative(); // status up2date - requestFocusInt(true); - display.dispatchMessagesNative(); // status up2date - - if(DEBUG_IMPLEMENTATION) { - System.err.println("Window fs done: " + WindowImpl.this); - } + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window fs done: " + WindowImpl.this); } } - } } finally { windowLock.unlock(); } sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener } } + private FullScreenActionImpl fullScreenAction = new FullScreenActionImpl(); public boolean setFullscreen(boolean fullscreen) { - runOnEDTIfAvail(true, new FullScreenActionImpl(fullscreen)); - return this.fullscreen; + synchronized(fullScreenAction) { + fullScreenAction.init(fullscreen); + if( fullScreenAction.nativeFullscreenChange() ) { + if(fullScreenAction.nativeFullscreenOn() && + isOffscreenInstance(WindowImpl.this, parentWindow)) { + // enable fullscreen on offscreen instance + if(null != parentWindow) { + nfs_parent = parentWindow; + reparentWindow(null, true); + } else { + throw new InternalError("Offscreen instance w/o parent unhandled"); + } + } + + runOnEDTIfAvail(true, fullScreenAction); + + if(fullScreenAction.nativeFullscreenOff() && null != nfs_parent) { + // disable fullscreen on offscreen instance + reparentWindow(nfs_parent, true); + nfs_parent = null; + } + + if(isVisible()) { + requestFocus(true /* wait */, this.fullscreen /* skipFocusAction */, true /* force */); + } + } + return this.fullscreen; + } } private class ScreenModeListenerImpl implements ScreenModeListener { @@ -1841,11 +1905,12 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // make sure only one repaint event is queued if(!repaintQueued) { repaintQueued=true; + final boolean discardTO = QUEUED_EVENT_TO <= System.currentTimeMillis()-e.getWhen(); if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.consumeEvent: queued "+e); + System.err.println("Window.consumeEvent: "+Thread.currentThread().getName()+" - queued "+e+", discard-to "+discardTO); // Thread.dumpStack(); - } - return false; + } + return discardTO; // discardTO:=true -> consumed } return true; } @@ -1856,11 +1921,12 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer case WindowEvent.EVENT_WINDOW_RESIZED: // queue event in case window is locked, ie in operation if( isWindowLocked() ) { + final boolean discardTO = QUEUED_EVENT_TO <= System.currentTimeMillis()-e.getWhen(); if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.consumeEvent: queued "+e); + System.err.println("Window.consumeEvent: "+Thread.currentThread().getName()+" - queued "+e+", discard-to "+discardTO); // Thread.dumpStack(); } - return false; + return discardTO; // discardTO:=true -> consumed } break; default: @@ -1918,11 +1984,11 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer y = lastMousePosition.getY(); } // clip coordinates to window dimension - x = Math.min(Math.max(x, 0), width-1); - y = Math.min(Math.max(y, 0), height-1); + x = Math.min(Math.max(x, 0), getWidth()-1); + y = Math.min(Math.max(y, 0), getHeight()-1); mouseInWindow = eventType == MouseEvent.EVENT_MOUSE_ENTERED; } - if(x<0||y<0||x>=width||y>=height) { + if(x<0||y<0||x>=getWidth()||y>=getHeight()) { return; // .. invalid .. } if(DEBUG_MOUSE_EVENT) { @@ -2308,15 +2374,14 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer /** Triggered by implementation's WM events to update the client-area size w/o insets/decorations. */ protected void sizeChanged(boolean defer, int newWidth, int newHeight, boolean force) { - if(force || width != newWidth || height != newHeight) { + if(force || getWidth() != newWidth || getHeight() != newHeight) { if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.sizeChanged: ("+getThreadName()+"): (defer: "+defer+") force "+force+", "+width+"x"+height+" -> "+newWidth+"x"+newHeight+" - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); + System.err.println("Window.sizeChanged: ("+getThreadName()+"): (defer: "+defer+") force "+force+", "+getWidth()+"x"+getHeight()+" -> "+newWidth+"x"+newHeight+" - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); } if(0>newWidth || 0>newHeight) { throw new NativeWindowException("Illegal width or height "+newWidth+"x"+newHeight+" (must be >= 0)"); } - width = newWidth; - height = newHeight; + defineSize(newWidth, newHeight); if(isNativeValid()) { if(!defer) { sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); @@ -2353,18 +2418,18 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer /** Triggered by implementation's WM events to update the position. */ protected void positionChanged(boolean defer, int newX, int newY) { - autoPosition = false; - if ( x != newX || y != newY ) { + if ( getX() != newX || getY() != newY ) { if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.positionChanged: ("+getThreadName()+"): (defer: "+defer+") "+x+"/"+y+" -> "+newX+"/"+newY+" - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); + System.err.println("Window.positionChanged: ("+getThreadName()+"): (defer: "+defer+") "+getX()+"/"+getY()+" -> "+newX+"/"+newY+" - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); } - x = newX; - y = newY; + definePosition(newX, newY); if(!defer) { sendWindowEvent(WindowEvent.EVENT_WINDOW_MOVED); } else { enqueueWindowEvent(false, WindowEvent.EVENT_WINDOW_MOVED); } + } else { + autoPosition = false; // ensure it's off even w/ same position } } @@ -2419,8 +2484,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer * Triggered by implementation's WM events to update the content */ protected void windowRepaint(boolean defer, int x, int y, int width, int height) { - width = ( 0 >= width ) ? this.width : width; - height = ( 0 >= height ) ? this.height : height; + width = ( 0 >= width ) ? getWidth() : width; + height = ( 0 >= height ) ? getHeight() : height; if(DEBUG_IMPLEMENTATION) { System.err.println("Window.windowRepaint "+getThreadName()+" (defer: "+defer+") "+x+"/"+y+" "+width+"x"+height); } diff --git a/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java b/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java index 8e9c028d4..ce8ed7c49 100644 --- a/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java +++ b/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java @@ -30,6 +30,8 @@ package jogamp.newt.awt.event; import java.awt.KeyboardFocusManager; +import javax.media.nativewindow.NativeWindow; + import jogamp.newt.driver.DriverUpdatePosition; import com.jogamp.newt.event.awt.AWTAdapter; @@ -43,8 +45,11 @@ public class AWTParentWindowAdapter extends AWTWindowAdapter implements java.awt.event.HierarchyListener { - public AWTParentWindowAdapter(com.jogamp.newt.Window downstream) { + NativeWindow downstreamParent; + + public AWTParentWindowAdapter(NativeWindow downstreamParent, com.jogamp.newt.Window downstream) { super(downstream); + this.downstreamParent = downstreamParent; } public AWTAdapter addTo(java.awt.Component awtComponent) { @@ -61,13 +66,17 @@ public class AWTParentWindowAdapter // forward focus to NEWT child final com.jogamp.newt.Window newtChild = getNewtWindow(); final boolean isOnscreen = newtChild.isNativeValid() && newtChild.getGraphicsConfiguration().getChosenCapabilities().isOnscreen(); + final boolean isParent = downstreamParent == newtChild.getParent(); + final boolean isFullscreen = newtChild.isFullscreen(); if(DEBUG_IMPLEMENTATION) { - System.err.println("AWT: focusGained: onscreen "+ isOnscreen+", "+e); + System.err.println("AWT: focusGained: onscreen "+ isOnscreen+", "+e+", isParent: "+isParent+", isFS "+isFullscreen); } - if(isOnscreen) { - KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner(); + if(isParent) { + if(isOnscreen && !isFullscreen) { + KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner(); + } + newtChild.requestFocus(false); } - newtChild.requestFocus(false); } public void focusLost(java.awt.event.FocusEvent e) { diff --git a/src/newt/classes/jogamp/newt/driver/android/AndroidWindow.java b/src/newt/classes/jogamp/newt/driver/android/AndroidWindow.java index 6348cf19e..52c789a4d 100644 --- a/src/newt/classes/jogamp/newt/driver/android/AndroidWindow.java +++ b/src/newt/classes/jogamp/newt/driver/android/AndroidWindow.java @@ -177,19 +177,18 @@ public class AndroidWindow extends jogamp.newt.WindowImpl implements Callback2 { // sh.setType(SurfaceHolder.SURFACE_TYPE_NORMAL); // default size -> TBD ! - this.width = 0; - this.height = 0; + defineSize(0, 0); } public SurfaceView getAndroidView() { return androidView; } public void setAndroidWindow(android.view.Window window) { - System.err.println("setandroidWindow: "+window+", "+width+"x"+height); + System.err.println("setandroidWindow: "+window+", "+getWidth()+"x"+getHeight()); androidWindow = window; androidWindowConfigurationPreCreate(); - if(width>0 && height>0 && !isFullscreen()) { + if(getWidth()>0 && getHeight()>0 && !isFullscreen()) { if(null != androidWindow) { - androidWindow.setLayout(width, height); + androidWindow.setLayout(getWidth(), getHeight()); } } } @@ -205,7 +204,7 @@ public class AndroidWindow extends jogamp.newt.WindowImpl implements Callback2 { @Override protected void createNativeImpl() { Log.d(MD.TAG, "createNativeImpl 0 - surfaceHandle 0x"+Long.toHexString(surfaceHandle)+ - ", format "+format+", "+x+"/"+y+" "+width+"x"+height+" - "+Thread.currentThread().getName()); + ", format "+format+", "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()+" - "+Thread.currentThread().getName()); Thread.dumpStack(); if(0!=getParentWindowHandle()) { throw new NativeWindowException("Window parenting not supported (yet)"); @@ -328,7 +327,7 @@ public class AndroidWindow extends jogamp.newt.WindowImpl implements Callback2 { // public void surfaceCreated(SurfaceHolder holder) { - Log.d(MD.TAG, "surfaceCreated: "+x+"/"+y+" "+width+"x"+height); + Log.d(MD.TAG, "surfaceCreated: "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()); } public void surfaceChanged(SurfaceHolder aHolder, int aFormat, int aWidth, int aHeight) { @@ -347,9 +346,7 @@ public class AndroidWindow extends jogamp.newt.WindowImpl implements Callback2 { getScreen().getCurrentScreenMode(); // if ScreenMode changed .. trigger ScreenMode event } - if(0>x || 0>y) { - x = 0; - y = 0; + if(0>getX() || 0>getY()) { positionChanged(false, 0, 0); } @@ -365,7 +362,7 @@ public class AndroidWindow extends jogamp.newt.WindowImpl implements Callback2 { Log.d(MD.TAG, "surfaceRealized: isValid: "+surface.isValid()+ ", new surfaceHandle 0x"+Long.toHexString(surfaceHandle)+", format: "+format+ - ", "+x+"/"+y+" "+nWidth+"x"+nHeight+", visible: "+isVisible()); + ", "+getX()+"/"+getY()+" "+nWidth+"x"+nHeight+", visible: "+isVisible()); if(isVisible()) { setVisible(true); diff --git a/src/newt/classes/jogamp/newt/driver/awt/AWTWindow.java b/src/newt/classes/jogamp/newt/driver/awt/AWTWindow.java index e9e3ec0ba..2c921e7b2 100644 --- a/src/newt/classes/jogamp/newt/driver/awt/AWTWindow.java +++ b/src/newt/classes/jogamp/newt/driver/awt/AWTWindow.java @@ -59,13 +59,12 @@ public class AWTWindow extends WindowImpl { this(null); } - public static Class[] getCustomConstructorArgumentTypes() { - return new Class[] { Container.class } ; + public static Class<?>[] getCustomConstructorArgumentTypes() { + return new Class<?>[] { Container.class } ; } public AWTWindow(Container container) { super(); - title = "AWT NewtWindow"; this.container = container; if(container instanceof Frame) { frame = (Frame) container; @@ -99,10 +98,8 @@ public class AWTWindow extends WindowImpl { owningFrame=true; } else { owningFrame=false; - width = container.getWidth(); - height = container.getHeight(); - x = container.getX(); - y = container.getY(); + defineSize(container.getWidth(), container.getHeight()); + definePosition(container.getX(), container.getY()); } if(null!=frame) { frame.setTitle(getTitle()); @@ -117,11 +114,11 @@ public class AWTWindow extends WindowImpl { // canvas.addComponentListener(listener); container.add(canvas, BorderLayout.CENTER); - container.setSize(width, height); - container.setLocation(x, y); + container.setSize(getWidth(), getHeight()); + container.setLocation(getX(), getY()); new AWTWindowAdapter(this).addTo(container); // fwd all AWT Window events to here - reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(FLAG_CHANGE_VISIBILITY | FLAG_CHANGE_DECORATION, true)); + reconfigureWindowImpl(getX(), getY(), getWidth(), getHeight(), getReconfigureFlags(FLAG_CHANGE_VISIBILITY | FLAG_CHANGE_DECORATION, true)); // throws exception if failed .. setWindowHandle(1); // just a marker .. @@ -221,15 +218,13 @@ public class AWTWindow extends WindowImpl { @Override public void windowMoved(com.jogamp.newt.event.WindowEvent e) { if(null!=container) { - x = container.getX(); - y = container.getY(); + definePosition(container.getX(), container.getY()); } } @Override public void windowResized(com.jogamp.newt.event.WindowEvent e) { if(null!=canvas) { - width = canvas.getWidth(); - height = canvas.getHeight(); + defineSize(canvas.getWidth(), canvas.getHeight()); } } } diff --git a/src/newt/classes/jogamp/newt/driver/broadcom/egl/Window.java b/src/newt/classes/jogamp/newt/driver/broadcom/egl/Window.java index 6f66eedd3..bd63f83ab 100644 --- a/src/newt/classes/jogamp/newt/driver/broadcom/egl/Window.java +++ b/src/newt/classes/jogamp/newt/driver/broadcom/egl/Window.java @@ -35,7 +35,6 @@ package jogamp.newt.driver.broadcom.egl; import jogamp.opengl.egl.*; import javax.media.nativewindow.*; -import javax.media.nativewindow.awt.AWTGraphicsConfiguration; import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.Point; import javax.media.opengl.GLCapabilitiesImmutable; @@ -62,7 +61,7 @@ public class Window extends jogamp.newt.WindowImpl { setGraphicsConfiguration(cfg); setSizeImpl(getScreen().getWidth(), getScreen().getHeight()); - setWindowHandle(realizeWindow(true, width, height)); + setWindowHandle(realizeWindow(true, getWidth(), getHeight())); if (0 == getWindowHandle()) { throw new NativeWindowException("Error native Window Handle is null"); } @@ -81,8 +80,7 @@ public class Window extends jogamp.newt.WindowImpl { // n/a in BroadcomEGL System.err.println("BCEGL Window.setSizeImpl n/a in BroadcomEGL with realized window"); } else { - this.width = width; - this.height = height; + defineSize(width, height); } } @@ -101,8 +99,7 @@ public class Window extends jogamp.newt.WindowImpl { // n/a in BroadcomEGL System.err.println("BCEGL Window.setSizeImpl n/a in BroadcomEGL with realized window"); } else { - this.width=(width>0)?width:this.width; - this.height=(height>0)?height:this.height; + defineSize((width>0)?width:getWidth(), (height>0)?height:getHeight()); } } if(x>=0 || y>=0) { @@ -152,8 +149,7 @@ public class Window extends jogamp.newt.WindowImpl { } private void windowCreated(int cfgID, int width, int height) { - this.width = width; - this.height = height; + defineSize(width, height); GLCapabilitiesImmutable capsReq = (GLCapabilitiesImmutable) getGraphicsConfiguration().getRequestedCapabilities(); final AbstractGraphicsConfiguration cfg = EGLGraphicsConfiguration.create(capsReq, getScreen().getGraphicsScreen(), cfgID); if (null == cfg) { diff --git a/src/newt/classes/jogamp/newt/driver/intel/gdl/Window.java b/src/newt/classes/jogamp/newt/driver/intel/gdl/Window.java index 873d0a0c1..09e0e3016 100644 --- a/src/newt/classes/jogamp/newt/driver/intel/gdl/Window.java +++ b/src/newt/classes/jogamp/newt/driver/intel/gdl/Window.java @@ -64,7 +64,7 @@ public class Window extends jogamp.newt.WindowImpl { synchronized(Window.class) { setWindowHandle(nextWindowHandle++); // just a marker - surfaceHandle = CreateSurface(aDevice.getHandle(), getScreen().getWidth(), getScreen().getHeight(), x, y, width, height); + surfaceHandle = CreateSurface(aDevice.getHandle(), getScreen().getWidth(), getScreen().getHeight(), getX(), getY(), getWidth(), getHeight()); if (surfaceHandle == 0) { throw new NativeWindowException("Error creating window"); } @@ -138,10 +138,8 @@ public class Window extends jogamp.newt.WindowImpl { private native void SetBounds0(long surfaceHandle, int scrn_width, int scrn_height, int x, int y, int width, int height); private void updateBounds(int x, int y, int width, int height) { - this.x = x; - this.y = y; - this.width = width; - this.height = height; + definePosition(x, y); + defineSize(width, height); } private long surfaceHandle; diff --git a/src/newt/classes/jogamp/newt/driver/kd/KDWindow.java b/src/newt/classes/jogamp/newt/driver/kd/KDWindow.java index 92f8251bc..8ae0b6587 100644 --- a/src/newt/classes/jogamp/newt/driver/kd/KDWindow.java +++ b/src/newt/classes/jogamp/newt/driver/kd/KDWindow.java @@ -34,7 +34,6 @@ package jogamp.newt.driver.kd; import jogamp.newt.*; -import jogamp.newt.driver.intel.gdl.Display; import jogamp.opengl.egl.*; import javax.media.nativewindow.*; import javax.media.nativewindow.util.Insets; @@ -102,8 +101,8 @@ public class KDWindow extends WindowImpl { } // int _x=(x>=0)?x:this.x; // int _y=(x>=0)?y:this.y; - width=(width>0)?width:this.width; - height=(height>0)?height:this.height; + width=(width>0)?width:getWidth(); + height=(height>0)?height:getHeight(); if(width>0 || height>0) { setSize0(eglWindowHandle, width, height); } @@ -145,8 +144,8 @@ public class KDWindow extends WindowImpl { @Override protected void sizeChanged(boolean defer, int newWidth, int newHeight, boolean force) { - if(fullscreen) { - ((KDScreen)getScreen()).sizeChanged(width, height); + if(isFullscreen()) { + ((KDScreen)getScreen()).sizeChanged(getWidth(), getHeight()); } super.sizeChanged(defer, newWidth, newHeight, force); } diff --git a/src/newt/classes/jogamp/newt/driver/macosx/MacScreen.java b/src/newt/classes/jogamp/newt/driver/macosx/MacScreen.java index 3204982be..b9c725fd4 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/MacScreen.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/MacScreen.java @@ -38,16 +38,24 @@ import java.util.List; import javax.media.nativewindow.DefaultGraphicsScreen; import javax.media.nativewindow.util.Dimension; +import javax.media.nativewindow.util.DimensionImmutable; import javax.media.nativewindow.util.Point; import jogamp.newt.ScreenImpl; +import com.jogamp.common.util.IntObjectHashMap; import com.jogamp.newt.ScreenMode; import com.jogamp.newt.util.ScreenModeUtil; public class MacScreen extends ScreenImpl { + + // caching native CGDisplayScreenSize() results, since it's ridiculous slow (~6 ms each call) + private static IntObjectHashMap/*<int, DimensionImmutable>*/ scrnIdx2Dimension; + static { MacDisplay.initSingleton(); + scrnIdx2Dimension = new IntObjectHashMap(); + scrnIdx2Dimension.setKeyNotFoundValue(null); } public MacScreen() { @@ -63,7 +71,18 @@ public class MacScreen extends ScreenImpl { private static native int getHeightImpl0(int scrn_idx); private int[] getScreenModeIdx(int idx) { - int[] modeProps = getScreenMode0(screen_idx, idx); + // caching native CGDisplayScreenSize() results, since it's ridiculous slow (~6 ms each call) + DimensionImmutable dim = (DimensionImmutable) scrnIdx2Dimension.get(screen_idx); + if(null == dim) { + int[] res = getScreenSizeMM0(screen_idx); + if(null == res || 0 == res.length) { + return null; + } + dim = new Dimension(res[0], res[1]); + scrnIdx2Dimension.put(screen_idx, dim); + } + + int[] modeProps = getScreenMode0(screen_idx, idx, dim.getWidth(), dim.getHeight()); if (null == modeProps || 0 == modeProps.length) { return null; } @@ -117,7 +136,8 @@ public class MacScreen extends ScreenImpl { virtualSize.setWidth(getWidthImpl0(screen_idx)); virtualSize.setHeight(getHeightImpl0(screen_idx)); } - - private native int[] getScreenMode0(int screen_index, int mode_index); + + private native int[] getScreenSizeMM0(int screen_idx); + private native int[] getScreenMode0(int screen_index, int mode_index, int widthMM, int heightMM); private native boolean setScreenMode0(int screen_index, int mode_idx); } diff --git a/src/newt/classes/jogamp/newt/driver/macosx/MacWindow.java b/src/newt/classes/jogamp/newt/driver/macosx/MacWindow.java index 75a3cf6d5..c926d44ee 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/MacWindow.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/MacWindow.java @@ -67,7 +67,7 @@ public class MacWindow extends WindowImpl implements SurfaceChangeable, DriverCl throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); } setGraphicsConfiguration(cfg); - reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(FLAG_CHANGE_VISIBILITY, true)); + reconfigureWindowImpl(getX(), getY(), getWidth(), getHeight(), getReconfigureFlags(FLAG_CHANGE_VISIBILITY, true)); if (0 == getWindowHandle()) { throw new NativeWindowException("Error creating window"); } @@ -247,8 +247,8 @@ public class MacWindow extends WindowImpl implements SurfaceChangeable, DriverCl @Override protected void sizeChanged(boolean defer, int newWidth, int newHeight, boolean force) { - if(width != newWidth || height != newHeight) { - final Point p0S = getTopLevelLocationOnScreen(x, y); + if(getWidth() != newWidth || getHeight() != newHeight) { + final Point p0S = getTopLevelLocationOnScreen(getX(), getY()); setFrameTopLeftPoint0(getParentWindowHandle(), getWindowHandle(), p0S.getX(), p0S.getY()); } super.sizeChanged(defer, newWidth, newHeight, force); @@ -271,7 +271,7 @@ public class MacWindow extends WindowImpl implements SurfaceChangeable, DriverCl @Override protected boolean setPointerVisibleImpl(final boolean pointerVisible) { if( !isOffscreenInstance ) { - return setPointerVisible0(getWindowHandle(), pointerVisible); + return setPointerVisible0(getWindowHandle(), hasFocus(), pointerVisible); } // else may need offscreen solution ? FIXME return false; } @@ -296,10 +296,13 @@ public class MacWindow extends WindowImpl implements SurfaceChangeable, DriverCl // Note that we send the key char for the key code on this // platform -- we do not get any useful key codes out of the system final int keyCode2 = MacKeyUtil.validateKeyCode(keyCode, keyChar); - if(DEBUG_IMPLEMENTATION) System.err.println("MacWindow.sendKeyEvent "+Thread.currentThread().getName()+" char: 0x"+Integer.toHexString(keyChar)+", code 0x"+Integer.toHexString(keyCode)+" -> 0x"+Integer.toHexString(keyCode2)); - // only deliver keyChar on key Typed events, harmonizing platform behavior - keyChar = KeyEvent.EVENT_KEY_TYPED == eventType ? keyChar : (char)-1; - super.sendKeyEvent(eventType, modifiers, keyCode2, keyChar); + final boolean valid = validateKeyEvent(eventType, modifiers, keyCode); + if(DEBUG_IMPLEMENTATION) System.err.println("MacWindow.sendKeyEvent "+Thread.currentThread().getName()+" char: 0x"+Integer.toHexString(keyChar)+", code 0x"+Integer.toHexString(keyCode)+" -> 0x"+Integer.toHexString(keyCode2)+", valid "+valid); + if(valid) { + // only deliver keyChar on key Typed events, harmonizing platform behavior + keyChar = KeyEvent.EVENT_KEY_TYPED == eventType ? keyChar : (char)-1; + super.sendKeyEvent(eventType, modifiers, keyCode2, keyChar); + } } @Override @@ -307,12 +310,37 @@ public class MacWindow extends WindowImpl implements SurfaceChangeable, DriverCl // Note that we send the key char for the key code on this // platform -- we do not get any useful key codes out of the system final int keyCode2 = MacKeyUtil.validateKeyCode(keyCode, keyChar); - if(DEBUG_IMPLEMENTATION) System.err.println("MacWindow.enqueueKeyEvent "+Thread.currentThread().getName()+" char: 0x"+Integer.toHexString(keyChar)+", code 0x"+Integer.toHexString(keyCode)+" -> 0x"+Integer.toHexString(keyCode2)); - // only deliver keyChar on key Typed events, harmonizing platform behavior - keyChar = KeyEvent.EVENT_KEY_TYPED == eventType ? keyChar : (char)-1; - super.enqueueKeyEvent(wait, eventType, modifiers, keyCode2, keyChar); + final boolean valid = validateKeyEvent(eventType, modifiers, keyCode); + if(DEBUG_IMPLEMENTATION) System.err.println("MacWindow.enqueueKeyEvent "+Thread.currentThread().getName()+" char: 0x"+Integer.toHexString(keyChar)+", code 0x"+Integer.toHexString(keyCode)+" -> 0x"+Integer.toHexString(keyCode2)+", valid "+valid); + if(valid) { + // only deliver keyChar on key Typed events, harmonizing platform behavior + keyChar = KeyEvent.EVENT_KEY_TYPED == eventType ? keyChar : (char)-1; + super.enqueueKeyEvent(wait, eventType, modifiers, keyCode2, keyChar); + } } + private int keyDownModifiers = 0; + private int keyDownCode = 0; + + private boolean validateKeyEvent(int eventType, int modifiers, int keyCode) { + switch(eventType) { + case KeyEvent.EVENT_KEY_PRESSED: + keyDownModifiers = modifiers; + keyDownCode = keyCode; + return true; + case KeyEvent.EVENT_KEY_RELEASED: + return keyDownModifiers == modifiers && keyDownCode == keyCode; + case KeyEvent.EVENT_KEY_TYPED: + final boolean matchKeyDown = keyDownModifiers == modifiers && keyDownCode == keyCode; + keyDownModifiers = 0; + keyDownCode = 0; + return matchKeyDown; + default: + throw new NativeWindowException("Unexpected key event type " + eventType); + } + } + + //---------------------------------------------------------------------- // Internals only // @@ -381,7 +409,7 @@ public class MacWindow extends WindowImpl implements SurfaceChangeable, DriverCl private native void setFrameTopLeftPoint0(long parentWindowHandle, long window, int x, int y); private native void setAlwaysOnTop0(long window, boolean atop); private static native Object getLocationOnScreen0(long windowHandle, int src_x, int src_y); - private static native boolean setPointerVisible0(long windowHandle, boolean visible); + private static native boolean setPointerVisible0(long windowHandle, boolean hasFocus, boolean visible); private static native boolean confinePointer0(long windowHandle, boolean confine); private static native void warpPointer0(long windowHandle, int x, int y); diff --git a/src/newt/classes/jogamp/newt/driver/windows/WindowsWindow.java b/src/newt/classes/jogamp/newt/driver/windows/WindowsWindow.java index ff3bd5ef6..d14c47f5a 100644 --- a/src/newt/classes/jogamp/newt/driver/windows/WindowsWindow.java +++ b/src/newt/classes/jogamp/newt/driver/windows/WindowsWindow.java @@ -115,7 +115,7 @@ public class WindowsWindow extends WindowImpl { final int flags = getReconfigureFlags(0, true) & ( FLAG_IS_ALWAYSONTOP | FLAG_IS_UNDECORATED ) ; setWindowHandle(CreateWindow0(display.getHInstance(), display.getWindowClassName(), display.getWindowClassName(), - getParentWindowHandle(), x, y, width, height, autoPosition, flags)); + getParentWindowHandle(), getX(), getY(), getWidth(), getHeight(), autoPosition(), flags)); if (getWindowHandle() == 0) { throw new NativeWindowException("Error creating window"); } @@ -220,7 +220,7 @@ public class WindowsWindow extends WindowImpl { public void run() { final Point p0 = getLocationOnScreenImpl(0, 0); res[0] = Boolean.valueOf(confinePointer0(getWindowHandle(), confine, - p0.getX(), p0.getY(), p0.getX()+width, p0.getY()+height)); + p0.getX(), p0.getY(), p0.getX()+getWidth(), p0.getY()+getHeight())); } }); return res[0].booleanValue(); diff --git a/src/newt/classes/jogamp/newt/driver/x11/X11Window.java b/src/newt/classes/jogamp/newt/driver/x11/X11Window.java index 33b541c34..c975306b4 100644 --- a/src/newt/classes/jogamp/newt/driver/x11/X11Window.java +++ b/src/newt/classes/jogamp/newt/driver/x11/X11Window.java @@ -79,7 +79,7 @@ public class X11Window extends WindowImpl { setWindowHandle(CreateWindow0(getParentWindowHandle(), display.getEDTHandle(), screen.getIndex(), visualID, display.getJavaObjectAtom(), display.getWindowDeleteAtom(), - x, y, width, height, autoPosition, flags)); + getX(), getY(), getWidth(), getHeight(), autoPosition(), flags)); windowHandleClose = getWindowHandle(); if (0 == windowHandleClose) { throw new NativeWindowException("Error creating window"); diff --git a/src/newt/native/MacWindow.m b/src/newt/native/MacWindow.m index ddd59f0a1..6d96e01fb 100644 --- a/src/newt/native/MacWindow.m +++ b/src/newt/native/MacWindow.m @@ -44,6 +44,10 @@ #import <stdio.h> +#ifdef DBG_PERF + #include "timespec.h" +#endif + static const char * const ClazzNamePoint = "javax/media/nativewindow/util/Point"; static const char * const ClazzAnyCstrName = "<init>"; static const char * const ClazzNamePointCstrSignature = "(II)V"; @@ -315,11 +319,60 @@ static long GetDictionaryLong(CFDictionaryRef theDict, const void* key) /* * Class: jogamp_newt_driver_macosx_MacScreen + * Method: getScreenSizeMM0 + * Signature: (I)[I + */ +JNIEXPORT jintArray JNICALL Java_jogamp_newt_driver_macosx_MacScreen_getScreenSizeMM0 + (JNIEnv *env, jobject obj, jint scrn_idx) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + +#ifdef DBG_PERF + struct timespec t0, t1, td; + long td_ms; + timespec_now(&t0); +#endif + + NSScreen *screen = NewtScreen_getNSScreenByIndex((int)scrn_idx); +#ifdef DBG_PERF + timespec_now(&t1); timespec_subtract(&td, &t1, &t0); td_ms = timespec_milliseconds(&td); + fprintf(stderr, "MacScreen_getScreenSizeMM0.1: %ld ms\n", td_ms); fflush(NULL); +#endif + + CGDirectDisplayID display = NewtScreen_getCGDirectDisplayIDByNSScreen(screen); +#ifdef DBG_PERF + timespec_now(&t1); timespec_subtract(&td, &t1, &t0); td_ms = timespec_milliseconds(&td); + fprintf(stderr, "MacScreen_getScreenSizeMM0.2: %ld ms\n", td_ms); fflush(NULL); +#endif + + CGSize screenDim = CGDisplayScreenSize(display); +#ifdef DBG_PERF + timespec_now(&t1); timespec_subtract(&td, &t1, &t0); td_ms = timespec_milliseconds(&td); + fprintf(stderr, "MacScreen_getScreenSizeMM0.3: %ld ms\n", td_ms); fflush(NULL); +#endif + + jint prop[ 2 ]; + prop[0] = (jint) screenDim.width; + prop[1] = (jint) screenDim.height; + + jintArray properties = (*env)->NewIntArray(env, 2); + if (properties == NULL) { + NewtCommon_throwNewRuntimeException(env, "Could not allocate int array of size 2"); + } + (*env)->SetIntArrayRegion(env, properties, 0, 2, prop); + + [pool release]; + + return properties; +} + +/* + * Class: jogamp_newt_driver_macosx_MacScreen * Method: getScreenMode0 - * Signature: (II)[I + * Signature: (IIII)[I */ JNIEXPORT jintArray JNICALL Java_jogamp_newt_driver_macosx_MacScreen_getScreenMode0 - (JNIEnv *env, jobject obj, jint scrn_idx, jint mode_idx) + (JNIEnv *env, jobject obj, jint scrn_idx, jint mode_idx, jint widthMM, jint heightMM) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; @@ -360,7 +413,6 @@ JNIEXPORT jintArray JNICALL Java_jogamp_newt_driver_macosx_MacScreen_getScreenMo } // mode = CGDisplayModeRetain(mode); // 10.6 on CGDisplayModeRef - CGSize screenDim = CGDisplayScreenSize(display); int mWidth = CGDDGetModeWidth(mode); int mHeight = CGDDGetModeHeight(mode); @@ -383,8 +435,8 @@ JNIEXPORT jintArray JNICALL Java_jogamp_newt_driver_macosx_MacScreen_getScreenMo prop[propIndex++] = mWidth; prop[propIndex++] = mHeight; prop[propIndex++] = CGDDGetModeBitsPerPixel(mode); - prop[propIndex++] = (jint) screenDim.width; - prop[propIndex++] = (jint) screenDim.height; + prop[propIndex++] = widthMM; + prop[propIndex++] = heightMM; prop[propIndex++] = CGDDGetModeRefreshRate(mode); prop[propIndex++] = ccwRot; prop[propIndex - NUM_SCREEN_MODE_PROPERTIES_ALL] = ( -1 < mode_idx ) ? propIndex-1 : propIndex ; // count == NUM_SCREEN_MODE_PROPERTIES_ALL @@ -421,9 +473,10 @@ JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_macosx_MacScreen_setScreenMod CGDirectDisplayID display = NewtScreen_getCGDirectDisplayIDByNSScreen(screen); CFArrayRef availableModes = CGDisplayAvailableModes(display); +#ifdef VERBOSE_ON CFIndex numberOfAvailableModes = CFArrayGetCount(availableModes); CFIndex numberOfAvailableModesRots = ROTMODES_PER_REALMODE * numberOfAvailableModes; - +#endif CFDictionaryRef mode = (CFDictionaryRef)CFArrayGetValueAtIndex(availableModes, mode_idx / ROTMODES_PER_REALMODE); // mode = CGDisplayModeRetain(mode); // 10.6 on CGDisplayModeRef @@ -525,7 +578,8 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_macosx_MacWindow_createWindow0 styleMask: (NSUInteger) styleMask backing: (NSBackingStoreType) bufferingType defer: NO - screen: myScreen]; + screen: myScreen + isFullscreenWindow: fullscreen]; [myWindow setReleasedWhenClosed: YES]; // default [myWindow setPreservesContentDuringLiveResize: NO]; @@ -645,6 +699,19 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_MacWindow_close0 DBG_PRINT( "windowClose.0 - %p,%d view %p,%d, parent %p\n", mWin, getRetainCount(mWin), mView, getRetainCount(mView), pWin); + if(NULL!=mView) { + jobject javaWindowObject = [mView getJavaWindowObject]; + if( false == [mView getDestroyNotifySent] ) { + [mView setDestroyNotifySent: true]; + } else if(NULL!=javaWindowObject) { + DBG_PRINT( "windowClose.Error: javaWindowObject not NULL (%p), destroyNotifySent==true\n", javaWindowObject); + } + if(NULL!=javaWindowObject) { + (*env)->DeleteGlobalRef(env, javaWindowObject); + [mView setJavaWindowObject: NULL]; + } + } + NS_DURING if(NULL!=mView) { // Available >= 10.5 - Makes the menubar disapear @@ -670,7 +737,8 @@ NS_ENDHANDLER // This probably happens b/c it sends events to the main loop // but our resources are gone ?! // However, issuing a simple release seems to work quite well. - [mWin release]; + // [mWin release]; + [mWin performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO]; DBG_PRINT( "windowClose.X - %p,%d view %p,%d, parent %p\n", mWin, getRetainCount(mWin), mView, getRetainCount(mView), pWin); @@ -991,10 +1059,11 @@ JNIEXPORT jobject JNICALL Java_jogamp_newt_driver_macosx_MacWindow_getLocationOn * Signature: (JZ)Z */ JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_macosx_MacWindow_setPointerVisible0 - (JNIEnv *env, jclass clazz, jlong window, jboolean mouseVisible) + (JNIEnv *env, jclass clazz, jlong window, jboolean hasFocus, jboolean mouseVisible) { NewtMacWindow *mWin = (NewtMacWindow*) ((intptr_t) window); - [mWin setMouseVisible: ( JNI_TRUE == mouseVisible ) ? YES : NO]; + [mWin setMouseVisible: ( JNI_TRUE == mouseVisible ) ? YES : NO + hasFocus: ( JNI_TRUE == hasFocus ) ? YES : NO]; return JNI_TRUE; } diff --git a/src/newt/native/NewtMacWindow.h b/src/newt/native/NewtMacWindow.h index 3ba89de1e..f576f75f7 100644 --- a/src/newt/native/NewtMacWindow.h +++ b/src/newt/native/NewtMacWindow.h @@ -104,6 +104,7 @@ @interface NewtMacWindow : NSWindow #endif { + BOOL isFullscreenWindow; BOOL mouseConfined; BOOL mouseVisible; BOOL mouseInside; @@ -119,7 +120,8 @@ styleMask: (NSUInteger) windowStyle backing: (NSBackingStoreType) bufferingType defer: (BOOL) deferCreation - screen:(NSScreen *)screen; + screen:(NSScreen *)screen + isFullscreenWindow:(BOOL)isfs; - (void) updateInsets: (JNIEnv*) env; - (void) attachToParent: (NSWindow*) parent; @@ -130,17 +132,36 @@ - (NSPoint) getLocationOnScreen: (NSPoint) p; - (NSPoint) screenPos2NewtClientWinPos: (NSPoint) p; +- (BOOL) isMouseInside; - (void) cursorHide:(BOOL)v; -- (void) setMouseVisible:(BOOL)v; +- (void) setMouseVisible:(BOOL)v hasFocus:(BOOL)focus; - (void) setMouseConfined:(BOOL)v; - (void) setMousePosition:(NSPoint)p; +- (void) sendKeyEvent: (NSEvent*) event eventType: (jint) evType; +- (void) sendMouseEvent: (NSEvent*) event eventType: (jint) evType; +- (void) focusChanged: (BOOL) gained; + - (BOOL) becomeFirstResponder; - (BOOL) resignFirstResponder; +- (BOOL) canBecomeKeyWindow; - (void) becomeKeyWindow; - (void) resignKeyWindow; - (void) windowDidBecomeKey: (NSNotification *) notification; - (void) windowDidResignKey: (NSNotification *) notification; -- (void) focusChanged: (BOOL) gained; +- (void) keyDown: (NSEvent*) theEvent; +- (void) keyUp: (NSEvent*) theEvent; +- (void) mouseEntered: (NSEvent*) theEvent; +- (void) mouseExited: (NSEvent*) theEvent; +- (void) mouseMoved: (NSEvent*) theEvent; +- (void) scrollWheel: (NSEvent*) theEvent; +- (void) mouseDown: (NSEvent*) theEvent; +- (void) mouseDragged: (NSEvent*) theEvent; +- (void) mouseUp: (NSEvent*) theEvent; +- (void) rightMouseDown: (NSEvent*) theEvent; +- (void) rightMouseDragged: (NSEvent*) theEvent; +- (void) rightMouseUp: (NSEvent*) theEvent; +- (void) otherMouseDown: (NSEvent*) theEvent; +- (void) otherMouseUp: (NSEvent*) theEvent; @end diff --git a/src/newt/native/NewtMacWindow.m b/src/newt/native/NewtMacWindow.m index ce41673c4..402389e71 100644 --- a/src/newt/native/NewtMacWindow.m +++ b/src/newt/native/NewtMacWindow.m @@ -238,10 +238,14 @@ static jmethodID windowRepaintID = NULL; DBG_PRINT("*************** dirtyRect: %p %lf/%lf %lfx%lf\n", javaWindowObject, dirtyRect.origin.x, dirtyRect.origin.y, dirtyRect.size.width, dirtyRect.size.height); + if(NULL==javaWindowObject) { + DBG_PRINT("drawRect: null javaWindowObject\n"); + return; + } int shallBeDetached = 0; JNIEnv* env = NewtCommon_GetJNIEnv(jvmHandle, jvmVersion, &shallBeDetached); if(NULL==env) { - DBG_PRINT("viewDidHide: null JNIEnv\n"); + DBG_PRINT("drawRect: null JNIEnv\n"); return; } @@ -258,6 +262,10 @@ static jmethodID windowRepaintID = NULL; - (void) viewDidHide { + if(NULL==javaWindowObject) { + DBG_PRINT("viewDidHide: null javaWindowObject\n"); + return; + } int shallBeDetached = 0; JNIEnv* env = NewtCommon_GetJNIEnv(jvmHandle, jvmVersion, &shallBeDetached); if(NULL==env) { @@ -276,10 +284,14 @@ static jmethodID windowRepaintID = NULL; - (void) viewDidUnhide { + if(NULL==javaWindowObject) { + DBG_PRINT("viewDidUnhide: null javaWindowObject\n"); + return; + } int shallBeDetached = 0; JNIEnv* env = NewtCommon_GetJNIEnv(jvmHandle, jvmVersion, &shallBeDetached); if(NULL==env) { - DBG_PRINT("viewDidHide: null JNIEnv\n"); + DBG_PRINT("viewDidUnhide: null JNIEnv\n"); return; } @@ -328,12 +340,14 @@ static jmethodID windowRepaintID = NULL; backing: (NSBackingStoreType) bufferingType defer: (BOOL) deferCreation screen:(NSScreen *)screen + isFullscreenWindow:(BOOL)isfs { id res = [super initWithContentRect: contentRect styleMask: windowStyle backing: bufferingType defer: deferCreation screen: screen]; + isFullscreenWindow = isfs; // Why is this necessary? Without it we don't get any of the // delegate methods like resizing and window movement. [self setDelegate: self]; @@ -389,8 +403,10 @@ static jmethodID windowRepaintID = NULL; { DBG_PRINT( "detachFromParent.1\n"); [self setParentWindow: nil]; - DBG_PRINT( "detachFromParent.2\n"); - [parent removeChildWindow: self]; + if(NULL != parent) { + DBG_PRINT( "detachFromParent.2\n"); + [parent removeChildWindow: self]; + } DBG_PRINT( "detachFromParent.X\n"); } @@ -463,15 +479,60 @@ static jmethodID windowRepaintID = NULL; // NSRect rS = [win convertRectFromScreen: r]; // 10.7 NSPoint oS = [self convertScreenToBase: r.origin]; oS.y = viewFrame.size.height - oS.y; // y-flip - return oS; } -- (BOOL) canBecomeKeyWindow +- (BOOL) isMouseInside { - // Even if the window is borderless, we still want it to be able - // to become the key window to receive keyboard events - return YES; + NSView* view = [self contentView]; + NSRect viewFrame = [view frame]; + NSPoint l1 = [NSEvent mouseLocation]; + NSPoint l0 = [self screenPos2NewtClientWinPos: l1]; + return viewFrame.origin.x <= l0.x && l0.x < (viewFrame.origin.x+viewFrame.size.width) && + viewFrame.origin.y <= l0.y && l0.y < (viewFrame.origin.y+viewFrame.size.height) ; +} + +- (void) setMouseVisible:(BOOL)v hasFocus:(BOOL)focus +{ + mouseVisible = v; + mouseInside = [self isMouseInside]; + DBG_PRINT( "setMouseVisible: confined %d, visible %d (current: %d), mouseInside %d, hasFocus %d\n", + mouseConfined, mouseVisible, !cursorIsHidden, mouseInside, focus); + if(YES == focus && YES == mouseInside) { + [self cursorHide: !mouseVisible]; + } +} + +- (void) cursorHide:(BOOL)v +{ + DBG_PRINT( "cursorHide: %d -> %d\n", cursorIsHidden, v); + if(v) { + if(!cursorIsHidden) { + [NSCursor hide]; + cursorIsHidden = YES; + } + } else { + if(cursorIsHidden) { + [NSCursor unhide]; + cursorIsHidden = NO; + } + } +} + +- (void) setMouseConfined:(BOOL)v +{ + mouseConfined = v; + DBG_PRINT( "setMouseConfined: confined %d, visible %d\n", mouseConfined, mouseVisible); +} + +- (void) setMousePosition:(NSPoint)p +{ + NSScreen* screen = [self screen]; + NSRect screenRect = [screen frame]; + + CGPoint pt = { p.x, screenRect.size.height - p.y }; // y-flip (CG is top-left origin) + CGEventRef ev = CGEventCreateMouseEvent (NULL, kCGEventMouseMoved, pt, kCGMouseButtonLeft); + CGEventPost (kCGHIDEventTap, ev); } static jint mods2JavaMods(NSUInteger mods) @@ -538,17 +599,6 @@ static jint mods2JavaMods(NSUInteger mods) } } -- (void) keyDown: (NSEvent*) theEvent -{ - [self sendKeyEvent: theEvent eventType: EVENT_KEY_PRESSED]; -} - -- (void) keyUp: (NSEvent*) theEvent -{ - [self sendKeyEvent: theEvent eventType: EVENT_KEY_RELEASED]; - [self sendKeyEvent: theEvent eventType: EVENT_KEY_TYPED]; -} - - (void) sendMouseEvent: (NSEvent*) event eventType: (jint) evType { NSView* nsview = [self contentView]; @@ -624,53 +674,100 @@ static jint mods2JavaMods(NSUInteger mods) } } -- (void) setMouseVisible:(BOOL)v +- (void) focusChanged: (BOOL) gained { - mouseVisible = v; - DBG_PRINT( "setMouseVisible: confined %d, visible %d\n", mouseConfined, mouseVisible); - if(YES == mouseInside) { - [self cursorHide: !mouseVisible]; + DBG_PRINT( "focusChanged: gained %d\n", gained); + NSView* nsview = [self contentView]; + if( ! [nsview isMemberOfClass:[NewtView class]] ) { + return; + } + NewtView* view = (NewtView *) nsview; + jobject javaWindowObject = [view getJavaWindowObject]; + if (javaWindowObject == NULL) { + DBG_PRINT("focusChanged: null javaWindowObject\n"); + return; + } + int shallBeDetached = 0; + JavaVM *jvmHandle = [view getJVMHandle]; + JNIEnv* env = NewtCommon_GetJNIEnv(jvmHandle, [view getJVMVersion], &shallBeDetached); + if(NULL==env) { + DBG_PRINT("focusChanged: null JNIEnv\n"); + return; + } + + (*env)->CallVoidMethod(env, javaWindowObject, focusChangedID, JNI_FALSE, (gained == YES) ? JNI_TRUE : JNI_FALSE); + + if (shallBeDetached) { + (*jvmHandle)->DetachCurrentThread(jvmHandle); } } -- (void) cursorHide:(BOOL)v +- (BOOL) becomeFirstResponder { - if(v) { - if(!cursorIsHidden) { - [NSCursor hide]; - cursorIsHidden = YES; - } - } else { - if(cursorIsHidden) { - [NSCursor unhide]; - cursorIsHidden = NO; - } + DBG_PRINT( "*************** becomeFirstResponder\n"); + return [super becomeFirstResponder]; +} + +- (BOOL) resignFirstResponder +{ + DBG_PRINT( "*************** resignFirstResponder\n"); + return [super resignFirstResponder]; +} + +- (BOOL) canBecomeKeyWindow +{ + // Even if the window is borderless, we still want it to be able + // to become the key window to receive keyboard events + return YES; +} + +- (void) becomeKeyWindow +{ + DBG_PRINT( "*************** becomeKeyWindow\n"); + [super becomeKeyWindow]; +} + +- (void) resignKeyWindow +{ + DBG_PRINT( "*************** resignKeyWindow: isFullscreen %d\n", (int)isFullscreenWindow); + if(!isFullscreenWindow) { + [super resignKeyWindow]; } } -- (void) setMouseConfined:(BOOL)v +- (void) windowDidBecomeKey: (NSNotification *) notification { - mouseConfined = v; - DBG_PRINT( "setMouseConfined: confined %d, visible %d\n", mouseConfined, mouseVisible); + DBG_PRINT( "*************** windowDidBecomeKey\n"); + mouseInside = [self isMouseInside]; + if(YES == mouseInside) { + [self cursorHide: !mouseVisible]; + } + [self focusChanged: YES]; } -- (void) setMousePosition:(NSPoint)p +- (void) windowDidResignKey: (NSNotification *) notification { - NSScreen* screen = [self screen]; - NSRect screenRect = [screen frame]; + DBG_PRINT( "*************** windowDidResignKey\n"); + // Implicit mouse exit by OS X + [self focusChanged: NO]; +} - CGPoint pt = { p.x, screenRect.size.height - p.y }; // y-flip (CG is top-left origin) - CGEventRef ev = CGEventCreateMouseEvent (NULL, kCGEventMouseMoved, pt, kCGMouseButtonLeft); - CGEventPost (kCGHIDEventTap, ev); - NSPoint l0 = [NSEvent mouseLocation]; - [self screenPos2NewtClientWinPos: l0]; +- (void) keyDown: (NSEvent*) theEvent +{ + [self sendKeyEvent: theEvent eventType: EVENT_KEY_PRESSED]; +} + +- (void) keyUp: (NSEvent*) theEvent +{ + [self sendKeyEvent: theEvent eventType: EVENT_KEY_RELEASED]; + [self sendKeyEvent: theEvent eventType: EVENT_KEY_TYPED]; } - (void) mouseEntered: (NSEvent*) theEvent { DBG_PRINT( "mouseEntered: confined %d, visible %d\n", mouseConfined, mouseVisible); mouseInside = YES; - [self setMouseVisible: mouseVisible]; + [self cursorHide: !mouseVisible]; if(NO == mouseConfined) { [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_ENTERED]; } @@ -859,68 +956,4 @@ static jint mods2JavaMods(NSUInteger mods) [pool release]; } -- (BOOL) becomeFirstResponder -{ - DBG_PRINT( "*************** becomeFirstResponder\n"); - return [super becomeFirstResponder]; -} - -- (BOOL) resignFirstResponder -{ - DBG_PRINT( "*************** resignFirstResponder\n"); - return [super resignFirstResponder]; -} - -- (void) becomeKeyWindow -{ - DBG_PRINT( "*************** becomeKeyWindow\n"); - [super becomeKeyWindow]; -} - -- (void) resignKeyWindow -{ - DBG_PRINT( "*************** resignKeyWindow\n"); - [super resignKeyWindow]; -} - -- (void) windowDidBecomeKey: (NSNotification *) notification -{ - DBG_PRINT( "*************** windowDidBecomeKey\n"); - [self focusChanged: YES]; -} - -- (void) windowDidResignKey: (NSNotification *) notification -{ - DBG_PRINT( "*************** windowDidResignKey\n"); - [self focusChanged: NO]; -} - -- (void) focusChanged: (BOOL) gained -{ - DBG_PRINT( "focusChanged: gained %d\n", gained); - NSView* nsview = [self contentView]; - if( ! [nsview isMemberOfClass:[NewtView class]] ) { - return; - } - NewtView* view = (NewtView *) nsview; - jobject javaWindowObject = [view getJavaWindowObject]; - if (javaWindowObject == NULL) { - DBG_PRINT("focusChanged: null javaWindowObject\n"); - return; - } - int shallBeDetached = 0; - JavaVM *jvmHandle = [view getJVMHandle]; - JNIEnv* env = NewtCommon_GetJNIEnv(jvmHandle, [view getJVMVersion], &shallBeDetached); - if(NULL==env) { - DBG_PRINT("focusChanged: null JNIEnv\n"); - return; - } - - (*env)->CallVoidMethod(env, javaWindowObject, focusChangedID, JNI_FALSE, (gained == YES) ? JNI_TRUE : JNI_FALSE); - - if (shallBeDetached) { - (*jvmHandle)->DetachCurrentThread(jvmHandle); - } -} - @end diff --git a/src/newt/native/X11Window.c b/src/newt/native/X11Window.c index 0a7e1cf77..6953140c0 100644 --- a/src/newt/native/X11Window.c +++ b/src/newt/native/X11Window.c @@ -169,16 +169,21 @@ static Window NewtWindows_getParent (Display *dpy, Window w) { } return 0; // Error } -static Status NewtWindows_getParentPosition (Display *dpy, Window w, int *x_return, int *y_return) { +static void NewtWindows_setCWAbove(Display *dpy, Window w) { + XWindowChanges xwc; + memset(&xwc, 0, sizeof(XWindowChanges)); + xwc.stack_mode = Above; + XConfigureWindow(dpy, w, CWStackMode, &xwc); + XSync(dpy, False); +} +static Status NewtWindows_getWindowPositionRelative2Parent (Display *dpy, Window w, int *x_return, int *y_return) { Window root_return; unsigned int width_return, height_return; unsigned int border_width_return; unsigned int depth_return; - Window parent = NewtWindows_getParent(dpy, w); - if(0 != parent) { - XGetGeometry(dpy, parent, &root_return, x_return, y_return, &width_return, - &height_return, &border_width_return, &depth_return); + if(0 != XGetGeometry(dpy, w, &root_return, x_return, y_return, &width_return, + &height_return, &border_width_return, &depth_return)) { return 1; // OK } return 0; // Error @@ -224,52 +229,6 @@ static Status NewtWindows_getFrameExtends(Display *dpy, Window window, int *left return 1; // Ok } -Status NewtWindows_updateInsets(JNIEnv *env, jobject jwindow, Display *dpy, Window window, int *left, int *right, int *top, int *bottom) { - if(0 != NewtWindows_getFrameExtends(dpy, window, left, right, top, bottom)) { - DBG_PRINT( "NewtWindows_updateInsets: insets by _NET_FRAME_EXTENTS [ l %d, r %d, t %d, b %d ]\n", - *left, *right, *top, *bottom); - (*env)->CallVoidMethod(env, jwindow, insetsChangedID, JNI_FALSE, *left, *right, *top, *bottom); - return 1; // OK - } else if(0 != NewtWindows_getParentPosition (dpy, window, left, top)) { - *right = *left; *bottom = *left; - DBG_PRINT( "NewtWindows_updateInsets: insets by parent position [ l %d, r %d, t %d, b %d ]\n", - *left, *right, *top, *bottom); - (*env)->CallVoidMethod(env, jwindow, insetsChangedID, JNI_FALSE, *left, *right, *top, *bottom); - return 1; // OK - } - return 0; // Error -} - -static void NewtWindows_setCWAbove(Display *dpy, Window w) { - XWindowChanges xwc; - memset(&xwc, 0, sizeof(XWindowChanges)); - xwc.stack_mode = Above; - XConfigureWindow(dpy, w, CWStackMode, &xwc); - XSync(dpy, False); -} - -static void NewtWindows_requestFocus (JNIEnv *env, jobject window, Display *dpy, Window w, jboolean force) { - XWindowAttributes xwa; - Window focus_return; - int revert_to_return; - - XGetInputFocus(dpy, &focus_return, &revert_to_return); - DBG_PRINT( "X11: requestFocus dpy %p,win %p, force %d, hasFocus %d\n", dpy, (void*)w, force, focus_return==w); - - if( JNI_TRUE==force || focus_return!=w) { - DBG_PRINT( "X11: XRaiseWindow dpy %p, win %p\n", dpy, (void*)w); - XRaiseWindow(dpy, w); - NewtWindows_setCWAbove(dpy, w); - // Avoid 'BadMatch' errors from XSetInputFocus, ie if window is not viewable - XGetWindowAttributes(dpy, w, &xwa); - if(xwa.map_state == IsViewable) { - DBG_PRINT( "X11: XSetInputFocus dpy %p,win %pd\n", dpy, (void*)w); - XSetInputFocus(dpy, w, RevertToParent, CurrentTime); - } - } - DBG_PRINT( "X11: requestFocus dpy %p,win %p, force %d - FIN\n", dpy, (void*)w, force); - XSync(dpy, False); -} #define DECOR_USE_MWM 1 // works for known WMs // #define DECOR_USE_EWMH 1 // haven't seen this to work (NORMAL->POPUP, never gets undecorated) @@ -307,6 +266,29 @@ static void NewtWindows_setDecorations (Display *dpy, Window w, Bool decorated) XSync(dpy, False); } +static Bool NewtWindows_hasDecorations (Display *dpy, Window w) { + Bool decor = False; + +#ifdef DECOR_USE_MWM + Atom _MOTIF_WM_HINTS = XInternAtom( dpy, "_MOTIF_WM_HINTS", False ); + unsigned char *wm_data; + Atom wm_type; + int wm_format; + unsigned long wm_nitems, wm_bytes_after; + + if( Success == XGetWindowProperty(dpy, w, _MOTIF_WM_HINTS, 0, PROP_MWM_HINTS_ELEMENTS, False, AnyPropertyType, + &wm_type, &wm_format, &wm_nitems, &wm_bytes_after, &wm_data) ) { + if(wm_type != None) { + // unsigned long mwmhints[PROP_MWM_HINTS_ELEMENTS] = { MWM_HINTS_DECORATIONS, 0, decorated, 0, 0 }; // flags, functions, decorations, input_mode, status + unsigned long *hints = (unsigned long *) wm_data; + decor = ( 0 != (hints[0] & MWM_HINTS_DECORATIONS) ) && ( 0 != hints[2] ); + } + } +#endif + + return decor; +} + static void NewtWindows_setNormalWindowEWMH (Display *dpy, Window w) { Atom _NET_WM_WINDOW_TYPE = XInternAtom( dpy, "_NET_WM_WINDOW_TYPE", False ); Atom types[1]={0}; @@ -430,6 +412,56 @@ static Bool NewtWindows_setFullscreenEWMH (Display *dpy, Window root, Window w, return res; } + +Status NewtWindows_updateInsets(JNIEnv *env, jobject jwindow, Display *dpy, Window window, int *left, int *right, int *top, int *bottom) { + if(0 != NewtWindows_getFrameExtends(dpy, window, left, right, top, bottom)) { + DBG_PRINT( "NewtWindows_updateInsets: insets by _NET_FRAME_EXTENTS [ l %d, r %d, t %d, b %d ]\n", + *left, *right, *top, *bottom); + (*env)->CallVoidMethod(env, jwindow, insetsChangedID, JNI_FALSE, *left, *right, *top, *bottom); + return 1; // OK + } + + Bool hasDecor = NewtWindows_hasDecorations (dpy, window); + if(hasDecor) { + // The following logic only works if window is top-level _and_ the WM + // has 'decorated' our client window w/ another parent window _within_ the actual 'framed' window. + Window parent = NewtWindows_getParent(dpy, window); + if(0 != NewtWindows_getWindowPositionRelative2Parent (dpy, parent, left, top)) { + *right = *left; *bottom = *top; + DBG_PRINT( "NewtWindows_updateInsets: insets by parent position [ l %d, r %d, t %d, b %d ]\n", + *left, *right, *top, *bottom); + (*env)->CallVoidMethod(env, jwindow, insetsChangedID, JNI_FALSE, *left, *right, *top, *bottom); + return 1; // OK + } + } + DBG_PRINT( "NewtWindows_updateInsets: cannot determine insets - hasDecor %d\n", hasDecor); + return 0; // Error +} + +static void NewtWindows_requestFocus (JNIEnv *env, jobject window, Display *dpy, Window w, jboolean force) { + XWindowAttributes xwa; + Window focus_return; + int revert_to_return; + + XSync(dpy, False); + XGetInputFocus(dpy, &focus_return, &revert_to_return); + DBG_PRINT( "X11: requestFocus dpy %p,win %p, force %d, hasFocus %d\n", dpy, (void*)w, force, focus_return==w); + + if( JNI_TRUE==force || focus_return!=w) { + DBG_PRINT( "X11: XRaiseWindow dpy %p, win %p\n", dpy, (void*)w); + XRaiseWindow(dpy, w); + NewtWindows_setCWAbove(dpy, w); + // Avoid 'BadMatch' errors from XSetInputFocus, ie if window is not viewable + XGetWindowAttributes(dpy, w, &xwa); + DBG_PRINT( "X11: XSetInputFocus dpy %p,win %p, isViewable %d\n", dpy, (void*)w, (xwa.map_state == IsViewable)); + if(xwa.map_state == IsViewable) { + XSetInputFocus(dpy, w, RevertToParent, CurrentTime); + } + } + DBG_PRINT( "X11: requestFocus dpy %p,win %p, force %d - FIN\n", dpy, (void*)w, force); + XSync(dpy, False); +} + /** * Window */ @@ -601,11 +633,13 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_x11_X11Window_CreateWindow0 // we can pre-map the window here to be able to gather the insets and position. { XEvent event; - int left, right, top, bottom; + int left=0, right=0, top=0, bottom=0; XMapWindow(dpy, window); XIfEvent( dpy, &event, WaitForMapNotify, (XPointer) window ); // wait to get proper insets values + XSync(dpy, False); + // send insets before visibility, allowing java code a proper sync point! NewtWindows_updateInsets(env, jwindow, dpy, window, &left, &right, &top, &bottom); (*env)->CallVoidMethod(env, jwindow, visibleChangedID, JNI_FALSE, JNI_TRUE); @@ -683,6 +717,15 @@ static Bool WaitForReparentNotify( Display *dpy, XEvent *event, XPointer arg ) { } #endif +/** + * KDE cause lost input focus in fullscreen mode. + * Using 'XGrabKeyboard(..)' would prevent the loss, + * but also would disable WM task switcher etc. + * + * #define FS_GRAB_KEYBOARD 1 + * + */ + /* * Class: jogamp_newt_driver_x11_X11Window * Method: reconfigureWindow0 @@ -730,6 +773,17 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_x11_X11Window_reconfigureWindow0 Bool enable = TST_FLAG_CHANGE_FULLSCREEN(flags) ? TST_FLAG_IS_FULLSCREEN(flags) : TST_FLAG_IS_ALWAYSONTOP(flags) ; if( NewtWindows_setFullscreenEWMH(dpy, root, w, fsEWMHFlags, isVisible, enable) ) { NewtDisplay_displayDispatchErrorHandlerEnable(0, env); + #ifdef FS_GRAB_KEYBOARD + if(TST_FLAG_CHANGE_FULLSCREEN(flags)) { + if(TST_FLAG_IS_FULLSCREEN(flags)) { + XGrabKeyboard(dpy, w, True, GrabModeAsync, GrabModeAsync, CurrentTime); + } else { + XUngrabKeyboard(dpy, CurrentTime); + } + } else if(TST_FLAG_CHANGE_ALWAYSONTOP(flags) && !TST_FLAG_IS_ALWAYSONTOP(flags)) { + XUngrabKeyboard(dpy, CurrentTime); + } + #endif return; } } @@ -744,6 +798,9 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_x11_X11Window_reconfigureWindow0 if( fsEWMHFlags && ( ( TST_FLAG_CHANGE_FULLSCREEN(flags) && !TST_FLAG_IS_FULLSCREEN(flags) ) || ( TST_FLAG_CHANGE_ALWAYSONTOP(flags) && !TST_FLAG_IS_ALWAYSONTOP(flags) ) ) ) { // FS off NewtWindows_setFullscreenEWMH(dpy, root, w, fsEWMHFlags, isVisible, False); + #ifdef FS_GRAB_KEYBOARD + XUngrabKeyboard(dpy, CurrentTime); + #endif } if( TST_FLAG_CHANGE_PARENTING(flags) && !TST_FLAG_HAS_PARENT(flags) ) { @@ -791,6 +848,11 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_x11_X11Window_reconfigureWindow0 if( fsEWMHFlags && ( ( TST_FLAG_CHANGE_FULLSCREEN(flags) && TST_FLAG_IS_FULLSCREEN(flags) ) || ( TST_FLAG_CHANGE_ALWAYSONTOP(flags) && TST_FLAG_IS_ALWAYSONTOP(flags) ) ) ) { // FS on NewtWindows_setFullscreenEWMH(dpy, root, w, fsEWMHFlags, isVisible, True); + #ifdef FS_GRAB_KEYBOARD + if(TST_FLAG_CHANGE_FULLSCREEN(flags) && TST_FLAG_IS_FULLSCREEN(flags)) { + XGrabKeyboard(dpy, w, True, GrabModeAsync, GrabModeAsync, CurrentTime); + } + #endif } NewtDisplay_displayDispatchErrorHandlerEnable(0, env); |