6522725: Component in a minimized Frame has focus and receives key events
authorant
Wed, 09 Apr 2008 09:37:07 +0400
changeset 442 687798d7d7b6
parent 441 f5da1014ed23
child 444 b40ad157413b
child 447 0fb5f354ad70
6522725: Component in a minimized Frame has focus and receives key events Summary: XAWT: a window natively focused may request focus in it only synthetically Reviewed-by: son
jdk/src/solaris/classes/sun/awt/X11/XComponentPeer.java
jdk/src/solaris/classes/sun/awt/X11/XDecoratedPeer.java
jdk/src/solaris/classes/sun/awt/X11/XWindowPeer.java
jdk/test/java/awt/Focus/IconifiedFrameFocusChangeTest/IconifiedFrameFocusChangeTest.java
--- a/jdk/src/solaris/classes/sun/awt/X11/XComponentPeer.java	Tue Apr 08 13:32:30 2008 +0400
+++ b/jdk/src/solaris/classes/sun/awt/X11/XComponentPeer.java	Wed Apr 09 09:37:07 2008 +0400
@@ -420,40 +420,36 @@
           case SNFH_SUCCESS_PROCEED:
               // Currently we just generate focus events like we deal with lightweight instead of calling
               // XSetInputFocus on native window
-              if (focusLog.isLoggable(Level.FINER)) focusLog.finer("Proceeding with request to " + lightweightChild + " in " + target);
+              if (focusLog.isLoggable(Level.FINER)) focusLog.finer("Proceeding with request to " +
+                  lightweightChild + " in " + target);
               /**
                * The problems with requests in non-focused window arise because shouldNativelyFocusHeavyweight
                * checks that native window is focused while appropriate WINDOW_GAINED_FOCUS has not yet
                * been processed - it is in EventQueue. Thus, SNFH allows native request and stores request record
-               * in requests list - and it breaks our requests sequence as first record on WGF should be the last focus
-               * owner which had focus before WLF. So, we should not add request record for such requests
+               * in requests list - and it breaks our requests sequence as first record on WGF should be the last
+               * focus owner which had focus before WLF. So, we should not add request record for such requests
                * but store this component in mostRecent - and return true as before for compatibility.
                */
               Window parentWindow = getContainingWindow(target);
-              if (parentWindow != null) {
-                  // and check that it is focused
-                  if (!parentWindow.isFocused()) {
-                      XWindowPeer wpeer = (XWindowPeer)parentWindow.getPeer();
-                      /*
-                       * Fix for 6314575.
-                       * Shouldn't restore focus on 'actualFocusedWindow'
-                       * when a component inside a Frame is requesting it.
-                       */
-                      wpeer.setActualFocusedWindow(null);
+              if (parentWindow == null) {
+                  return rejectFocusRequestHelper("WARNING: Parent window is null");
+              }
+              XWindowPeer wpeer = (XWindowPeer)parentWindow.getPeer();
+              if (wpeer == null) {
+                  return rejectFocusRequestHelper("WARNING: Parent window's peer is null");
+              }
+              /*
+               * Passing null 'actualFocusedWindow' as we don't want to restore focus on it
+               * when a component inside a Frame is requesting focus.
+               * See 6314575 for details.
+               */
+              boolean res = wpeer.requestWindowFocus(null);
 
-                      boolean res = wpeer.requestWindowFocus();
-                      if (focusLog.isLoggable(Level.FINER)) focusLog.finer("Requested window focus: " + res);
-                      // If parent window can be made focused and has been made focused(synchronously)
-                      // then we can proceed with children, otherwise we retreat.
-                      if (!(res && parentWindow.isFocused())) {
-                          focusLog.finer("Waiting for asynchronous processing of window focus request");
-                          KeyboardFocusManagerPeerImpl.removeLastFocusRequest(target);
-                          return false;
-                      }
-                  }
-              } else {
-                  if (focusLog.isLoggable(Level.FINER)) focusLog.finer("WARNING: Parent window is null");
-                  return false;
+              if (focusLog.isLoggable(Level.FINER)) focusLog.finer("Requested window focus: " + res);
+              // If parent window can be made focused and has been made focused(synchronously)
+              // then we can proceed with children, otherwise we retreat.
+              if (!(res && parentWindow.isFocused())) {
+                  return rejectFocusRequestHelper("Waiting for asynchronous processing of the request");
               }
 
               // NOTE: We simulate heavyweight behavior of Motif - component receives focus right
@@ -469,6 +465,12 @@
         return false;
     }
 
