9d2e0b2a80
Reviewed-by: aivanov
327 lines
9.7 KiB
Java
327 lines
9.7 KiB
Java
/*
|
|
* Copyright (c) 2007, 2023, 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.AWTException;
|
|
import java.awt.Component;
|
|
import java.awt.EventQueue;
|
|
import java.awt.Point;
|
|
import java.awt.Rectangle;
|
|
import java.awt.event.InputEvent;
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
|
|
import javax.swing.SwingUtilities;
|
|
|
|
/**
|
|
* JRobot is a wrapper around java.awt.Robot that provides some convenience
|
|
* methods.
|
|
* <p>When using jtreg you would include this class via something like:
|
|
* <pre>{@code
|
|
* @library ../../../regtesthelpers
|
|
* @build JRobot
|
|
* }</pre>
|
|
*/
|
|
public class JRobot extends java.awt.Robot {
|
|
private static final int DEFAULT_DELAY = 550;
|
|
private static final int INTERNAL_DELAY = 250;
|
|
|
|
private int delay;
|
|
private boolean delaysEnabled;
|
|
|
|
protected JRobot(boolean enableDelays) throws AWTException {
|
|
super();
|
|
delaysEnabled = enableDelays;
|
|
setAutoWaitForIdle(enableDelays);
|
|
if (enableDelays) {
|
|
setAutoDelay(INTERNAL_DELAY);
|
|
setDelay(DEFAULT_DELAY);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return a JRobot. Delays are enabled by default.
|
|
* @return a JRobot
|
|
*/
|
|
public static JRobot getRobot() {
|
|
return getRobot(true);
|
|
}
|
|
|
|
/**
|
|
* Create a JRobot. The parameter controls whether delays are enabled.
|
|
* @param enableDelays controls whether delays are enabled.
|
|
* @return a JRobot
|
|
*/
|
|
public static JRobot getRobot(boolean enableDelays) {
|
|
JRobot robot = null;
|
|
try {
|
|
robot = new JRobot(enableDelays);
|
|
} catch (AWTException e) {
|
|
System.err.println("Coudn't create Robot, details below");
|
|
throw new Error(e);
|
|
}
|
|
return robot;
|
|
}
|
|
|
|
/**
|
|
* Press and release a key.
|
|
* @param keycode which key to press. For example, KeyEvent.VK_DOWN
|
|
*/
|
|
public void hitKey(int keycode) {
|
|
keyPress(keycode);
|
|
keyRelease(keycode);
|
|
delay();
|
|
}
|
|
|
|
/**
|
|
* Press and release a key with modifiers.
|
|
* @param keys keys to press. Keys are pressed in order they are passed as
|
|
* parameters to this method. All keys except the last one are considered
|
|
* modifiers. For example, to press Ctrl+Shift+T, call:
|
|
* hitKey(KeyEvent.VK_CONTROL, KeyEvent.VK_SHIFT, KeyEvent.VK_T);
|
|
*/
|
|
public void hitKey(int... keys) {
|
|
for (int i = 0; i < keys.length; i++) {
|
|
keyPress(keys[i]);
|
|
}
|
|
|
|
for (int i = keys.length - 1; i >= 0; i--) {
|
|
keyRelease(keys[i]);
|
|
}
|
|
delay();
|
|
}
|
|
|
|
/**
|
|
* Move mouse cursor to the center of the Component.
|
|
* <p>
|
|
* <b>Note:</b> This method is executed on EDT.
|
|
*
|
|
* @param c Component the mouse is placed over
|
|
*/
|
|
public void moveMouseTo(Component c) {
|
|
Point p = getCenterPoint(c);
|
|
mouseMove(p.x, p.y);
|
|
delay();
|
|
}
|
|
|
|
private static Point getCenterPoint(Component c) {
|
|
AtomicReference<Point> result = new AtomicReference<>();
|
|
if (SwingUtilities.isEventDispatchThread()) {
|
|
result.set(getCenterPointImpl(c));
|
|
} else {
|
|
try {
|
|
SwingUtilities.invokeAndWait(() -> result.set(getCenterPointImpl(c)));
|
|
} catch (InterruptedException | InvocationTargetException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
return result.get();
|
|
}
|
|
|
|
private static Point getCenterPointImpl(Component c) {
|
|
return centerOf(new Rectangle(c.getLocationOnScreen(), c.getSize()));
|
|
}
|
|
|
|
/**
|
|
* Move mouse smoothly from (x0, y0) to (x1, y1).
|
|
*/
|
|
public void glide(int x0, int y0, int x1, int y1) {
|
|
float dmax = (float)Math.max(Math.abs(x1 - x0), Math.abs(y1 - y0));
|
|
float dx = (x1 - x0) / dmax;
|
|
float dy = (y1 - y0) / dmax;
|
|
|
|
mouseMove(x0, y0);
|
|
for (int i=1; i<=dmax; i++) {
|
|
mouseMove((int)(x0 + dx*i), (int)(y0 + dy*i));
|
|
}
|
|
delay();
|
|
}
|
|
|
|
/**
|
|
* Perform a mouse click, i.e. press and release mouse button(s).
|
|
* @param buttons mouse button(s).
|
|
* For example, MouseEvent.BUTTON1_MASK
|
|
*/
|
|
public void clickMouse(int buttons) {
|
|
mousePress(buttons);
|
|
mouseRelease(buttons);
|
|
delay();
|
|
}
|
|
|
|
/**
|
|
* Perform a click with the first mouse button.
|
|
*/
|
|
public void clickMouse() {
|
|
clickMouse(InputEvent.BUTTON1_DOWN_MASK);
|
|
}
|
|
|
|
/**
|
|
* Click in the center of the given Component.
|
|
* <p>
|
|
* <b>Note:</b> This method is executed on EDT.
|
|
*
|
|
* @param c the Component to click on
|
|
* @param buttons mouse button(s)
|
|
*/
|
|
public void clickMouseOn(Component c, int buttons) {
|
|
moveMouseTo(c);
|
|
clickMouse(buttons);
|
|
}
|
|
|
|
/**
|
|
* Click the first mouse button in the center of the given Component
|
|
* <p>
|
|
* <b>Note:</b> This method is executed on EDT.
|
|
*
|
|
* @param c the Component to click on
|
|
*/
|
|
public void clickMouseOn(Component c) {
|
|
clickMouseOn(c, InputEvent.BUTTON1_DOWN_MASK);
|
|
}
|
|
|
|
/**
|
|
* Return whether delays are enabled
|
|
* @return whether delays are enabled
|
|
*/
|
|
public boolean getDelaysEnabled() {
|
|
return delaysEnabled;
|
|
}
|
|
|
|
/**
|
|
* Delay execution by delay milliseconds
|
|
*/
|
|
public void delay() {
|
|
delay(delay);
|
|
}
|
|
|
|
/**
|
|
* Return the delay amount, in milliseconds
|
|
*/
|
|
public int getDelay() {
|
|
return delay;
|
|
}
|
|
|
|
/**
|
|
* Set the delay amount, in milliseconds
|
|
*/
|
|
public void setDelay(int delay) {
|
|
this.delay = delay;
|
|
}
|
|
|
|
/**
|
|
* Waits until all events currently on the event queue have been processed.
|
|
* Does nothing if called on EDT
|
|
*/
|
|
public synchronized void waitForIdle() {
|
|
if (!EventQueue.isDispatchThread()) {
|
|
super.waitForIdle();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calculate the center of the Rectangle passed, and return them
|
|
* in a Point object.
|
|
* @param r a non-null Rectangle
|
|
* @return a new Point object containing coordinates of r's center
|
|
*/
|
|
public static Point centerOf(Rectangle r) {
|
|
return centerOf(r, new Point());
|
|
}
|
|
|
|
/**
|
|
* Calculate the center of the Rectangle passed, and store it in p.
|
|
* @param r a non-null Rectangle
|
|
* @param p a non-null Point that receives coordinates of r's center
|
|
* @return p
|
|
*/
|
|
public static Point centerOf(Rectangle r, Point p) {
|
|
p.x = r.x + r.width / 2;
|
|
p.y = r.y + r.height / 2;
|
|
return p;
|
|
}
|
|
|
|
/**
|
|
* Convert a rectangle from coordinate system of Component c to
|
|
* screen coordinate system.
|
|
* <p>
|
|
* <b>Note:</b> This method is executed on EDT.
|
|
*
|
|
* @param r a non-null Rectangle
|
|
* @param c a Component whose coordinate system is used for conversion
|
|
*/
|
|
public void convertRectToScreen(Rectangle r, Component c) {
|
|
AtomicReference<Point> p = new AtomicReference<>();
|
|
if (SwingUtilities.isEventDispatchThread()) {
|
|
p.set(c.getLocationOnScreen());
|
|
} else {
|
|
try {
|
|
SwingUtilities.invokeAndWait(() -> p.set(c.getLocationOnScreen()));
|
|
} catch (InterruptedException | InvocationTargetException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
r.setLocation(p.get());
|
|
}
|
|
|
|
/**
|
|
* Compares two rectangles pixel-by-pixel.
|
|
* @param r0 the first area
|
|
* @param r1 the second area
|
|
* return true if all pixels in the two areas are identical
|
|
*/
|
|
public boolean compareRects(Rectangle r0, Rectangle r1) {
|
|
int xShift = r1.x - r0.x;
|
|
int yShift = r1.y - r0.y;
|
|
|
|
for (int y = r0.y; y < r0.y + r0.height; y++) {
|
|
for (int x = r0.x; x < r0.x + r0.width; x++) {
|
|
if (!comparePixels(x, y, x + xShift, y + yShift)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Compares colors of two points on the screen.
|
|
* @param p0 the first point
|
|
* @param p1 the second point
|
|
* return true if the two points have the same color
|
|
*/
|
|
public boolean comparePixels(Point p0, Point p1) {
|
|
return comparePixels(p0.x, p0.y, p1.x, p1.y);
|
|
}
|
|
|
|
/**
|
|
* Compares colors of two points on the screen.
|
|
* @param x0 the x coordinate of the first point
|
|
* @param y0 the y coordinate of the first point
|
|
* @param x1 the x coordinate of the second point
|
|
* @param y1 the y coordinate of the second point
|
|
* return true if the two points have the same color
|
|
*/
|
|
public boolean comparePixels(int x0, int y0, int x1, int y1) {
|
|
return (getPixelColor(x0, y0).equals(getPixelColor(x1, y1)));
|
|
}
|
|
}
|