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