# HG changeset patch # User ant # Date 1207719427 -14400 # Node ID 687798d7d7b64de1d5d2efacd4de42cc77bd5b0f # Parent f5da1014ed2380b466f8cc58f72a56a28cbea7db 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 diff -r f5da1014ed23 -r 687798d7d7b6 jdk/src/solaris/classes/sun/awt/X11/XComponentPeer.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) { diff -r f5da1014ed23 -r 687798d7d7b6 jdk/src/solaris/classes/sun/awt/X11/XDecoratedPeer.java --- 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; diff -r f5da1014ed23 -r 687798d7d7b6 jdk/src/solaris/classes/sun/awt/X11/XWindowPeer.java --- 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; diff -r f5da1014ed23 -r 687798d7d7b6 jdk/test/java/awt/Focus/IconifiedFrameFocusChangeTest/IconifiedFrameFocusChangeTest.java --- /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); + } +}