/* * Copyright (c) 1999, 2014, 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * 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. */ package java.awt; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; 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 * is needed. The primary purpose of Robot is to facilitate * automated testing of Java platform implementations. *

* Using the class to generate input events differs from posting * events to the AWT event queue or AWT components in that the * events are generated in the platform's native input * queue. For example, Robot.mouseMove will actually move * the mouse cursor instead of just generating mouse move events. *

* 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. *

* 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 AWTException 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 * (or not enabled) by the X server. *

* Applications that use Robot for purposes other than self-testing should * handle these error conditions gracefully. * * @author Robi Khan * @since 1.3 */ public class Robot { 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. * * @throws AWTException if the platform configuration does not allow * low-level input control. This exception is always thrown when * GraphicsEnvironment.isHeadless() returns true * @throws SecurityException if createRobot permission is not granted * @see java.awt.GraphicsEnvironment#isHeadless * @see SecurityManager#checkPermission * @see AWTPermission */ public Robot() throws AWTException { if (GraphicsEnvironment.isHeadless()) { throw new AWTException("headless environment"); } init(GraphicsEnvironment.getLocalGraphicsEnvironment() .getDefaultScreenDevice()); } /** * Creates a Robot for the given screen device. Coordinates passed * to Robot method calls like mouseMove and createScreenCapture will * be interpreted as being in the same coordinate system as the * specified screen. Note that depending on the platform configuration, * multiple screens may either: *

* This constructor is meant for the latter case. *

* If screen devices are reconfigured such that the coordinate system is * affected, the behavior of existing Robot objects is undefined. * * @param screen A screen GraphicsDevice indicating the coordinate * system the Robot will operate in. * @throws AWTException if the platform configuration does not allow * low-level input control. This exception is always thrown when * GraphicsEnvironment.isHeadless() returns true. * @throws IllegalArgumentException if screen is not a screen * GraphicsDevice. * @throws SecurityException if createRobot permission is not granted * @see java.awt.GraphicsEnvironment#isHeadless * @see GraphicsDevice * @see SecurityManager#checkPermission * @see AWTPermission */ public Robot(GraphicsDevice screen) throws AWTException { checkIsScreenDevice(screen); init(screen); } private void init(GraphicsDevice screen) throws AWTException { checkRobotAllowed(); Toolkit toolkit = Toolkit.getDefaultToolkit(); if (toolkit instanceof ComponentFactory) { peer = ((ComponentFactory)toolkit).createRobot(this, screen); disposer = new RobotDisposer(peer); sun.java2d.Disposer.addRecord(anchor, disposer); } initLegalButtonMask(); } private static synchronized void initLegalButtonMask() { if (LEGAL_BUTTON_MASK != 0) return; int tmpMask = 0; if (Toolkit.getDefaultToolkit().areExtraMouseButtonsEnabled()){ if (Toolkit.getDefaultToolkit() instanceof SunToolkit) { final int buttonsNumber = ((SunToolkit)(Toolkit.getDefaultToolkit())).getNumberOfButtons(); for (int i = 0; i < buttonsNumber; i++){ tmpMask |= InputEvent.getMaskForButton(i+1); } } } tmpMask |= InputEvent.BUTTON1_MASK| InputEvent.BUTTON2_MASK| InputEvent.BUTTON3_MASK| InputEvent.BUTTON1_DOWN_MASK| InputEvent.BUTTON2_DOWN_MASK| InputEvent.BUTTON3_DOWN_MASK; LEGAL_BUTTON_MASK = tmpMask; } /* determine if the security policy allows Robot's to be created */ private void checkRobotAllowed() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission(AWTPermissions.CREATE_ROBOT_PERMISSION); } } /* check if the given device is a screen device */ private void checkIsScreenDevice(GraphicsDevice device) { if (device == null || device.getType() != GraphicsDevice.TYPE_RASTER_SCREEN) { throw new IllegalArgumentException("not a valid screen device"); } } private transient Object anchor = new Object(); static class RobotDisposer implements sun.java2d.DisposerRecord { private final RobotPeer peer; public RobotDisposer(RobotPeer peer) { this.peer = peer; } public void dispose() { if (peer != null) { peer.dispose(); } } } private transient RobotDisposer disposer; /** * Moves mouse pointer to given screen coordinates. * @param x X position * @param y Y position */ public synchronized void mouseMove(int x, int y) { 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. *

* It is allowed to use only a combination of valid values as a {@code buttons} parameter. * A valid combination consists of {@code InputEvent.BUTTON1_DOWN_MASK}, * {@code InputEvent.BUTTON2_DOWN_MASK}, {@code InputEvent.BUTTON3_DOWN_MASK} * and values returned by the * {@link InputEvent#getMaskForButton(int) InputEvent.getMaskForButton(button)} method. * * The valid combination also depends on a * {@link Toolkit#areExtraMouseButtonsEnabled() Toolkit.areExtraMouseButtonsEnabled()} value as follows: *

*

* The following standard button masks are also accepted: *

* However, it is recommended to use {@code InputEvent.BUTTON1_DOWN_MASK}, * {@code InputEvent.BUTTON2_DOWN_MASK}, {@code InputEvent.BUTTON3_DOWN_MASK} instead. * Either extended {@code _DOWN_MASK} or old {@code _MASK} values * should be used, but both those models should not be mixed. * @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 #mouseRelease(int) * @see InputEvent#getMaskForButton(int) * @see Toolkit#areExtraMouseButtonsEnabled() * @see java.awt.MouseInfo#getNumberOfButtons() * @see java.awt.event.MouseEvent */ public synchronized void mousePress(int buttons) { checkButtonsArgument(buttons); peer.mousePress(buttons); afterEvent(); } /** * Releases one or more mouse buttons. * * @param buttons the Button mask; a combination of one or more * mouse button masks. *

* It is allowed to use only a combination of valid values as a {@code buttons} parameter. * A valid combination consists of {@code InputEvent.BUTTON1_DOWN_MASK}, * {@code InputEvent.BUTTON2_DOWN_MASK}, {@code InputEvent.BUTTON3_DOWN_MASK} * and values returned by the * {@link InputEvent#getMaskForButton(int) InputEvent.getMaskForButton(button)} method. * * The valid combination also depends on a * {@link Toolkit#areExtraMouseButtonsEnabled() Toolkit.areExtraMouseButtonsEnabled()} value as follows: *

*

* The following standard button masks are also accepted: *

* However, it is recommended to use {@code InputEvent.BUTTON1_DOWN_MASK}, * {@code InputEvent.BUTTON2_DOWN_MASK}, {@code InputEvent.BUTTON3_DOWN_MASK} instead. * Either extended {@code _DOWN_MASK} or old {@code _MASK} values * should be used, but both those models should not be mixed. * @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 InputEvent#getMaskForButton(int) * @see Toolkit#areExtraMouseButtonsEnabled() * @see java.awt.MouseInfo#getNumberOfButtons() * @see java.awt.event.MouseEvent */ public synchronized void mouseRelease(int buttons) { 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"); } } /** * Rotates the scroll wheel on wheel-equipped mice. * * @param wheelAmt number of "notches" to move the mouse wheel * Negative values indicate movement up/away from the user, * positive values indicate movement down/towards the user. * * @since 1.4 */ public synchronized void mouseWheel(int wheelAmt) { peer.mouseWheel(wheelAmt); afterEvent(); } /** * Presses a given key. The key should be released using the * keyRelease method. *

* Key codes that have more than one physical key associated with them * (e.g. 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. KeyEvent.VK_A) * @throws IllegalArgumentException if keycode is not * a valid key * @see #keyRelease(int) * @see java.awt.event.KeyEvent */ public synchronized void keyPress(int keycode) { checkKeycodeArgument(keycode); peer.keyPress(keycode); afterEvent(); } /** * Releases a given key. *

* Key codes that have more than one physical key associated with them * (e.g. KeyEvent.VK_SHIFT could mean either the * left or right shift key) will map to the left key. * * @param keycode Key to release (e.g. KeyEvent.VK_A) * @throws IllegalArgumentException if keycode is not a * valid key * @see #keyPress(int) * @see java.awt.event.KeyEvent */ public synchronized void keyRelease(int keycode) { checkKeycodeArgument(keycode); peer.keyRelease(keycode); afterEvent(); } /** * Successively presses and releases a given key. *

* 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 if (keycode == KeyEvent.VK_UNDEFINED) { throw new IllegalArgumentException("Invalid key code"); } } /** * Returns the color of a pixel at the given screen coordinates. * @param x X position of pixel * @param y Y position of pixel * @return Color of the pixel */ public synchronized Color getPixelColor(int x, int y) { Color color = new Color(peer.getRGBPixel(x, y)); return color; } /** * Creates an image containing pixels read from the screen. This image does * not include the mouse cursor. * @param screenRect Rect to capture in screen coordinates * @return The captured image * @throws IllegalArgumentException if screenRect width and height are not greater than zero * @throws SecurityException if readDisplayPixels permission is not granted * @see SecurityManager#checkPermission * @see AWTPermission */ public synchronized BufferedImage createScreenCapture(Rectangle screenRect) { checkScreenCaptureAllowed(); checkValidRect(screenRect); BufferedImage image; DataBufferInt buffer; WritableRaster raster; if (screenCapCM == null) { /* * Fix for 4285201 * Create a DirectColorModel equivalent to the default RGB ColorModel, * except with no Alpha component. */ screenCapCM = new DirectColorModel(24, /* red mask */ 0x00FF0000, /* green mask */ 0x0000FF00, /* blue mask */ 0x000000FF); } // need to sync the toolkit prior to grabbing the pixels since in some // cases rendering to the screen may be delayed Toolkit.getDefaultToolkit().sync(); int pixels[]; int[] bandmasks = new int[3]; pixels = peer.getRGBPixels(screenRect); buffer = new DataBufferInt(pixels, pixels.length); bandmasks[0] = screenCapCM.getRedMask(); bandmasks[1] = screenCapCM.getGreenMask(); bandmasks[2] = screenCapCM.getBlueMask(); raster = Raster.createPackedRaster(buffer, screenRect.width, screenRect.height, screenRect.width, bandmasks, null); SunWritableRaster.makeTrackable(buffer); image = new BufferedImage(screenCapCM, raster, false, null); return image; } private static void checkValidRect(Rectangle rect) { if (rect.width <= 0 || rect.height <= 0) { throw new IllegalArgumentException("Rectangle width and height must be > 0"); } } private static void checkScreenCaptureAllowed() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission(AWTPermissions.READ_DISPLAY_PIXELS_PERMISSION); } } /* * Called after an event is generated */ private void afterEvent() { autoWaitForIdle(); autoDelay(); } /** * Returns whether this Robot automatically invokes waitForIdle * after generating an event. * @return Whether waitForIdle is automatically called */ public synchronized boolean isAutoWaitForIdle() { return isAutoWaitForIdle; } /** * Sets whether this Robot automatically invokes waitForIdle * after generating an event. * @param isOn Whether waitForIdle is automatically invoked */ public synchronized void setAutoWaitForIdle(boolean isOn) { isAutoWaitForIdle = isOn; } /* * Calls waitForIdle after every event if so desired. */ private void autoWaitForIdle() { if (isAutoWaitForIdle) { 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() { return autoDelay; } /** * Sets the number of milliseconds this Robot sleeps after generating an event. * * @param ms the delay duration in milliseconds * @throws IllegalArgumentException If {@code ms} * is not between 0 and 60,000 milliseconds inclusive */ public synchronized void setAutoDelay(int ms) { checkDelayArgument(ms); autoDelay = ms; } /* * Automatically sleeps for the specified interval after event generated. */ private void autoDelay() { delay(autoDelay); } /** * Sleeps for the specified time. * To catch any InterruptedExceptions that occur, * Thread.sleep() may be used instead. * * @param ms time to sleep in milliseconds * @throws IllegalArgumentException if {@code ms} * is not between 0 and 60,000 milliseconds inclusive * @see java.lang.Thread#sleep */ public synchronized void delay(int ms) { checkDelayArgument(ms); try { Thread.sleep(ms); } catch(InterruptedException ite) { ite.printStackTrace(); } } private void checkDelayArgument(int ms) { if (ms < 0 || ms > MAX_DELAY) { 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"); } } /** * Returns a string representation of this Robot. * * @return the string representation. */ public synchronized String toString() { String params = "autoDelay = "+getAutoDelay()+", "+"autoWaitForIdle = "+isAutoWaitForIdle(); return getClass().getName() + "[ " + params + " ]"; } }