Showing the changes to java/pkg/java/awt/Robot.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,593 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1999, 2009, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt; | ||
| 27 | |||
| 28 | import java.awt.event.InputEvent; | ||
| 29 | import java.awt.event.KeyEvent; | ||
| 30 | import java.awt.image.BufferedImage; | ||
| 31 | import java.awt.image.DataBufferInt; | ||
| 32 | import java.awt.image.DirectColorModel; | ||
| 33 | import java.awt.image.Raster; | ||
| 34 | import java.awt.image.WritableRaster; | ||
| 35 | import java.awt.peer.RobotPeer; | ||
| 36 | import java.lang.reflect.InvocationTargetException; | ||
| 37 | import sun.awt.ComponentFactory; | ||
| 38 | import sun.awt.SunToolkit; | ||
| 39 | import sun.awt.image.SunWritableRaster; | ||
| 40 | import sun.security.util.SecurityConstants; | ||
| 41 | |||
| 42 | /** | ||
| 43 | * This class is used to generate native system input events | ||
| 44 | * for the purposes of test automation, self-running demos, and | ||
| 45 | * other applications where control of the mouse and keyboard | ||
| 46 | * is needed. The primary purpose of Robot is to facilitate | ||
| 47 | * automated testing of Java platform implementations. | ||
| 48 | * <p> | ||
| 49 | * Using the class to generate input events differs from posting | ||
| 50 | * events to the AWT event queue or AWT components in that the | ||
| 51 | * events are generated in the platform's native input | ||
| 52 | * queue. For example, <code>Robot.mouseMove</code> will actually move | ||
| 53 | * the mouse cursor instead of just generating mouse move events. | ||
| 54 | * <p> | ||
| 55 | * Note that some platforms require special privileges or extensions | ||
| 56 | * to access low-level input control. If the current platform configuration | ||
| 57 | * does not allow input control, an <code>AWTException</code> will be thrown | ||
| 58 | * when trying to construct Robot objects. For example, X-Window systems | ||
| 59 | * will throw the exception if the XTEST 2.2 standard extension is not supported | ||
| 60 | * (or not enabled) by the X server. | ||
| 61 | * <p> | ||
| 62 | * Applications that use Robot for purposes other than self-testing should | ||
| 63 | * handle these error conditions gracefully. | ||
| 64 | * | ||
| 65 | * @author Robi Khan | ||
| 66 | * @since 1.3 | ||
| 67 | */ | ||
| 68 | public class Robot { | ||
| 69 | private static final int MAX_DELAY = 60000; | ||
| 70 | private RobotPeer peer; | ||
| 71 | private boolean isAutoWaitForIdle = false; | ||
| 72 | private int autoDelay = 0; | ||
| 73 | private static int LEGAL_BUTTON_MASK = 0; | ||
| 74 | |||
| 75 | // location of robot's GC, used in mouseMove(), getPixelColor() and captureScreenImage() | ||
| 76 | private Point gdLoc; | ||
| 77 | |||
| 78 | private DirectColorModel screenCapCM = null; | ||
| 79 | |||
| 80 | /** | ||
| 81 | * Constructs a Robot object in the coordinate system of the primary screen. | ||
| 82 | * <p> | ||
| 83 | * | ||
| 84 | * @throws AWTException if the platform configuration does not allow | ||
| 85 | * low-level input control. This exception is always thrown when | ||
| 86 | * GraphicsEnvironment.isHeadless() returns true | ||
| 87 | * @throws SecurityException if <code>createRobot</code> permission is not granted | ||
| 88 | * @see java.awt.GraphicsEnvironment#isHeadless | ||
| 89 | * @see SecurityManager#checkPermission | ||
| 90 | * @see AWTPermission | ||
| 91 | */ | ||
| 92 | public Robot() throws AWTException { | ||
| 93 | if (GraphicsEnvironment.isHeadless()) { | ||
| 94 | throw new AWTException("headless environment"); | ||
| 95 | } | ||
| 96 | init(GraphicsEnvironment.getLocalGraphicsEnvironment() | ||
| 97 | .getDefaultScreenDevice()); | ||
| 98 | } | ||
| 99 | |||
| 100 | /** | ||
| 101 | * Creates a Robot for the given screen device. Coordinates passed | ||
| 102 | * to Robot method calls like mouseMove and createScreenCapture will | ||
| 103 | * be interpreted as being in the same coordinate system as the | ||
| 104 | * specified screen. Note that depending on the platform configuration, | ||
| 105 | * multiple screens may either: | ||
| 106 | * <ul> | ||
| 107 | * <li>share the same coordinate system to form a combined virtual screen</li> | ||
| 108 | * <li>use different coordinate systems to act as independent screens</li> | ||
| 109 | * </ul> | ||
| 110 | * This constructor is meant for the latter case. | ||
| 111 | * <p> | ||
| 112 | * If screen devices are reconfigured such that the coordinate system is | ||
| 113 | * affected, the behavior of existing Robot objects is undefined. | ||
| 114 | * | ||
| 115 | * @param screen A screen GraphicsDevice indicating the coordinate | ||
| 116 | * system the Robot will operate in. | ||
| 117 | * @throws AWTException if the platform configuration does not allow | ||
| 118 | * low-level input control. This exception is always thrown when | ||
| 119 | * GraphicsEnvironment.isHeadless() returns true. | ||
| 120 | * @throws IllegalArgumentException if <code>screen</code> is not a screen | ||
| 121 | * GraphicsDevice. | ||
| 122 | * @throws SecurityException if <code>createRobot</code> permission is not granted | ||
| 123 | * @see java.awt.GraphicsEnvironment#isHeadless | ||
| 124 | * @see GraphicsDevice | ||
| 125 | * @see SecurityManager#checkPermission | ||
| 126 | * @see AWTPermission | ||
| 127 | */ | ||
| 128 | public Robot(GraphicsDevice screen) throws AWTException { | ||
| 129 | checkIsScreenDevice(screen); | ||
| 130 | init(screen); | ||
| 131 | } | ||
| 132 | |||
| 133 | private void init(GraphicsDevice screen) throws AWTException { | ||
| 134 | checkRobotAllowed(); | ||
| 135 | gdLoc = screen.getDefaultConfiguration().getBounds().getLocation(); | ||
| 136 | Toolkit toolkit = Toolkit.getDefaultToolkit(); | ||
| 137 | if (toolkit instanceof ComponentFactory) { | ||
| 138 | peer = ((ComponentFactory)toolkit).createRobot(this, screen); | ||
| 139 | disposer = new RobotDisposer(peer); | ||
| 140 | sun.java2d.Disposer.addRecord(anchor, disposer); | ||
| 141 | } | ||
| 142 | initLegalButtonMask(); | ||
| 143 | } | ||
| 144 | |||
| 145 | private static synchronized void initLegalButtonMask() { | ||
| 146 | if (LEGAL_BUTTON_MASK != 0) return; | ||
| 147 | |||
| 148 | int tmpMask = 0; | ||
| 149 | if (Toolkit.getDefaultToolkit().areExtraMouseButtonsEnabled()){ | ||
| 150 | if (Toolkit.getDefaultToolkit() instanceof SunToolkit) { | ||
| 151 | final int buttonsNumber = ((SunToolkit)(Toolkit.getDefaultToolkit())).getNumberOfButtons(); | ||
| 152 | for (int i = 0; i < buttonsNumber; i++){ | ||
| 153 | tmpMask |= InputEvent.getMaskForButton(i+1); | ||
| 154 | } | ||
| 155 | } | ||
| 156 | } | ||
| 157 | tmpMask |= InputEvent.BUTTON1_MASK| | ||
| 158 | InputEvent.BUTTON2_MASK| | ||
| 159 | InputEvent.BUTTON3_MASK| | ||
| 160 | InputEvent.BUTTON1_DOWN_MASK| | ||
| 161 | InputEvent.BUTTON2_DOWN_MASK| | ||
| 162 | InputEvent.BUTTON3_DOWN_MASK; | ||
| 163 | LEGAL_BUTTON_MASK = tmpMask; | ||
| 164 | } | ||
| 165 | |||
| 166 | /* determine if the security policy allows Robot's to be created */ | ||
| 167 | private void checkRobotAllowed() { | ||
| 168 | SecurityManager security = System.getSecurityManager(); | ||
| 169 | if (security != null) { | ||
| 170 | security.checkPermission(SecurityConstants.AWT.CREATE_ROBOT_PERMISSION); | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | /* check if the given device is a screen device */ | ||
| 175 | private void checkIsScreenDevice(GraphicsDevice device) { | ||
| 176 | if (device == null || device.getType() != GraphicsDevice.TYPE_RASTER_SCREEN) { | ||
| 177 | throw new IllegalArgumentException("not a valid screen device"); | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | private transient Object anchor = new Object(); | ||
| 182 | |||
| 183 | static class RobotDisposer implements sun.java2d.DisposerRecord { | ||
| 184 | private final RobotPeer peer; | ||
| 185 | public RobotDisposer(RobotPeer peer) { | ||
| 186 | this.peer = peer; | ||
| 187 | } | ||
| 188 | public void dispose() { | ||
| 189 | if (peer != null) { | ||
| 190 | peer.dispose(); | ||
| 191 | } | ||
| 192 | } | ||
| 193 | } | ||
| 194 | |||
| 195 | private transient RobotDisposer disposer; | ||
| 196 | |||
| 197 | /** | ||
| 198 | * Moves mouse pointer to given screen coordinates. | ||
| 199 | * @param x X position | ||
| 200 | * @param y Y position | ||
| 201 | */ | ||
| 202 | public synchronized void mouseMove(int x, int y) { | ||
| 203 | peer.mouseMove(gdLoc.x + x, gdLoc.y + y); | ||
| 204 | afterEvent(); | ||
| 205 | } | ||
| 206 | |||
| 207 | /** | ||
| 208 | * Presses one or more mouse buttons. The mouse buttons should | ||
| 209 | * be released using the {@link #mouseRelease(int)} method. | ||
| 210 | * | ||
| 211 | * @param buttons the Button mask; a combination of one or more | ||
| 212 | * mouse button masks. | ||
| 213 | * <p> | ||
| 214 | * It is allowed to use only a combination of valid values as a {@code buttons} parameter. | ||
| 215 | * A valid combination consists of {@code InputEvent.BUTTON1_DOWN_MASK}, | ||
| 216 | * {@code InputEvent.BUTTON2_DOWN_MASK}, {@code InputEvent.BUTTON3_DOWN_MASK} | ||
| 217 | * and values returned by the | ||
| 218 | * {@link InputEvent#getMaskForButton(int) InputEvent.getMaskForButton(button)} method. | ||
| 219 | * | ||
| 220 | * The valid combination also depends on a | ||
| 221 | * {@link Toolkit#areExtraMouseButtonsEnabled() Toolkit.areExtraMouseButtonsEnabled()} value as follows: | ||
| 222 | * <ul> | ||
| 223 | * <li> If support for extended mouse buttons is | ||
| 224 | * {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java | ||
| 225 | * then it is allowed to use only the following standard button masks: | ||
| 226 | * {@code InputEvent.BUTTON1_DOWN_MASK}, {@code InputEvent.BUTTON2_DOWN_MASK}, | ||
| 227 | * {@code InputEvent.BUTTON3_DOWN_MASK}. | ||
| 228 | * <li> If support for extended mouse buttons is | ||
| 229 | * {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java | ||
| 230 | * then it is allowed to use the standard button masks | ||
| 231 | * and masks for existing extended mouse buttons, if the mouse has more then three buttons. | ||
| 232 | * In that way, it is allowed to use the button masks corresponding to the buttons | ||
| 233 | * in the range from 1 to {@link java.awt.MouseInfo#getNumberOfButtons() MouseInfo.getNumberOfButtons()}. | ||
| 234 | * <br> | ||
| 235 | * It is recommended to use the {@link InputEvent#getMaskForButton(int) InputEvent.getMaskForButton(button)} | ||
| 236 | * method to obtain the mask for any mouse button by its number. | ||
| 237 | * </ul> | ||
| 238 | * <p> | ||
| 239 | * The following standard button masks are also accepted: | ||
| 240 | * <ul> | ||
| 241 | * <li>{@code InputEvent.BUTTON1_MASK} | ||
| 242 | * <li>{@code InputEvent.BUTTON2_MASK} | ||
| 243 | * <li>{@code InputEvent.BUTTON3_MASK} | ||
| 244 | * </ul> | ||
| 245 | * However, it is recommended to use {@code InputEvent.BUTTON1_DOWN_MASK}, | ||
| 246 | * {@code InputEvent.BUTTON2_DOWN_MASK}, {@code InputEvent.BUTTON3_DOWN_MASK} instead. | ||
| 247 | * Either extended {@code _DOWN_MASK} or old {@code _MASK} values | ||
| 248 | * should be used, but both those models should not be mixed. | ||
| 249 | * @throws IllegalArgumentException if the {@code buttons} mask contains the mask for extra mouse button | ||
| 250 | * and support for extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java | ||
| 251 | * @throws IllegalArgumentException if the {@code buttons} mask contains the mask for extra mouse button | ||
| 252 | * that does not exist on the mouse and support for extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java | ||
| 253 | * @see #mouseRelease(int) | ||
| 254 | * @see InputEvent#getMaskForButton(int) | ||
| 255 | * @see Toolkit#areExtraMouseButtonsEnabled() | ||
| 256 | * @see java.awt.MouseInfo#getNumberOfButtons() | ||
| 257 | * @see java.awt.event.MouseEvent | ||
| 258 | */ | ||
| 259 | public synchronized void mousePress(int buttons) { | ||
| 260 | checkButtonsArgument(buttons); | ||
| 261 | peer.mousePress(buttons); | ||
| 262 | afterEvent(); | ||
| 263 | } | ||
| 264 | |||
| 265 | /** | ||
| 266 | * Releases one or more mouse buttons. | ||
| 267 | * | ||
| 268 | * @param buttons the Button mask; a combination of one or more | ||
| 269 | * mouse button masks. | ||
| 270 | * <p> | ||
| 271 | * It is allowed to use only a combination of valid values as a {@code buttons} parameter. | ||
| 272 | * A valid combination consists of {@code InputEvent.BUTTON1_DOWN_MASK}, | ||
| 273 | * {@code InputEvent.BUTTON2_DOWN_MASK}, {@code InputEvent.BUTTON3_DOWN_MASK} | ||
| 274 | * and values returned by the | ||
| 275 | * {@link InputEvent#getMaskForButton(int) InputEvent.getMaskForButton(button)} method. | ||
| 276 | * | ||
| 277 | * The valid combination also depends on a | ||
| 278 | * {@link Toolkit#areExtraMouseButtonsEnabled() Toolkit.areExtraMouseButtonsEnabled()} value as follows: | ||
| 279 | * <ul> | ||
| 280 | * <li> If the support for extended mouse buttons is | ||
| 281 | * {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java | ||
| 282 | * then it is allowed to use only the following standard button masks: | ||
| 283 | * {@code InputEvent.BUTTON1_DOWN_MASK}, {@code InputEvent.BUTTON2_DOWN_MASK}, | ||
| 284 | * {@code InputEvent.BUTTON3_DOWN_MASK}. | ||
| 285 | * <li> If the support for extended mouse buttons is | ||
| 286 | * {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java | ||
| 287 | * then it is allowed to use the standard button masks | ||
| 288 | * and masks for existing extended mouse buttons, if the mouse has more then three buttons. | ||
| 289 | * In that way, it is allowed to use the button masks corresponding to the buttons | ||
| 290 | * in the range from 1 to {@link java.awt.MouseInfo#getNumberOfButtons() MouseInfo.getNumberOfButtons()}. | ||
| 291 | * <br> | ||
| 292 | * It is recommended to use the {@link InputEvent#getMaskForButton(int) InputEvent.getMaskForButton(button)} | ||
| 293 | * method to obtain the mask for any mouse button by its number. | ||
| 294 | * </ul> | ||
| 295 | * <p> | ||
| 296 | * The following standard button masks are also accepted: | ||
| 297 | * <ul> | ||
| 298 | * <li>{@code InputEvent.BUTTON1_MASK} | ||
| 299 | * <li>{@code InputEvent.BUTTON2_MASK} | ||
| 300 | * <li>{@code InputEvent.BUTTON3_MASK} | ||
| 301 | * </ul> | ||
| 302 | * However, it is recommended to use {@code InputEvent.BUTTON1_DOWN_MASK}, | ||
| 303 | * {@code InputEvent.BUTTON2_DOWN_MASK}, {@code InputEvent.BUTTON3_DOWN_MASK} instead. | ||
| 304 | * Either extended {@code _DOWN_MASK} or old {@code _MASK} values | ||
| 305 | * should be used, but both those models should not be mixed. | ||
| 306 | * @throws IllegalArgumentException if the {@code buttons} mask contains the mask for extra mouse button | ||
| 307 | * and support for extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java | ||
| 308 | * @throws IllegalArgumentException if the {@code buttons} mask contains the mask for extra mouse button | ||
| 309 | * that does not exist on the mouse and support for extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java | ||
| 310 | * @see #mousePress(int) | ||
| 311 | * @see InputEvent#getMaskForButton(int) | ||
| 312 | * @see Toolkit#areExtraMouseButtonsEnabled() | ||
| 313 | * @see java.awt.MouseInfo#getNumberOfButtons() | ||
| 314 | * @see java.awt.event.MouseEvent | ||
| 315 | */ | ||
| 316 | public synchronized void mouseRelease(int buttons) { | ||
| 317 | checkButtonsArgument(buttons); | ||
| 318 | peer.mouseRelease(buttons); | ||
| 319 | afterEvent(); | ||
| 320 | } | ||
| 321 | |||
| 322 | private void checkButtonsArgument(int buttons) { | ||
| 323 | if ( (buttons|LEGAL_BUTTON_MASK) != LEGAL_BUTTON_MASK ) { | ||
| 324 | throw new IllegalArgumentException("Invalid combination of button flags"); | ||
| 325 | } | ||
| 326 | } | ||
| 327 | |||
| 328 | /** | ||
| 329 | * Rotates the scroll wheel on wheel-equipped mice. | ||
| 330 | * | ||
| 331 | * @param wheelAmt number of "notches" to move the mouse wheel | ||
| 332 | * Negative values indicate movement up/away from the user, | ||
| 333 | * positive values indicate movement down/towards the user. | ||
| 334 | * | ||
| 335 | * @since 1.4 | ||
| 336 | */ | ||
| 337 | public synchronized void mouseWheel(int wheelAmt) { | ||
| 338 | peer.mouseWheel(wheelAmt); | ||
| 339 | afterEvent(); | ||
| 340 | } | ||
| 341 | |||
| 342 | /** | ||
| 343 | * Presses a given key. The key should be released using the | ||
| 344 | * <code>keyRelease</code> method. | ||
| 345 | * <p> | ||
| 346 | * Key codes that have more than one physical key associated with them | ||
| 347 | * (e.g. <code>KeyEvent.VK_SHIFT</code> could mean either the | ||
| 348 | * left or right shift key) will map to the left key. | ||
| 349 | * | ||
| 350 | * @param keycode Key to press (e.g. <code>KeyEvent.VK_A</code>) | ||
| 351 | * @throws IllegalArgumentException if <code>keycode</code> is not | ||
| 352 | * a valid key | ||
| 353 | * @see #keyRelease(int) | ||
| 354 | * @see java.awt.event.KeyEvent | ||
| 355 | */ | ||
| 356 | public synchronized void keyPress(int keycode) { | ||
| 357 | checkKeycodeArgument(keycode); | ||
| 358 | peer.keyPress(keycode); | ||
| 359 | afterEvent(); | ||
| 360 | } | ||
| 361 | |||
| 362 | /** | ||
| 363 | * Releases a given key. | ||
| 364 | * <p> | ||
| 365 | * Key codes that have more than one physical key associated with them | ||
| 366 | * (e.g. <code>KeyEvent.VK_SHIFT</code> could mean either the | ||
| 367 | * left or right shift key) will map to the left key. | ||
| 368 | * | ||
| 369 | * @param keycode Key to release (e.g. <code>KeyEvent.VK_A</code>) | ||
| 370 | * @throws IllegalArgumentException if <code>keycode</code> is not a | ||
| 371 | * valid key | ||
| 372 | * @see #keyPress(int) | ||
| 373 | * @see java.awt.event.KeyEvent | ||
| 374 | */ | ||
| 375 | public synchronized void keyRelease(int keycode) { | ||
| 376 | checkKeycodeArgument(keycode); | ||
| 377 | peer.keyRelease(keycode); | ||
| 378 | afterEvent(); | ||
| 379 | } | ||
| 380 | |||
| 381 | private void checkKeycodeArgument(int keycode) { | ||
| 382 | // rather than build a big table or switch statement here, we'll | ||
| 383 | // just check that the key isn't VK_UNDEFINED and assume that the | ||
| 384 | // peer implementations will throw an exception for other bogus | ||
| 385 | // values e.g. -1, 999999 | ||
| 386 | if (keycode == KeyEvent.VK_UNDEFINED) { | ||
| 387 | throw new IllegalArgumentException("Invalid key code"); | ||
| 388 | } | ||
| 389 | } | ||
| 390 | |||
| 391 | /** | ||
| 392 | * Returns the color of a pixel at the given screen coordinates. | ||
| 393 | * @param x X position of pixel | ||
| 394 | * @param y Y position of pixel | ||
| 395 | * @return Color of the pixel | ||
| 396 | */ | ||
| 397 | public synchronized Color getPixelColor(int x, int y) { | ||
| 398 | Color color = new Color(peer.getRGBPixel(gdLoc.x + x, gdLoc.y + y)); | ||
| 399 | return color; | ||
| 400 | } | ||
| 401 | |||
| 402 | /** | ||
| 403 | * Creates an image containing pixels read from the screen. This image does | ||
| 404 | * not include the mouse cursor. | ||
| 405 | * @param screenRect Rect to capture in screen coordinates | ||
| 406 | * @return The captured image | ||
| 407 | * @throws IllegalArgumentException if <code>screenRect</code> width and height are not greater than zero | ||
| 408 | * @throws SecurityException if <code>readDisplayPixels</code> permission is not granted | ||
| 409 | * @see SecurityManager#checkPermission | ||
| 410 | * @see AWTPermission | ||
| 411 | */ | ||
| 412 | public synchronized BufferedImage createScreenCapture(Rectangle screenRect) { | ||
| 413 | checkScreenCaptureAllowed(); | ||
| 414 | |||
| 415 | // according to the spec, screenRect is relative to robot's GD | ||
| 416 | Rectangle translatedRect = new Rectangle(screenRect); | ||
| 417 | translatedRect.translate(gdLoc.x, gdLoc.y); | ||
| 418 | checkValidRect(translatedRect); | ||
| 419 | |||
| 420 | BufferedImage image; | ||
| 421 | DataBufferInt buffer; | ||
| 422 | WritableRaster raster; | ||
| 423 | |||
| 424 | if (screenCapCM == null) { | ||
| 425 | /* | ||
| 426 | * Fix for 4285201 | ||
| 427 | * Create a DirectColorModel equivalent to the default RGB ColorModel, | ||
| 428 | * except with no Alpha component. | ||
| 429 | */ | ||
| 430 | |||
| 431 | screenCapCM = new DirectColorModel(24, | ||
| 432 | /* red mask */ 0x00FF0000, | ||
| 433 | /* green mask */ 0x0000FF00, | ||
| 434 | /* blue mask */ 0x000000FF); | ||
| 435 | } | ||
| 436 | |||
| 437 | // need to sync the toolkit prior to grabbing the pixels since in some | ||
| 438 | // cases rendering to the screen may be delayed | ||
| 439 | Toolkit.getDefaultToolkit().sync(); | ||
| 440 | |||
| 441 | int pixels[]; | ||
| 442 | int[] bandmasks = new int[3]; | ||
| 443 | |||
| 444 | pixels = peer.getRGBPixels(translatedRect); | ||
| 445 | buffer = new DataBufferInt(pixels, pixels.length); | ||
| 446 | |||
| 447 | bandmasks[0] = screenCapCM.getRedMask(); | ||
| 448 | bandmasks[1] = screenCapCM.getGreenMask(); | ||
| 449 | bandmasks[2] = screenCapCM.getBlueMask(); | ||
| 450 | |||
| 451 | raster = Raster.createPackedRaster(buffer, translatedRect.width, translatedRect.height, translatedRect.width, bandmasks, null); | ||
| 452 | SunWritableRaster.makeTrackable(buffer); | ||
| 453 | |||
| 454 | image = new BufferedImage(screenCapCM, raster, false, null); | ||
| 455 | |||
| 456 | return image; | ||
| 457 | } | ||
| 458 | |||
| 459 | private static void checkValidRect(Rectangle rect) { | ||
| 460 | if (rect.width <= 0 || rect.height <= 0) { | ||
| 461 | throw new IllegalArgumentException("Rectangle width and height must be > 0"); | ||
| 462 | } | ||
| 463 | } | ||
| 464 | |||
| 465 | private static void checkScreenCaptureAllowed() { | ||
| 466 | SecurityManager security = System.getSecurityManager(); | ||
| 467 | if (security != null) { | ||
| 468 | security.checkPermission( | ||
| 469 | SecurityConstants.AWT.READ_DISPLAY_PIXELS_PERMISSION); | ||
| 470 | } | ||
| 471 | } | ||
| 472 | |||
| 473 | /* | ||
| 474 | * Called after an event is generated | ||
| 475 | */ | ||
| 476 | private void afterEvent() { | ||
| 477 | autoWaitForIdle(); | ||
| 478 | autoDelay(); | ||
| 479 | } | ||
| 480 | |||
| 481 | /** | ||
| 482 | * Returns whether this Robot automatically invokes <code>waitForIdle</code> | ||
| 483 | * after generating an event. | ||
| 484 | * @return Whether <code>waitForIdle</code> is automatically called | ||
| 485 | */ | ||
| 486 | public synchronized boolean isAutoWaitForIdle() { | ||
| 487 | return isAutoWaitForIdle; | ||
| 488 | } | ||
| 489 | |||
| 490 | /** | ||
| 491 | * Sets whether this Robot automatically invokes <code>waitForIdle</code> | ||
| 492 | * after generating an event. | ||
| 493 | * @param isOn Whether <code>waitForIdle</code> is automatically invoked | ||
| 494 | */ | ||
| 495 | public synchronized void setAutoWaitForIdle(boolean isOn) { | ||
| 496 | isAutoWaitForIdle = isOn; | ||
| 497 | } | ||
| 498 | |||
| 499 | /* | ||
| 500 | * Calls waitForIdle after every event if so desired. | ||
| 501 | */ | ||
| 502 | private void autoWaitForIdle() { | ||
| 503 | if (isAutoWaitForIdle) { | ||
| 504 | waitForIdle(); | ||
| 505 | } | ||
| 506 | } | ||
| 507 | |||
| 508 | /** | ||
| 509 | * Returns the number of milliseconds this Robot sleeps after generating an event. | ||
| 510 | */ | ||
| 511 | public synchronized int getAutoDelay() { | ||
| 512 | return autoDelay; | ||
| 513 | } | ||
| 514 | |||
| 515 | /** | ||
| 516 | * Sets the number of milliseconds this Robot sleeps after generating an event. | ||
| 517 | * @throws IllegalArgumentException If <code>ms</code> is not between 0 and 60,000 milliseconds inclusive | ||
| 518 | */ | ||
| 519 | public synchronized void setAutoDelay(int ms) { | ||
| 520 | checkDelayArgument(ms); | ||
| 521 | autoDelay = ms; | ||
| 522 | } | ||
| 523 | |||
| 524 | /* | ||
| 525 | * Automatically sleeps for the specified interval after event generated. | ||
| 526 | */ | ||
| 527 | private void autoDelay() { | ||
| 528 | delay(autoDelay); | ||
| 529 | } | ||
| 530 | |||
| 531 | /** | ||
| 532 | * Sleeps for the specified time. | ||
| 533 | * To catch any <code>InterruptedException</code>s that occur, | ||
| 534 | * <code>Thread.sleep()</code> may be used instead. | ||
| 535 | * @param ms time to sleep in milliseconds | ||
| 536 | * @throws IllegalArgumentException if <code>ms</code> is not between 0 and 60,000 milliseconds inclusive | ||
| 537 | * @see java.lang.Thread#sleep | ||
| 538 | */ | ||
| 539 | public synchronized void delay(int ms) { | ||
| 540 | checkDelayArgument(ms); | ||
| 541 | try { | ||
| 542 | Thread.sleep(ms); | ||
| 543 | } catch(InterruptedException ite) { | ||
| 544 | ite.printStackTrace(); | ||
| 545 | } | ||
| 546 | } | ||
| 547 | |||
| 548 | private void checkDelayArgument(int ms) { | ||
| 549 | if (ms < 0 || ms > MAX_DELAY) { | ||
| 550 | throw new IllegalArgumentException("Delay must be to 0 to 60,000ms"); | ||
| 551 | } | ||
| 552 | } | ||
| 553 | |||
| 554 | /** | ||
| 555 | * Waits until all events currently on the event queue have been processed. | ||
| 556 | * @throws IllegalThreadStateException if called on the AWT event dispatching thread | ||
| 557 | */ | ||
| 558 | public synchronized void waitForIdle() { | ||
| 559 | checkNotDispatchThread(); | ||
| 560 | // post a dummy event to the queue so we know when | ||
| 561 | // all the events before it have been processed | ||
| 562 | try { | ||
| 563 | SunToolkit.flushPendingEvents(); | ||
| 564 | EventQueue.invokeAndWait( new Runnable() { | ||
| 565 | public void run() { | ||
| 566 | // dummy implementation | ||
| 567 | } | ||
| 568 | } ); | ||
| 569 | } catch(InterruptedException ite) { | ||
| 570 | System.err.println("Robot.waitForIdle, non-fatal exception caught:"); | ||
| 571 | ite.printStackTrace(); | ||
| 572 | } catch(InvocationTargetException ine) { | ||
| 573 | System.err.println("Robot.waitForIdle, non-fatal exception caught:"); | ||
| 574 | ine.printStackTrace(); | ||
| 575 | } | ||
| 576 | } | ||
| 577 | |||
| 578 | private void checkNotDispatchThread() { | ||
| 579 | if (EventQueue.isDispatchThread()) { | ||
| 580 | throw new IllegalThreadStateException("Cannot call method from the event dispatcher thread"); | ||
| 581 | } | ||
| 582 | } | ||
| 583 | |||
| 584 | /** | ||
| 585 | * Returns a string representation of this Robot. | ||
| 586 | * | ||
| 587 | * @return the string representation. | ||
| 588 | */ | ||
| 589 | public synchronized String toString() { | ||
| 590 | String params = "autoDelay = "+getAutoDelay()+", "+"autoWaitForIdle = "+isAutoWaitForIdle(); | ||
| 591 | return getClass().getName() + "[ " + params + " ]"; | ||
| 592 | } | ||
| 593 | } | ||