aboutsummaryrefslogtreecommitdiffstats
path: root/src/newt
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2012-01-18 03:34:30 +0100
committerSven Gothel <[email protected]>2012-01-18 03:34:30 +0100
commitd8bbddaea08bd6ae75bc5255ab33cbf15cf3a7be (patch)
tree33209b877be0a83c4b7f3e4a67003e5c0aef0ca9 /src/newt
parentd25c25339d8878980c5f45cd1ee602767ffe0f33 (diff)
Newt: Add fullscreen for offscreen windows (currently OSX only); Focus handling; Misc.
- Add fullscreen for offscreen windows (currently OSX only), - Focus handling - requestFocus() @ creation - remove requestFocus() delay in setFullscreen() since focus loss in fullscreen mode is caused by a KDE 'misbehavior' in general. See X11Common.c FS_GRAB_KEYBOARD, an experimental focus loss prevention, disabled due to it's behavioral impact of removing the ability to use WM keyboard commands (task switcher). - Remove pending events waiting longer than TO (1200ms)
Diffstat (limited to 'src/newt')
-rw-r--r--src/newt/classes/jogamp/newt/WindowImpl.java240
-rw-r--r--src/newt/native/X11Window.c31
2 files changed, 173 insertions, 98 deletions
diff --git a/src/newt/classes/jogamp/newt/WindowImpl.java b/src/newt/classes/jogamp/newt/WindowImpl.java
index 10fa36a83..3d7b8ec1c 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
@@ -97,6 +100,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
protected 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 NativeWindow nfs_parent = null; // non fullscreen parent, in case explicit reparenting is performed (offscreen)
protected String title = "Newt Window";
protected boolean undecorated = false;
protected boolean alwaysOnTop = false;
@@ -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;
@@ -302,10 +305,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
}
}
}
@@ -1180,7 +1188,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
}
}
@@ -1566,14 +1574,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;
@@ -1584,14 +1600,23 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
}
public void requestFocus(boolean wait) {
- if(!hasFocus() && 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);
}
}
@@ -1661,113 +1686,132 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
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 = 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(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
-
- 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));
- if(isVisible()) {
- // Request focus 'later' allowing native WM to complete event handling,
- // this is required especially on X11 to guarantee a focused fullscreen window.
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) { }
- requestFocus(true);
+ 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;
}
- return this.fullscreen;
}
private class ScreenModeListenerImpl implements ScreenModeListener {
@@ -1853,11 +1897,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;
}
@@ -1868,11 +1913,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:
diff --git a/src/newt/native/X11Window.c b/src/newt/native/X11Window.c
index 8cc9952d6..10ce8945b 100644
--- a/src/newt/native/X11Window.c
+++ b/src/newt/native/X11Window.c
@@ -253,6 +253,7 @@ static void NewtWindows_requestFocus (JNIEnv *env, jobject window, Display *dpy,
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);
@@ -262,8 +263,8 @@ static void NewtWindows_requestFocus (JNIEnv *env, jobject window, Display *dpy,
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) {
- DBG_PRINT( "X11: XSetInputFocus dpy %p,win %p\n", dpy, (void*)w);
XSetInputFocus(dpy, w, RevertToParent, CurrentTime);
}
}
@@ -683,6 +684,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 +740,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 +765,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 +815,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);