JDK-4150021 : Frame cannot handle keyboard events (keyTyped) if buttons are added to frame.
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.1.3,1.4.0
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS:
    generic,solaris_2.5.1,windows_98,windows_2000 generic,solaris_2.5.1,windows_98,windows_2000
  • CPU: generic,x86,sparc
  • Submitted: 1998-06-17
  • Updated: 2003-04-12
  • Resolved: 2002-08-22
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
1.4.2 mantisFixed
Related Reports
Duplicate :  
Relates :  
Relates :  
Description

Name: jtC48319			Date: 06/17/98


Steps to recreate problem.

This is a simple program that changes the 
backroung color of the frame.  You have two 
options; yellow and blue.

Press 'y' for yellow and 'b' for blue.
Nothing happens.  If you click the buttons with
the mouse, everythings works as expected.  It
appears that the frame can never regain focus to
accept key event(as defined in Core Java 1.1
Foundations).  I tried to get the focus for the 
frame by using the requestFocus() method, but it
does nothing(which I noticed was already reported).

NOTE:  We are going through e-mail updates.  If
the provided e-mail is return, please send all 
future correspondence to ###@###.###.
Thank you.
 
Here is the code:

import java.awt.*;
import java.awt.event.*;

public class ButtonTest extends Frame
{

public ButtonTest()
{
  addWindowListener(new WindowAdapter()
  {
   public void windowClosing(WindowEvent e)
   {System.exit(0);}
  } );

  setSize(300, 200);
  setTitle("Button Test");
  setLayout(new FlowLayout());

  //Create button, add event handler, and add to frame
  Button yellow = new Button("Yellow");
  yellow.addActionListener(new ActionListener()
  {
   public void actionPerformed(ActionEvent e)
   {setBackground(Color.yellow);}
  } );
  add(yellow);

  //Create button, add event handler, and add to frame
  Button blue = new Button("Blue");
  blue.addActionListener(new ActionListener()
  {
   public void actionPerformed(ActionEvent e)
   {setBackground(Color.blue);}
  } );
  add(blue);

  //I can never pass through this callback,
  //the frame never seems to have focus    
  addKeyListener(new KeyAdapter()
  {
   public void keyTyped(KeyEvent e)
   { ButtonTest.this.keyTyped(e); }
  } );

  //Tried using this method to gain focus for frame
  //but it does not work.
  requestFocus();
}

public void keyTyped(KeyEvent evt)
{
  char keyChar = Character.toLowerCase(evt.getKeyChar());
  if (keyChar == 'y') setBackground(Color.yellow);
  if (keyChar == 'b') setBackground(Color.blue);
}

public static void main(String args[])
{
  Frame f = new ButtonTest();
  f.show();
}

}
(Review ID: 25346)
======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: mantis mantis-b02 FIXED IN: mantis mantis-b02 INTEGRATED IN: mantis mantis-b02
24-08-2004

EVALUATION eric.hawkes@eng 1998-06-18 This bug sounds as though it has the same root cause as 4033894 and/or 4027897. The problem in each case seems to be that the frame cannot regain focus after it has lost it. Name: rpR10076 Date: 11/16/2001 commit to hopper and tiger ====================================================================== Name: osR10079 Date: 12/21/2001 In the test user tries to listen KeyEvents on Frame. For this reason KeyListener is registered on the frame, and requestFocus() is called on the frame. But requestFocus() is called before frame showing, and AWT rejects this call because the frame is not visible. Thus after application start focus is placed on button (initial component), not on the frame itself. To fix this problem we need to call requestFocus() after setVisible(true). But here we have other problem: requestFocus() is called on main thread, and at the same time on EDT DefaultKeyboardFocusManager dispatches WINDOW_GAINED_FOCUS (which comes as a result of setVisible(true)) and in this handler it determines component which should be a focus owner and requests focus on this component. To find out this component, DKFM call getMostRecentFocusOwner() method on focused window. Thus we have the following situation: 1. On EDT we call Window.getMostRecentFocusOwner(), which returns the button as new focus owner, since it is initial component for this frame. 2. On main thread we call requestFocus() on the frame. 3. On EDT we call requstFocus() on the button. And after that the button becomes a focus owner. So, user expects the frame to be a focus owner and we try to do so (for this reason we have most recent focus owner, which we update in requestFocus()). But because of the thread race and the fact that our code is not thread safe, focus is placed on the button. ###@###.### 21 Dec 2001 ====================================================================== Name: osR10079 Date: 08/12/2002 ======================================================================
24-08-2004

