JDK-4124969 : Wrong status in FocusOut event when open system menu.
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.2.0
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: solaris_2.5.1
  • CPU: generic
  • Submitted: 1998-04-02
  • Updated: 1999-02-03
  • Resolved: 1999-02-03
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.2.2 1.2.2Fixed
Related Reports
Relates :  
Description
When opening the system menu, FocusOut event will be sent to the components
within the frame, and it should be a temporary one just like what we get 
when opening a user defined menu, not a permanent one. The following test
case shows the problem.


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

class TestFocus extends Component implements FocusListener {
	boolean haveFocus = false;
	public TestFocus(){
	        super();
		addFocusListener(this);
		addMouseListener(new MouseFocusListener(this));
	}

        public Dimension getPreferredSize(){
	  return new Dimension(200, 150);
        }

	public void focusGained(FocusEvent e){
	  System.out.println("==>FocusGained:" + e);
	  if (e.getSource() == this){
	    haveFocus = true;
	    paint(getGraphics());
	  }
	}

	public void focusLost(FocusEvent e){
	  System.out.println("==>FocusLost  :" + e);
	  if (e.getSource() == this){
	    haveFocus = false;
	    paint(getGraphics());
	  }
	}

	public void paint(Graphics g){
		g.setColor(Color.yellow);
		Dimension size = getSize();
		g.fillRect(0, 0, size.width, size.height);
		if (haveFocus){
			g.setColor(Color.black);
			g.drawRect(0, 0, size.width - 1, size.height - 1);
			g.drawRect(1, 1, size.width - 3, size.height - 3);
		}
	}

        public static void main(String[] args) {
                Frame f = new Frame("Test Focus");
		f.setLayout(new BorderLayout());
                TestFocus test = new TestFocus();
		f.add(test);
                f.setSize(new Dimension(320,200));

		//f.addFocusListener(test);
                MenuBar mb = new MenuBar();
                Menu m1 = new Menu("menu");
                m1.add( new MenuItem("item1"));
                m1.add( new MenuItem("item2"));
                mb.add(m1);
                f.setMenuBar(mb);
 
                f.addWindowListener( new WindowAdapter() {
                        public void WindowClosing( WindowEvent e ) {
                                e.getWindow().dispose();
                                System.exit(0);
                        }
                        public void WindowClosed( WindowEvent e ) {
                                System.exit(0);
                        }
                } );
		f.pack();
                f.setVisible(true);
		test.requestFocus();
        }

  class MouseFocusListener extends MouseAdapter {
    private Component target;
    MouseFocusListener(Component target) {
        this.target = target;
    }
    public void mouseClicked(MouseEvent e) {
        target.requestFocus();
    } 
  }  
}

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: 1.2.2 FIXED IN: 1.2.2 INTEGRATED IN: 1.2.2
14-06-2004

