test/jdk/lib/testlibrary/ExtendedRobot.java
branchihse-manpages-branch
changeset 57049 d13c49f43710
parent 57048 b2ed864c52b5
parent 52793 df065f8356d7
child 57050 746a7ee75caa
equal deleted inserted replaced
57048:b2ed864c52b5 57049:d13c49f43710
     1 /*
       
     2  * Copyright (c) 2014, 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 import java.awt.AWTException;
       
    27 import java.awt.Robot;
       
    28 import java.awt.GraphicsDevice;
       
    29 import java.awt.Toolkit;
       
    30 import java.awt.Point;
       
    31 import java.awt.MouseInfo;
       
    32 import java.awt.event.InputEvent;
       
    33 import java.awt.event.KeyEvent;
       
    34 
       
    35 /**
       
    36  * ExtendedRobot is a subclass of {@link java.awt.Robot}. It provides some convenience methods that are
       
    37  * ought to be moved to {@link java.awt.Robot} class.
       
    38  * <p>
       
    39  * ExtendedRobot uses delay {@link #getSyncDelay()} to make syncing threads with {@link #waitForIdle()}
       
    40  * more stable. This delay can be set once on creating object and could not be changed throughout object
       
    41  * lifecycle. Constructor reads vm integer property {@code java.awt.robotdelay} and sets the delay value
       
    42  * equal to the property value. If the property was not set 500 milliseconds default value is used.
       
    43  * <p>
       
    44  * When using jtreg you would include this class via something like:
       
    45  * <pre>
       
    46  * {@literal @}library ../../../../lib/testlibrary
       
    47  * {@literal @}build ExtendedRobot
       
    48  * </pre>
       
    49  *
       
    50  * @author      Dmitriy Ermashov
       
    51  * @since       9
       
    52  */
       
    53 
       
    54 public class ExtendedRobot extends Robot {
       
    55 
       
    56     private static int DEFAULT_SPEED = 20;       // Speed for mouse glide and click
       
    57     private static int DEFAULT_SYNC_DELAY = 500; // Default Additional delay for waitForIdle()
       
    58     private static int DEFAULT_STEP_LENGTH = 2;  // Step length (in pixels) for mouse glide
       
    59 
       
    60     private final int syncDelay = DEFAULT_SYNC_DELAY;
       
    61 
       
    62     //TODO: uncomment three lines below after moving functionality to java.awt.Robot
       
    63     //{
       
    64     //    syncDelay = AccessController.doPrivileged(new GetIntegerAction("java.awt.robotdelay", DEFAULT_SYNC_DELAY));
       
    65     //}
       
    66 
       
    67     /**
       
    68      * Constructs an ExtendedRobot object in the coordinate system of the primary screen.
       
    69      *
       
    70      * @throws  AWTException if the platform configuration does not allow low-level input
       
    71      *          control. This exception is always thrown when
       
    72      *          GraphicsEnvironment.isHeadless() returns true
       
    73      * @throws  SecurityException if {@code createRobot} permission is not granted
       
    74      *
       
    75      * @see     java.awt.GraphicsEnvironment#isHeadless
       
    76      * @see     SecurityManager#checkPermission
       
    77      * @see     java.awt.AWTPermission
       
    78      */
       
    79     public ExtendedRobot() throws AWTException {
       
    80         super();
       
    81     }
       
    82 
       
    83     /**
       
    84      * Creates an ExtendedRobot for the given screen device. Coordinates passed
       
    85      * to ExtendedRobot method calls like mouseMove and createScreenCapture will
       
    86      * be interpreted as being in the same coordinate system as the specified screen.
       
    87      * Note that depending on the platform configuration, multiple screens may either:
       
    88      * <ul>
       
    89      * <li>share the same coordinate system to form a combined virtual screen</li>
       
    90      * <li>use different coordinate systems to act as independent screens</li>
       
    91      * </ul>
       
    92      * This constructor is meant for the latter case.
       
    93      * <p>
       
    94      * If screen devices are reconfigured such that the coordinate system is
       
    95      * affected, the behavior of existing ExtendedRobot objects is undefined.
       
    96      *
       
    97      * @param   screen  A screen GraphicsDevice indicating the coordinate
       
    98      *                  system the Robot will operate in.
       
    99      * @throws  AWTException if the platform configuration does not allow low-level input
       
   100      *          control. This exception is always thrown when
       
   101      *          GraphicsEnvironment.isHeadless() returns true.
       
   102      * @throws  IllegalArgumentException if {@code screen} is not a screen
       
   103      *          GraphicsDevice.
       
   104      * @throws  SecurityException if {@code createRobot} permission is not granted
       
   105      *
       
   106      * @see     java.awt.GraphicsEnvironment#isHeadless
       
   107      * @see     GraphicsDevice
       
   108      * @see     SecurityManager#checkPermission
       
   109      * @see     java.awt.AWTPermission
       
   110      */
       
   111     public ExtendedRobot(GraphicsDevice screen) throws AWTException {
       
   112         super(screen);
       
   113     }
       
   114 
       
   115     /**
       
   116      * Returns delay length for {@link #waitForIdle()} method
       
   117      *
       
   118      * @return  Current delay value
       
   119      *
       
   120      * @see     #waitForIdle()
       
   121      */
       
   122     public int getSyncDelay(){ return this.syncDelay; }
       
   123 
       
   124     /**
       
   125      * Clicks mouse button(s) by calling {@link java.awt.Robot#mousePress(int)} and
       
   126      * {@link java.awt.Robot#mouseRelease(int)} methods
       
   127      *
       
   128      *
       
   129      * @param   buttons The button mask; a combination of one or more mouse button masks.
       
   130      * @throws  IllegalArgumentException if the {@code buttons} mask contains the mask for
       
   131      *          extra mouse button and support for extended mouse buttons is
       
   132      *          {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
       
   133      * @throws  IllegalArgumentException if the {@code buttons} mask contains the mask for
       
   134      *          extra mouse button that does not exist on the mouse and support for extended
       
   135      *          mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() enabled}
       
   136      *          by Java
       
   137      *
       
   138      * @see     #mousePress(int)
       
   139      * @see     #mouseRelease(int)
       
   140      * @see     InputEvent#getMaskForButton(int)
       
   141      * @see     Toolkit#areExtraMouseButtonsEnabled()
       
   142      * @see     java.awt.event.MouseEvent
       
   143      */
       
   144     public void click(int buttons) {
       
   145         mousePress(buttons);
       
   146         waitForIdle(DEFAULT_SPEED);
       
   147         mouseRelease(buttons);
       
   148         waitForIdle();
       
   149     }
       
   150 
       
   151     /**
       
   152      * Clicks mouse button 1
       
   153      *
       
   154      * @throws  IllegalArgumentException if the {@code buttons} mask contains the mask for
       
   155      *          extra mouse button and support for extended mouse buttons is
       
   156      *          {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
       
   157      * @throws  IllegalArgumentException if the {@code buttons} mask contains the mask for
       
   158      *          extra mouse button that does not exist on the mouse and support for extended
       
   159      *          mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() enabled}
       
   160      *          by Java
       
   161      *
       
   162      * @see     #click(int)
       
   163      */
       
   164     public void click() {
       
   165         click(InputEvent.BUTTON1_DOWN_MASK);
       
   166     }
       
   167 
       
   168     /**
       
   169      * Waits until all events currently on the event queue have been processed with given
       
   170      * delay after syncing threads. It uses more advanced method of synchronizing threads
       
   171      * unlike {@link java.awt.Robot#waitForIdle()}
       
   172      *
       
   173      * @param   delayValue  Additional delay length in milliseconds to wait until thread
       
   174      *                      sync been completed
       
   175      * @throws  sun.awt.SunToolkit.IllegalThreadException if called on the AWT event
       
   176      *          dispatching thread
       
   177      */
       
   178     public synchronized void waitForIdle(int delayValue) {
       
   179         super.waitForIdle();
       
   180         delay(delayValue);
       
   181     }
       
   182 
       
   183     /**
       
   184      * Waits until all events currently on the event queue have been processed with delay
       
   185      * {@link #getSyncDelay()} after syncing threads. It uses more advanced method of
       
   186      * synchronizing threads unlike {@link java.awt.Robot#waitForIdle()}
       
   187      *
       
   188      * @throws  sun.awt.SunToolkit.IllegalThreadException if called on the AWT event
       
   189      *          dispatching thread
       
   190      *
       
   191      * @see     #waitForIdle(int)
       
   192      */
       
   193     @Override
       
   194     public synchronized void waitForIdle() {
       
   195         waitForIdle(syncDelay);
       
   196     }
       
   197 
       
   198     /**
       
   199      * Move the mouse in multiple steps from where it is
       
   200      * now to the destination coordinates.
       
   201      *
       
   202      * @param   x   Destination point x coordinate
       
   203      * @param   y   Destination point y coordinate
       
   204      *
       
   205      * @see     #glide(int, int, int, int)
       
   206      */
       
   207     public void glide(int x, int y) {
       
   208         Point p = MouseInfo.getPointerInfo().getLocation();
       
   209         glide(p.x, p.y, x, y);
       
   210     }
       
   211 
       
   212     /**
       
   213      * Move the mouse in multiple steps from where it is
       
   214      * now to the destination point.
       
   215      *
       
   216      * @param   dest    Destination point
       
   217      *
       
   218      * @see     #glide(int, int)
       
   219      */
       
   220     public void glide(Point dest) {
       
   221         glide(dest.x, dest.y);
       
   222     }
       
   223 
       
   224     /**
       
   225      * Move the mouse in multiple steps from source coordinates
       
   226      * to the destination coordinates.
       
   227      *
       
   228      * @param   fromX   Source point x coordinate
       
   229      * @param   fromY   Source point y coordinate
       
   230      * @param   toX     Destination point x coordinate
       
   231      * @param   toY     Destination point y coordinate
       
   232      *
       
   233      * @see     #glide(int, int, int, int, int, int)
       
   234      */
       
   235     public void glide(int fromX, int fromY, int toX, int toY) {
       
   236         glide(fromX, fromY, toX, toY, DEFAULT_STEP_LENGTH, DEFAULT_SPEED);
       
   237     }
       
   238 
       
   239     /**
       
   240      * Move the mouse in multiple steps from source point to the
       
   241      * destination point with default speed and step length.
       
   242      *
       
   243      * @param   src     Source point
       
   244      * @param   dest    Destination point
       
   245      *
       
   246      * @see     #glide(int, int, int, int, int, int)
       
   247      */
       
   248     public void glide(Point src, Point dest) {
       
   249         glide(src.x, src.y, dest.x, dest.y, DEFAULT_STEP_LENGTH, DEFAULT_SPEED);
       
   250     }
       
   251 
       
   252     /**
       
   253      * Move the mouse in multiple steps from source point to the
       
   254      * destination point with given speed and step length.
       
   255      *
       
   256      * @param   srcX        Source point x cordinate
       
   257      * @param   srcY        Source point y cordinate
       
   258      * @param   destX       Destination point x cordinate
       
   259      * @param   destY       Destination point y cordinate
       
   260      * @param   stepLength  Approximate length of one step
       
   261      * @param   speed       Delay between steps.
       
   262      *
       
   263      * @see     #mouseMove(int, int)
       
   264      * @see     #delay(int)
       
   265      */
       
   266      public void glide(int srcX, int srcY, int destX, int destY, int stepLength, int speed) {
       
   267         int stepNum;
       
   268         double tDx, tDy;
       
   269         double dx, dy, ds;
       
   270         double x, y;
       
   271 
       
   272         dx = (destX - srcX);
       
   273         dy = (destY - srcY);
       
   274         ds = Math.sqrt(dx*dx + dy*dy);
       
   275 
       
   276         tDx = dx / ds * stepLength;
       
   277         tDy = dy / ds * stepLength;
       
   278 
       
   279         int stepsCount = (int) ds / stepLength;
       
   280 
       
   281         // Walk the mouse to the destination one step at a time
       
   282         mouseMove(srcX, srcY);
       
   283 
       
   284         for (x = srcX, y = srcY, stepNum = 0;
       
   285              stepNum < stepsCount;
       
   286              stepNum++) {
       
   287             x += tDx;
       
   288             y += tDy;
       
   289             mouseMove((int)x, (int)y);
       
   290             delay(speed);
       
   291         }
       
   292 
       
   293         // Ensure the mouse moves to the right destination.
       
   294         // The steps may have led the mouse to a slightly wrong place.
       
   295         mouseMove(destX, destY);
       
   296     }
       
   297 
       
   298     /**
       
   299      * Moves mouse pointer to given screen coordinates.
       
   300      *
       
   301      * @param   position    Target position
       
   302      *
       
   303      * @see     java.awt.Robot#mouseMove(int, int)
       
   304      */
       
   305     public synchronized void mouseMove(Point position) {
       
   306         mouseMove(position.x, position.y);
       
   307     }
       
   308 
       
   309 
       
   310     /**
       
   311      * Emulate native drag and drop process using {@code InputEvent.BUTTON1_DOWN_MASK}.
       
   312      * The method successively moves mouse cursor to point with coordinates
       
   313      * ({@code fromX}, {@code fromY}), presses mouse button 1, drag mouse to
       
   314      * point with coordinates ({@code toX}, {@code toY}) and releases mouse
       
   315      * button 1 at last.
       
   316      *
       
   317      * @param   fromX   Source point x coordinate
       
   318      * @param   fromY   Source point y coordinate
       
   319      * @param   toX     Destination point x coordinate
       
   320      * @param   toY     Destination point y coordinate
       
   321      *
       
   322      * @see     #mousePress(int)
       
   323      * @see     #glide(int, int, int, int)
       
   324      */
       
   325     public void dragAndDrop(int fromX, int fromY, int toX, int toY){
       
   326         mouseMove(fromX, fromY);
       
   327         mousePress(InputEvent.BUTTON1_DOWN_MASK);
       
   328         waitForIdle();
       
   329         glide(toX, toY);
       
   330         mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
       
   331         waitForIdle();
       
   332     }
       
   333 
       
   334     /**
       
   335      * Emulate native drag and drop process using {@code InputEvent.BUTTON1_DOWN_MASK}.
       
   336      * The method successively moves mouse cursor to point {@code from},
       
   337      * presses mouse button 1, drag mouse to point {@code to} and releases
       
   338      * mouse button 1 at last.
       
   339      *
       
   340      * @param   from    Source point
       
   341      * @param   to      Destination point
       
   342      *
       
   343      * @see     #mousePress(int)
       
   344      * @see     #glide(int, int, int, int)
       
   345      * @see     #dragAndDrop(int, int, int, int)
       
   346      */
       
   347     public void dragAndDrop(Point from, Point to){
       
   348         dragAndDrop(from.x, from.y, to.x, to.y);
       
   349     }
       
   350 
       
   351     /**
       
   352      * Successively presses and releases a given key.
       
   353      * <p>
       
   354      * Key codes that have more than one physical key associated with them
       
   355      * (e.g. {@code KeyEvent.VK_SHIFT} could mean either the
       
   356      * left or right shift key) will map to the left key.
       
   357      *
       
   358      * @param   keycode Key to press (e.g. {@code KeyEvent.VK_A})
       
   359      * @throws  IllegalArgumentException if {@code keycode} is not
       
   360      *          a valid key
       
   361      *
       
   362      * @see     java.awt.Robot#keyPress(int)
       
   363      * @see     java.awt.Robot#keyRelease(int)
       
   364      * @see     java.awt.event.KeyEvent
       
   365      */
       
   366     public void type(int keycode) {
       
   367         keyPress(keycode);
       
   368         waitForIdle(DEFAULT_SPEED);
       
   369         keyRelease(keycode);
       
   370         waitForIdle(DEFAULT_SPEED);
       
   371     }
       
   372 
       
   373     /**
       
   374      * Types given character
       
   375      *
       
   376      * @param   c   Character to be typed (e.g. {@code 'a'})
       
   377      *
       
   378      * @see     #type(int)
       
   379      * @see     java.awt.event.KeyEvent
       
   380      */
       
   381     public void type(char c) {
       
   382         type(KeyEvent.getExtendedKeyCodeForChar(c));
       
   383     }
       
   384 
       
   385     /**
       
   386      * Types given array of characters one by one
       
   387      *
       
   388      * @param   symbols Array of characters to be typed
       
   389      *
       
   390      * @see     #type(char)
       
   391      */
       
   392     public void type(char[] symbols) {
       
   393         for (int i = 0; i < symbols.length; i++) {
       
   394             type(symbols[i]);
       
   395         }
       
   396     }
       
   397 
       
   398     /**
       
   399      * Types given string
       
   400      *
       
   401      * @param   s   String to be typed
       
   402      *
       
   403      * @see     #type(char[])
       
   404      */
       
   405     public void type(String s) {
       
   406         type(s.toCharArray());
       
   407     }
       
   408 }