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 37 import sun.awt.AWTPermissions; 38 import sun.awt.ComponentFactory; 39 import sun.awt.SunToolkit; 40 import sun.awt.image.SunWritableRaster; 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 private DirectColorModel screenCapCM = null; 76 77 /** 78 * Constructs a Robot object in the coordinate system of the primary screen. 79 * 80 * @throws AWTException if the platform configuration does not allow 81 * low-level input control. This exception is always thrown when 82 * GraphicsEnvironment.isHeadless() returns true 83 * @throws SecurityException if <code>createRobot</code> permission is not granted 84 * @see java.awt.GraphicsEnvironment#isHeadless 85 * @see SecurityManager#checkPermission 86 * @see AWTPermission 87 */ 88 public Robot() throws AWTException { 89 if (GraphicsEnvironment.isHeadless()) { 90 throw new AWTException("headless environment"); 91 } 92 init(GraphicsEnvironment.getLocalGraphicsEnvironment() 93 .getDefaultScreenDevice()); 94 } 95 96 /** 97 * Creates a Robot for the given screen device. Coordinates passed 98 * to Robot method calls like mouseMove and createScreenCapture will 99 * be interpreted as being in the same coordinate system as the 100 * specified screen. Note that depending on the platform configuration, 101 * multiple screens may either: 102 * <ul> 103 * <li>share the same coordinate system to form a combined virtual screen</li> 104 * <li>use different coordinate systems to act as independent screens</li> 105 * </ul> 106 * This constructor is meant for the latter case. 107 * <p> 108 * If screen devices are reconfigured such that the coordinate system is 109 * affected, the behavior of existing Robot objects is undefined. 110 * 111 * @param screen A screen GraphicsDevice indicating the coordinate 112 * system the Robot will operate in. 113 * @throws AWTException if the platform configuration does not allow 114 * low-level input control. This exception is always thrown when 115 * GraphicsEnvironment.isHeadless() returns true. 116 * @throws IllegalArgumentException if <code>screen</code> is not a screen 117 * GraphicsDevice. 118 * @throws SecurityException if <code>createRobot</code> permission is not granted 119 * @see java.awt.GraphicsEnvironment#isHeadless 120 * @see GraphicsDevice 121 * @see SecurityManager#checkPermission 122 * @see AWTPermission 123 */ 124 public Robot(GraphicsDevice screen) throws AWTException { 125 checkIsScreenDevice(screen); 126 init(screen); 127 } 128 129 private void init(GraphicsDevice screen) throws AWTException { 130 checkRobotAllowed(); 131 Toolkit toolkit = Toolkit.getDefaultToolkit(); 132 if (toolkit instanceof ComponentFactory) { 133 peer = ((ComponentFactory)toolkit).createRobot(this, screen); 134 disposer = new RobotDisposer(peer); 135 sun.java2d.Disposer.addRecord(anchor, disposer); 136 } 137 initLegalButtonMask(); 138 } 319 throw new IllegalArgumentException("Invalid combination of button flags"); 320 } 321 } 322 323 /** 324 * Rotates the scroll wheel on wheel-equipped mice. 325 * 326 * @param wheelAmt number of "notches" to move the mouse wheel 327 * Negative values indicate movement up/away from the user, 328 * positive values indicate movement down/towards the user. 329 * 330 * @since 1.4 331 */ 332 public synchronized void mouseWheel(int wheelAmt) { 333 peer.mouseWheel(wheelAmt); 334 afterEvent(); 335 } 336 337 /** 338 * Presses a given key. The key should be released using the 339 * <code>keyRelease</code> method. 340 * <p> 341 * Key codes that have more than one physical key associated with them 342 * (e.g. <code>KeyEvent.VK_SHIFT</code> could mean either the 343 * left or right shift key) will map to the left key. 344 * 345 * @param keycode Key to press (e.g. <code>KeyEvent.VK_A</code>) 346 * @throws IllegalArgumentException if <code>keycode</code> is not 347 * a valid key 348 * @see #keyRelease(int) 349 * @see java.awt.event.KeyEvent 350 */ 351 public synchronized void keyPress(int keycode) { 352 checkKeycodeArgument(keycode); 353 peer.keyPress(keycode); 354 afterEvent(); 355 } 356 357 /** 358 * Releases a given key. 359 * <p> 360 * Key codes that have more than one physical key associated with them 361 * (e.g. <code>KeyEvent.VK_SHIFT</code> could mean either the 362 * left or right shift key) will map to the left key. 363 * 364 * @param keycode Key to release (e.g. <code>KeyEvent.VK_A</code>) 365 * @throws IllegalArgumentException if <code>keycode</code> is not a 366 * valid key 367 * @see #keyPress(int) 368 * @see java.awt.event.KeyEvent 369 */ 370 public synchronized void keyRelease(int keycode) { 371 checkKeycodeArgument(keycode); 372 peer.keyRelease(keycode); 373 afterEvent(); 374 } 375 376 private void checkKeycodeArgument(int keycode) { 377 // rather than build a big table or switch statement here, we'll 378 // just check that the key isn't VK_UNDEFINED and assume that the 379 // peer implementations will throw an exception for other bogus 380 // values e.g. -1, 999999 381 if (keycode == KeyEvent.VK_UNDEFINED) { 382 throw new IllegalArgumentException("Invalid key code"); 383 } 384 } 385 386 /** 387 * Returns the color of a pixel at the given screen coordinates. 388 * @param x X position of pixel 389 * @param y Y position of pixel 390 * @return Color of the pixel 391 */ 392 public synchronized Color getPixelColor(int x, int y) { 393 Color color = new Color(peer.getRGBPixel(x, y)); 394 return color; 395 } 396 397 /** 398 * Creates an image containing pixels read from the screen. This image does 399 * not include the mouse cursor. 400 * @param screenRect Rect to capture in screen coordinates 401 * @return The captured image 402 * @throws IllegalArgumentException if <code>screenRect</code> width and height are not greater than zero 403 * @throws SecurityException if <code>readDisplayPixels</code> permission is not granted 404 * @see SecurityManager#checkPermission 405 * @see AWTPermission 406 */ 407 public synchronized BufferedImage createScreenCapture(Rectangle screenRect) { 408 checkScreenCaptureAllowed(); 409 410 checkValidRect(screenRect); 411 412 BufferedImage image; 413 DataBufferInt buffer; 414 WritableRaster raster; 415 416 if (screenCapCM == null) { 417 /* 418 * Fix for 4285201 419 * Create a DirectColorModel equivalent to the default RGB ColorModel, 420 * except with no Alpha component. 421 */ 422 423 screenCapCM = new DirectColorModel(24, 453 throw new IllegalArgumentException("Rectangle width and height must be > 0"); 454 } 455 } 456 457 private static void checkScreenCaptureAllowed() { 458 SecurityManager security = System.getSecurityManager(); 459 if (security != null) { 460 security.checkPermission(AWTPermissions.READ_DISPLAY_PIXELS_PERMISSION); 461 } 462 } 463 464 /* 465 * Called after an event is generated 466 */ 467 private void afterEvent() { 468 autoWaitForIdle(); 469 autoDelay(); 470 } 471 472 /** 473 * Returns whether this Robot automatically invokes <code>waitForIdle</code> 474 * after generating an event. 475 * @return Whether <code>waitForIdle</code> is automatically called 476 */ 477 public synchronized boolean isAutoWaitForIdle() { 478 return isAutoWaitForIdle; 479 } 480 481 /** 482 * Sets whether this Robot automatically invokes <code>waitForIdle</code> 483 * after generating an event. 484 * @param isOn Whether <code>waitForIdle</code> is automatically invoked 485 */ 486 public synchronized void setAutoWaitForIdle(boolean isOn) { 487 isAutoWaitForIdle = isOn; 488 } 489 490 /* 491 * Calls waitForIdle after every event if so desired. 492 */ 493 private void autoWaitForIdle() { 494 if (isAutoWaitForIdle) { 495 waitForIdle(); 496 } 497 } 498 499 /** 500 * Returns the number of milliseconds this Robot sleeps after generating an event. 501 * 502 * @return the delay duration in milliseconds 503 */ 504 public synchronized int getAutoDelay() { 509 * Sets the number of milliseconds this Robot sleeps after generating an event. 510 * 511 * @param ms the delay duration in milliseconds 512 * @throws IllegalArgumentException If {@code ms} 513 * is not between 0 and 60,000 milliseconds inclusive 514 */ 515 public synchronized void setAutoDelay(int ms) { 516 checkDelayArgument(ms); 517 autoDelay = ms; 518 } 519 520 /* 521 * Automatically sleeps for the specified interval after event generated. 522 */ 523 private void autoDelay() { 524 delay(autoDelay); 525 } 526 527 /** 528 * Sleeps for the specified time. 529 * To catch any <code>InterruptedException</code>s that occur, 530 * <code>Thread.sleep()</code> may be used instead. 531 * 532 * @param ms time to sleep in milliseconds 533 * @throws IllegalArgumentException if {@code ms} 534 * is not between 0 and 60,000 milliseconds inclusive 535 * @see java.lang.Thread#sleep 536 */ 537 public synchronized void delay(int ms) { 538 checkDelayArgument(ms); 539 try { 540 Thread.sleep(ms); 541 } catch(InterruptedException ite) { 542 ite.printStackTrace(); 543 } 544 } 545 546 private void checkDelayArgument(int ms) { 547 if (ms < 0 || ms > MAX_DELAY) { 548 throw new IllegalArgumentException("Delay must be to 0 to 60,000ms"); 549 } 550 } | 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 37 import sun.awt.AWTPermissions; 38 import sun.awt.ComponentFactory; 39 import sun.awt.SunToolkit; 40 import sun.awt.image.SunWritableRaster; 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} 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} 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 private DirectColorModel screenCapCM = null; 76 77 /** 78 * Constructs a Robot object in the coordinate system of the primary screen. 79 * 80 * @throws AWTException if the platform configuration does not allow 81 * low-level input control. This exception is always thrown when 82 * GraphicsEnvironment.isHeadless() returns true 83 * @throws SecurityException if {@code createRobot} permission is not granted 84 * @see java.awt.GraphicsEnvironment#isHeadless 85 * @see SecurityManager#checkPermission 86 * @see AWTPermission 87 */ 88 public Robot() throws AWTException { 89 if (GraphicsEnvironment.isHeadless()) { 90 throw new AWTException("headless environment"); 91 } 92 init(GraphicsEnvironment.getLocalGraphicsEnvironment() 93 .getDefaultScreenDevice()); 94 } 95 96 /** 97 * Creates a Robot for the given screen device. Coordinates passed 98 * to Robot method calls like mouseMove and createScreenCapture will 99 * be interpreted as being in the same coordinate system as the 100 * specified screen. Note that depending on the platform configuration, 101 * multiple screens may either: 102 * <ul> 103 * <li>share the same coordinate system to form a combined virtual screen</li> 104 * <li>use different coordinate systems to act as independent screens</li> 105 * </ul> 106 * This constructor is meant for the latter case. 107 * <p> 108 * If screen devices are reconfigured such that the coordinate system is 109 * affected, the behavior of existing Robot objects is undefined. 110 * 111 * @param screen A screen GraphicsDevice indicating the coordinate 112 * system the Robot will operate in. 113 * @throws AWTException if the platform configuration does not allow 114 * low-level input control. This exception is always thrown when 115 * GraphicsEnvironment.isHeadless() returns true. 116 * @throws IllegalArgumentException if {@code screen} is not a screen 117 * GraphicsDevice. 118 * @throws SecurityException if {@code createRobot} permission is not granted 119 * @see java.awt.GraphicsEnvironment#isHeadless 120 * @see GraphicsDevice 121 * @see SecurityManager#checkPermission 122 * @see AWTPermission 123 */ 124 public Robot(GraphicsDevice screen) throws AWTException { 125 checkIsScreenDevice(screen); 126 init(screen); 127 } 128 129 private void init(GraphicsDevice screen) throws AWTException { 130 checkRobotAllowed(); 131 Toolkit toolkit = Toolkit.getDefaultToolkit(); 132 if (toolkit instanceof ComponentFactory) { 133 peer = ((ComponentFactory)toolkit).createRobot(this, screen); 134 disposer = new RobotDisposer(peer); 135 sun.java2d.Disposer.addRecord(anchor, disposer); 136 } 137 initLegalButtonMask(); 138 } 319 throw new IllegalArgumentException("Invalid combination of button flags"); 320 } 321 } 322 323 /** 324 * Rotates the scroll wheel on wheel-equipped mice. 325 * 326 * @param wheelAmt number of "notches" to move the mouse wheel 327 * Negative values indicate movement up/away from the user, 328 * positive values indicate movement down/towards the user. 329 * 330 * @since 1.4 331 */ 332 public synchronized void mouseWheel(int wheelAmt) { 333 peer.mouseWheel(wheelAmt); 334 afterEvent(); 335 } 336 337 /** 338 * Presses a given key. The key should be released using the 339 * {@code keyRelease} method. 340 * <p> 341 * Key codes that have more than one physical key associated with them 342 * (e.g. {@code KeyEvent.VK_SHIFT} could mean either the 343 * left or right shift key) will map to the left key. 344 * 345 * @param keycode Key to press (e.g. {@code KeyEvent.VK_A}) 346 * @throws IllegalArgumentException if {@code keycode} is not 347 * a valid key 348 * @see #keyRelease(int) 349 * @see java.awt.event.KeyEvent 350 */ 351 public synchronized void keyPress(int keycode) { 352 checkKeycodeArgument(keycode); 353 peer.keyPress(keycode); 354 afterEvent(); 355 } 356 357 /** 358 * Releases a given key. 359 * <p> 360 * Key codes that have more than one physical key associated with them 361 * (e.g. {@code KeyEvent.VK_SHIFT} could mean either the 362 * left or right shift key) will map to the left key. 363 * 364 * @param keycode Key to release (e.g. {@code KeyEvent.VK_A}) 365 * @throws IllegalArgumentException if {@code keycode} is not a 366 * valid key 367 * @see #keyPress(int) 368 * @see java.awt.event.KeyEvent 369 */ 370 public synchronized void keyRelease(int keycode) { 371 checkKeycodeArgument(keycode); 372 peer.keyRelease(keycode); 373 afterEvent(); 374 } 375 376 private void checkKeycodeArgument(int keycode) { 377 // rather than build a big table or switch statement here, we'll 378 // just check that the key isn't VK_UNDEFINED and assume that the 379 // peer implementations will throw an exception for other bogus 380 // values e.g. -1, 999999 381 if (keycode == KeyEvent.VK_UNDEFINED) { 382 throw new IllegalArgumentException("Invalid key code"); 383 } 384 } 385 386 /** 387 * Returns the color of a pixel at the given screen coordinates. 388 * @param x X position of pixel 389 * @param y Y position of pixel 390 * @return Color of the pixel 391 */ 392 public synchronized Color getPixelColor(int x, int y) { 393 Color color = new Color(peer.getRGBPixel(x, y)); 394 return color; 395 } 396 397 /** 398 * Creates an image containing pixels read from the screen. This image does 399 * not include the mouse cursor. 400 * @param screenRect Rect to capture in screen coordinates 401 * @return The captured image 402 * @throws IllegalArgumentException if {@code screenRect} width and height are not greater than zero 403 * @throws SecurityException if {@code readDisplayPixels} permission is not granted 404 * @see SecurityManager#checkPermission 405 * @see AWTPermission 406 */ 407 public synchronized BufferedImage createScreenCapture(Rectangle screenRect) { 408 checkScreenCaptureAllowed(); 409 410 checkValidRect(screenRect); 411 412 BufferedImage image; 413 DataBufferInt buffer; 414 WritableRaster raster; 415 416 if (screenCapCM == null) { 417 /* 418 * Fix for 4285201 419 * Create a DirectColorModel equivalent to the default RGB ColorModel, 420 * except with no Alpha component. 421 */ 422 423 screenCapCM = new DirectColorModel(24, 453 throw new IllegalArgumentException("Rectangle width and height must be > 0"); 454 } 455 } 456 457 private static void checkScreenCaptureAllowed() { 458 SecurityManager security = System.getSecurityManager(); 459 if (security != null) { 460 security.checkPermission(AWTPermissions.READ_DISPLAY_PIXELS_PERMISSION); 461 } 462 } 463 464 /* 465 * Called after an event is generated 466 */ 467 private void afterEvent() { 468 autoWaitForIdle(); 469 autoDelay(); 470 } 471 472 /** 473 * Returns whether this Robot automatically invokes {@code waitForIdle} 474 * after generating an event. 475 * @return Whether {@code waitForIdle} is automatically called 476 */ 477 public synchronized boolean isAutoWaitForIdle() { 478 return isAutoWaitForIdle; 479 } 480 481 /** 482 * Sets whether this Robot automatically invokes {@code waitForIdle} 483 * after generating an event. 484 * @param isOn Whether {@code waitForIdle} is automatically invoked 485 */ 486 public synchronized void setAutoWaitForIdle(boolean isOn) { 487 isAutoWaitForIdle = isOn; 488 } 489 490 /* 491 * Calls waitForIdle after every event if so desired. 492 */ 493 private void autoWaitForIdle() { 494 if (isAutoWaitForIdle) { 495 waitForIdle(); 496 } 497 } 498 499 /** 500 * Returns the number of milliseconds this Robot sleeps after generating an event. 501 * 502 * @return the delay duration in milliseconds 503 */ 504 public synchronized int getAutoDelay() { 509 * Sets the number of milliseconds this Robot sleeps after generating an event. 510 * 511 * @param ms the delay duration in milliseconds 512 * @throws IllegalArgumentException If {@code ms} 513 * is not between 0 and 60,000 milliseconds inclusive 514 */ 515 public synchronized void setAutoDelay(int ms) { 516 checkDelayArgument(ms); 517 autoDelay = ms; 518 } 519 520 /* 521 * Automatically sleeps for the specified interval after event generated. 522 */ 523 private void autoDelay() { 524 delay(autoDelay); 525 } 526 527 /** 528 * Sleeps for the specified time. 529 * To catch any {@code InterruptedException}s that occur, 530 * {@code Thread.sleep()} may be used instead. 531 * 532 * @param ms time to sleep in milliseconds 533 * @throws IllegalArgumentException if {@code ms} 534 * is not between 0 and 60,000 milliseconds inclusive 535 * @see java.lang.Thread#sleep 536 */ 537 public synchronized void delay(int ms) { 538 checkDelayArgument(ms); 539 try { 540 Thread.sleep(ms); 541 } catch(InterruptedException ite) { 542 ite.printStackTrace(); 543 } 544 } 545 546 private void checkDelayArgument(int ms) { 547 if (ms < 0 || ms > MAX_DELAY) { 548 throw new IllegalArgumentException("Delay must be to 0 to 60,000ms"); 549 } 550 } |