Jogamp
Bug 1249: NEWT/X11 Visibility: Listening to more events for updated state; Adding...
authorSven Gothel <sgothel@jausoft.com>
Fri, 9 Oct 2015 23:47:07 +0000 (01:47 +0200)
committerSven Gothel <sgothel@jausoft.com>
Fri, 9 Oct 2015 23:47:07 +0000 (01:47 +0200)
1) More visibility detection on post ConfigureNotify events,
since the latter may not yet contain the updated visibility state
as it whould (WM bug!):
  - EnterNotify
  - LeaveNotify
  - Disabled
    - Expose
    - VisibilityNotify

2) Introducing quirks.
   Setting QUIRK_BIT_VISIBILITY to handle the issue where
   visibility -> false could not even be set.

make/scripts/tests.sh
src/newt/classes/jogamp/newt/WindowImpl.java
src/newt/native/X11Display.c
src/newt/native/X11Window.c

index 60ef1e9..62f7dc5 100644 (file)
@@ -252,7 +252,7 @@ function jrun() {
     #D_ARGS="-Dnewt.debug.Window -Djogl.debug.GLDrawable"
     #D_ARGS="-Dnewt.debug.Window -Dnewt.debug.Window.KeyEvent"
     #D_ARGS="-Dnewt.debug.Window -Dnewt.debug.Window.MouseEvent -Dnewt.debug.Window.KeyEvent"
-    #D_ARGS="-Dnewt.debug.Window"
+    D_ARGS="-Dnewt.debug.Window"
     #D_ARGS="-Dnewt.debug.Window.visibility.failure.freeze"
     #D_ARGS="-Xprof"
     #D_ARGS="-Dnativewindow.debug=all -Djogl.debug=all -Dnewt.debug=all"
@@ -474,7 +474,7 @@ function testawtswt() {
 #testawt com.jogamp.opengl.test.junit.jogl.demos.gl2.awt.TestGearsAWTAnalyzeBug455 $*
 #testawt com.jogamp.opengl.test.junit.jogl.demos.gl2.awt.TestGearsGLJPanelAWT $*
 #testawt com.jogamp.opengl.test.junit.jogl.demos.gl2.awt.TestGearsGLJPanelAWTBug450 $*
-testawt com.jogamp.opengl.test.junit.jogl.demos.gl2.newt.TestGearsNewtAWTWrapper $*
+#testawt com.jogamp.opengl.test.junit.jogl.demos.gl2.newt.TestGearsNewtAWTWrapper $*
 #testnoawt com.jogamp.opengl.test.junit.jogl.demos.gl2.newt.TestGearsNEWT $*
 #testnoawt com.jogamp.opengl.test.junit.jogl.demos.gl2.newt.TestTeapotNEWT $*
 #testnoawt com.jogamp.opengl.test.junit.jogl.demos.gl3.newt.TestGeomShader01TextureGL3NEWT $*
@@ -809,8 +809,11 @@ testawt com.jogamp.opengl.test.junit.jogl.demos.gl2.newt.TestGearsNewtAWTWrapper
 # Bug 1249 - NEWT X11: 
 #   - setVisible(false) IconicState not listening to _NET_WM_STATE_HIDDEN; 
 #   - setVisible(true) not restoring from _NET_WM_STATE_HIDDEN
-#testnoawt com.jogamp.opengl.test.junit.newt.TestGLWindows00NEWT $*
+#testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $*
+testnoawt com.jogamp.opengl.test.junit.newt.TestGLWindows00NEWT $*
 #testnoawt com.jogamp.opengl.test.junit.newt.parenting.TestParenting01NEWT $*
+#testnoawt com.jogamp.opengl.test.junit.newt.TestDisplayLifecycle01NEWT
+#testnoawt com.jogamp.opengl.test.junit.newt.TestDisplayLifecycle02NEWT
 
 #
 # NEWT Parenting (w/ NEWT, AWT or SWT)
index 107851d..ab2cf97 100644 (file)
@@ -149,7 +149,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
     private static final PointerType[] constMousePointerTypes = new PointerType[] { PointerType.Mouse };
 
     //
-    // Volatile: Multithread Mutable Access
+    // Volatile: Multithreaded Mutable Access
     //
     private volatile long windowHandle = 0; // lifecycle critical
     private volatile int pixWidth = 128, pixHeight = 128; // client-area size w/o insets in pixel units, default: may be overwritten by user
@@ -182,6 +182,25 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
     private LifecycleHook lifecycleHook = null;
 
     //
+    // Quirks
+    //
+
+    /**
+     * Bug 1249 and Bug 1250: Visibility issues on X11
+     * <ul>
+     * <li>setVisible(false) IconicState not listening to _NET_WM_STATE_HIDDEN</li>
+     * <li>setVisible(true) not restoring from _NET_WM_STATE_HIDDEN</li>
+     * </ul>
+     * <p>
+     * If {@code true} fall back to traditional visibility state,
+     * i.e. {@code fast=true}.
+     * </p>
+     */
+    static final int QUIRK_BIT_VISIBILITY = 0;
+    /** Regular state mask */
+    /* pp */ static final Bitfield quirks = Bitfield.Factory.synchronize(Bitfield.Factory.create(32));
+
+    //
     // State Mask
     //
 
@@ -1206,9 +1225,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
         }
         reconfigureWindowImpl(x, y, width, height, mask);
     }
