--- old/src/macosx/classes/sun/lwawt/LWWindowPeer.java 2012-04-18 18:48:37.000000000 +0400 +++ new/src/macosx/classes/sun/lwawt/LWWindowPeer.java 2012-04-18 18:48:37.000000000 +0400 @@ -669,39 +669,42 @@ } } else { if (targetPeer != lastMouseEventPeer) { - // lastMouseEventPeer may be null if mouse was out of Java windows - if (lastMouseEventPeer != null && lastMouseEventPeer.isEnabled()) { - // Sometimes, MOUSE_EXITED is not sent by delegate (or is sent a bit - // later), in which case lastWindowPeer is another window - if (lastWindowPeer != this) { - Point oldp = lastMouseEventPeer.windowToLocal(x, y, lastWindowPeer); - // Additionally translate from this to lastWindowPeer coordinates - Rectangle lr = lastWindowPeer.getBounds(); - oldp.x += r.x - lr.x; - oldp.y += r.y - lr.y; - postEvent(new MouseEvent(lastMouseEventPeer.getTarget(), - MouseEvent.MOUSE_EXITED, - when, modifiers, - oldp.x, oldp.y, screenX, screenY, - clickCount, popupTrigger, button)); - } else { - Point oldp = lastMouseEventPeer.windowToLocal(x, y, this); - postEvent(new MouseEvent(lastMouseEventPeer.getTarget(), - MouseEvent.MOUSE_EXITED, + + if(id != MouseEvent.MOUSE_DRAGGED){ + // lastMouseEventPeer may be null if mouse was out of Java windows + if (lastMouseEventPeer != null && lastMouseEventPeer.isEnabled()) { + // Sometimes, MOUSE_EXITED is not sent by delegate (or is sent a bit + // later), in which case lastWindowPeer is another window + if (lastWindowPeer != this) { + Point oldp = lastMouseEventPeer.windowToLocal(x, y, lastWindowPeer); + // Additionally translate from this to lastWindowPeer coordinates + Rectangle lr = lastWindowPeer.getBounds(); + oldp.x += r.x - lr.x; + oldp.y += r.y - lr.y; + postEvent(new MouseEvent(lastMouseEventPeer.getTarget(), + MouseEvent.MOUSE_EXITED, + when, modifiers, + oldp.x, oldp.y, screenX, screenY, + clickCount, popupTrigger, button)); + } else { + Point oldp = lastMouseEventPeer.windowToLocal(x, y, this); + postEvent(new MouseEvent(lastMouseEventPeer.getTarget(), + MouseEvent.MOUSE_EXITED, + when, modifiers, + oldp.x, oldp.y, screenX, screenY, + clickCount, popupTrigger, button)); + } + } + if (targetPeer != null && targetPeer.isEnabled() && id != MouseEvent.MOUSE_ENTERED) { + Point newp = targetPeer.windowToLocal(x, y, curWindowPeer); + postEvent(new MouseEvent(targetPeer.getTarget(), + MouseEvent.MOUSE_ENTERED, when, modifiers, - oldp.x, oldp.y, screenX, screenY, + newp.x, newp.y, screenX, screenY, clickCount, popupTrigger, button)); } } lastMouseEventPeer = targetPeer; - if (targetPeer != null && targetPeer.isEnabled() && id != MouseEvent.MOUSE_ENTERED) { - Point newp = targetPeer.windowToLocal(x, y, curWindowPeer); - postEvent(new MouseEvent(targetPeer.getTarget(), - MouseEvent.MOUSE_ENTERED, - when, modifiers, - newp.x, newp.y, screenX, screenY, - clickCount, popupTrigger, button)); - } } // TODO: fill "bdata" member of AWTEvent --- old/src/macosx/native/sun/awt/AWTView.h 2012-04-18 18:48:38.000000000 +0400 +++ new/src/macosx/native/sun/awt/AWTView.h 2012-04-18 18:48:38.000000000 +0400 @@ -52,9 +52,12 @@ BOOL fPAHNeedsToSelect; id cglLayer; // is a sublayer of view.layer + + BOOL mouseIsOver; } @property (nonatomic, retain) id cglLayer; +@property (nonatomic) BOOL mouseIsOver; - (id) initWithRect:(NSRect) rect platformView:(jobject)cPlatformView windowLayer:(CALayer*)windowLayer; - (void) deliverJavaMouseEvent: (NSEvent *) event; --- old/src/macosx/native/sun/awt/AWTView.m 2012-04-18 18:48:39.000000000 +0400 +++ new/src/macosx/native/sun/awt/AWTView.m 2012-04-18 18:48:39.000000000 +0400 @@ -61,6 +61,7 @@ @synthesize _dropTarget; @synthesize _dragSource; @synthesize cglLayer; +@synthesize mouseIsOver; // Note: Must be called on main (AppKit) thread only - (id) initWithRect: (NSRect) rect @@ -299,6 +300,16 @@ */ -(void) deliverJavaMouseEvent: (NSEvent *) event { + + NSEventType type = [event type]; + + // check synthesized mouse entered/exited events + if((type == NSMouseEntered && mouseIsOver) || (type == NSMouseExited && !mouseIsOver)){ + return; + }else if((type == NSMouseEntered && !mouseIsOver) || (type == NSMouseExited && mouseIsOver)){ + mouseIsOver = !mouseIsOver; + } + [AWTToolkit eventCountPlusPlus]; JNIEnv *env = [ThreadUtilities getJNIEnv]; @@ -306,7 +317,6 @@ NSPoint eventLocation = [event locationInWindow]; NSPoint localPoint = [self convertPoint: eventLocation fromView: nil]; NSPoint absP = [NSEvent mouseLocation]; - NSEventType type = [event type]; // Convert global numbers between Cocoa's coordinate system and Java. // TODO: need consitent way for doing that both with global as well as with local coordinates. --- old/src/macosx/native/sun/awt/AWTWindow.h 2012-04-18 18:48:40.000000000 +0400 +++ new/src/macosx/native/sun/awt/AWTWindow.h 2012-04-18 18:48:40.000000000 +0400 @@ -42,7 +42,7 @@ NSWindow *growBoxWindow; NSSize javaMinSize; NSSize javaMaxSize; - jint styleBits; + jint styleBits; } @property (nonatomic, retain) JNFWeakJObjectWrapper *javaPlatformWindow; @@ -58,6 +58,7 @@ contentView:(NSView *)contentView; - (void) adjustGrowBoxWindow; +- (BOOL) isTopmostWindowUnderMouse; @end #endif _AWTWINDOW_H --- old/src/macosx/native/sun/awt/AWTWindow.m 2012-04-18 18:48:41.000000000 +0400 +++ new/src/macosx/native/sun/awt/AWTWindow.m 2012-04-18 18:48:41.000000000 +0400 @@ -230,6 +230,66 @@ return self; } +-(BOOL) isTopmostWindowUnderMouse{ + + int currentWinID = [self windowNumber]; + + NSRect screenRect = [[NSScreen mainScreen] frame]; + NSPoint nsMouseLocation = [NSEvent mouseLocation]; + CGPoint cgMouseLocation = CGPointMake(nsMouseLocation.x, screenRect.size.height - nsMouseLocation.y); + + NSMutableArray *windows = (NSMutableArray *)CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID); + + + for (NSDictionary *window in windows) { + int layer = [[window objectForKey:(id)kCGWindowLayer] intValue]; + if (layer == 0) { + int winID = [[window objectForKey:(id)kCGWindowNumber] intValue]; + CGRect rect; + CGRectMakeWithDictionaryRepresentation((CFDictionaryRef)[window objectForKey:(id)kCGWindowBounds], &rect); + if(CGRectContainsPoint(rect, cgMouseLocation)){ + return currentWinID == winID; + }else if(currentWinID == winID){ + return NO; + } + } + } + return NO; +} + +- (void) synthesizeMouseEnteredExitedEvents{ + + int eventType = 0; + BOOL isUnderMouse = [self isTopmostWindowUnderMouse]; + BOOL mouseIsOver = [[self contentView] mouseIsOver]; + + if(isUnderMouse && !mouseIsOver){ + eventType = NSMouseEntered; + }else if(!isUnderMouse && mouseIsOver){ + eventType = NSMouseExited; + }else{ + return; + } + + NSPoint screenLocation = [NSEvent mouseLocation]; + NSPoint windowLocation = [self convertScreenToBase: screenLocation]; + int modifierFlags = (eventType == NSMouseEntered) ? NSMouseEnteredMask : NSMouseExitedMask; + + NSEvent * mouseEvent = [NSEvent + enterExitEventWithType: eventType + location: windowLocation + modifierFlags: modifierFlags + timestamp: 0 + windowNumber: [self windowNumber] + context: nil + eventNumber: 0 + trackingNumber: 0 + userData: nil + ]; + + [[self contentView] deliverJavaMouseEvent: mouseEvent]; +} + - (void) dealloc { AWT_ASSERT_APPKIT_THREAD; @@ -713,6 +773,8 @@ // ensure we repaint the whole window after the resize operation // (this will also re-enable screen updates, which were disabled above) // TODO: send PaintEvent + + [window synthesizeMouseEnteredExitedEvents]; }]; JNF_COCOA_EXIT(env); @@ -765,6 +827,7 @@ AWT_ASSERT_APPKIT_THREAD; [window orderBack:nil]; + [window synthesizeMouseEnteredExitedEvents]; }]; JNF_COCOA_EXIT(env); @@ -790,6 +853,8 @@ } else { [window orderFront:window]; } + + [window synthesizeMouseEnteredExitedEvents]; }]; JNF_COCOA_EXIT(env); --- old/src/macosx/native/sun/awt/CWrapper.m 2012-04-18 18:48:42.000000000 +0400 +++ new/src/macosx/native/sun/awt/CWrapper.m 2012-04-18 18:48:42.000000000 +0400 @@ -70,6 +70,11 @@ on:window withObject:nil waitUntilDone:NO]; + + [JNFRunLoop performOnMainThread:@selector(synthesizeMouseEnteredExitedEvents) + on:window + withObject:nil + waitUntilDone:NO]; JNF_COCOA_EXIT(env); } @@ -176,6 +181,11 @@ on:window withObject:window waitUntilDone:NO]; + + [JNFRunLoop performOnMainThread:@selector(synthesizeMouseEnteredExitedEvents) + on:window + withObject:nil + waitUntilDone:NO]; JNF_COCOA_EXIT(env); } @@ -196,6 +206,11 @@ on:window withObject:window waitUntilDone:NO]; + + [JNFRunLoop performOnMainThread:@selector(synthesizeMouseEnteredExitedEvents) + on:window + withObject:nil + waitUntilDone:NO]; JNF_COCOA_EXIT(env); } @@ -216,6 +231,11 @@ on:window withObject:nil waitUntilDone:NO]; + + [JNFRunLoop performOnMainThread:@selector(synthesizeMouseEnteredExitedEvents) + on:window + withObject:nil + waitUntilDone:NO]; JNF_COCOA_EXIT(env); } @@ -333,6 +353,7 @@ NSRect frame = NSMakeRect(x, y, w, h); [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ [window setFrame:frame display:display]; + [window synthesizeMouseEnteredExitedEvents]; }]; JNF_COCOA_EXIT(env); @@ -476,6 +497,11 @@ on:window withObject:nil waitUntilDone:NO]; + + [JNFRunLoop performOnMainThread:@selector(synthesizeMouseEnteredExitedEvents) + on:window + withObject:nil + waitUntilDone:NO]; JNF_COCOA_EXIT(env); } --- old/test/java/awt/regtesthelpers/Util.java 2012-04-18 18:48:43.000000000 +0400 +++ new/test/java/awt/regtesthelpers/Util.java 2012-04-18 18:48:43.000000000 +0400 @@ -600,4 +600,34 @@ time, printEvent); } + + + /** + * Invokes the task on the EDT thread. + * + * @return result of the task + */ + public static T invokeOnEDT(final java.util.concurrent.Callable task) throws Exception { + final java.util.List result = new java.util.ArrayList(1); + final Exception[] exception = new Exception[1]; + + javax.swing.SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + try { + result.add(task.call()); + } catch (Exception e) { + exception[0] = e; + } + } + }); + + if (exception[0] != null) { + throw exception[0]; + } + + return result.get(0); + } + } --- /dev/null 2012-04-18 18:48:44.000000000 +0400 +++ new/test/java/awt/Mouse/EnterExitEvents/FrameStateTest.java 2012-04-18 18:48:44.000000000 +0400 @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + @test + @bug 7154048 + @summary Resized programmatically window does not receive mouse entered/exited events + @author alexandr.scherbatiy area=awt.event + @run main FrameStateTest + */ + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import sun.awt.SunToolkit; + +public class FrameStateTest { + + private static volatile int mouseEnteredCount = 0; + private static volatile int mouseExitedCount = 0; + private static JFrame frame; + + public static void main(String[] args) throws Exception { + + SunToolkit toolkit = (SunToolkit) Toolkit.getDefaultToolkit(); + Robot robot = new Robot(); + robot.setAutoDelay(50); + robot.mouseMove(100, 100); + + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + createAndShowGUI(); + } + }); + + + toolkit.realSync(); + + if (mouseEnteredCount != 1) { + throw new RuntimeException("No Mouse Entered event!"); + } + + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + frame.setExtendedState(Frame.ICONIFIED); + } + }); + + toolkit.realSync(); + + if (mouseExitedCount != 1) { + throw new RuntimeException("No Mouse Exited event!"); + } + + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + System.out.println("NORMAL"); + frame.setExtendedState(Frame.NORMAL); + } + }); + + toolkit.realSync(); + + if (mouseEnteredCount != 2) { + throw new RuntimeException("No Mouse Entered event!"); + } + + robot.mouseMove(500, 500); + + toolkit.realSync(); + + if (mouseExitedCount != 2) { + throw new RuntimeException("No Mouse Exited event!"); + } + + + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + frame.setExtendedState(Frame.MAXIMIZED_BOTH); + } + }); + + toolkit.realSync(); + + if (mouseEnteredCount != 3) { + throw new RuntimeException("No Mouse Entered event!"); + } + + + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + frame.setExtendedState(Frame.NORMAL); + } + }); + + toolkit.realSync(); + + if (mouseExitedCount != 3) { + throw new RuntimeException("No Mouse Exited event!"); + } + + } + + private static void createAndShowGUI() { + + frame = new JFrame("Main Frame"); + frame.setSize(300, 200); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + frame.addMouseListener(new MouseAdapter() { + + @Override + public void mouseEntered(MouseEvent e) { + mouseEnteredCount++; + } + + @Override + public void mouseExited(MouseEvent e) { + mouseExitedCount++; + } + }); + + frame.setVisible(true); + } +} --- /dev/null 2012-04-18 18:48:45.000000000 +0400 +++ new/test/java/awt/Mouse/EnterExitEvents/WindowDragTest.java 2012-04-18 18:48:45.000000000 +0400 @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + @test + @bug 7154048 + @summary Window created under a mouse does not receive mouse enter event + @library ../../regtesthelpers + @build Util + @author alexandr.scherbatiy area=awt.event + @run main WindowDragTest + */ + + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; + +import java.util.concurrent.*; +import sun.awt.SunToolkit; + +import test.java.awt.regtesthelpers.Util; + +public class WindowDragTest { + + private static volatile int dragWindowMouseEnteredCount = 0; + private static volatile int dragWindowMouseReleasedCount = 0; + private static volatile int buttonMouseEnteredCount = 0; + private static volatile int labelMouseReleasedCount = 0; + + private static MyDragWindow dragWindow; + private static JLabel label; + private static JButton button; + + public static void main(String[] args) throws Exception { + + SunToolkit toolkit = (SunToolkit) Toolkit.getDefaultToolkit(); + Robot robot = new Robot(); + robot.setAutoDelay(50); + + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + createAndShowGUI(); + } + }); + + toolkit.realSync(); + + Point pointToClick = Util.invokeOnEDT(new Callable() { + + @Override + public Point call() throws Exception { + return getCenterPoint(label); + } + }); + + + robot.mouseMove(pointToClick.x, pointToClick.y); + robot.mousePress(InputEvent.BUTTON1_MASK); + toolkit.realSync(); + + if (dragWindowMouseEnteredCount != 1) { + throw new RuntimeException("No MouseEntered event on Drag Window!"); + } + + Point pointToDrag = Util.invokeOnEDT(new Callable() { + + @Override + public Point call() throws Exception { + button.addMouseListener(new ButtonMouseListener()); + return getCenterPoint(button); + } + }); + + robot.mouseMove(pointToDrag.x, pointToDrag.y); + toolkit.realSync(); + + if (buttonMouseEnteredCount != 0) { + throw new RuntimeException("Extra MouseEntered event on button!"); + } + + robot.mouseRelease(InputEvent.BUTTON1_MASK); + toolkit.realSync(); + + if (labelMouseReleasedCount != 1) { + throw new RuntimeException("No MouseReleased event on label!"); + } + + } + + private static Point getCenterPoint(Component comp) { + Point p = comp.getLocationOnScreen(); + Rectangle rect = comp.getBounds(); + return new Point(p.x + rect.width / 2, p.y + rect.height / 2); + + + } + + private static void createAndShowGUI() { + + JFrame frame = new JFrame("Main Frame"); + frame.setSize(300, 200); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + label = new JLabel("Label"); + + LabelMouseListener listener = new LabelMouseListener(frame); + label.addMouseListener(listener); + label.addMouseMotionListener(listener); + + button = new JButton("Button"); + Panel panel = new Panel(new BorderLayout()); + + panel.add(label, BorderLayout.NORTH); + panel.add(button, BorderLayout.CENTER); + + frame.getContentPane().add(panel); + frame.setVisible(true); + + } + + private static Point getAbsoluteLocation(MouseEvent e) { + return new Point(e.getXOnScreen(), e.getYOnScreen()); + } + + static class MyDragWindow extends Window { + + static int d = 30; + + public MyDragWindow(Window parent, Point location) { + super(parent); + setSize(150, 150); + setVisible(true); + JPanel panel = new JPanel(); + add(panel); + setLocation(location.x - d, location.y - d); + addMouseListener(new DragWindowMouseListener()); + } + + void dragTo(Point point) { + setLocation(point.x - d, point.y - d); + } + } + + static class LabelMouseListener extends MouseAdapter { + + Point origin; + Window parent; + + public LabelMouseListener(Window parent) { + this.parent = parent; + } + + @Override + public void mousePressed(MouseEvent e) { + if (dragWindow == null) { + dragWindow = new MyDragWindow(parent, getAbsoluteLocation(e)); + } else { + dragWindow.setVisible(true); + dragWindow.dragTo(getAbsoluteLocation(e)); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + labelMouseReleasedCount++; + if (dragWindow != null) { + dragWindow.setVisible(false); + } + } + + public void mouseDragged(MouseEvent e) { + if (dragWindow != null) { + dragWindow.dragTo(getAbsoluteLocation(e)); + } + } + } + + static class DragWindowMouseListener extends MouseAdapter { + + @Override + public void mouseEntered(MouseEvent e) { + dragWindowMouseEnteredCount++; + } + + @Override + public void mouseReleased(MouseEvent e) { + dragWindowMouseReleasedCount++; + } + } + + static class ButtonMouseListener extends MouseAdapter { + + @Override + public void mouseEntered(MouseEvent e) { + buttonMouseEnteredCount++; + } + + } +}