WORK AROUND Name: jtC48319 Date: 06/17/98 It appears that when the buttons are added to the frame that the buttons maintain the focus. Meaning if there are buttons on the frame, the frame will not accept keyboard events. If the buttons are not installed everything works just fine. The only way I could get the key events to work was to add a key listner to each of the buttons. That way, no matter which button had focus they each would process a key event. The code I had to add to make this work is as follows and belongs in the constructor. yellow.addKeyListener(new KeyAdapter() { public void keyTyped(KeyEvent e) {ButtonTest.this.keyTyped(e);} } blue.addKeyListener(new KeyAdapter() { public void keyTyped(KeyEvent e) {ButtonTest.this.keyTyped(e);} } Now this works, but I do not think this is the way it should work. I would think that the button component should not have to handle all key events. The frame component should be able to process key events whether or not it has buttons. Thank you. jukka@canada 1998-06-17 The test program will work if keyPressed is used instead of keyTyped. 41,42c41,42 < public void keyTyped(KeyEvent e) < { ButtonTest.this.keyTyped(e); } --- > public void keyPressed(KeyEvent e) > { ButtonTest.this.keyPressed(e); } 50c50 < public void keyTyped(KeyEvent evt) --- > public void keyPressed(KeyEvent evt) ====================================================================== Name: osR10079 Date: 12/21/2001 We can provide two workarounds for this problem: 1. invoke frame.requestFocus() on EDT by invokeAndWait()/invokeLater(). 2. make the frame the initial component by changing FocusTraversalPolicy or by calling setFocusable(true) on the frame. ###@###.### 21 Dec 2001 ======================================================================
24-08-2004

SUGGESTED FIX Name: osR10079 Date: 08/12/2002 The fix for problem with requesting focus just after showing frame. ------- KeyboardFocusManager.java ------- *** /tmp/sccs.HEaGeu Tue Jul 2 12:01:33 2002 --- KeyboardFocusManager.java Tue Jun 25 17:49:51 2002 *************** *** 1609,1623 **** } } static synchronized void setMostRecentFocusOwner(Window window, ! Component component) { ! // ATTN: component has a strong reference to window via chain ! // of Component.parent fields. Since WeakHasMap refers to its ! // values strongly, we need to break the strong link from the ! // value (component) back to its key (window). ! WeakReference weakValue = null; ! if (component != null) { ! weakValue = new WeakReference(component); ! } mostRecentFocusOwners.put(window, weakValue); } static void clearMostRecentFocusOwner(Component comp) { --- 1609,1623 ---- } } static synchronized void setMostRecentFocusOwner(Window window, ! Component component) { ! // ATTN: component has a strong reference to window via chain ! // of Component.parent fields. Since WeakHasMap refers to its ! // values strongly, we need to break the strong link from the ! // value (component) back to its key (window). ! WeakReference weakValue = null; ! if (component != null) { ! weakValue = new WeakReference(component); ! } mostRecentFocusOwners.put(window, weakValue); } static void clearMostRecentFocusOwner(Component comp) { *************** *** 1634,1649 **** } } ! if ((window != null) ! && (getMostRecentFocusOwner((Window)window) == comp)) ! { ! setMostRecentFocusOwner((Window)window, null); } } static synchronized Component getMostRecentFocusOwner(Window window) { ! WeakReference weakValue = ! (WeakReference)mostRecentFocusOwners.get(window); ! return weakValue == null ? null : (Component)weakValue.get(); } /** --- 1634,1651 ---- } } ! synchronized (KeyboardFocusManager.class) { ! if ((window != null) ! && (getMostRecentFocusOwner((Window)window) == comp)) ! { ! setMostRecentFocusOwner((Window)window, null); ! } } } static synchronized Component getMostRecentFocusOwner(Window window) { ! WeakReference weakValue = ! (WeakReference)mostRecentFocusOwners.get(window); ! return weakValue == null ? null : (Component)weakValue.get(); } /** ------- DefaultKeyboardFocusManager.java ------- *** /tmp/sccs.IEaGeu Tue Jul 2 12:01:33 2002 --- DefaultKeyboardFocusManager.java Tue Jul 2 11:54:04 2002 *************** *** 254,274 **** } } - // Identify which Component should initially gain focus in - // the Window. - // - // * If we're in SendMessage, then this is a synthetic - // WINDOW_GAINED_FOCUS message which was generated by a - // the FOCUS_GAINED handler. Allow the Component to which the - // FOCUS_GAINED message was targeted to receive the focus. - // * Otherwise, look up the correct Component here. If we - // perform the lookup after the call to setGlobalFocusOwner, - // 'null' will be returned. - Component requestingComp = getCurrentWaitingRequest(newFocusedWindow); - Component toFocus = (inSendMessage == 0) - ? newFocusedWindow.getMostRecentFocusOwner() - : null; - setGlobalFocusedWindow(newFocusedWindow); if (newFocusedWindow != getGlobalFocusedWindow()) { --- 254,259 ---- *************** *** 285,298 **** // Make sure that the focus change request doesn't change the // focused Window in case we are no longer the focused Window // when the request is handled. if (requestingComp != null) { FocusEvent defGain = new FocusEvent(requestingComp, FocusEvent.FOCUS_GAINED, false); sendMessage(requestingComp, defGain); ! } else if (toFocus != null) { ! toFocus.requestFocusInWindow(); ! } Window realOppositeWindow = this.realOppositeWindow; if (realOppositeWindow != we.getOppositeWindow()) { we = new WindowEvent(newFocusedWindow, --- 270,313 ---- // Make sure that the focus change request doesn't change the // focused Window in case we are no longer the focused Window // when the request is handled. + Component requestingComp = getCurrentWaitingRequest(newFocusedWindow); if (requestingComp != null) { FocusEvent defGain = new FocusEvent(requestingComp, FocusEvent.FOCUS_GAINED, false); sendMessage(requestingComp, defGain); ! } else if (inSendMessage == 0) { ! // Identify which Component should initially gain focus ! // in the Window. ! // ! // * If we're in SendMessage, then this is a synthetic ! // WINDOW_GAINED_FOCUS message which was generated by a ! // the FOCUS_GAINED handler. Allow the Component to ! // which the FOCUS_GAINED message was targeted to ! // receive the focus. ! // * Otherwise, look up the correct Component here. ! // We don't use Window.getMostRecentFocusOwner because ! // window is focused now and 'null' will be returned + + // Calculating of most recent focus owner and focus + // request should be synchronized on KeyboardFocusManager.class + // to prevent from thread race when user will request + // focus between calculation and our request. + // But if focus transfer is synchronous, this synchronization + // may cause deadlock, thus we don't synchronize this block. + Component toFocus = KeyboardFocusManager. + getMostRecentFocusOwner(newFocusedWindow); + if ((toFocus == null) && + newFocusedWindow.isFocusableWindow()) + { + toFocus = newFocusedWindow.getFocusTraversalPolicy(). + getInitialComponent(newFocusedWindow); + } + if (toFocus != null) { + toFocus.requestFocusInWindow(); + } + } + Window realOppositeWindow = this.realOppositeWindow; if (realOppositeWindow != we.getOppositeWindow()) { we = new WindowEvent(newFocusedWindow, ======================================================================
24-08-2004