src/java.desktop/share/classes/java/awt/Robot.java
Print this page
*** 31,45 ****
import java.awt.image.DataBufferInt;
import java.awt.image.DirectColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.awt.peer.RobotPeer;
! import java.lang.reflect.InvocationTargetException;
import sun.awt.AWTPermissions;
import sun.awt.ComponentFactory;
import sun.awt.SunToolkit;
import sun.awt.image.SunWritableRaster;
/**
* This class is used to generate native system input events
* for the purposes of test automation, self-running demos, and
* other applications where control of the mouse and keyboard
--- 31,48 ----
import java.awt.image.DataBufferInt;
import java.awt.image.DirectColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.awt.peer.RobotPeer;
! import java.security.AccessController;
!
import sun.awt.AWTPermissions;
import sun.awt.ComponentFactory;
+ import sun.awt.ExtendedKeyCodes;
import sun.awt.SunToolkit;
import sun.awt.image.SunWritableRaster;
+ import sun.security.action.GetIntegerAction;
/**
* This class is used to generate native system input events
* for the purposes of test automation, self-running demos, and
* other applications where control of the mouse and keyboard
*** 50,59 ****
--- 53,69 ----
* events to the AWT event queue or AWT components in that the
* events are generated in the platform's native input
* queue. For example, <code>Robot.mouseMove</code> will actually move
* the mouse cursor instead of just generating mouse move events.
* <p>
+ * Robot uses delay {@link #getSyncDelay()} to make syncing threads
+ * with {@link #waitForIdle()} more stable. This delay can be set once
+ * on creating object and could not be changed throughout object lifecycle.
+ * Constructor reads vm integer property {@code java.awt.robotdelay} and
+ * sets the delay value equal to the property value. If the property was
+ * not set 500 milliseconds default value is used.
+ * <p>
* Note that some platforms require special privileges or extensions
* to access low-level input control. If the current platform configuration
* does not allow input control, an <code>AWTException</code> will be thrown
* when trying to construct Robot objects. For example, X-Window systems
* will throw the exception if the XTEST 2.2 standard extension is not supported
*** 69,78 ****
--- 79,98 ----
private static final int MAX_DELAY = 60000;
private RobotPeer peer;
private boolean isAutoWaitForIdle = false;
private int autoDelay = 0;
private static int LEGAL_BUTTON_MASK = 0;
+ private static int DEFAULT_SPEED = 20; // Speed for mouse glide and click
+ private static int DEFAULT_SYNC_DELAY = 500; // Default Additional delay for waitForIdle()
+ private static int DEFAULT_STEP_LENGTH = 2; // Step length (in pixels) for mouse glide
+
+ private final int syncDelay;
+ {
+ syncDelay = AccessController.doPrivileged(
+ new GetIntegerAction("java.awt.robotdelay", DEFAULT_SYNC_DELAY));
+ }
+
private DirectColorModel screenCapCM = null;
/**
* Constructs a Robot object in the coordinate system of the primary screen.
*** 198,207 ****
--- 218,353 ----
peer.mouseMove(x, y);
afterEvent();
}
/**
+ * Moves mouse pointer to given screen coordinates.
+ *
+ * @param position Target position
+ *
+ * @see #mouseMove(int, int)
+ */
+ public synchronized void mouseMove(Point position) {
+ mouseMove(position.x, position.y);
+ }
+
+ /**
+ * Move mouse cursor to the center of the Component.
+ *
+ * @param c Component the mouse is placed over
+ *
+ * @see #mouseMove(int, int)
+ */
+ public synchronized void mouseMove(Component c) {
+ Point p = c.getLocationOnScreen();
+ Dimension size = c.getSize();
+ p.x += size.width / 2;
+ p.y += size.height / 2;
+ mouseMove(p.x, p.y);
+ }
+
+ /**
+ * Move the mouse in multiple steps from where it is
+ * now to the destination coordinates.
+ *
+ * @param x Destination point x coordinate
+ * @param y Destination point y coordinate
+ *
+ * @see #glide(int, int, int, int)
+ */
+ public void glide(int x, int y) {
+ Point p = MouseInfo.getPointerInfo().getLocation();
+ glide(p.x, p.y, x, y);
+ }
+
+ /**
+ * Move the mouse in multiple steps from where it is
+ * now to the destination point.
+ *
+ * @param dest Destination point
+ *
+ * @see #glide(int, int)
+ */
+ public void glide(Point dest) {
+ glide(dest.x, dest.y);
+ }
+
+ /**
+ * Move the mouse in multiple steps from source coordinates
+ * to the destination coordinates.
+ *
+ * @param fromX Source point x coordinate
+ * @param fromY Source point y coordinate
+ * @param toX Destination point x coordinate
+ * @param toY Destination point y coordinate
+ *
+ * @see #glide(int, int, int, int, int, int)
+ */
+ public void glide(int fromX, int fromY, int toX, int toY) {
+ glide(fromX, fromY, toX, toY, DEFAULT_STEP_LENGTH, DEFAULT_SPEED);
+ }
+
+ /**
+ * Move the mouse in multiple steps from source point to the
+ * destination point with default speed and step length.
+ *
+ * @param src Source point
+ * @param dest Destination point
+ *
+ * @see #glide(int, int, int, int, int, int)
+ */
+ public void glide(Point src, Point dest) {
+ glide(src.x, src.y, dest.x, dest.y, DEFAULT_STEP_LENGTH, DEFAULT_SPEED);
+ }
+
+ /**
+ * Move the mouse in multiple steps from source point to the
+ * destination point with given speed and step length.
+ *
+ * @param srcX Source point x cordinate
+ * @param srcY Source point y cordinate
+ * @param destX Destination point x cordinate
+ * @param destY Destination point y cordinate
+ * @param stepLength Approximate length of one step
+ * @param speed Delay between steps.
+ *
+ * @see #mouseMove(int, int)
+ * @see #delay(int)
+ */
+ public void glide(int srcX, int srcY, int destX, int destY, int stepLength, int speed) {
+ int stepNum;
+ double tDx, tDy;
+ double dx, dy, ds;
+ double x, y;
+
+ dx = (destX - srcX);
+ dy = (destY - srcY);
+ ds = Math.sqrt(dx*dx + dy*dy);
+
+ tDx = dx / ds * stepLength;
+ tDy = dy / ds * stepLength;
+
+ int stepsCount = (int) ds / stepLength;
+
+ // Walk the mouse to the destination one step at a time
+ mouseMove(srcX, srcY);
+
+ for (x = srcX, y = srcY, stepNum = 0;
+ stepNum < stepsCount;
+ stepNum++) {
+ x += tDx;
+ y += tDy;
+ mouseMove((int)x, (int)y);
+ delay(speed);
+ }
+
+ // Ensure the mouse moves to the right destination.
+ // The steps may have led the mouse to a slightly wrong place.
+ mouseMove(destX, destY);
+ }
+
+ /**
* Presses one or more mouse buttons. The mouse buttons should
* be released using the {@link #mouseRelease(int)} method.
*
* @param buttons the Button mask; a combination of one or more
* mouse button masks.
*** 312,321 ****
--- 458,552 ----
checkButtonsArgument(buttons);
peer.mouseRelease(buttons);
afterEvent();
}
+ /**
+ * Clicks mouse button(s) by calling {@link #mousePress(int)} and
+ * {@link #mouseRelease(int)} methods
+ *
+ *
+ * @param buttons The button mask; a combination of one or more mouse button masks.
+ * @throws IllegalArgumentException if the {@code buttons} mask contains the mask for
+ * extra mouse button and support for extended mouse buttons is
+ * {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
+ * @throws IllegalArgumentException if the {@code buttons} mask contains the mask for
+ * extra mouse button that does not exist on the mouse and support for extended
+ * mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() enabled}
+ * by Java
+ *
+ * @see #mousePress(int)
+ * @see #mouseRelease(int)
+ * @see InputEvent#getMaskForButton(int)
+ * @see Toolkit#areExtraMouseButtonsEnabled()
+ * @see java.awt.event.MouseEvent
+ */
+ public void click(int buttons) {
+ mousePress(buttons);
+ waitForIdle(DEFAULT_SPEED);
+ mouseRelease(buttons);
+ waitForIdle();
+ }
+
+ /**
+ * Clicks mouse button 1
+ *
+ * @throws IllegalArgumentException if the {@code buttons} mask contains the mask for
+ * extra mouse button and support for extended mouse buttons is
+ * {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
+ * @throws IllegalArgumentException if the {@code buttons} mask contains the mask for
+ * extra mouse button that does not exist on the mouse and support for extended
+ * mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() enabled}
+ * by Java
+ *
+ * @see #click(int)
+ */
+ public void click() {
+ click(InputEvent.BUTTON1_DOWN_MASK);
+ }
+
+ /**
+ * Emulate native drag and drop process using {@code InputEvent.BUTTON1_DOWN_MASK}.
+ * The method successively moves mouse cursor to point with coordinates
+ * ({@code fromX}, {@code fromY}), presses mouse button 1, drag mouse to
+ * point with coordinates ({@code toX}, {@code toY}) and releases mouse
+ * button 1 at last.
+ *
+ * @param fromX Source point x coordinate
+ * @param fromY Source point y coordinate
+ * @param toX Destination point x coordinate
+ * @param toY Destination point y coordinate
+ *
+ * @see #mousePress(int)
+ * @see #glide(int, int, int, int)
+ */
+ public void dragAndDrop(int fromX, int fromY, int toX, int toY) {
+ mouseMove(fromX, fromY);
+ mousePress(InputEvent.BUTTON1_DOWN_MASK);
+ waitForIdle();
+ glide(toX, toY);
+ mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
+ waitForIdle();
+ }
+
+ /**
+ * Emulate native drag and drop process using {@code InputEvent.BUTTON1_DOWN_MASK}.
+ * The method successively moves mouse cursor to point {@code from},
+ * presses mouse button 1, drag mouse to point {@code to} and releases
+ * mouse button 1 at last.
+ *
+ * @param from Source point
+ * @param to Destination point
+ *
+ * @see #mousePress(int)
+ * @see #glide(int, int, int, int)
+ * @see #dragAndDrop(int, int, int, int)
+ */
+ public void dragAndDrop(Point from, Point to) {
+ dragAndDrop(from.x, from.y, to.x, to.y);
+ }
+
private void checkButtonsArgument(int buttons) {
if ( (buttons|LEGAL_BUTTON_MASK) != LEGAL_BUTTON_MASK ) {
throw new IllegalArgumentException("Invalid combination of button flags");
}
}
*** 371,380 ****
--- 602,669 ----
checkKeycodeArgument(keycode);
peer.keyRelease(keycode);
afterEvent();
}
+ /**
+ * Successively presses and releases a given key.
+ * <p>
+ * Key codes that have more than one physical key associated with them
+ * (e.g. {@code KeyEvent.VK_SHIFT} could mean either the
+ * left or right shift key) will map to the left key.
+ *
+ * @param keycode Key to press (e.g. {@code KeyEvent.VK_A})
+ * @throws IllegalArgumentException if {@code keycode} is not
+ * a valid key
+ *
+ * @see #keyPress(int)
+ * @see #keyRelease(int)
+ * @see java.awt.event.KeyEvent
+ */
+ public void type(int keycode) {
+ keyPress(keycode);
+ waitForIdle(DEFAULT_SPEED);
+ keyRelease(keycode);
+ waitForIdle();
+ }
+
+ /**
+ * Types given character
+ *
+ * @param c Character to be typed (e.g. {@code 'a'})
+ *
+ * @see #type(int)
+ * @see java.awt.event.KeyEvent
+ */
+ public void type(char c) {
+ type(ExtendedKeyCodes.getExtendedKeyCodeForChar(c));
+ }
+
+ /**
+ * Types given array of characters one by one
+ *
+ * @param symbols Array of characters to be typed
+ *
+ * @see #type(char)
+ */
+ public void type(char[] symbols) {
+ for (int i = 0; i < symbols.length; i++) {
+ type(symbols[i]);
+ }
+ }
+
+ /**
+ * Types given string
+ *
+ * @param s String to be typed
+ *
+ * @see #type(char[])
+ */
+ public void type(String s) {
+ type(s.toCharArray());
+ }
+
private void checkKeycodeArgument(int keycode) {
// rather than build a big table or switch statement here, we'll
// just check that the key isn't VK_UNDEFINED and assume that the
// peer implementations will throw an exception for other bogus
// values e.g. -1, 999999
*** 495,504 ****
--- 784,804 ----
waitForIdle();
}
}
/**
+ * Returns delay length for {@link #waitForIdle()} method
+ *
+ * @return Current delay value
+ *
+ * @see #waitForIdle()
+ */
+ public int getSyncDelay() {
+ return this.syncDelay;
+ }
+
+ /**
* Returns the number of milliseconds this Robot sleeps after generating an event.
*
* @return the delay duration in milliseconds
*/
public synchronized int getAutoDelay() {
*** 548,580 ****
throw new IllegalArgumentException("Delay must be to 0 to 60,000ms");
}
}
/**
! * Waits until all events currently on the event queue have been processed.
! * @throws IllegalThreadStateException if called on the AWT event dispatching thread
*/
! public synchronized void waitForIdle() {
! checkNotDispatchThread();
! // post a dummy event to the queue so we know when
! // all the events before it have been processed
! try {
SunToolkit.flushPendingEvents();
! EventQueue.invokeAndWait( new Runnable() {
! public void run() {
! // dummy implementation
}
! } );
! } catch(InterruptedException ite) {
! System.err.println("Robot.waitForIdle, non-fatal exception caught:");
ite.printStackTrace();
- } catch(InvocationTargetException ine) {
- System.err.println("Robot.waitForIdle, non-fatal exception caught:");
- ine.printStackTrace();
}
}
private void checkNotDispatchThread() {
if (EventQueue.isDispatchThread()) {
throw new IllegalThreadStateException("Cannot call method from the event dispatcher thread");
}
}
--- 848,902 ----
throw new IllegalArgumentException("Delay must be to 0 to 60,000ms");
}
}
/**
! * Waits until all events currently on the event queue have been processed with given
! * delay after syncing threads.
! *
! * @param delayValue Additional delay length in milliseconds to wait until thread
! * sync been completed
! * @throws sun.awt.SunToolkit.IllegalThreadException if called on the AWT event
! * dispatching thread
*/
! public synchronized void waitForIdle(int delayValue) {
SunToolkit.flushPendingEvents();
! ((SunToolkit) Toolkit.getDefaultToolkit()).realSync();
! delay(delayValue);
}
!
! /**
! * Static method for synchronizing event queue with current thread. Waits until
! * all events currently on the event queue have been processed with a small delay
! * of 500 ms after syncing threads.
! *
! * @throws sun.awt.SunToolkit.IllegalThreadException if called on the AWT event
! * dispatching thread
! */
! public static synchronized void syncEventQueue() {
! SunToolkit.flushPendingEvents();
! ((SunToolkit) Toolkit.getDefaultToolkit()).realSync();
! try {
! Thread.sleep(DEFAULT_SYNC_DELAY);
! } catch (InterruptedException ite) {
ite.printStackTrace();
}
}
+ /**
+ * Waits until all events currently on the event queue have been processed with delay
+ * {@link #getSyncDelay()} after syncing threads.
+ *
+ * @throws sun.awt.SunToolkit.IllegalThreadException if called on the AWT event
+ * dispatching thread
+ *
+ * @see #waitForIdle(int)
+ */
+ public synchronized void waitForIdle() {
+ waitForIdle(syncDelay);
+ }
+
private void checkNotDispatchThread() {
if (EventQueue.isDispatchThread()) {
throw new IllegalThreadStateException("Cannot call method from the event dispatcher thread");
}
}