+
     final void setVisibleActionImpl(final boolean visible) {
         boolean nativeWindowCreated = false;
-        boolean madeVisible = false;
+        int madeVisible = -1;
 
         final RecursiveLock _lock = windowLock;
         _lock.lock();
@@ -1226,16 +1246,31 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
             if(!isNativeValid() && visible) {
                 if( 0<getWidth()*getHeight() ) {
                     nativeWindowCreated = createNative();
-                    madeVisible = nativeWindowCreated;
+                    madeVisible = nativeWindowCreated ? 1 : -1;
                 }
                 // always flag visible, allowing a retry ..
                 stateMask.set(STATE_BIT_VISIBLE);
             } else if(stateMask.get(STATE_BIT_VISIBLE) != visible) {
                 if(isNativeValid()) {
                     // Skip WM if child-window!
-                    setVisibleImpl(visible /* visible */, isChildWindow() /* fast */, getX(), getY(), getWidth(), getHeight());
-                    WindowImpl.this.waitForVisible(visible, false);
-                    madeVisible = visible;
+                    final boolean hasVisibilityQuirk = quirks.get(QUIRK_BIT_VISIBILITY);
+                    setVisibleImpl(visible /* visible */, hasVisibilityQuirk || isChildWindow() /* fast */,
+                                   getX(), getY(), getWidth(), getHeight());
+                    if( 0 > WindowImpl.this.waitForVisible(visible, false) ) {
+                        if( !hasVisibilityQuirk ) {
+                            quirks.set(QUIRK_BIT_VISIBILITY);
+                            if( DEBUG_IMPLEMENTATION ) {
+                                System.err.println("Setting VISIBILITY QUIRK, due to setVisible("+visible+") failure");
+                            }
+                            setVisibleImpl(visible /* visible */, true /* fast */,
+                                           getX(), getY(), getWidth(), getHeight());
+                            if( 0 <= WindowImpl.this.waitForVisible(visible, false) ) {
+                                madeVisible = visible ? 1 : 0;
+                            } // else: still not working .. bail out
+                        } // else: no other remedy known .. bail out
+                    } else {
+                        madeVisible = visible ? 1 : 0;
+                    }
                 } else {
                     stateMask.set(STATE_BIT_VISIBLE);
                 }
@@ -1267,7 +1302,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
             }
             _lock.unlock();
         }
-        if( nativeWindowCreated || madeVisible ) {
+        if( nativeWindowCreated || 1==madeVisible ) {
             sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener
         }
     }
@@ -3354,7 +3389,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
     //
 
     public final void sendMouseEvent(final short eventType, final int modifiers,
-                               final int x, final int y, final short button, final float rotation) {
+                                     final int x, final int y, final short button, final float rotation) {
         doMouseEvent(false, false, eventType, modifiers, x, y, button, MouseEvent.getRotationXYZ(rotation, modifiers), 1f);
     }
 
@@ -4685,41 +4720,57 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
     // Accumulated actions
     //
 
