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"); } }