From 461cb84277b40d01c5d04be3c74f25d8667a207c Mon Sep 17 00:00:00 2001 From: Alisen Chung Date: Tue, 3 Jun 2025 18:02:47 +0000 Subject: [PATCH] 8345538: Robot.mouseMove doesn't clamp bounds on macOS when trying to move mouse off screen Reviewed-by: honkar, prr --- .../classes/sun/lwawt/macosx/CRobot.java | 40 ++++++++- .../java/awt/Robot/MouseMoveOffScreen.java | 83 +++++++++++++++++++ 2 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 test/jdk/java/awt/Robot/MouseMoveOffScreen.java diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CRobot.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CRobot.java index 5dba06cb612..036da04a7cb 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CRobot.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CRobot.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, 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 @@ -25,6 +25,8 @@ package sun.lwawt.macosx; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; import java.awt.Point; import java.awt.Rectangle; import java.awt.peer.RobotPeer; @@ -63,7 +65,41 @@ final class CRobot implements RobotPeer { mouseLastX = x; mouseLastY = y; - mouseEvent(mouseLastX, mouseLastY, mouseButtonsState, true, true); + int leastDiff = Integer.MAX_VALUE; + int finX = x; + int finY = y; + + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice[] gs = ge.getScreenDevices(); + Rectangle[] allScreenBounds = new Rectangle[gs.length]; + + for (int i = 0; i < gs.length; i++) { + allScreenBounds[i] = gs[i].getDefaultConfiguration().getBounds(); + } + + for (Rectangle screenBounds : allScreenBounds) { + Point cP = calcClosestPoint(x, y, screenBounds); + + int currXDiff = Math.abs(x - cP.x); + int currYDiff = Math.abs(y - cP.y); + int currDiff = (int) Math.round(Math.hypot(currXDiff, currYDiff)); + + if (currDiff == 0) { + mouseEvent(mouseLastX, mouseLastY, mouseButtonsState, true, true); + return; + } if (currDiff < leastDiff) { + finX = cP.x; + finY = cP.y; + leastDiff = currDiff; + } + } + + mouseEvent(finX, finY, mouseButtonsState, true, true); + } + + private Point calcClosestPoint(int x, int y, Rectangle screenBounds) { + return new Point(Math.min(Math.max(x, screenBounds.x), screenBounds.x + screenBounds.width - 1), + Math.min(Math.max(y, screenBounds.y), screenBounds.y + screenBounds.height - 1)); } /** diff --git a/test/jdk/java/awt/Robot/MouseMoveOffScreen.java b/test/jdk/java/awt/Robot/MouseMoveOffScreen.java new file mode 100644 index 00000000000..809e3dd1ff1 --- /dev/null +++ b/test/jdk/java/awt/Robot/MouseMoveOffScreen.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2025, 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. + */ + +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.MouseInfo; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; + +/* + * @test + * @bug 8345538 + * @summary Tests mouseMove clamping to screen bounds when set to move offscreen + * @requires (os.family == "mac") + * @key headful + * @run main MouseMoveOffScreen + */ + +public class MouseMoveOffScreen { + private static final Point STARTING_LOC = new Point(200, 200); + private static final Point OFF_SCREEN_LOC = new Point(20000, 200); + private static Rectangle[] r; + + public static void main(String[] args) throws Exception { + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice[] gs = ge.getScreenDevices(); + r = new Rectangle[gs.length]; + + for (int i = 0; i < gs.length; i++) { + r[i] = gs[i].getDefaultConfiguration().getBounds(); + System.out.println("Screen: "+ gs[i].getIDstring() + " Bounds: " + r[i]); + } + + Point offsc = validateOffScreen(OFF_SCREEN_LOC); + Robot robot = new Robot(); + robot.mouseMove(STARTING_LOC.x, STARTING_LOC.y); + robot.delay(500); + robot.mouseMove(offsc.x, offsc.y); + robot.delay(500); + + Point currLoc = MouseInfo.getPointerInfo().getLocation(); + + if (currLoc == null) { + throw new RuntimeException("Test Failed, getLocation returned null."); + } + + System.out.println("Current mouse location: " + currLoc); + if (currLoc.equals(OFF_SCREEN_LOC)) { + throw new RuntimeException("Test Failed, robot moved mouse off screen."); + } + } + + private static Point validateOffScreen(Point p) { + for (Rectangle rect : r) { + if (rect.contains(p)) { + return validateOffScreen(new Point(p.x * 2, p.y)); + } + } + return p; + } +}