Save This Page
Home » openjdk-7 » java » awt » [javadoc | source]
    1   /*
    2    * Copyright 1999-2007 Sun Microsystems, Inc.  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.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   package java.awt;
   27   
   28   import java.awt.peer;
   29   import java.awt.image;
   30   import java.awt.event;
   31   import java.lang.reflect.InvocationTargetException;
   32   import sun.awt.ComponentFactory;
   33   import sun.awt.SunToolkit;
   34   import sun.security.util.SecurityConstants;
   35   
   36   /**
   37    * This class is used to generate native system input events
   38    * for the purposes of test automation, self-running demos, and
   39    * other applications where control of the mouse and keyboard
   40    * is needed. The primary purpose of Robot is to facilitate
   41    * automated testing of Java platform implementations.
   42    * <p>
   43    * Using the class to generate input events differs from posting
   44    * events to the AWT event queue or AWT components in that the
   45    * events are generated in the platform's native input
   46    * queue. For example, <code>Robot.mouseMove</code> will actually move
   47    * the mouse cursor instead of just generating mouse move events.
   48    * <p>
   49    * Note that some platforms require special privileges or extensions
   50    * to access low-level input control. If the current platform configuration
   51    * does not allow input control, an <code>AWTException</code> will be thrown
   52    * when trying to construct Robot objects. For example, X-Window systems
   53    * will throw the exception if the XTEST 2.2 standard extension is not supported
   54    * (or not enabled) by the X server.
   55    * <p>
   56    * Applications that use Robot for purposes other than self-testing should
   57    * handle these error conditions gracefully.
   58    *
   59    * @author      Robi Khan
   60    * @since       1.3
   61    */
   62   public class Robot {
   63       private static final int MAX_DELAY = 60000;
   64       private RobotPeer peer;
   65       private boolean isAutoWaitForIdle = false;
   66       private int autoDelay = 0;
   67       private static final int LEGAL_BUTTON_MASK =
   68                                               InputEvent.BUTTON1_MASK|
   69                                               InputEvent.BUTTON2_MASK|
   70                                               InputEvent.BUTTON3_MASK;
   71   
   72       // location of robot's GC, used in mouseMove(), getPixelColor() and captureScreenImage()
   73       private Point gdLoc;
   74   
   75       private DirectColorModel screenCapCM = null;
   76   
   77       /**
   78        * Constructs a Robot object in the coordinate system of the primary screen.
   79        * <p>
   80        *
   81        * @throws  AWTException if the platform configuration does not allow
   82        * low-level input control.  This exception is always thrown when
   83        * GraphicsEnvironment.isHeadless() returns true
   84        * @throws  SecurityException if <code>createRobot</code> permission is not granted
   85        * @see     java.awt.GraphicsEnvironment#isHeadless
   86        * @see     SecurityManager#checkPermission
   87        * @see     AWTPermission
   88        */
   89       public Robot() throws AWTException {
   90           if (GraphicsEnvironment.isHeadless()) {
   91               throw new AWTException("headless environment");
   92           }
   93           init(GraphicsEnvironment.getLocalGraphicsEnvironment()
   94               .getDefaultScreenDevice());
   95       }
   96   
   97       /**
   98        * Creates a Robot for the given screen device. Coordinates passed
   99        * to Robot method calls like mouseMove and createScreenCapture will
  100        * be interpreted as being in the same coordinate system as the
  101        * specified screen. Note that depending on the platform configuration,
  102        * multiple screens may either:
  103        * <ul>
  104        * <li>share the same coordinate system to form a combined virtual screen</li>
  105        * <li>use different coordinate systems to act as independent screens</li>
  106        * </ul>
  107        * This constructor is meant for the latter case.
  108        * <p>
  109        * If screen devices are reconfigured such that the coordinate system is
  110        * affected, the behavior of existing Robot objects is undefined.
  111        *
  112        * @param screen    A screen GraphicsDevice indicating the coordinate
  113        *                  system the Robot will operate in.
  114        * @throws  AWTException if the platform configuration does not allow
  115        * low-level input control.  This exception is always thrown when
  116        * GraphicsEnvironment.isHeadless() returns true.
  117        * @throws  IllegalArgumentException if <code>screen</code> is not a screen
  118        *          GraphicsDevice.
  119        * @throws  SecurityException if <code>createRobot</code> permission is not granted
  120        * @see     java.awt.GraphicsEnvironment#isHeadless
  121        * @see     GraphicsDevice
  122        * @see     SecurityManager#checkPermission
  123        * @see     AWTPermission
  124        */
  125       public Robot(GraphicsDevice screen) throws AWTException {
  126           checkIsScreenDevice(screen);
  127           init(screen);
  128       }
  129   
  130       private void init(GraphicsDevice screen) throws AWTException {
  131           checkRobotAllowed();
  132           gdLoc = screen.getDefaultConfiguration().getBounds().getLocation();
  133           Toolkit toolkit = Toolkit.getDefaultToolkit();
  134           if (toolkit instanceof ComponentFactory) {
  135               peer = ((ComponentFactory)toolkit).createRobot(this, screen);
  136               disposer = new RobotDisposer(peer);
  137               sun.java2d.Disposer.addRecord(anchor, disposer);
  138           }
  139       }
  140   
  141       /* determine if the security policy allows Robot's to be created */
  142       private void checkRobotAllowed() {
  143           SecurityManager security = System.getSecurityManager();
  144           if (security != null) {
  145               security.checkPermission(SecurityConstants.CREATE_ROBOT_PERMISSION);
  146           }
  147       }
  148   
  149       /* check if the given device is a screen device */
  150       private void checkIsScreenDevice(GraphicsDevice device) {
  151           if (device == null || device.getType() != GraphicsDevice.TYPE_RASTER_SCREEN) {
  152               throw new IllegalArgumentException("not a valid screen device");
  153           }
  154       }
  155   
  156       private transient Object anchor = new Object();
  157   
  158       static class RobotDisposer implements sun.java2d.DisposerRecord {
  159           private final RobotPeer peer;
  160           public RobotDisposer(RobotPeer peer) {
  161               this.peer = peer;
  162           }
  163           public void dispose() {
  164               if (peer != null) {
  165                   peer.dispose();
  166               }
  167           }
  168       }
  169   
  170       private transient RobotDisposer disposer;
  171   
  172       /**
  173        * Moves mouse pointer to given screen coordinates.
  174        * @param x         X position
  175        * @param y         Y position
  176        */
  177       public synchronized void mouseMove(int x, int y) {
  178           peer.mouseMove(gdLoc.x + x, gdLoc.y + y);
  179           afterEvent();
  180       }
  181   
  182       /**
  183        * Presses one or more mouse buttons.  The mouse buttons should
  184        * be released using the <code>mouseRelease</code> method.
  185        *
  186        * @param buttons   the Button mask; a combination of one or more
  187        * of these flags:
  188        * <ul>
  189        * <li><code>InputEvent.BUTTON1_MASK</code>
  190        * <li><code>InputEvent.BUTTON2_MASK</code>
  191        * <li><code>InputEvent.BUTTON3_MASK</code>
  192        * </ul>
  193        * @throws  IllegalArgumentException if the button mask is not a
  194        *          valid combination
  195        * @see #mouseRelease(int)
  196        */
  197       public synchronized void mousePress(int buttons) {
  198           checkButtonsArgument(buttons);
  199           peer.mousePress(buttons);
  200           afterEvent();
  201       }
  202   
  203       /**
  204        * Releases one or more mouse buttons.
  205        *
  206        * @param buttons   the Button mask; a combination of one or more
  207        * of these flags:
  208        * <ul>
  209        * <li><code>InputEvent.BUTTON1_MASK</code>
  210        * <li><code>InputEvent.BUTTON2_MASK</code>
  211        * <li><code>InputEvent.BUTTON3_MASK</code>
  212        * </ul>
  213        * @see #mousePress(int)
  214        * @throws  IllegalArgumentException if the button mask is not a valid
  215        *          combination
  216        */
  217       public synchronized void mouseRelease(int buttons) {
  218           checkButtonsArgument(buttons);
  219           peer.mouseRelease(buttons);
  220           afterEvent();
  221       }
  222   
  223       private void checkButtonsArgument(int buttons) {
  224           if ( (buttons|LEGAL_BUTTON_MASK) != LEGAL_BUTTON_MASK ) {
  225               throw new IllegalArgumentException("Invalid combination of button flags");
  226           }
  227       }
  228   
  229       /**
  230        * Rotates the scroll wheel on wheel-equipped mice.
  231        *
  232        * @param wheelAmt  number of "notches" to move the mouse wheel
  233        *                  Negative values indicate movement up/away from the user,
  234        *                  positive values indicate movement down/towards the user.
  235        *
  236        * @since 1.4
  237        */
  238       public synchronized void mouseWheel(int wheelAmt) {
  239           peer.mouseWheel(wheelAmt);
  240           afterEvent();
  241       }
  242   
  243       /**
  244        * Presses a given key.  The key should be released using the
  245        * <code>keyRelease</code> method.
  246        * <p>
  247        * Key codes that have more than one physical key associated with them
  248        * (e.g. <code>KeyEvent.VK_SHIFT</code> could mean either the
  249        * left or right shift key) will map to the left key.
  250        *
  251        * @param   keycode Key to press (e.g. <code>KeyEvent.VK_A</code>)
  252        * @throws  IllegalArgumentException if <code>keycode</code> is not
  253        *          a valid key
  254        * @see     #keyRelease(int)
  255        * @see     java.awt.event.KeyEvent
  256        */
  257       public synchronized void keyPress(int keycode) {
  258           checkKeycodeArgument(keycode);
  259           peer.keyPress(keycode);
  260           afterEvent();
  261       }
  262   
  263       /**
  264        * Releases a given key.
  265        * <p>
  266        * Key codes that have more than one physical key associated with them
  267        * (e.g. <code>KeyEvent.VK_SHIFT</code> could mean either the
  268        * left or right shift key) will map to the left key.
  269        *
  270        * @param   keycode Key to release (e.g. <code>KeyEvent.VK_A</code>)
  271        * @throws  IllegalArgumentException if <code>keycode</code> is not a
  272        *          valid key
  273        * @see  #keyPress(int)
  274        * @see     java.awt.event.KeyEvent
  275        */
  276       public synchronized void keyRelease(int keycode) {
  277           checkKeycodeArgument(keycode);
  278           peer.keyRelease(keycode);
  279           afterEvent();
  280       }
  281   
  282       private void checkKeycodeArgument(int keycode) {
  283           // rather than build a big table or switch statement here, we'll
  284           // just check that the key isn't VK_UNDEFINED and assume that the
  285           // peer implementations will throw an exception for other bogus
  286           // values e.g. -1, 999999
  287           if (keycode == KeyEvent.VK_UNDEFINED) {
  288               throw new IllegalArgumentException("Invalid key code");
  289           }
  290       }
  291   
  292       /**
  293        * Returns the color of a pixel at the given screen coordinates.
  294        * @param   x       X position of pixel
  295        * @param   y       Y position of pixel
  296        * @return  Color of the pixel
  297        */
  298       public synchronized Color getPixelColor(int x, int y) {
  299           Color color = new Color(peer.getRGBPixel(gdLoc.x + x, gdLoc.y + y));
  300           return color;
  301       }
  302   
  303       /**
  304        * Creates an image containing pixels read from the screen.  This image does
  305        * not include the mouse cursor.
  306        * @param   screenRect      Rect to capture in screen coordinates
  307        * @return  The captured image
  308        * @throws  IllegalArgumentException if <code>screenRect</code> width and height are not greater than zero
  309        * @throws  SecurityException if <code>readDisplayPixels</code> permission is not granted
  310        * @see     SecurityManager#checkPermission
  311        * @see     AWTPermission
  312        */
  313       public synchronized BufferedImage createScreenCapture(Rectangle screenRect) {
  314           checkScreenCaptureAllowed();
  315   
  316           // according to the spec, screenRect is relative to robot's GD
  317           Rectangle translatedRect = new Rectangle(screenRect);
  318           translatedRect.translate(gdLoc.x, gdLoc.y);
  319           checkValidRect(translatedRect);
  320   
  321           BufferedImage image;
  322           DataBufferInt buffer;
  323           WritableRaster raster;
  324   
  325           if (screenCapCM == null) {
  326               /*
  327                * Fix for 4285201
  328                * Create a DirectColorModel equivalent to the default RGB ColorModel,
  329                * except with no Alpha component.
  330                */
  331   
  332               screenCapCM = new DirectColorModel(24,
  333                                                  /* red mask */    0x00FF0000,
  334                                                  /* green mask */  0x0000FF00,
  335                                                  /* blue mask */   0x000000FF);
  336           }
  337   
  338           int pixels[];
  339           int[] bandmasks = new int[3];
  340   
  341           pixels = peer.getRGBPixels(translatedRect);
  342           buffer = new DataBufferInt(pixels, pixels.length);
  343   
  344           bandmasks[0] = screenCapCM.getRedMask();
  345           bandmasks[1] = screenCapCM.getGreenMask();
  346           bandmasks[2] = screenCapCM.getBlueMask();
  347   
  348           raster = Raster.createPackedRaster(buffer, translatedRect.width, translatedRect.height, translatedRect.width, bandmasks, null);
  349   
  350           image = new BufferedImage(screenCapCM, raster, false, null);
  351   
  352           return image;
  353       }
  354   
  355       private static void checkValidRect(Rectangle rect) {
  356           if (rect.width <= 0 || rect.height <= 0) {
  357               throw new IllegalArgumentException("Rectangle width and height must be > 0");
  358           }
  359       }
  360   
  361       private static void checkScreenCaptureAllowed() {
  362           SecurityManager security = System.getSecurityManager();
  363           if (security != null) {
  364               security.checkPermission(
  365                   SecurityConstants.READ_DISPLAY_PIXELS_PERMISSION);
  366           }
  367       }
  368   
  369       /*
  370        * Called after an event is generated
  371        */
  372       private void afterEvent() {
  373           autoWaitForIdle();
  374           autoDelay();
  375       }
  376   
  377       /**
  378        * Returns whether this Robot automatically invokes <code>waitForIdle</code>
  379        * after generating an event.
  380        * @return Whether <code>waitForIdle</code> is automatically called
  381        */
  382       public synchronized boolean isAutoWaitForIdle() {
  383           return isAutoWaitForIdle;
  384       }
  385   
  386       /**
  387        * Sets whether this Robot automatically invokes <code>waitForIdle</code>
  388        * after generating an event.
  389        * @param   isOn    Whether <code>waitForIdle</code> is automatically invoked
  390        */
  391       public synchronized void setAutoWaitForIdle(boolean isOn) {
  392           isAutoWaitForIdle = isOn;
  393       }
  394   
  395       /*
  396        * Calls waitForIdle after every event if so desired.
  397        */
  398       private void autoWaitForIdle() {
  399           if (isAutoWaitForIdle) {
  400               waitForIdle();
  401           }
  402       }
  403   
  404       /**
  405        * Returns the number of milliseconds this Robot sleeps after generating an event.
  406        */
  407       public synchronized int getAutoDelay() {
  408           return autoDelay;
  409       }
  410   
  411       /**
  412        * Sets the number of milliseconds this Robot sleeps after generating an event.
  413        * @throws  IllegalArgumentException If <code>ms</code> is not between 0 and 60,000 milliseconds inclusive
  414        */
  415       public synchronized void setAutoDelay(int ms) {
  416           checkDelayArgument(ms);
  417           autoDelay = ms;
  418       }
  419   
  420       /*
  421        * Automatically sleeps for the specified interval after event generated.
  422        */
  423       private void autoDelay() {
  424           delay(autoDelay);
  425       }
  426   
  427       /**
  428        * Sleeps for the specified time.
  429        * To catch any <code>InterruptedException</code>s that occur,
  430        * <code>Thread.sleep()</code> may be used instead.
  431        * @param   ms      time to sleep in milliseconds
  432        * @throws  IllegalArgumentException if <code>ms</code> is not between 0 and 60,000 milliseconds inclusive
  433        * @see     java.lang.Thread#sleep
  434        */
  435       public synchronized void delay(int ms) {
  436           checkDelayArgument(ms);
  437           try {
  438               Thread.sleep(ms);
  439           } catch(InterruptedException ite) {
  440               ite.printStackTrace();
  441           }
  442       }
  443   
  444       private void checkDelayArgument(int ms) {
  445           if (ms < 0 || ms > MAX_DELAY) {
  446               throw new IllegalArgumentException("Delay must be to 0 to 60,000ms");
  447           }
  448       }
  449   
  450       /**
  451        * Waits until all events currently on the event queue have been processed.
  452        * @throws  IllegalThreadStateException if called on the AWT event dispatching thread
  453        */
  454       public synchronized void waitForIdle() {
  455           checkNotDispatchThread();
  456           // post a dummy event to the queue so we know when
  457           // all the events before it have been processed
  458           try {
  459               SunToolkit.flushPendingEvents();
  460               EventQueue.invokeAndWait( new Runnable() {
  461                                               public void run() {
  462                                                   // dummy implementation
  463                                               }
  464                                           } );
  465           } catch(InterruptedException ite) {
  466               System.err.println("Robot.waitForIdle, non-fatal exception caught:");
  467               ite.printStackTrace();
  468           } catch(InvocationTargetException ine) {
  469               System.err.println("Robot.waitForIdle, non-fatal exception caught:");
  470               ine.printStackTrace();
  471           }
  472       }
  473   
  474       private void checkNotDispatchThread() {
  475           if (EventQueue.isDispatchThread()) {
  476               throw new IllegalThreadStateException("Cannot call method from the event dispatcher thread");
  477           }
  478       }
  479   
  480       /**
  481        * Returns a string representation of this Robot.
  482        *
  483        * @return  the string representation.
  484        */
  485       public synchronized String toString() {
  486           String params = "autoDelay = "+getAutoDelay()+", "+"autoWaitForIdle = "+isAutoWaitForIdle();
  487           return getClass().getName() + "[ " + params + " ]";
  488       }
  489   }

Save This Page
Home » openjdk-7 » java » awt » [javadoc | source]