/* |
|
* 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.lang.reflect.InvocationTargetException; |
|
import sun.awt.ComponentFactory; |
|
import sun.awt.SunToolkit; |
|
import sun.awt.image.SunWritableRaster; |
|
import sun.security.util.SecurityConstants; |
|
/** |
|
* 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. |
|
* <p> |
|
* 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, <code>Robot.mouseMove</code> will actually move |
|
* the mouse cursor instead of just generating mouse move events. |
|
* <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 |
|
* (or not enabled) by the X server. |
|
* <p> |
|
* 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 DirectColorModel screenCapCM = null; |
|
/** |
|
* Constructs a Robot object in the coordinate system of the primary screen. |
|
* <p> |
|
* |
|
* @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 <code>createRobot</code> 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: |
|
* <ul> |
|
* <li>share the same coordinate system to form a combined virtual screen</li> |
|
* <li>use different coordinate systems to act as independent screens</li> |
|
* </ul> |
|
* This constructor is meant for the latter case. |
|
* <p> |
|
* 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 <code>screen</code> is not a screen |
|
* GraphicsDevice. |
|
* @throws SecurityException if <code>createRobot</code> 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(SecurityConstants.AWT.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(); |
|
} |
|
/** |
|
* 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. |
|
* <p> |
|
* 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: |
|
* <ul> |
|
* <li> If support for extended mouse buttons is |
|
* {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java |
|
* then it is allowed to use only the following standard button masks: |
|
* {@code InputEvent.BUTTON1_DOWN_MASK}, {@code InputEvent.BUTTON2_DOWN_MASK}, |
|
* {@code InputEvent.BUTTON3_DOWN_MASK}. |
|
* <li> If support for extended mouse buttons is |
|
* {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java |
|
* then it is allowed to use the standard button masks |
|
* and masks for existing extended mouse buttons, if the mouse has more then three buttons. |
|
* In that way, it is allowed to use the button masks corresponding to the buttons |
|
* in the range from 1 to {@link java.awt.MouseInfo#getNumberOfButtons() MouseInfo.getNumberOfButtons()}. |
|
* <br> |
|
* It is recommended to use the {@link InputEvent#getMaskForButton(int) InputEvent.getMaskForButton(button)} |
|
* method to obtain the mask for any mouse button by its number. |
|
* </ul> |
|
* <p> |
|
* The following standard button masks are also accepted: |
|
* <ul> |
|
* <li>{@code InputEvent.BUTTON1_MASK} |
|
* <li>{@code InputEvent.BUTTON2_MASK} |
|
* <li>{@code InputEvent.BUTTON3_MASK} |
|
* </ul> |
|
* 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. |
|
* <p> |
|
* 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: |
|
* <ul> |
|
* <li> If the support for extended mouse buttons is |
|
* {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java |
|
* then it is allowed to use only the following standard button masks: |
|
* {@code InputEvent.BUTTON1_DOWN_MASK}, {@code InputEvent.BUTTON2_DOWN_MASK}, |
|
* {@code InputEvent.BUTTON3_DOWN_MASK}. |
|
* <li> If the support for extended mouse buttons is |
|
* {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java |
|
* then it is allowed to use the standard button masks |
|
* and masks for existing extended mouse buttons, if the mouse has more then three buttons. |
|
* In that way, it is allowed to use the button masks corresponding to the buttons |
|
* in the range from 1 to {@link java.awt.MouseInfo#getNumberOfButtons() MouseInfo.getNumberOfButtons()}. |
|
* <br> |
|
* It is recommended to use the {@link InputEvent#getMaskForButton(int) InputEvent.getMaskForButton(button)} |
|
* method to obtain the mask for any mouse button by its number. |
|
* </ul> |
|
* <p> |
|
* The following standard button masks are also accepted: |
|
* <ul> |
|
* <li>{@code InputEvent.BUTTON1_MASK} |
|
* <li>{@code InputEvent.BUTTON2_MASK} |
|
* <li>{@code InputEvent.BUTTON3_MASK} |
|
* </ul> |
|
* 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(); |
|
} |
|
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 |
|
* <code>keyRelease</code> method. |
|
* <p> |
|
* Key codes that have more than one physical key associated with them |
|
* (e.g. <code>KeyEvent.VK_SHIFT</code> 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</code>) |
|
* @throws IllegalArgumentException if <code>keycode</code> 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. |
|
* <p> |
|
* Key codes that have more than one physical key associated with them |
|
* (e.g. <code>KeyEvent.VK_SHIFT</code> could mean either the |
|
* left or right shift key) will map to the left key. |
|
* |
|
* @param keycode Key to release (e.g. <code>KeyEvent.VK_A</code>) |
|
* @throws IllegalArgumentException if <code>keycode</code> 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(); |
|
} |
|
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) { |
|
checkScreenCaptureAllowed(); |
|
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 <code>screenRect</code> width and height are not greater than zero |
|
* @throws SecurityException if <code>readDisplayPixels</code> 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( |
|
SecurityConstants.AWT.READ_DISPLAY_PIXELS_PERMISSION); |
|
} |
|
} |
|
/* |
|
* Called after an event is generated |
|
*/ |
|
private void afterEvent() { |
|
autoWaitForIdle(); |
|
autoDelay(); |
|
} |
|
/** |
|
* Returns whether this Robot automatically invokes <code>waitForIdle</code> |
|
* after generating an event. |
|
* @return Whether <code>waitForIdle</code> is automatically called |
|
*/ |
|
public synchronized boolean isAutoWaitForIdle() { |
|
return isAutoWaitForIdle; |
|
} |
|
/** |
|
* Sets whether this Robot automatically invokes <code>waitForIdle</code> |
|
* after generating an event. |
|
* @param isOn Whether <code>waitForIdle</code> 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 the number of milliseconds this Robot sleeps after generating an event. |
|
*/ |
|
public synchronized int getAutoDelay() { |
|
return autoDelay; |
|
} |
|
/** |
|
* Sets the number of milliseconds this Robot sleeps after generating an event. |
|
* @throws IllegalArgumentException If <code>ms</code> 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 <code>InterruptedException</code>s that occur, |
|
* <code>Thread.sleep()</code> may be used instead. |
|
* @param ms time to sleep in milliseconds |
|
* @throws IllegalArgumentException if <code>ms</code> 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. |
|
* @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"); |
|
} |
|
} |
|
/** |
|
* 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 + " ]"; |
|
} |
|
} |