+    /** Triggered by implementation. */
+    protected final void sendMouseEventRequestFocus(final short eventType, final int modifiers,
+                                                    final int x, final int y, final short button, final float rotation) {
+        sendMouseEvent(eventType, modifiers, x, y, button, rotation);
+        requestFocus(false /* wait */);
+    }
     /**
-     * Triggered by implementation's WM events to update the client-area position, size, insets and maximized flags.
+     * Triggered by implementation's WM events to update the visibility state and send- or enqueue one mouse event
      *
      * @param defer
-     * @param newX
-     * @param newY
-     * @param newWidth
-     * @param newHeight
-     * @param left insets, -1 ignored
-     * @param right insets, -1 ignored
-     * @param top insets, -1 ignored
-     * @param bottom insets, -1 ignored
-     * @param focusChange -1 ignored, 0 unfocused, > 0 focused
      * @param visibleChange -1 ignored, 0 invisible, > 0 visible
-     * @param force
+     * @param entranceChange -1 ignored, 0 exit, > 0 enter
+     * @param eventType 0 ignored, > 0 [send|enqueue]MouseEvent
+     * @param modifiers
+     * @param x
+     * @param y
+     * @param button
+     * @param rotation
      */
-    protected final void sizePosInsetsFocusVisibleChanged(final boolean defer,
-                                                          final int newX, final int newY,
-                                                          final int newWidth, final int newHeight,
-                                                          final int left, final int right, final int top, final int bottom,
-                                                          final int focusChange,
-                                                          final int visibleChange,
-                                                          final boolean force) {
-        sizeChanged(defer, newWidth, newHeight, force);
-        positionChanged(defer, newX, newY);
-        insetsChanged(defer, left, right, top, bottom);
-        if( 0 <= focusChange ) { // ignore focus < 0
-            focusChanged(defer, 0 < focusChange);
+    protected final void visibleChangedSendMouseEvent(final boolean defer, final int visibleChange,
+                                                      final short eventType, final int modifiers,
+                                                      final int x, final int y, final short button, final float rotation) {
+        if( 0 <= visibleChange ) { // ignore visible < 0
+            visibleChanged(defer, 0 < visibleChange);
+        }
+        if( 0 < eventType ) {
+            if( defer ) {
+                enqueueMouseEvent(false /* wait */, eventType, modifiers, x, y, button, rotation);
+            } else {
+                sendMouseEvent(eventType, modifiers, x, y, button, rotation);
+            }
         }
+    }
+    /**
+     * Triggered by implementation's WM events to update the content
+     * @param defer if true sent event later, otherwise wait until processed.
+     * @param visibleChange -1 ignored, 0 invisible, > 0 visible
+     * @param x dirty-region y-pos in pixel units
+     * @param y dirty-region x-pos in pixel units
+     * @param width dirty-region width in pixel units
+     * @param height dirty-region height in pixel units
+     */
+    protected final void visibleChangedWindowRepaint(final boolean defer, final int visibleChange,
+                                                     final int x, final int y, final int width, final int height) {
         if( 0 <= visibleChange ) { // ignore visible < 0
             visibleChanged(defer, 0 < visibleChange);
         }
+        windowRepaint(defer, x, y, width, height);
     }
     /**
-     * Triggered by implementation's WM events to update the client-area position, size, insets and maximized flags.
+     * Triggered by implementation's WM events to update the focus and visibility state
      *
      * @param defer
      * @param focusChange -1 ignored, 0 unfocused, > 0 focused
@@ -4761,6 +4812,39 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
      * @param newY
      * @param newWidth
      * @param newHeight
+     * @param left insets, -1 ignored
+     * @param right insets, -1 ignored
+     * @param top insets, -1 ignored
+     * @param bottom insets, -1 ignored
+     * @param focusChange -1 ignored, 0 unfocused, > 0 focused
+     * @param visibleChange -1 ignored, 0 invisible, > 0 visible
+     * @param force
+     */
+    protected final void sizePosInsetsFocusVisibleChanged(final boolean defer,
+                                                          final int newX, final int newY,
+                                                          final int newWidth, final int newHeight,
+                                                          final int left, final int right, final int top, final int bottom,
+                                                          final int focusChange,
+                                                          final int visibleChange,
+                                                          final boolean force) {
+        sizeChanged(defer, newWidth, newHeight, force);
+        positionChanged(defer, newX, newY);
+        insetsChanged(defer, left, right, top, bottom);
+        if( 0 <= focusChange ) { // ignore focus < 0
+            focusChanged(defer, 0 < focusChange);
+        }
+        if( 0 <= visibleChange ) { // ignore visible < 0
+            visibleChanged(defer, 0 < visibleChange);
+        }
+    }
+    /**
+     * Triggered by implementation's WM events to update the client-area position, size, insets and maximized flags.
+     *
+     * @param defer
+     * @param newX
+     * @param newY
+     * @param newWidth
+     * @param newHeight
      * @param maxHorzChange -1 ignored, 0 !maximized, > 0 maximized
      * @param maxVertChange -1 ignored, 0 !maximized, > 0 maximized
      * @param left insets, -1 ignored
@@ -4787,12 +4871,6 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
             visibleChanged(defer, 0 < visibleChange);
         }
     }
-    /** Triggered by implementation. */
-    protected final void sendMouseEventRequestFocus(final short eventType, final int modifiers,
-                               final int x, final int y, final short button, final float rotation) {
-        sendMouseEvent(eventType, modifiers, x, y, button, rotation);
-        requestFocus(false /* wait */);
-    }
 
     //
     // Reflection helper ..
index ab5afd1..32e0f87 100644 (file)
@@ -53,6 +53,8 @@ static jmethodID windowRepaintID = NULL;
 static jmethodID sendMouseEventID = NULL;
 static jmethodID sendKeyEventID = NULL;
 static jmethodID sendMouseEventRequestFocusID = NULL;
+static jmethodID visibleChangedWindowRepaintID = NULL;
+static jmethodID visibleChangedSendMouseEventID = NULL;
 static jmethodID sizePosMaxInsetsVisibleChangedID = NULL;
 
 /**
@@ -260,8 +262,10 @@ JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_x11_DisplayDriver_initIDs0
     reparentNotifyID = (*env)->GetMethodID(env, X11NewtWindowClazz, "reparentNotify", "(J)V");
     windowDestroyNotifyID = (*env)->GetMethodID(env, X11NewtWindowClazz, "windowDestroyNotify", "(Z)Z");
     windowRepaintID = (*env)->GetMethodID(env, X11NewtWindowClazz, "windowRepaint", "(ZIIII)V");
+    visibleChangedWindowRepaintID = (*env)->GetMethodID(env, X11NewtWindowClazz, "visibleChangedWindowRepaint", "(ZIIIII)V");
     sendMouseEventID = (*env)->GetMethodID(env, X11NewtWindowClazz, "sendMouseEvent", "(SIIISF)V");
     sendMouseEventRequestFocusID = (*env)->GetMethodID(env, X11NewtWindowClazz, "sendMouseEventRequestFocus", "(SIIISF)V");
+    visibleChangedSendMouseEventID = (*env)->GetMethodID(env, X11NewtWindowClazz, "visibleChangedSendMouseEvent", "(ZISIIISF)V");
     sendKeyEventID = (*env)->GetMethodID(env, X11NewtWindowClazz, "sendKeyEvent", "(SISSCLjava/lang/String;)V");
 
     if (displayCompletedID == NULL ||
@@ -278,8 +282,10 @@ JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_x11_DisplayDriver_initIDs0
         reparentNotifyID == NULL ||
         windowDestroyNotifyID == NULL ||
         windowRepaintID == NULL ||
+        visibleChangedWindowRepaintID == NULL ||
         sendMouseEventID == NULL ||
         sendMouseEventRequestFocusID == NULL ||
+        visibleChangedSendMouseEventID == NULL ||
         sendKeyEventID == NULL) {
         return JNI_FALSE;
     }
@@ -564,15 +570,23 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_x11_DisplayDriver_DispatchMessage
                 break;
             case EnterNotify:
                 DBG_PRINT( "X11: event . EnterNotify call %p %d/%d\n", (void*)evt.xcrossing.window, evt.xcrossing.x, evt.xcrossing.y);
-                (*env)->CallVoidMethod(env, jw->jwindow, sendMouseEventID, (jshort) EVENT_MOUSE_ENTERED, 
-                                      modifiers,
-                                      (jint) evt.xcrossing.x, (jint) evt.xcrossing.y, (jshort) 0, 0.0f /*rotation*/); 
+                {
+                    uint32_t netWMState = NewtWindows_getNET_WM_STATE(dpy, jw);
+                    int visibleChange = NewtWindows_updateVisibility(env, dpy, jw, netWMState, "EnterNotify");
+                    (*env)->CallVoidMethod(env, jw->jwindow, visibleChangedSendMouseEventID, JNI_FALSE, (jint)visibleChange, 
+                                      (jshort) EVENT_MOUSE_ENTERED, modifiers,
+                                      (jint) evt.xcrossing.x, (jint) evt.xcrossing.y, (jshort) 0, 0.0f /*rotation*/);
+                }
                 break;
             case LeaveNotify:
                 DBG_PRINT( "X11: event . LeaveNotify call %p %d/%d\n", (void*)evt.xcrossing.window, evt.xcrossing.x, evt.xcrossing.y);
-                (*env)->CallVoidMethod(env, jw->jwindow, sendMouseEventID, (jshort) EVENT_MOUSE_EXITED, 
-                                      modifiers,
-                                      (jint) evt.xcrossing.x, (jint) evt.xcrossing.y, (jshort) 0, 0.0f /*rotation*/); 
+                {
+                    uint32_t netWMState = NewtWindows_getNET_WM_STATE(dpy, jw);
+                    int visibleChange = NewtWindows_updateVisibility(env, dpy, jw, netWMState, "LeaveNotify");
+                    (*env)->CallVoidMethod(env, jw->jwindow, visibleChangedSendMouseEventID, JNI_FALSE, (jint)visibleChange, 
+                                      (jshort) EVENT_MOUSE_EXITED, modifiers,
+                                      (jint) evt.xcrossing.x, (jint) evt.xcrossing.y, (jshort) 0, 0.0f /*rotation*/);
+                }
                 break;
             case MappingNotify:
                 DBG_PRINT( "X11: event . MappingNotify call %p type %d\n", (void*)evt.xmapping.window, evt.xmapping.type);
@@ -621,6 +635,8 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_x11_DisplayDriver_DispatchMessage
                 }
                 break;
             case ClientMessage:
