EVALUATION
Not reproducible in 1.1.7 or 1.2.
mike.bronson@eng 1998-07-14
This problem occurs only on Windows. It is caused by WINDOW_ACTIVATED events getting queued up in the java EventQueue together with more than one window making a call to requestFocus() from within windowActivated() (ie. making a call to get the focus immediately after the window's got the focus).
The solution is to make remove any preceeding WINDOW_ACTIVATED events in the EventQueue when a new WINDOW_ACTIVATED event is posted onto the queue, on the basis that any window receiving focus notification by a preceeding WINDOW_ACTIVATED event will only have the focus for a negligible amount of time, & therefore the elimination of the event will not have a negative impact on the system as a whole.
neil.richards@eng 1998-09-28
-----------------------------------------------------------------------------
jeff.dunn@Eng 1998-10-02
Removing WINDOW_ACTIVATED events makes me very nervous. activated and
deactivated events must arrive in pairs and in the correct order.
Realistically, we have not properly defined what happens when
Frame.requestFocus() in invoked. Does that mean that we want
keyboard events to go to that Frame, or for the Frame to be
activated. or both. Since one would assume that the application
code is not trying to activate the window, because it is
already active. It must be trying to get keyboard focus
to the Frame itself, which is an unusual thing to do.
If we define Frame.requestFocus() to mean "requestActivation()",
then we must ignore the requestFocus() entirely, because the
Frame is already active.
In any case, I think removing WINDOW_ACTIVATED events is not
the right thing to do. I'm getting a concensus on what exactly
Frame.requestFocus() means, and then we'll figure out how
to handle this.
-----------------------------------------------------------------------------
This problem shows that it is dangerous to honor WM_AWT_COMPONENT_SETFOCUS messages (sent just after activation notification & in requestFocus()) if java's view of what is the active window is not in sync with what the real active window is. Therefore a track needs to be kept of what *java believes* is the active window, & extra checking added to the processing for WM_AWT_COMPONENT_SETFOCUS to selectively ignore focus requests when the system is in this condition. (This in part mimics the behaviour of actual java events received on Solaris, where the problem does not exist).
For similar reasons, a track of what java believes the window with focus is needs to be kept in order to filter out focus requests made when that is inconsistant with the actual window is (ie. to avoid the possibility of focus looping).
As a result of this screening, a focus request maybe received for a window which already holds the native focus. In this case, an explicit WM_SETFOCUS message needs to be sent to the window (a call to ::SetFocus() in this instance would be filtered by the OS), unless the system has already received notification via a WM_SETFOCUS message (ie. a WM_SETFOCUS message has been received but the corresponding FOCUS_GAINED event is still in the java event queue). This needs to be tracked by a third variable, which records which window the VM has been notified has the (native) focus.
neil.richards@eng 1998-11-18
Verified the problem as fixed, however the regression test is not up to spec. Will notify the awt team, but the bug is fixed. Verified via direct use of java. Causes error using JavaTest 2.0.
john.s.lee@Eng 1999-01-21
Seems this fix breaks 4140890. Re-opening the bug for now...
robi.khan@eng 1999-02-09
Decomitting fix for 1.1.8 because it causes regression.
robi.khan@eng 1999-03-01
Removed fix entirely, since other regressions have been traced back to it. Will have to wait for general focus rewrite in 1.3.
robi.khan@eng 1999-03-03
###@###.### 10/6/04 12:35 GMT
|
SUGGESTED FIX
(diff src/win32/sun/windows/awt_Window.cpp 1.54 1.55)
678,679c678,680
< ASSERT(peer != NULL);
< AwtComponent * component = PDATA(AwtComponent, peer);
---
>
> if ((peer != NULL) && (unhand(peer)->pData != 0)) {
> AwtComponent * component = PDATA(AwtComponent, peer);
681,686c682,688
< // Request the focus, passing the containing window as the wParam so
< // SetFocus() will not be called (causing unwanted activation) if
< // the components ancestor top-level window is not active
< // (see AwtComponent handling of WM_AWT_COMPONENT_SETFOCUS)
< WPARAM wParam = (WPARAM)window->GetHWnd();
< VERIFY(::PostMessage(component->GetHWnd(), WM_AWT_COMPONENT_SETFOCUS, wParam, 0));
---
> // Request the focus, passing the containing window as the wParam so
> // SetFocus() will not be called (causing unwanted activation) if
> // the components ancestor top-level window is not active
> // (see AwtComponent handling of WM_AWT_COMPONENT_SETFOCUS)
> component->SendMessage(WM_AWT_COMPONENT_SETFOCUS,
> (WPARAM)window->GetHWnd());
> }
(diff src/win32/sun/windows/awt_Component.h 1.72 1.73)
215a216,219
> INLINE HWND GetActiveHWnd() { return m_activeHWnd; }
> INLINE HWND GetFocusHWnd() { return m_focusHWnd; }
> INLINE HWND GetNativeFocusHWnd() { return m_nativeFocusHWnd; }
>
260a265,266
> void SendWPostponedMessageEvent(UINT message, WPARAM wParam=0, LPARAM lParam=0);
>
352a359,360
> virtual MsgRouting WmAwtComponentSetFocus(HWND windowHWnd);
>
398a407,409
> static HWND m_activeHWnd;
> static HWND m_focusHWnd;
> static HWND m_nativeFocusHWnd;
(diff src/win32/sun/windows/awt_Frame.cpp 1.49 1.50)
174a175
> SendWPostponedMessageEvent(WM_AWT_COMPONENT_ACTIVATE, (WPARAM)FALSE);
178a180
> SendWPostponedMessageEvent(WM_AWT_COMPONENT_ACTIVATE, (WPARAM)TRUE);
(diff src/win32/sun/windows/awtmsg.h 1.26 1.27)
2c2
< * @(#)awtmsg.h 1.26 98/08/25
---
> * @(#)awtmsg.h 1.27 98/11/18
47a48,49
> #define WM_AWT_COMPONENT_ACTIVATE AWT_MSG_BASE+26
> #define WM_AWT_COMPONENT_GOTFOCUS AWT_MSG_BASE+27
(diff src/win32/sun/windows/awt_Component.cpp 1.173 1.174)
39a40
> #include <sun_awt_windows_WPostponedMessageEvent.h>
57a59,61
> HWND AwtComponent::m_activeHWnd = NULL;
> HWND AwtComponent::m_focusHWnd = NULL;
> HWND AwtComponent::m_nativeFocusHWnd = NULL;
747,760c751,752
< if (wParam != NULL) {
< if ( ((HWND) wParam) == ::GetActiveWindow()) {
< ::SetFocus(GetHWnd());
< } else {
< // Don't call SetFocus if our top-level window is not
< // active since SetFocus would activate it.
< ::SendMessage((HWND) lParam, WM_KILLFOCUS, (WPARAM)GetHWnd(), 0);
< ::SendMessage(GetHWnd(), WM_SETFOCUS, lParam, 0);
< }
< } else {
< ::SetFocus(GetHWnd());
< }
< mr = mrConsume;
< break;
---
> mr = WmAwtComponentSetFocus((HWND)wParam);
> break;
761a754,769
> case WM_AWT_COMPONENT_ACTIVATE:
> if (wParam) {
> m_activeHWnd = GetHWnd();
> } else if (m_activeHWnd == GetHWnd()) {
> m_activeHWnd = NULL;
> }
> break;
>
> case WM_AWT_COMPONENT_GOTFOCUS:
> if (wParam) {
> m_focusHWnd = GetHWnd();
> } else if (m_focusHWnd == GetHWnd()) {
> m_focusHWnd = NULL;
> }
> break;
>
836a845,846
> m_nativeFocusHWnd = GetHWnd();
> SendWPostponedMessageEvent(WM_AWT_COMPONENT_GOTFOCUS, (WPARAM)TRUE);
856a867,870
> if (m_nativeFocusHWnd == GetHWnd()) {
> m_nativeFocusHWnd = NULL;
> }
> SendWPostponedMessageEvent(WM_AWT_COMPONENT_GOTFOCUS, (WPARAM)FALSE);
1717a1732,1770
> MsgRouting AwtComponent::WmAwtComponentSetFocus(HWND windowHWnd)
> {
> BOOL javaWindowActive; //Java thinks it has an active window.
> BOOL activeWindowInSync; //Java & native think the same window's active.
> BOOL focusWindowInSync; //Java & native think the same window's got focus.
> BOOL gotNativeFocus; //Native already has focus.
> BOOL gotNativeFocusMsg; //VM already notified of native having focus.
> BOOL honorSetFocus; //Is VM in a state/needs to honor focus request.
>
> javaWindowActive = (GetActiveHWnd() != NULL);
> activeWindowInSync = (GetActiveHWnd() == ::GetActiveWindow());
> focusWindowInSync = (GetFocusHWnd() == ::GetFocus());
> gotNativeFocus = (GetHWnd() == ::GetFocus());
> gotNativeFocusMsg = (GetHWnd() == GetNativeFocusHWnd());
> honorSetFocus = ((!javaWindowActive || (activeWindowInSync &&
> focusWindowInSync)) &&
> !(gotNativeFocus && gotNativeFocusMsg));
>
> if (honorSetFocus) {
> BOOL windowHWndActive; //windowHWnd exists & is active.
> BOOL normalSetFocus; //Use ::SetFocus() to set focus.
>
> windowHWndActive =
> ((windowHWnd != NULL) && (windowHWnd == ::GetActiveWindow()));
> normalSetFocus =
> ((windowHWndActive || javaWindowActive) && !gotNativeFocus);
>
> if (normalSetFocus) {
> ::SetFocus(GetHWnd());
> } else if (gotNativeFocus) {
> ::SendMessage(GetHWnd(), WM_SETFOCUS, (WPARAM)GetHWnd(), 0);
> } else {
> ::SendMessage(::GetFocus(), WM_KILLFOCUS, (WPARAM)GetHWnd(), 0);
> ::SendMessage(GetHWnd(), WM_SETFOCUS, (WPARAM)::GetFocus(), 0);
> }
> }
> return mrConsume;
> }
>
1840a1894,1914
> void AwtComponent::SendWPostponedMessageEvent(UINT message, WPARAM wParam, LPARAM lParam)
> {
> static ClassClass* classEvent = NULL;
> if (classEvent == NULL) {
> char* clsName = "sun/awt/windows/WPostponedMessageEvent";
> classEvent = FindStickySystemClass(EE(), clsName, TRUE);
> if (classEvent == NULL) {
> SignalError(0, JAVAPKG "ClassNotFoundException", clsName);
> return;
> }
> ASSERT(classEvent);
> }
>
> Hsun_awt_windows_WPostponedMessageEvent* hEvent =
> (Hsun_awt_windows_WPostponedMessageEvent*)
> execute_java_constructor(EE(), NULL, classEvent, "(Ljava/lang/Object;Lsun/awt/windows/WObjectPeer;III)", GetTarget(), GetPeer(), (long)message, (long)wParam, (long)lParam);
> ASSERT(!exceptionOccurred(EE()));
> ASSERT(hEvent != NULL);
> SendEvent((Hjava_awt_AWTEvent*)hEvent);
> }
>
2293c2367,2368
< VERIFY(::PostMessage(p->GetHWnd(), WM_AWT_COMPONENT_SETFOCUS, 0, 0));
---
>
> p->SendMessage(WM_AWT_COMPONENT_SETFOCUS);
2573a2649,2657
>
> void
> sun_awt_windows_WPostponedMessageEvent_dispatchImpl(Hsun_awt_windows_WPostponedMessageEvent* self, Hsun_awt_windows_WObjectPeer* peer, long message, long wParam, long lParam)
> {
> if ((peer != NULL) && (unhand(peer)->pData != 0)) {
> AwtComponent* p = PDATA(AwtComponent, peer);
> ::SendMessage((HWND)p->GetHWnd(), (UINT)message, (WPARAM)wParam, (LPARAM)lParam);
> }
> }
(diff src/win32/sun/sun/awt/windows/WPostponedMessageEvent.java 1.0 1.1)
0a1,39
> /*
> * @(#)WPostponedMessageEvent.java 1.1 98/11/20
> *
> * Copyright 1995-1998 by Sun Microsystems, Inc.,
> * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
> * All rights reserved.
> *
> * This software is the confidential and proprietary information
> * of Sun Microsystems, Inc. ("Confidential Information"). You
> * shall not disclose such Confidential Information and shall use
> * it only in accordance with the terms of the license agreement
> * you entered into with Sun.
> */
> package sun.awt.windows;
>
> import java.awt.AWTEvent;
> import java.awt.peer.ActiveEvent;
>
> class WPostponedMessageEvent extends AWTEvent implements ActiveEvent
> {
> private WObjectPeer peer;
> private int message;
> private int wParam;
> private int lParam;
>
> public WPostponedMessageEvent(Object target, WObjectPeer peer, int message, int wParam, int lParam) {
> super(target, 0);
> this.peer = peer;
> this.message = message;
> this.wParam = wParam;
> this.lParam = lParam;
> }
>
> public void dispatch() {
> dispatchImpl(peer, message, wParam, lParam);
> }
>
> private native void dispatchImpl(WObjectPeer peer, int message, int wParam, int lParam);
> }
(diff build/win32/sun/winawt/classes.mk 1.32 1.33)
64a65
> $(AWT_CLASSES)\windows\WPostponedMessageEvent.class 189a191
> sun.awt.windows.WPostponedMessageEvent
=====
Testcase:
(diff test/java/awt/Frame/RequestFocusVibration/RequestFocusVibration.html 1.0 1.1)
0a1,5
> <html>
> <body>
> <applet code=RequestFocusVibration.class width=300 height=350></applet>
> </body>
> </html>
(diff test/java/awt/Frame/RequestFocusVibration/RequestFocusVibration.java 1.0 1.1)
0a1,117
> /*
> * Licensed Materials - Property of IBM
> *
> * RequestFocusVibration.java
> *
> * (C) Copyright IBM Corporation 1998 All Rights Reserved.
> *
> * US Government Users Restricted Rights - Use, duplication or disclosure
> * restricted by GSA ADP Schedule Contract with IBM Corp.
> */
>
> /* 1.1 98/11/18
> @bug 4109702
> @summary Test of requestFocus() calls within multiple windows
> @run applet/manual=yesno/timeout=30 RequestFocusVibration.html
> @author Neil Richards
> */
>
> import java.awt.*;
> import java.awt.event.*;
> import java.applet.*;
>
> public class RequestFocusVibration extends Applet implements MouseListener, WindowListener, Runnable {
> int count;
> Frame target;
> int fWidth;
>
> public RequestFocusVibration() {
> this(null);
> }
>
> public RequestFocusVibration(Frame f) {
> target = f;
> fWidth = 0;
> count = 0;
> }
>
> public void init() {
> setLayout(new BorderLayout());
> Panel p = new Panel();
> add("North", p);
> setBackground(Color.red);
> p.setLayout(new GridLayout(0,1,20,0));
> p.setBackground(Color.white);
> p.add(new Label("Click in the red area 3 times quickly.", Label.LEFT));
> p.add(new Label("This will create 3 new windows.", Label.LEFT));
> p.add(new Label(" ", Label.LEFT));
> p.add(new Label("If these windows \"flash\" or \"vibrate\"", Label.LEFT));
> p.add(new Label("rapidly, the test has failed.", Label.LEFT));
> p.add(new Label(" ", Label.LEFT));
> p.add(new Label("The windows should be destroyed after 10", Label.LEFT));
> p.add(new Label("seconds, allowing you to press the pass", Label.LEFT));
> p.add(new Label("or fail button.", Label.LEFT));
> addMouseListener(this);
> }
>
> public void mouseClicked (MouseEvent evt) {
> synchronized(this) {
> if (count < 3) {
> count++;
>
> String title = new String("Window " + count);
>
> if (fWidth == 0) {
> fWidth = getFontMetrics(getFont()).stringWidth(title) + 100;
> }
> Rectangle r = getBounds();
>
> Frame f = new Frame(title);
> f.setSize(new Dimension(fWidth, fWidth));
> f.setLocation(r.x + r.width + 10, r.y +
> ((count - 1) * 30));
> f.addWindowListener(this);
> f.show();
>
> Thread t = new Thread(new RequestFocusVibration(f));
> t.start();
>
> // Addition of the line below increased the chances of inducing the
> // problem significantly. This suggests a timing problem.
> try {
> Thread.sleep(1000);
> } catch ( InterruptedException e) {}
> }
> }
> }
>
> public void mouseEntered (MouseEvent evt) {}
> public void mouseExited (MouseEvent evt) {}
> public void mousePressed (MouseEvent evt) {}
> public void mouseReleased (MouseEvent evt) {}
>
> public void windowActivated(WindowEvent e) {
> e.getWindow().requestFocus();
> }
>
> public void windowClosing(WindowEvent e) {
> e.getWindow().dispose();
> }
>
> public void windowClosed(WindowEvent e) {
> count--;
> }
> public void windowDeactivated(WindowEvent e) {}
> public void windowDeiconified(WindowEvent e) {}
> public void windowIconified(WindowEvent e) {}
> public void windowOpened(WindowEvent e) {}
>
> public void run(){
> try {
> Thread.currentThread().sleep(10000);
> } catch (InterruptedException e) {}
> target.dispose();
> }
> }
>
>
neil.richards@eng 1998-11-20
|