+    private boolean rejectFocusRequestHelper(String logMsg) {
+        if (focusLog.isLoggable(Level.FINER)) focusLog.finer(logMsg);
+        KeyboardFocusManagerPeerImpl.removeLastFocusRequest(target);
+        return false;
+    }
+
     void handleJavaFocusEvent(AWTEvent e) {
         if (focusLog.isLoggable(Level.FINER)) focusLog.finer(e.toString());
         if (e.getID() == FocusEvent.FOCUS_GAINED) {
--- a/jdk/src/solaris/classes/sun/awt/X11/XDecoratedPeer.java	Tue Apr 08 13:32:30 2008 +0400
+++ b/jdk/src/solaris/classes/sun/awt/X11/XDecoratedPeer.java	Wed Apr 09 09:37:07 2008 +0400
@@ -1013,16 +1013,6 @@
 
     private void handleWmTakeFocus(XClientMessageEvent cl) {
         focusLog.log(Level.FINE, "WM_TAKE_FOCUS on {0}", new Object[]{this});
-        // A workaround to Metacity issue (see 6613426).
-        // The first check is to skip redundant WM_TAKE_FOCUS on click
-        // in a focused frame. The second check is to allow requesting focus
-        // on click in a frame when its owned window is currently focused.
-        if (this == getNativeFocusedWindowPeer() &&
-            target == XKeyboardFocusManagerPeer.getCurrentNativeFocusedWindow())
-        {
-            focusLog.fine("The window is already focused, skipping.");
-            return;
-        }
         requestWindowFocus(cl.get_data(1), true);
     }
 
@@ -1124,53 +1114,51 @@
         focusLog.fine("Request for decorated window focus");
         // If this is Frame or Dialog we can't assure focus request success - but we still can try
         // If this is Window and its owner Frame is active we can be sure request succedded.
-        Window win = (Window)target;
         Window focusedWindow = XKeyboardFocusManagerPeer.getCurrentNativeFocusedWindow();
         Window activeWindow = XWindowPeer.getDecoratedOwner(focusedWindow);
 
         focusLog.log(Level.FINER, "Current window is: active={0}, focused={1}",
-                     new Object[]{ Boolean.valueOf(win == activeWindow),
-                                   Boolean.valueOf(win == focusedWindow)});
+                     new Object[]{ Boolean.valueOf(target == activeWindow),
+                                   Boolean.valueOf(target == focusedWindow)});
 
         XWindowPeer toFocus = this;
         while (toFocus.nextTransientFor != null) {
             toFocus = toFocus.nextTransientFor;
         }
-
-        if (this == toFocus) {
-            if (focusAllowedFor()) {
-                if (win == activeWindow && win != focusedWindow) {
-                    // Happens when focus is on window child
-                    focusLog.fine("Focus is on child window - transfering it back");
-                    handleWindowFocusInSync(-1);
-                } else {
-                    focusLog.fine("Requesting focus to this window");
-                    if (timeProvided) {
-                        requestXFocus(time);
-                    } else {
-                        requestXFocus();
-                    }
-                }
-                return true;
-            } else {
-                return false;
-            }
-        }
-        else if (toFocus.focusAllowedFor()) {
-            focusLog.fine("Requesting focus to " + toFocus);
-            if (timeProvided) {
-                toFocus.requestXFocus(time);
-            } else {
-                toFocus.requestXFocus();
-            }
-            return false;
-        }
-        else
-        {
+        if (toFocus == null || !toFocus.focusAllowedFor()) {
             // This might change when WM will have property to determine focus policy.
             // Right now, because policy is unknown we can't be sure we succedded
             return false;
         }
+        if (this == toFocus) {
+            if (isWMStateNetHidden()) {
+                focusLog.fine("The window is unmapped, so rejecting the request");
+                return false;
+            }
+            if (target == activeWindow && target != focusedWindow) {
+                // Happens when an owned window is currently focused
+                focusLog.fine("Focus is on child window - transfering it back to the owner");
+                handleWindowFocusInSync(-1);
+                return true;
+            }
+            Window realNativeFocusedWindow = XWindowPeer.getNativeFocusedWindow();
+            focusLog.finest("Real native focused window: " + realNativeFocusedWindow +
+                            "\nKFM's focused window: " + focusedWindow);
+
+            // See 6522725, 6613426.
+            if (target == realNativeFocusedWindow) {
+                focusLog.fine("The window is already natively focused.");
+                return true;
+            }
+        }
+        focusLog.fine("Requesting focus to " + (this == toFocus ? "this window" : toFocus));
+
+        if (timeProvided) {
+            toFocus.requestXFocus(time);
+        } else {
+            toFocus.requestXFocus();
+        }
+        return (this == toFocus);
     }
 
     XWindowPeer actualFocusedWindow = null;
--- a/jdk/src/solaris/classes/sun/awt/X11/XWindowPeer.java	Tue Apr 08 13:32:30 2008 +0400
+++ b/jdk/src/solaris/classes/sun/awt/X11/XWindowPeer.java	Wed Apr 09 09:37:07 2008 +0400
@@ -582,7 +582,7 @@
     }
 
     /*
-     * Converts native focused X window id into Java peer.
+     * Retrives real native focused window and converts it into Java peer.
      */
     static XWindowPeer getNativeFocusedWindowPeer() {
         XBaseWindow baseWindow = XToolkit.windowToXWindow(xGetInputFocus());
@@ -591,6 +591,14 @@
                ((XFocusProxyWindow)baseWindow).getOwner() : null;
     }
 