+                DBG_PRINT( "X11: event . ClientMessage call %p type 0x%X, sendEvent %d\n", 
+                    (void*)evt.xclient.window, (unsigned int)evt.xclient.message_type, evt.xclient.send_event);
                 if (evt.xclient.send_event==True && evt.xclient.data.l[0]==wm_delete_atom) { // windowDeleteAtom
                     jboolean closed;
                     DBG_PRINT( "X11: event . ClientMessage call %p type 0x%X ..\n", 
@@ -651,13 +667,32 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_x11_DisplayDriver_DispatchMessage
                 }
                 break;
 
+            case  VisibilityNotify:
+                DBG_PRINT( "X11: event .  VisibilityNotify call %p\n", (void*)evt.xvisibility.window);
+                {
+                    #if 0
+                    uint32_t netWMState = NewtWindows_getNET_WM_STATE(dpy, jw);
+                    int visibleChange = NewtWindows_updateVisibility(env, dpy, jw, netWMState, "VisibilityNotify");
+                    if( 0 <= visibleChange ) {
+                        (*env)->CallVoidMethod(env, jw->jwindow, visibleChangedID, JNI_FALSE, 0 < visibleChange ? JNI_TRUE : JNI_FALSE);
+                    }
+                    #endif
+                }
+                break;
+
+
             case Expose:
                 DBG_PRINT( "X11: event . Expose call %p %d/%d %dx%d count %d\n", (void*)evt.xexpose.window,
                     evt.xexpose.x, evt.xexpose.y, evt.xexpose.width, evt.xexpose.height, evt.xexpose.count);
-
                 if (evt.xexpose.count == 0 && evt.xexpose.width > 0 && evt.xexpose.height > 0) {
                     (*env)->CallVoidMethod(env, jw->jwindow, windowRepaintID, JNI_FALSE,
                         evt.xexpose.x, evt.xexpose.y, evt.xexpose.width, evt.xexpose.height);
+                    #if 0
+                    uint32_t netWMState = NewtWindows_getNET_WM_STATE(dpy, jw);
+                    int visibleChange = NewtWindows_updateVisibility(env, dpy, jw, netWMState, "Expose");
+                    (*env)->CallVoidMethod(env, jw->jwindow, visibleChangedWindowRepaintID, JNI_FALSE, (jint)visibleChange,
+                        evt.xexpose.x, evt.xexpose.y, evt.xexpose.width, evt.xexpose.height);
+                    #endif
                 }
                 break;
 
index f10db31..de2bddc 100644 (file)
@@ -848,6 +848,7 @@ JNIEXPORT jlongArray JNICALL Java_jogamp_newt_driver_x11_WindowDriver_CreateWind
     xswa.event_mask  = X11_MOUSE_EVENT_MASK;
     xswa.event_mask |= KeyPressMask | KeyReleaseMask ;
     xswa.event_mask |= FocusChangeMask | SubstructureNotifyMask | StructureNotifyMask | ExposureMask;
+    // xswa.event_mask |= VisibilityChangeMask;
 
     {
         int _x = x, _y = y; // pos for CreateWindow, might be tweaked
http://JogAmp.org git info: FAQ, tutorial and man pages.