EVALUATION isa.hashim@Eng 1998-04-28 ------------------------- [FYI - This problem is also seen when running under jdk1.1.6 (on Solaris 2.5.1/2.6)] When tested with jdk1.2, the supplied test program behaves differently on Solaris 2.5.1 and 2.6. I decided to take some time to try to figure out why since the fix for this would need to work in both environments as well. On 2.5.1 -------- - menubar menu causes a temporary focus loss - system menu causes permanent focus loss On 2.6 ------ - menubar menu causes permanent focus loss - system menu causes permanent focus loss Looking at the code that determines if a focus loss is temporary/permanent: ===============src/solaris/native/sun/awt/canvas.c========================================= 2 * @(#)canvas.c 1.107 98/04/02 .... 472 void 473 handleFocusEvent(Widget w, 474 XFocusChangeEvent * fevent, .... 479 if (fevent->type == FocusIn) { .... 487 } else { 488 /* FocusOut */ .... 493 /* If the last focus event caused the window to be deactivated, 494 * then this focus-out event is temporary. 495 */ 496 if (getAncestorShell(w) == deactivated_shell) { 497 temp = TRUE; 498 deactivated_shell = NULL; 499 } else if (QLength(awt_display) > 0) { 500 /* If there are more events in the queue, look ahead for any focus 501 * events caused by a grab (i.e. a menu invocation) - if they exist, 502 * then this focus-out event is temporary. 503 */ 504 XEvent nevent; 505 506 XPeekEvent(awt_display, &nevent); 507 if ((nevent.type == FocusOut || nevent.type == FocusIn) && 508 nevent.xfocus.mode == NotifyGrab) { 509 temp = TRUE; 510 } 511 } 512 awt_post_java_focus_event(client_data, 513 java_awt_event_FocusEvent_FOCUS_LOST, 514 0, 515 temp); ========================================================================================== As far as I can understand, the logic (at lines 499 onward) is - when a FocusOut event is received, check if any other focus type events are in the X event queue. These focus events could be for windows other than the window that is currently losing focus. If any of these focus events were sent as a result of a grab, then this focus loss is temporary. The inconsistencies between 2.5.1/2.6 is due to the fact that the code above depends on a particular type of event that is at the *head* of the X event queue when the FocusOut event is received. If a different event type is found or no events are found, the logic fails. I wrote generic Motif programs to print out the event at the head of the queue (via XPeekEvent) and they were different for 2.5.1/2.6: Note: "NotifyNormal, NotifyAncestor" means a focus event with mode = NotifyNormal, detail = NotifyAncestor On 2.5.1 -------- focusEventHandler() called for: 500001c FocusOut event type NotifyNormal, NotifyAncestor Event in Q for XPeekEvent(): => focus event: NotifyGrab, NotifyInferior On 2.6 ------ focusEventHandler() called for: 880001c FocusOut event type NotifyNormal, NotifyAncestor Event in Q for XPeekEvent(): => focus event: NotifyNormal, NotifyInferior So, the check at line 508 for NotifyGrab fails when running on 2.6. This bug addresses the focus loss seen when the system (window manager) menu is used. The suggested fix involves making focus events seen in the Q with mode = NotifyWhileGrabbed also cause for this focus loss to be marked as temporary: 507 if ((nevent.type == FocusOut || nevent.type == FocusIn) && 508 (nevent.xfocus.mode == NotifyGrab || 509 nevent.xfocus.mode == NotifyWhileGrabbed)) { 510 temp = TRUE; 511 } The fix works 99% of the time on 2.5.1 - there are some cases where I see the needed event *not* at the head of the X Event queue right after the system menu is posted (even in synchronous mode), and the logic breaks. The fix fails for 2.6 because the X Event queue is almost always empty after the system menu is posted. To sum up so far: ----------------- AWT 1.2 (on 2.6) menubar menus causes permanent focus loss due to assumptions on certain events being at the head of the X event Q. I will investigate if the event is on the Q but not necessarily at the head of it. I will look into the system menu problems after that. isa.hashim@Eng 1998-05-01 ------------------------- Got more info on *why* this the focus events seen are different on 2.5.1 and 2.6. I used xscope to observe the requests/events that are made/seen when the menubar menu is posted. I noticed that on 2.5.1, a keyboard grab is made and then the input focus is requested for. This is where the NotifyGrab comes from, I presume. I ran java_g under dbx and added breakpoints for XGrabKeyboard() and XSetInputFocus(). On 2.5.1, the grab call is made first, at: ========================================================================================== (dbx) w =>[1] XGrabKeyboard(0xed230, 0x500003e, 0x1, 0x0, 0x0, 0x0), at 0xef51c75c [2] GrabDevice(0x1f87c8, 0x1, 0x0, 0x0, 0x0, 0x0), at 0xeda20274 [3] XtGrabKeyboard(0x1f87c8, 0x1, 0x0, 0x0, 0x0, 0xffffffff), at 0xeda20424 [4] _XmGrabKeyboard(0x1f87c8, 0x1, 0x0, 0x0, 0x0, 0xfffffffb), at 0xedb5a33c [5] _XmMenuFocus(0x1f87c8, 0x0, 0x0, 0xedff0428, 0x223740, 0xedff00a7), at 0xe db71e0c [6] MenuBarSelect(0x1f73b8, 0xedff0428, 0xed230, 0xeda4f464, 0xeda4f474, 0x0), at 0xedb28718 [7] HandleActions(0x1f73b8, 0xedff0428, 0x1f6c80, 0x0, 0x1fefc4, 0xeda4f45c), at 0xeda32054 [8] HandleSimpleState(0x1, 0x0, 0x1, 0x0, 0x0, 0x1fefc4), at 0xeda3263c [9] _XtTranslateEvent(0x1f73b8, 0xedff0428, 0x8000, 0x4, 0x0, 0x1f73e8), at 0x eda32be8 [10] DispatchEvent(0xedff0428, 0x1f73b8, 0x4, 0x0, 0xedbf3e8c, 0x0), at 0xeda1 5c08 [11] DecideToDispatch(0xedff0428, 0x18f1cc, 0x4, 0x1f73b8, 0x1f73b8, 0x0), at 0xeda1632c [12] XtDispatchEvent(0xedff0428, 0xedff0428, 0x18e304, 0x0, 0x1, 0x18e2f8), at 0xeda1640c [13] awt_MToolkit_loop(0x0, 0x2a014, 0x0, 0x0, 0x1aaabc, 0xff00), at 0xedcf710 0 [14] Java_sun_awt_motif_MToolkit_run(0x1a8730, 0xedff05ac, 0x1, 0xef2c8, 0xef6 a3308, 0x0), at 0xedcf83b8 [15] invoke_V_V(0xee3118d0, 0xef2c8, 0x1, 0x1a8730, 0xef7f566c, 0x0), at 0xef6 a0850 [16] invokeLazyNativeMethod(0xee3118d0, 0xef2c8, 0x1, 0x1a8730, 0x400, 0x28295 600), at 0xef68b564 [17] ExecuteJava_C(0xedff0d68, 0x1a8730, 0x1aaa54, 0x1aaa70, 0x1aaa58, 0x4366b), at 0xef6b9648 [18] do_execute_java_method_vararg(0x1a8730, 0xee311458, 0xef7351d4, 0xef7351d 8, 0x0, 0x0), at 0xef6a5174 [19] execute_java_dynamic_method(0x0, 0xee311458, 0xef7351d4, 0xef7351d8, 0x0, 0x7ff1ffc0), at 0xef6a3ecc [20] ThreadRT0(0xee311458, 0x490c, 0x0, 0x490c, 0xffffffff, 0xfff0bdc0), at 0x ef6e46a4 [21] saveStackBase(0x193050, 0x1, 0xedff0ef0, 0xef763224, 0x0, 0x0), at 0xef6f e0e4 [22] start_func(0xef6fe068, 0xefffdaa0, 0xfc145c08, 0x0, 0xfc145c90, 0x0), at 0xef763290 ========================================================================================== On 2.6, a call to XSetInputFocus() is made before any keyboard grab is made. I assume this is why I see a NotifyNormal focus event since a grab was not active at the time. I tracked this difference to code in the motif source - cdesrc/lib/Xm/RowCollumn.c ==================Solaris 2.5.1 - cdesrc/lib/Xm/RowCollumn.c============================== ... 5283 _XmMenuFocus( w, operation, _time ) ... 5352 case XmMENU_BEGIN: 5353 #if !( defined(NOT_SAMPLE) && defined (CMVC_3466) ) 5354 5355 /* We must grab the keyboard before the InputFocus is set for mwm 5356 * to work correctly. 5357 */ 5358 _XmGrabKeyboard(w, True, GrabModeSync, GrabModeSync, _time); 5359 #endif 5360 XGetInputFocus(XtDisplay(w), &mst->RC_menuFocus.oldFocus, &mst-> 5361 RC_menuFocus.oldRevert); 5362 mst->RC_menuFocus.oldWidget = XtWindowToWidget(XtDisplay(w), mst-> 5363 RC_menuFocus.oldFocus); 5364 if (mst->RC_menuFocus.oldWidget) 5365 XtAddCallback(mst->RC_menuFocus.oldWidget, XtNdestroyCallback, 5366 (XtCallbackProc)InvalidateOldFocus, (XtPointer) &mst-> 5367 RC_menuFocus.oldFocus); 5368 5369 SetInputFocus(XtDisplay(w), XtWindow(w), mst->RC_menuFocus.oldRevert, 5370 _time); 5371 #if !( defined(NOT_SAMPLE) && defined (CMVC_3466) ) 5372 XAllowEvents(XtDisplay(w), AsyncKeyboard, _time); 5373 5374 /* Call XAllowEvents(.., SyncKeyboard, ..) after the pointer is 5375 * grabbed. 5376 */ 5377 #endif 5378 XFlush(XtDisplay(w)); 5379 5380 break; ... ========================================================================================== On Solaris 2.6, the code surrounded by #if's, which is where the grab is made, is gone. I was also surprised as to why the behaviour is identical on Solaris 2.5.1/2.6 when I use jdk1.1.6 until Amy Fowler brought to my attention that jdk1.1.6 was shipped with it's own motif libraries. I checked the version of Motif libs shipped with jdk1.1.6 and saw that they were: gaby(isa):70 pwd /usr/local/java/jdk1.1.6/solaris/lib/sparc/green_threads gaby(isa):71 strings libXm.so.3 | grep Version | grep Motif @(#)OSF/Motif Version 1.2.3 which is the one shipped on Solaris 2.5.1. (BTW, the version string seen for the Motif lib on Solaris 2.6 is "@(#)OSF/Motif Version 1.2.6"). So, even when you run jdk1.1.6 on Solaris 2.6, it will use the Motif libs for 2.5.1, hiding the problem. --- So now that I know why the problem is seen, the next step is figuring out how to fix it. I spoke to Amy and it would appear that the code to determine if the focus loss was caused by a menu post is not 100% reliable - it assumes that a particular event will be at the head of the X event queue. Amy suggested that I look into setting some sort of state flag when an AWT menu is posted so that the code in canvas.c can check it instead of XPeek'ing the X event queue. isa.hashim@Eng 1998-05-05 ------------------------- I found out that in X, the FocusOut event is delivered before any menu XmNpopupCallback is called. So any state set in an XmNpopupCallback function is useless since it won't be seen by the FocusOut event handling code in time. Sigh. I'm not sure I know a better way to determine if the focus loss was caused by a menu/scrollbar. I will probably just check in the suggested fix. This will only fix the problems seen on 2.5.1. I will also probably file a separate bug to keep track of the problems seen on 2.6. isa.hashim@Eng 1998-05-06 ------------------------- I experimented with XmNmapCallback and got similar results - it is called after the FocusOut event is delivered. isa.hashim@Eng 1998-05-26 ------------------------- The plan is to check in the suggested fix plus some code to make the logic a bit more robust - we will look for the relevant FocusOut event in the entire event queue instead of just checking the one at the TOP. This will partially fix the problem described by this bug report. The other problems that I found on Solaris 2.6: - wmgr menus generate permanent java FocusLost events because the X FocusOut event is not in the queue or the queue is empty - user menus generate permanent java FocusLost events because the X FocusOut event is not in the queue or the queue is empty I do not know how to fix...
11-06-2004

SUGGESTED FIX [xueming.shen@japan] Seems to me the next coming in FocusOut event is in NotifyWhileGrabbed mode when opening the system menu which is not expected in current implementation. *** /tmp/geta3950 Thu Apr 2 12:54:25 1998 --- canvas.c Thu Apr 2 12:50:57 1998 *************** *** 507,513 **** XPeekEvent(awt_display, &nevent); if ((nevent.type == FocusOut || nevent.type == FocusIn) && ! nevent.xfocus.mode == NotifyGrab) { temp = TRUE; } } --- 507,513 ---- XPeekEvent(awt_display, &nevent); if ((nevent.type == FocusOut || nevent.type == FocusIn) && ! (nevent.xfocus.mode == NotifyGrab || nevent.xfocus.mode == NotifyWhileGrabbed)) { temp = TRUE; } } jenny.wang@East 1998-12-21 ------- canvas.c ------- 519,530c519,527 < } else if (QLength(awt_display) > 0) { < /* If there are more events in the queue, look ahead for any focus < * events caused by a grab (i.e. a menu invocation) - if they exist, < * then this focus-out event is temporary. < */ < XEvent nevent; < < XPeekEvent(awt_display, &nevent); < if ((nevent.type == FocusOut || nevent.type == FocusIn) && < nevent.xfocus.mode == NotifyGrab) { < temp = TRUE; < } --- > } else { > /* If the event is sent by client or it is caused by a grab > * (i.e. a menu invocation), then this focus-out event is > * temporary. > */ > if (fevent->send_event || > (fevent->mode == NotifyGrab || > fevent->mode == NotifyWhileGrabbed)) > temp = TRUE;
11-06-2004