diff options
-rw-r--r-- | src/newt/classes/jogamp/newt/WindowImpl.java | 104 | ||||
-rw-r--r-- | src/newt/classes/jogamp/newt/driver/windows/WindowsWindow.java | 18 | ||||
-rw-r--r-- | src/newt/classes/jogamp/newt/driver/x11/X11Window.java | 22 | ||||
-rw-r--r-- | src/newt/native/WindowsWindow.c | 96 | ||||
-rw-r--r-- | src/newt/native/X11Window.c | 306 | ||||
-rw-r--r-- | src/test/com/jogamp/opengl/test/junit/newt/TestWindows01NEWT.java | 151 |
6 files changed, 462 insertions, 235 deletions
diff --git a/src/newt/classes/jogamp/newt/WindowImpl.java b/src/newt/classes/jogamp/newt/WindowImpl.java index 7c8174a6f..75c3510b7 100644 --- a/src/newt/classes/jogamp/newt/WindowImpl.java +++ b/src/newt/classes/jogamp/newt/WindowImpl.java @@ -87,7 +87,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer protected CapabilitiesImmutable capsRequested = null; protected CapabilitiesChooser capabilitiesChooser = null; // default null -> default protected boolean fullscreen = false, hasFocus = false; - protected int width = 128, height = 128, x = 0, y = 0; // client-area size/pos w/o insets + protected int width = 128, height = 128; // client-area size w/o insets, default: may be overwritten by user + protected int x = -1, y = -1; // client-area pos w/o insets, default: undefined (allow WM to choose if not set by user) 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 @@ -257,9 +258,15 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(DEBUG_IMPLEMENTATION) { System.err.println("Window.createNative() START ("+getThreadName()+", "+this+")"); } + final boolean userPos = 0<=x && 0<=y; // user has specified a position + if( null != parentWindow && NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindow.lockSurface() ) { - throw new NativeWindowException("Parent surface lock: not ready: "+parentWindow); + throw new NativeWindowException("Parent surface lock: not ready: "+parentWindow); + } + if( !userPos && ( isUndecorated() || null != parentWindow ) ) { + // default child/undecorated window position is 0/0, if not set by user + x = 0; y = 0; } try { if(validateParentWindowHandle()) { @@ -267,11 +274,19 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer throw new InternalError("XXX"); } if(canCreateNativeImpl()) { + final int _x = x, _y = y; // orig req pos screen.addReference(); screenReferenceAdded = true; createNativeImpl(); screen.addScreenModeListener(screenModeListenerImpl); setTitleImpl(title); + waitForVisible(true, false); + if(userPos) { + // wait for user req position + waitForPosSize(_x, _y, -1, -1, false, TIMEOUT_NATIVEWINDOW); + } else { + waitForAnyPos(false, TIMEOUT_NATIVEWINDOW); + } } // always flag visible, // allowing to retry if visible && !isNativeValid() @@ -413,10 +428,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer * to insets and positioning a decorated window to 0/0, which would place the frame * outside of the screen.</p> * - * @param x client-area position - * @param y client-area position - * @param width client-area size - * @param height client-area size + * @param x client-area position, or <0 if unchanged + * @param y client-area position, or <0 if unchanged + * @param width client-area size, or <=0 if unchanged + * @param height client-area size, or <=0 if unchanged * @param flags bitfield of change and status flags * * @see #sizeChanged(int,int) @@ -1488,21 +1503,33 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer display.dispatchMessagesNative(); // status up2date boolean wasVisible = isVisible(); - reconfigureWindowImpl(x, y, w, h, - getReconfigureFlags( ( ( 0 != parentWindowHandle ) ? FLAG_CHANGE_PARENTING : 0 ) | - FLAG_CHANGE_FULLSCREEN | FLAG_CHANGE_DECORATION, wasVisible) ); + // 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; + } + 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 - + if(wasVisible) { - // visibility should be implicit if needed by native impl, - // however .. this is a little fallback code - if(!WindowImpl.this.waitForVisible(true, true, TIMEOUT_NATIVEWINDOW)) { - setVisibleImpl(true, x, y, w, h); - WindowImpl.this.waitForVisible(true, true, TIMEOUT_NATIVEWINDOW); - display.dispatchMessagesNative(); // status up2date - } - // ensure size is set, request focus .. and done + setVisibleImpl(true, x, y, w, h); + WindowImpl.this.waitForVisible(true, false); + display.dispatchMessagesNative(); // status up2date WindowImpl.this.waitForPosSize(-1, -1, w, h, false, TIMEOUT_NATIVEWINDOW); + display.dispatchMessagesNative(); // status up2date requestFocusImpl(true); display.dispatchMessagesNative(); // status up2date @@ -2047,16 +2074,15 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer DisplayImpl display = (DisplayImpl) screen.getDisplay(); for(long sleep = timeOut; 0<sleep && this.visible != visible; sleep-=10 ) { display.dispatchMessagesNative(); // status up2date - try { - Thread.sleep(10); - } catch (InterruptedException ie) {} + try { Thread.sleep(10); } catch (InterruptedException ie) {} sleep -=10; } if(this.visible != visible) { + final String msg = "Visibility not reached as requested within "+timeOut+"ms : requested "+visible+", is "+this.visible; if(failFast) { - throw new NativeWindowException("Visibility not reached as requested within "+timeOut+"ms : requested "+visible+", is "+this.visible); + throw new NativeWindowException(msg); } else if (DEBUG_IMPLEMENTATION) { - System.err.println("******* Visibility not reached as requested within "+timeOut+"ms : requested "+visible+", is "+this.visible); + System.err.println(msg); } } return this.visible == visible; @@ -2091,17 +2117,39 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer reached = true; } else { display.dispatchMessagesNative(); // status up2date - try { - Thread.sleep(10); - } catch (InterruptedException ie) {} + try { Thread.sleep(10); } catch (InterruptedException ie) {} + sleep -=10; + } + } + if(!reached) { + final String msg = "Size/Pos not reached as requested within "+timeOut+"ms : requested "+x+"/"+y+" "+w+"x"+h+", is "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight(); + if(failFast) { + throw new NativeWindowException(msg); + } else if (DEBUG_IMPLEMENTATION) { + System.err.println(msg); + } + } + return reached; + } + + private boolean waitForAnyPos(boolean failFast, long timeOut) { + DisplayImpl display = (DisplayImpl) screen.getDisplay(); + boolean reached = false; + for(long sleep = timeOut; !reached && 0<sleep; sleep-=10 ) { + if( 0<=getX() && 0<=getY() ) { + reached = true; + } else { + display.dispatchMessagesNative(); // status up2date + try { Thread.sleep(10); } catch (InterruptedException ie) {} sleep -=10; } } if(!reached) { + final String msg = "Any Pos not reached as requested within "+timeOut+"ms : is "+getX()+"/"+getY(); if(failFast) { - throw new NativeWindowException("Size/Pos not reached as requested within "+timeOut+"ms : requested "+x+"/"+y+" "+w+"x"+h+", is "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()); + throw new NativeWindowException(msg); } else if (DEBUG_IMPLEMENTATION) { - System.err.println("********** Size/Pos not reached as requested within "+timeOut+"ms : requested "+x+"/"+y+" "+w+"x"+h+", is "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()); + System.err.println(msg); } } return reached; diff --git a/src/newt/classes/jogamp/newt/driver/windows/WindowsWindow.java b/src/newt/classes/jogamp/newt/driver/windows/WindowsWindow.java index a78014468..7dd67e62b 100644 --- a/src/newt/classes/jogamp/newt/driver/windows/WindowsWindow.java +++ b/src/newt/classes/jogamp/newt/driver/windows/WindowsWindow.java @@ -156,14 +156,18 @@ public class WindowsWindow extends WindowImpl { final InsetsImmutable i = getInsets(); // client position -> top-level window position - x -= i.getLeftWidth() ; - y -= i.getTopHeight() ; - if( 0 > x ) { x = 0; } - if( 0 > y ) { y = 0; } + if(0<=x && 0<=y) { + x -= i.getLeftWidth() ; + y -= i.getTopHeight() ; + if( 0 > x ) { x = 0; } + if( 0 > y ) { y = 0; } + } - // client size -> top-level window size - width += i.getTotalWidth(); - height += i.getTotalHeight(); + if(0<width && 0<height) { + // client size -> top-level window size + width += i.getTotalWidth(); + height += i.getTotalHeight(); + } } reconfigureWindow0( getParentWindowHandle(), getWindowHandle(), x, y, width, height, flags); diff --git a/src/newt/classes/jogamp/newt/driver/x11/X11Window.java b/src/newt/classes/jogamp/newt/driver/x11/X11Window.java index 416bdbdf0..3c48ba4bf 100644 --- a/src/newt/classes/jogamp/newt/driver/x11/X11Window.java +++ b/src/newt/classes/jogamp/newt/driver/x11/X11Window.java @@ -34,6 +34,8 @@ package jogamp.newt.driver.x11; import jogamp.nativewindow.x11.X11Util; +import jogamp.newt.DisplayImpl; +import jogamp.newt.DisplayImpl.DisplayRunnable; import jogamp.newt.WindowImpl; import javax.media.nativewindow.*; import javax.media.nativewindow.x11.*; @@ -99,7 +101,7 @@ public class X11Window extends WindowImpl { getReconfigureFlagsAsString(null, flags)); } - if(0 == ( FLAG_IS_UNDECORATED & flags)) { + if(0 == ( FLAG_IS_UNDECORATED & flags) && 0<=x && 0<=y) { final InsetsImmutable i = getInsets(); // client position -> top-level window position @@ -119,12 +121,18 @@ public class X11Window extends WindowImpl { } @Override - protected void setTitleImpl(String title) { - setTitle0(getDisplayEDTHandle(), getWindowHandle(), title); + protected void setTitleImpl(final String title) { + runWithLockedDisplayHandle( new DisplayImpl.DisplayRunnable() { + public Object run(long dpy) { + setTitle0(dpy, getWindowHandle(), title); + return null; + } + }); } - protected Point getLocationOnScreenImpl(int x, int y) { - return X11Util.GetRelativeLocation( getDisplayEDTHandle(), getScreenIndex(), getWindowHandle(), 0 /*root win*/, x, y); + protected Point getLocationOnScreenImpl(final int x, final int y) { + // X11Util.GetRelativeLocation: locks display already ! + return X11Util.GetRelativeLocation( getScreen().getDisplay().getHandle(), getScreenIndex(), getWindowHandle(), 0 /*root win*/, x, y); } protected void updateInsetsImpl(Insets insets) { @@ -138,6 +146,10 @@ public class X11Window extends WindowImpl { private final long getDisplayEDTHandle() { return ((X11Display) getScreen().getDisplay()).getEDTHandle(); } + private final Object runWithLockedDisplayHandle(DisplayRunnable action) { + return ((DisplayImpl) getScreen().getDisplay()).runWithLockedDisplayHandle(action); + // return runWithTempDisplayHandle(action); + } protected static native boolean initIDs0(); private native long CreateWindow0(long parentWindowHandle, long display, int screen_index, diff --git a/src/newt/native/WindowsWindow.c b/src/newt/native/WindowsWindow.c index 900b5a037..1cb0f2036 100644 --- a/src/newt/native/WindowsWindow.c +++ b/src/newt/native/WindowsWindow.c @@ -1284,6 +1284,33 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_windows_WindowsWindow_getNewtWnd return (jlong) (intptr_t) wndProc; } +static void NewtWindow_setVisiblePosSize(HWND hwnd, BOOL top, BOOL visible, + int x, int y, int width, int height) +{ + UINT flags; + BOOL bRes; + + DBG_PRINT("*** WindowsWindow: NewtWindow_setVisiblePosSize %d/%d %dx%d, top %d, visible %d\n", + x, y, width, height, top, visible); + + if(visible) { + flags = SWP_SHOWWINDOW; + } else { + flags = SWP_NOACTIVATE | SWP_NOZORDER; + } + if(0>x || 0>y) { + flags |= SWP_NOMOVE; + } + if(0>=width || 0>=height ) { + flags |= SWP_NOSIZE; + } + + SetWindowPos(hwnd, top ? HWND_TOPMOST : HWND_TOP, x, y, width, height, flags); + + InvalidateRect(hwnd, NULL, TRUE); + UpdateWindow(hwnd); +} + /* * Class: jogamp_newt_driver_windows_WindowsWindow * Method: CreateWindow @@ -1301,6 +1328,7 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_windows_WindowsWindow_CreateWind int x=(int)jx, y=(int)jy; int width=(int)defaultWidth, height=(int)defaultHeight; HWND window = NULL; + int _x = x, _y = y; // pos for CreateWindow, might be tweaked #ifdef UNICODE wndClassName = NewtCommon_GetNullTerminatedStringChars(env, jWndClassName); @@ -1310,6 +1338,7 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_windows_WindowsWindow_CreateWind wndName = (*env)->GetStringUTFChars(env, jWndName, NULL); #endif + if(NULL!=parentWindow) { if (!IsWindow(parentWindow)) { DBG_PRINT("*** WindowsWindow: CreateWindow failure: Passed parentWindow %p is invalid\n", parentWindow); @@ -1320,14 +1349,17 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_windows_WindowsWindow_CreateWind windowStyle |= WS_POPUP | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX; } else { windowStyle |= WS_OVERLAPPEDWINDOW; - x = CW_USEDEFAULT; - y = 0; + if(0>_x || 0>_y) { + // user didn't requested specific position, use WM default + _x = CW_USEDEFAULT; + _y = 0; + } } (void) visualID; // FIXME: use the visualID .. window = CreateWindow(wndClassName, wndName, windowStyle, - x, y, width, height, + _x, _y, width, height, parentWindow, NULL, (HINSTANCE) (intptr_t) hInstance, NULL); @@ -1348,7 +1380,37 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_windows_WindowsWindow_CreateWind #else SetWindowLongPtr(window, GWLP_USERDATA, (intptr_t) wud); #endif - (void)UpdateInsets(env, wud->jinstance, window); + + // gather and adjust position and size + { + RECT rc; + RECT * insets; + BOOL userPos = 0<=x && 0<=y ; + + ShowWindow(window, SW_SHOW); + (*env)->CallVoidMethod(env, wud->jinstance, visibleChangedID, JNI_TRUE); + + insets = UpdateInsets(env, wud->jinstance, window); + if(!userPos) { + GetWindowRect(window, &rc); + x = rc.left + insets->left; // client coords + y = rc.top + insets->top; // client coords + } + DBG_PRINT("*** WindowsWindow: CreateWindow client: %d/%d %dx%d (is user-pos %d)\n", x, y, width, height, userPos); + + x -= insets->left; // top-level + y -= insets->top; // top-level + width += insets->left + insets->right; // top-level + height += insets->top + insets->bottom; // top-level + DBG_PRINT("*** WindowsWindow: CreateWindow top-level %d/%d %dx%d\n", x, y, width, height); + + if(userPos) { + // mark pos as undef, which cases java to wait for WM reported pos + (*env)->CallVoidMethod(env, wud->jinstance, positionChangedID, -1, -1); + } + NewtWindow_setVisiblePosSize(window, (NULL == parentWindow) ? TRUE : FALSE /* top */, + TRUE, x, y, width, height); + } } #ifdef UNICODE @@ -1377,28 +1439,6 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_windows_WindowsWindow_MonitorFro #endif } -void NewtWindow_setVisiblePosSize(JNIEnv *env, jobject obj, HWND hwnd, - BOOL top, BOOL undecorated, BOOL visible, - int x, int y, int width, int height) -{ - UINT flags; - BOOL bRes; - - DBG_PRINT("*** WindowsWindow: NewtWindow_setVisiblePosSize %d/%d %dx%d, top %d, undecorated %d, visible %d\n", - x, y, width, height, top, undecorated, visible); - - if(visible) { - flags = SWP_SHOWWINDOW; - } else { - flags = SWP_NOACTIVATE | SWP_NOZORDER; - } - - SetWindowPos(hwnd, top ? HWND_TOPMOST : HWND_TOP, x, y, width, height, flags); - - InvalidateRect(hwnd, NULL, TRUE); - UpdateWindow(hwnd); -} - static jboolean NewtWindows_setFullScreen(jboolean fullscreen) { int flags = 0; @@ -1488,8 +1528,8 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_windows_WindowsWindow_reconfigure SetParent(hwnd, hwndP ); } - NewtWindow_setVisiblePosSize(env, obj, hwnd, (NULL == hwndP) ? JNI_TRUE : JNI_FALSE /* top */, - TST_FLAG_IS_UNDECORATED(flags), TST_FLAG_IS_VISIBLE(flags), x, y, width, height); + NewtWindow_setVisiblePosSize(hwnd, (NULL == hwndP) ? TRUE : FALSE /* top */, + TST_FLAG_IS_VISIBLE(flags), x, y, width, height); if( TST_FLAG_CHANGE_VISIBILITY(flags) ) { if( TST_FLAG_IS_VISIBLE(flags) ) { diff --git a/src/newt/native/X11Window.c b/src/newt/native/X11Window.c index 092f3f7d3..39e8f9476 100644 --- a/src/newt/native/X11Window.c +++ b/src/newt/native/X11Window.c @@ -336,10 +336,12 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_x11_X11Display_DisplayRelease0 if(dpy==NULL) { NewtCommon_FatalError(env, "invalid display connection.."); } + // nothing to do to free the atoms ! (void) wm_javaobject_atom; (void) wm_delete_atom; + XSync(dpy, True); // discard all pending events DBG_PRINT("X11: X11Display_DisplayRelease dpy %p\n", dpy); } @@ -523,6 +525,14 @@ static Status NewtWindows_updateInsets(JNIEnv *env, jobject jwindow, Display *dp 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; @@ -535,6 +545,7 @@ static void NewtWindows_requestFocus (JNIEnv *env, jobject window, Display *dpy, if( JNI_TRUE==force || JNI_FALSE == (*env)->CallBooleanMethod(env, window, focusActionID) ) { DBG_PRINT( "X11: XRaiseWindow dpy %p,win %pd\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) { @@ -573,40 +584,112 @@ static void NewtWindows_setDecorations (Display *dpy, Window w, Bool decorated) #define _NET_WM_STATE_REMOVE 0 #define _NET_WM_STATE_ADD 1 -static void NewtWindows_setFullscreen (Display *dpy, Window root, Window w, Bool fullscreen) { +#define _NET_WM_ACTION_FULLSCREEN_SUPPORTED ( 1 << 0 ) +#define _NET_WM_ACTION_ABOVE_SUPPORTED ( 1 << 1 ) + +/** + * Set fullscreen using Extended Window Manager Hints (EWMH) + * + * Be aware that _NET_WM_STATE_FULLSCREEN requires a mapped window + * which shall be on the top of the stack to wor reliable. + * + * The WM will internally save the size and position when entering FS + * and resets it when leaving FS. + * The same is assumed for the decoration state. + */ +static int NewtWindows_isFullscreenEWMHSupported (Display *dpy, Window w) { + Atom _NET_WM_ALLOWED_ACTIONS = XInternAtom( dpy, "_NET_WM_ALLOWED_ACTIONS", False ); + Atom _NET_WM_ACTION_FULLSCREEN = XInternAtom( dpy, "_NET_WM_ACTION_FULLSCREEN", False ); + Atom _NET_WM_ACTION_ABOVE = XInternAtom( dpy, "_NET_WM_ACTION_ABOVE", False ); + Atom * actions; + Atom type; + unsigned long action_len, remain; + int res = 0, form, i; + Status s; + + if ( Success == (s = XGetWindowProperty(dpy, w, _NET_WM_ALLOWED_ACTIONS, 0, 1024, False, AnyPropertyType, + &type, &form, &action_len, &remain, (unsigned char**)&actions)) ) { + for(i=0; i<action_len; i++) { + if(_NET_WM_ACTION_FULLSCREEN == actions[i]) { + DBG_PRINT( "**************** X11: FS EWMH CHECK[%d]: _NET_WM_ACTION_FULLSCREEN (*)\n", i); + res |= _NET_WM_ACTION_FULLSCREEN_SUPPORTED ; + } else if(_NET_WM_ACTION_ABOVE == actions[i]) { + DBG_PRINT( "**************** X11: FS EWMH CHECK[%d]: _NET_WM_ACTION_ABOVE (*)\n", i); + res |= _NET_WM_ACTION_ABOVE_SUPPORTED ; + } +#ifdef VERBOSE_ON + else { + char * astr = XGetAtomName(dpy, actions[i]); + DBG_PRINT( "**************** X11: FS EWMH CHECK[%d]: %s (unused)\n", i, astr); + XFree(astr); + } +#endif + } + DBG_PRINT( "**************** X11: FS EWMH CHECK: 0x%X\n", res); + } else { + DBG_PRINT( "**************** X11: FS EWMH CHECK: XGetWindowProperty failed: %d\n", s); + } + // above code doesn't work reliable on KDE4 ... + res = _NET_WM_ACTION_FULLSCREEN_SUPPORTED | _NET_WM_ACTION_ABOVE_SUPPORTED ; + return res; +} + +static Bool NewtWindows_setFullscreenEWMH (Display *dpy, Window root, Window w, int emwhMask, Bool isVisible, Bool fullscreen) { Atom _NET_WM_STATE = XInternAtom( dpy, "_NET_WM_STATE", False ); Atom _NET_WM_STATE_ABOVE = XInternAtom( dpy, "_NET_WM_STATE_ABOVE", False ); Atom _NET_WM_STATE_FULLSCREEN = XInternAtom( dpy, "_NET_WM_STATE_FULLSCREEN", False ); - - Atom types[2]={0}; - int ntypes=0; + Status s = Success; + + if(0 == emwhMask) { + return False; + } - types[ntypes++] = _NET_WM_STATE_FULLSCREEN; - types[ntypes++] = _NET_WM_STATE_ABOVE; + if(!isVisible) { + if(True==fullscreen) { + // Update Client State first (-> ABOVE) + Atom types[2]={0}; + int ntypes=0; - XEvent xev; - memset ( &xev, 0, sizeof(xev) ); - - xev.type = ClientMessage; - xev.xclient.window = w; - xev.xclient.message_type = _NET_WM_STATE; - xev.xclient.format = 32; - - if(True==fullscreen) { - xev.xclient.data.l[0] = _NET_WM_STATE_ADD; - xev.xclient.data.l[1] = _NET_WM_STATE_FULLSCREEN; - xev.xclient.data.l[2] = _NET_WM_STATE_ABOVE; - xev.xclient.data.l[3] = 1; //source indication for normal applications - XChangeProperty( dpy, w, _NET_WM_STATE, XA_ATOM, 32, PropModeReplace, (unsigned char *)&types, ntypes); + if( 0 != ( _NET_WM_ACTION_FULLSCREEN_SUPPORTED & emwhMask ) ) { + types[ntypes++] = _NET_WM_STATE_FULLSCREEN; + } + if( 0 != ( _NET_WM_ACTION_ABOVE_SUPPORTED & emwhMask ) ) { + types[ntypes++] = _NET_WM_STATE_ABOVE; + } + XChangeProperty( dpy, w, _NET_WM_STATE, XA_ATOM, 32, PropModeReplace, (unsigned char *)&types, ntypes); + XSync(dpy, False); + DBG_PRINT( "X11: reconfigureWindow0 FULLSCREEN Old on:%d (xsend-status %d)\n", fullscreen, s); + } } else { - xev.xclient.data.l[0] = _NET_WM_STATE_REMOVE; - xev.xclient.data.l[1] = _NET_WM_STATE_FULLSCREEN; - xev.xclient.data.l[2] = _NET_WM_STATE_ABOVE; - xev.xclient.data.l[3] = 1; //source indication for normal applications - } + if(fullscreen) { + NewtWindows_setCWAbove(dpy, w); + } + XEvent xev; + long mask = SubstructureNotifyMask | SubstructureRedirectMask ; + const int src_i = 1; //source indication for normal applications + int i=0; + + memset ( &xev, 0, sizeof(xev) ); + + xev.type = ClientMessage; + xev.xclient.window = w; + xev.xclient.message_type = _NET_WM_STATE; + xev.xclient.format = 32; + + xev.xclient.data.l[i++] = ( True == fullscreen ) ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE ; + if( 0 != ( _NET_WM_ACTION_FULLSCREEN_SUPPORTED & emwhMask ) ) { + xev.xclient.data.l[i++] = _NET_WM_STATE_FULLSCREEN; + } + if( 0 != ( _NET_WM_ACTION_ABOVE_SUPPORTED & emwhMask ) ) { + xev.xclient.data.l[i++] = _NET_WM_STATE_ABOVE; + } + xev.xclient.data.l[3] = src_i; + s = XSendEvent (dpy, root, False, mask, &xev ); + } XSync(dpy, False); - XSendEvent (dpy, root, False, SubstructureRedirectMask | SubstructureNotifyMask, &xev ); + DBG_PRINT( "X11: reconfigureWindow0 FULLSCREEN EWMH ON %d, emwhMask 0x%X, visible %d (xsend-status %d)\n", fullscreen, emwhMask, isVisible, s); + return Success == s; } #define USE_SENDIO_DIRECT 1 @@ -1369,6 +1452,35 @@ JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_x11_X11Window_initIDs0 return JNI_TRUE; } +static Bool WaitForMapNotify( Display *dpy, XEvent *event, XPointer arg ) { + return (event->type == MapNotify) && (event->xmap.window == (Window) arg); +} + +static Bool WaitForUnmapNotify( Display *dpy, XEvent *event, XPointer arg ) { + return (event->type == UnmapNotify) && (event->xmap.window == (Window) arg); +} + +static void NewtWindows_setPosSize(Display *dpy, Window w, jint x, jint y, jint width, jint height) { + if(width>0 && height>0 || x>=0 && y>=0) { // resize/position if requested + XWindowChanges xwc; + int flags = 0; + + memset(&xwc, 0, sizeof(XWindowChanges)); + if(0<=x && 0<=y) { + flags |= CWX | CWY; + xwc.x=x; + xwc.y=y; + } + if(0<width && 0<height) { + flags |= CWWidth | CWHeight; + xwc.width=width; + xwc.height=height; + } + XConfigureWindow(dpy, w, flags, &xwc); + XSync(dpy, False); + } +} + /* * Class: jogamp_newt_driver_x11_X11Window * Method: CreateWindow @@ -1466,16 +1578,24 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_x11_X11Window_CreateWindow0 visual, AllocNone); - window = XCreateWindow(dpy, - windowParent, - x, y, - width, height, - 0, // border width - depth, - InputOutput, - visual, - attrMask, - &xswa); + { + int _x = x, _y = y; // pos for CreateWindow, might be tweaked + if(0>_x || 0>_y) { + // user didn't requested specific position, use WM default + _x = 0; + _y = 0; + } + window = XCreateWindow(dpy, + windowParent, + _x, _y, // only a hint, WM most likely will override + width, height, + 0, // border width + depth, + InputOutput, + visual, + attrMask, + &xswa); + } if(0==window) { NewtCommon_throwNewRuntimeException(env, "could not create Window, bail out!"); @@ -1490,13 +1610,36 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_x11_X11Window_CreateWindow0 XSync(dpy, False); // since native creation happens at setVisible(true) .. - // we can pre-map the window here to be able to gather the insets. - XMapWindow(dpy, window); - XSync(dpy, False); + // we can pre-map the window here to be able to gather the insets and position. { - // update insets + XEvent event; int left, right, top, bottom; + Bool userPos = 0<=x && 0<=y ; + + XMapWindow(dpy, window); + XIfEvent( dpy, &event, WaitForMapNotify, (XPointer) window ); // wait to get proper insets values + (*env)->CallVoidMethod(env, jwindow, visibleChangedID, JNI_TRUE); + NewtWindows_updateInsets(env, jwindow, dpy, window, &left, &right, &top, &bottom); + if(!userPos) { + // get position from WM + int dest_x, dest_y; + Window child; + XTranslateCoordinates(dpy, window, windowParent, 0, 0, &dest_x, &dest_y, &child); + x = (int)dest_x; y = (int)dest_y; + } + DBG_PRINT("X11: [CreateWindow]: client: %d/%d %dx%d (is user-pos %d)\n", x, y, width, height, userPos); + + x -= left; // top-level + y -= top; // top-level + if(0>x) { x = 0; } + if(0>y) { y = 0; } + DBG_PRINT("X11: [CreateWindow]: top-level: %d/%d\n", x, y); + if(userPos) { + // mark pos as undef, which cases java to wait for WM reported pos + (*env)->CallVoidMethod(env, jwindow, positionChangedID, -1, -1); + } + NewtWindows_setPosSize(dpy, window, x, y, width, height); } DBG_PRINT( "X11: [CreateWindow] created window %p on display %p\n", (void*)window, dpy); @@ -1547,31 +1690,6 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_x11_X11Window_CloseWindow0 DBG_PRINT( "X11: CloseWindow END\n"); } -static void NewtWindows_setPosSize(Display *dpy, int screen_index, Window w, jint x, jint y, jint width, jint height, Bool undecorated) -{ - if(width>0 && height>0 || x>=0 && y>=0) { // resize/position if requested - XWindowChanges xwc; - - DBG_PRINT( "X11: reconfigureWindow0 %d/%d %dx%d\n", x, y, width, height); - - memset(&xwc, 0, sizeof(XWindowChanges)); - xwc.x=x; - xwc.y=y; - xwc.width=width; - xwc.height=height; - XConfigureWindow(dpy, w, (CWX | CWY | CWWidth | CWHeight), &xwc); - XSync(dpy, False); - } -} - -static Bool WaitForMapNotify( Display *dpy, XEvent *event, XPointer arg ) { - return (event->type == MapNotify) && (event->xmap.window == (Window) arg); -} - -static Bool WaitForUnmapNotify( Display *dpy, XEvent *event, XPointer arg ) { - return (event->type == UnmapNotify) && (event->xmap.window == (Window) arg); -} - /* * Class: jogamp_newt_driver_x11_X11Window * Method: reconfigureWindow0 @@ -1582,33 +1700,42 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_x11_X11Window_reconfigureWindow0 jint x, jint y, jint width, jint height, jint flags) { Display * dpy = (Display *) (intptr_t) jdisplay; - Screen * scrn = ScreenOfDisplay(dpy, (int)screen_index); Window w = (Window)jwindow; - Window root = XRootWindowOfScreen(scrn); + Window root = RootWindow(dpy, screen_index); Window parent = (0!=jparent)?(Window)jparent:root; - Window topParentParent; - Window topParentWindow; XEvent event; - Bool tempInvisible = ( TST_FLAG_CHANGE_FULLSCREEN(flags) || TST_FLAG_CHANGE_PARENTING(flags) ) && - !TST_FLAG_CHANGE_VISIBILITY(flags) && TST_FLAG_IS_VISIBLE(flags) ; + Bool isVisible = !TST_FLAG_CHANGE_VISIBILITY(flags) && TST_FLAG_IS_VISIBLE(flags) ; + Bool tempInvisible = ( TST_FLAG_CHANGE_FULLSCREEN(flags) || TST_FLAG_CHANGE_PARENTING(flags) ) && isVisible ; + int fsEWMHMask = TST_FLAG_CHANGE_FULLSCREEN(flags) ? NewtWindows_isFullscreenEWMHSupported(dpy, w) : 0; displayDispatchErrorHandlerEnable(1, env); - topParentParent = NewtWindows_getParent (dpy, parent); - topParentWindow = NewtWindows_getParent (dpy, w); - - DBG_PRINT( "X11: reconfigureWindow0 dpy %p, scrn %d/%p, parent %p/%p (top %p), win %p (top %p), %d/%d %dx%d, parentChange %d, hasParent %d, decorationChange %d, undecorated %d, fullscreenChange %d, fullscreen %d, visibleChange %d, visible %d, tempInvisible %d\n", - (void*)dpy, screen_index, (void*)scrn, (void*) jparent, (void*)parent, (void*) topParentParent, (void*)w, (void*)topParentWindow, + DBG_PRINT( "X11: reconfigureWindow0 dpy %p, scrn %d, parent %p/%p, win %p, %d/%d %dx%d, parentChange %d, hasParent %d, decorationChange %d, undecorated %d, fullscreenChange %d, fullscreen %d, visibleChange %d, visible %d, tempInvisible %d, fsEWMHMask %d\n", + (void*)dpy, screen_index, (void*) jparent, (void*)parent, (void*)w, x, y, width, height, TST_FLAG_CHANGE_PARENTING(flags), TST_FLAG_HAS_PARENT(flags), TST_FLAG_CHANGE_DECORATION(flags), TST_FLAG_IS_UNDECORATED(flags), TST_FLAG_CHANGE_FULLSCREEN(flags), TST_FLAG_IS_FULLSCREEN(flags), - TST_FLAG_CHANGE_VISIBILITY(flags), TST_FLAG_IS_VISIBLE(flags), tempInvisible); + TST_FLAG_CHANGE_VISIBILITY(flags), TST_FLAG_IS_VISIBLE(flags), tempInvisible, fsEWMHMask); + + // FS Note: To toggle FS, utilizing the _NET_WM_STATE_FULLSCREEN WM state shall be enough. + // However, we have to consider other cases like reparenting and WM which don't support it. + + if( fsEWMHMask && TST_FLAG_CHANGE_FULLSCREEN(flags) && !TST_FLAG_CHANGE_PARENTING(flags) && isVisible ) { + NewtWindows_setFullscreenEWMH(dpy, root, w, fsEWMHMask, isVisible, TST_FLAG_IS_FULLSCREEN(flags)); + displayDispatchErrorHandlerEnable(0, env); + return; + } + + if( fsEWMHMask && TST_FLAG_CHANGE_FULLSCREEN(flags) && !TST_FLAG_IS_FULLSCREEN(flags) ) { // FS off + NewtWindows_setFullscreenEWMH(dpy, root, w, fsEWMHMask, isVisible, True); + } if( tempInvisible ) { DBG_PRINT( "X11: reconfigureWindow0 TEMP VISIBLE OFF\n"); XUnmapWindow(dpy, w); XIfEvent( dpy, &event, WaitForUnmapNotify, (XPointer) w ); + // no need to notify the java side .. just temp change } if( TST_FLAG_CHANGE_PARENTING(flags) && !TST_FLAG_HAS_PARENT(flags) ) { @@ -1618,26 +1745,12 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_x11_X11Window_reconfigureWindow0 XSync(dpy, False); } - if( TST_FLAG_CHANGE_FULLSCREEN(flags) && TST_FLAG_IS_FULLSCREEN(flags) ) { // FS on - // TOP: in -> out - DBG_PRINT( "X11: reconfigureWindow0 FULLSCREEN in->out\n"); - NewtWindows_setFullscreen(dpy, root, w, True ); - XSync(dpy, False); - } - - DBG_PRINT( "X11: reconfigureWindow0 DECORATIONS\n"); + DBG_PRINT( "X11: reconfigureWindow0 DECORATIONS %d\n", !TST_FLAG_IS_UNDECORATED(flags)); NewtWindows_setDecorations (dpy, w, TST_FLAG_IS_UNDECORATED(flags) ? False : True); XSync(dpy, False); - if( TST_FLAG_CHANGE_FULLSCREEN(flags) && !TST_FLAG_IS_FULLSCREEN(flags) ) { // FS off - // CHILD: out -> in - DBG_PRINT( "X11: reconfigureWindow0 FULLSCREEN out->in\n"); - NewtWindows_setFullscreen(dpy, root, w, False ); - XSync(dpy, False); - } - - DBG_PRINT( "X11: reconfigureWindow0 setPosSize\n"); - NewtWindows_setPosSize(dpy, screen_index, w, x, y, width, height, TST_FLAG_IS_UNDECORATED(flags) ? True : False); + DBG_PRINT( "X11: reconfigureWindow0 setPosSize %d/%d %dx%d\n", x, y, width, height); + NewtWindows_setPosSize(dpy, w, x, y, width, height); if( TST_FLAG_CHANGE_PARENTING(flags) && TST_FLAG_HAS_PARENT(flags) ) { // CHILD: out -> in @@ -1650,6 +1763,7 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_x11_X11Window_reconfigureWindow0 DBG_PRINT( "X11: reconfigureWindow0 TEMP VISIBLE ON\n"); XMapRaised(dpy, w); XIfEvent( dpy, &event, WaitForMapNotify, (XPointer) w ); + // no need to notify the java side .. just temp change } if( TST_FLAG_CHANGE_VISIBILITY(flags) ) { @@ -1663,6 +1777,10 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_x11_X11Window_reconfigureWindow0 XSync(dpy, False); } + if( fsEWMHMask && TST_FLAG_CHANGE_FULLSCREEN(flags) && TST_FLAG_IS_FULLSCREEN(flags) ) { // FS on + NewtWindows_setFullscreenEWMH(dpy, root, w, fsEWMHMask, isVisible, True); + } + displayDispatchErrorHandlerEnable(0, env); DBG_PRINT( "X11: reconfigureWindow0 X\n"); diff --git a/src/test/com/jogamp/opengl/test/junit/newt/TestWindows01NEWT.java b/src/test/com/jogamp/opengl/test/junit/newt/TestWindows01NEWT.java index 7c9de77b6..979f640e6 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/TestWindows01NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/TestWindows01NEWT.java @@ -28,19 +28,12 @@ package com.jogamp.opengl.test.junit.newt; -import java.lang.reflect.*; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - import org.junit.Assert; -import org.junit.Before; import org.junit.BeforeClass; -import org.junit.After; -import org.junit.AfterClass; import org.junit.Test; import javax.media.nativewindow.*; +import javax.media.nativewindow.util.Point; import com.jogamp.newt.*; import java.io.IOException; @@ -53,11 +46,13 @@ public class TestWindows01NEWT extends UITestCase { @BeforeClass public static void initClass() { NativeWindowFactory.initSingleton(true); - width = 640; - height = 480; + width = 256; + height = 256; } - static Window createWindow(Screen screen, Capabilities caps, int width, int height, boolean onscreen, boolean undecorated) { + static Window createWindow(Capabilities caps, int x, int y, int width, int height, boolean onscreen, boolean undecorated) throws InterruptedException { + final boolean userPos = x>=0 && y>=0 ; // user has specified a position + Assert.assertNotNull(caps); caps.setOnscreen(onscreen); // System.out.println("Requested: "+caps); @@ -65,110 +60,120 @@ public class TestWindows01NEWT extends UITestCase { // // Create native windowing resources .. X11/Win/OSX // - Window window = NewtFactory.createWindow(screen, caps); + Window window = NewtFactory.createWindow(caps); Assert.assertNotNull(window); + Screen screen = window.getScreen(); + Display display = screen.getDisplay(); window.setUndecorated(onscreen && undecorated); + if(userPos) { + window.setPosition(x, y); + } window.setSize(width, height); Assert.assertEquals(false,window.isNativeValid()); Assert.assertEquals(false,window.isVisible()); window.setVisible(true); + // System.err.println("************* Created: "+window); + + Assert.assertEquals(true,display.isNativeValid()); + Assert.assertEquals(true,screen.isNativeValid()); Assert.assertEquals(true,window.isVisible()); Assert.assertEquals(true,window.isNativeValid()); - // Assert.assertEquals(width,window.getWidth()); - // Assert.assertEquals(height,window.getHeight()); - // System.out.println("Created: "+window); + Assert.assertEquals(width, window.getWidth()); + Assert.assertEquals(height, window.getHeight()); + + Point p0 = window.getLocationOnScreen(null); + Assert.assertEquals(p0.getX(), window.getX()); + Assert.assertEquals(p0.getY(), window.getY()); + if(userPos) { + Assert.assertEquals(x, window.getX()); + Assert.assertEquals(y, window.getY()); + } - // - // Create native OpenGL resources .. XGL/WGL/CGL .. - // equivalent to GLAutoDrawable methods: setVisible(true) - // CapabilitiesImmutable chosenCapabilities = window.getGraphicsConfiguration().getNativeGraphicsConfiguration().getChosenCapabilities(); Assert.assertNotNull(chosenCapabilities); Assert.assertTrue(chosenCapabilities.getGreenBits()>=5); Assert.assertTrue(chosenCapabilities.getBlueBits()>=5); Assert.assertTrue(chosenCapabilities.getRedBits()>=5); Assert.assertEquals(chosenCapabilities.isOnscreen(),onscreen); - + return window; } - static void destroyWindow(Display display, Screen screen, Window window) { - if(null!=window) { - window.destroy(); + static void destroyWindow(Window window, boolean last) { + if(null==window) { + return; } - if(null!=screen) { - screen.destroy(); - } - if(null!=display) { - display.destroy(); + Screen screen = window.getScreen(); + Display display = screen.getDisplay(); + window.destroy(); + // System.err.println("************* Destroyed: "+window); + if(last) { + Assert.assertEquals(false,screen.isNativeValid()); + Assert.assertEquals(false,display.isNativeValid()); + } else { + Assert.assertEquals(true,screen.isNativeValid()); + Assert.assertEquals(true,display.isNativeValid()); } + Assert.assertEquals(false,window.isNativeValid()); + Assert.assertEquals(false,window.isVisible()); } + @Test - public void testWindowNativeRecreate01Simple() throws InterruptedException { + public void testWindowDecorSimpleWMPos() throws InterruptedException { Capabilities caps = new Capabilities(); Assert.assertNotNull(caps); - Display display = NewtFactory.createDisplay(null); // local display - Assert.assertNotNull(display); - Screen screen = NewtFactory.createScreen(display, 0); // screen 0 - Assert.assertNotNull(screen); - Window window = createWindow(screen, caps, width, height, true /* onscreen */, false /* undecorated */); - window.destroy(); - Assert.assertEquals(false,window.isNativeValid()); - Assert.assertEquals(false,window.isVisible()); + Window window = createWindow(caps, -1, -1, width, height, true /* onscreen */, false /* undecorated */); + destroyWindow(window, true); + } - window.setVisible(true); - Assert.assertEquals(true,window.isNativeValid()); - Assert.assertEquals(true,window.isVisible()); - Thread.sleep(100); // 100 ms - destroyWindow(display, screen, window); + @Test + public void testWindowDecorSimpleUserPos() throws InterruptedException { + Capabilities caps = new Capabilities(); + Assert.assertNotNull(caps); + + Window window = createWindow(caps, 100, 100, width, height, true /* onscreen */, false /* undecorated */); + destroyWindow(window, true); } @Test - public void testWindowDecor01Simple() throws InterruptedException { + public void testWindowNativeRecreate01Simple() throws InterruptedException { Capabilities caps = new Capabilities(); Assert.assertNotNull(caps); - Display display = NewtFactory.createDisplay(null); // local display - Assert.assertNotNull(display); - Screen screen = NewtFactory.createScreen(display, 0); // screen 0 - Assert.assertNotNull(screen); - - Window window = createWindow(screen, caps, width, height, true /* onscreen */, false /* undecorated */); - Thread.sleep(100); // 100 ms - destroyWindow(display, screen, window); - } + Window window = createWindow(caps, -1, -1, width, height, true /* onscreen */, false /* undecorated */); + destroyWindow(window, true); + + window.setVisible(true); + Assert.assertEquals(true,window.isNativeValid()); + Assert.assertEquals(true,window.isVisible()); + Assert.assertEquals(width, window.getWidth()); + Assert.assertEquals(height, window.getHeight()); + + destroyWindow(window, true); + } + @Test - public void testWindowDecor02DestroyWinTwiceA() throws InterruptedException { + public void testWindowDecorDestroyWinTwiceA() throws InterruptedException { Capabilities caps = new Capabilities(); Assert.assertNotNull(caps); - Display display = NewtFactory.createDisplay(null); // local display - Assert.assertNotNull(display); - Screen screen = NewtFactory.createScreen(display, 0); // screen 0 - Assert.assertNotNull(screen); - - Window window = createWindow(screen, caps, width, height, true /* onscreen */, false /* undecorated */); - Thread.sleep(100); // 100 ms - destroyWindow(null, null, window); - destroyWindow(display, screen, window); + + Window window = createWindow(caps, -1, -1, width, height, true /* onscreen */, false /* undecorated */); + destroyWindow(window, true); + destroyWindow(window, true); } @Test - public void testWindowDecor03TwoWin() throws InterruptedException { + public void testWindowDecorTwoWin() throws InterruptedException { Capabilities caps = new Capabilities(); Assert.assertNotNull(caps); - Display display = NewtFactory.createDisplay(null); // local display - Assert.assertNotNull(display); - Screen screen = NewtFactory.createScreen(display, 0); // screen 0 - Assert.assertNotNull(screen); - - Window window1 = createWindow(screen, caps, width, height, true /* onscreen */, false /* undecorated */); - Window window2 = createWindow(screen, caps, width, height, true /* onscreen */, false /* undecorated */); - Thread.sleep(100); // 100 ms - destroyWindow(null, null, window2); - destroyWindow(display, screen, window1); + + Window window1 = createWindow(caps, -1, -1, width, height, true /* onscreen */, false /* undecorated */); + Window window2 = createWindow(caps, 100, 100, width, height, true /* onscreen */, false /* undecorated */); + destroyWindow(window2, false); + destroyWindow(window1, true); } public static void main(String args[]) throws IOException { |