+    /*
+     * Retrives real native focused window and converts it into Java window.
+     */
+    static Window getNativeFocusedWindow() {
+        XWindowPeer peer = getNativeFocusedWindowPeer();
+        return peer != null ? (Window)peer.target : null;
+    }
+
     boolean isFocusableWindow() {
         if (XToolkit.isToolkitThread() || SunToolkit.isAWTLockHeldByCurrentThread())
         {
@@ -1252,7 +1260,7 @@
         return res;
     }
 
-    private boolean isWMStateNetHidden() {
+    protected boolean isWMStateNetHidden() {
         XNETProtocol protocol = XWM.getWM().getNETProtocol();
         return (protocol != null && protocol.isWMStateNetHidden(this));
     }
@@ -1740,6 +1748,11 @@
         return window;
     }
 
+    public boolean requestWindowFocus(XWindowPeer actualFocusedWindow) {
+        setActualFocusedWindow(actualFocusedWindow);
+        return requestWindowFocus();
+    }
+
     public boolean requestWindowFocus() {
         return requestWindowFocus(0, false);
     }
@@ -1748,25 +1761,25 @@
         focusLog.fine("Request for window focus");
         // If this is Frame or Dialog we can't assure focus request success - but we still can try
         // If this is Window and its owner Frame is active we can be sure request succedded.
-        Window win = (Window) target;
-        Window owner = XWindowPeer.getDecoratedOwner(win);
+        Window ownerWindow  = XWindowPeer.getDecoratedOwner((Window)target);
+        Window focusedWindow = XKeyboardFocusManagerPeer.getCurrentNativeFocusedWindow();
+        Window activeWindow = XWindowPeer.getDecoratedOwner(focusedWindow);
 
-        final Window activeWindow =
-                XWindowPeer.getDecoratedOwner(XKeyboardFocusManagerPeer.getCurrentNativeFocusedWindow());
-        if (activeWindow == owner) {
+        if (isWMStateNetHidden()) {
+            focusLog.fine("The window is unmapped, so rejecting the request");
+            return false;
+        }
+        if (activeWindow == ownerWindow) {
             focusLog.fine("Parent window is active - generating focus for this window");
             handleWindowFocusInSync(-1);
             return true;
-        } else {
-            focusLog.fine("Parent window is not active");
         }
-        ComponentPeer peer = ComponentAccessor.getPeer(owner);
-        if (peer instanceof XDecoratedPeer) {
-            XDecoratedPeer wpeer = (XDecoratedPeer) peer;
-            if (wpeer.requestWindowFocus(this, time, timeProvided)) {
-                focusLog.fine("Parent window accepted focus request - generating focus for this window");
-                return true;
-            }
+        focusLog.fine("Parent window is not active");
+
+        XDecoratedPeer wpeer = (XDecoratedPeer)ComponentAccessor.getPeer(ownerWindow);
+        if (wpeer != null && wpeer.requestWindowFocus(this, time, timeProvided)) {
+            focusLog.fine("Parent window accepted focus request - generating focus for this window");
+            return true;
         }
         focusLog.fine("Denied - parent window is not active and didn't accept focus request");
         return false;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/Focus/IconifiedFrameFocusChangeTest/IconifiedFrameFocusChangeTest.java	Wed Apr 09 09:37:07 2008 +0400
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+  @test
+  @bug       6522725
+  @summary   Tests for proper request-focus-back on FOCUS_LOST.
+  @author    Anton Tarasov: area=awt-focus
+  @library   ../../regtesthelpers
+  @build     Util
+  @run       main IconifiedFrameFocusChangeTest
+*/
+
+import java.awt.*;
+import java.applet.Applet;
+import java.awt.event.*;
+import test.java.awt.regtesthelpers.Util;
+
+public class IconifiedFrameFocusChangeTest extends Applet {
+    Frame testFrame = new Frame("Test Frame");
+    Frame otherFrame = new Frame("Other Frame");
+    Button testButton = new Button("test button");
+    Button otherButton = new Button("other button");
+    Robot robot;
+
+    public static void main(String[] args) {
+        IconifiedFrameFocusChangeTest app = new IconifiedFrameFocusChangeTest();
+        app.init();
+        app.start();
+    }
+
+    public void init() {
+        robot = Util.createRobot();
+
+        testFrame.add(testButton);
+        testFrame.pack();
+        otherFrame.add(otherButton);
+        otherFrame.pack();
+        otherFrame.setLocation(200, 0);
+
+        testButton.addFocusListener(new FocusAdapter() {
+            public void focusLost(FocusEvent e) {
+                testButton.requestFocus();
+            }
+        });
+    }
+
+    public void start() {
+        otherFrame.setVisible(true);
+        Util.waitForIdle(robot);
+        testFrame.setVisible(true);
+        Util.waitForIdle(robot);
+
+        if (!testButton.hasFocus()) {
+            throw new TestErrorException("wrong initial focus");
+        }
+
+        /*
+         * Iconify the Frame. Test that focus switches properly.
+         */
+        Runnable action = new Runnable() {
+            public void run() {
+                testFrame.setExtendedState(Frame.ICONIFIED);
+            }
+        };
+        if (!Util.trackFocusGained(otherButton, action, 2000, true)) {
+            throw new TestFailedException("iconifying focused window didn't trigger focus change");
+        }
+
+        /*
+         * Test that key events go into the focus owner.
+         */
+        action = new Runnable() {
+            public void run() {
+                robot.keyPress(KeyEvent.VK_SPACE);
+                robot.delay(50);
+                robot.keyRelease(KeyEvent.VK_SPACE);
+            }
+        };
+        if (!Util.trackActionPerformed(otherButton, action, 2000, true)) {
+            throw new TestFailedException("Java focus owner doesn't match to the native one");
+        }
+
+        System.out.println("Test passed.");
+    }
+}
+
+/**
+ * Thrown when the behavior being verified is found wrong.
+ */
+class TestFailedException extends RuntimeException {
+    TestFailedException(String msg) {
+        super("Test failed: " + msg);
+    }
+}
+
+/**
+ * Thrown when an error not related to the behavior being verified is encountered.
+ */
+class TestErrorException extends RuntimeException {
+    TestErrorException(String msg) {
+        super("Unexpected error: " + msg);
+    }
+}