diff options
Diffstat (limited to 'src/newt/native/X11Window.c')
-rw-r--r-- | src/newt/native/X11Window.c | 306 |
1 files changed, 212 insertions, 94 deletions
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"); |