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 38 * methods. It contains methods that are ought to be moved to {@code java.awt.Robot} 39 * class 40 * <p> 41 * When using jtreg you would include this class via something like: 42 * <pre> 43 * {@literal @}library ../../../../lib/testlibrary 44 * {@literal @}build ExtendedRobot 45 * </pre> 46 * 47 * @author Dmitriy Ermashov 48 * @since 1.9 49 */ 50 51 public class ExtendedRobot extends Robot { 52 53 private static int DEFAULT_SPEED = 20; // Speed for mouse glide and click 54 private static int DEFAULT_SYNC_DELAY = 500; // Additional delay for realSync 55 private static int DEFAULT_STEP_LENGTH = 2; // Step length (in pixels) for mouse glide 56 57 private int syncDelay = DEFAULT_SYNC_DELAY; 58 59 /** 60 * {@inheritDoc} 61 */ 62 public ExtendedRobot() throws AWTException { 63 super(); 64 } 65 66 /** 67 * {@inheritDoc} 68 */ 69 public ExtendedRobot(GraphicsDevice screen) throws AWTException { 70 super(screen); 71 } 72 73 /** 74 * Sets the delay length for {@link #waitForIdle()} method 75 * 76 * @param delay Delay value 77 */ 78 public void setSyncDelay(int delay){ 79 this.syncDelay = delay; 80 } 81 82 /** 83 * Returns delay length for {@link #waitForIdle()} method 84 * 85 * @return Current delay value 86 */ 87 public int getSyncDelay(){ 88 return this.syncDelay; 89 } 90 91 /** 92 * Clicks mouse button(s) by calling {@link java.awt.Robot#mousePress(int)} 93 * and {@link java.awt.Robot#mouseRelease(int)} methods 94 * 95 * @param buttons the Button mask; a combination of one or more 96 * mouse button masks. 97 * @see #mousePress(int) 98 * @see #mouseRelease(int) 99 */ 100 public void click(int buttons) { 101 mousePress(buttons); 102 waitForIdle(DEFAULT_SPEED); 103 mouseRelease(buttons); 104 waitForIdle(); 105 } 106 107 /** 108 * Clicks mouse button 1 109 * 110 * @see #click(int) 111 */ 112 public void click() { 113 click(InputEvent.BUTTON1_DOWN_MASK); 114 } 115 116 /** 117 * Waits until all events currently on the event queue have been processed. 118 * It uses more advanced method of synchronizing threads unlike {@link java.awt.Robot#waitForIdle()} 119 * 120 * @param delayValue Delay length in milliseconds to wait until realSync been executed 121 */ 122 public synchronized void waitForIdle(int delayValue) { 123 SunToolkit.flushPendingEvents(); 124 ((SunToolkit) Toolkit.getDefaultToolkit()).realSync(); 125 delay(delayValue); 126 } 127 128 /** 129 * Waits until all events currently on the event queue have been processed 130 * with delay {@link #getSyncDelay()} after using {@link sun.awt.SunToolkit#realSync()} call. 131 * It uses more advanced method of synchronizing threads unlike {@link java.awt.Robot#waitForIdle()} 132 * 133 * @see #waitForIdle(int) 134 */ 135 @Override 136 public synchronized void waitForIdle() { 137 waitForIdle(syncDelay); 138 } 139 140 /** 141 * Move the mouse in multiple steps from where it is 142 * now to the destination coordinates.<p> 143 * 144 * @param x Destination point x coordinate 145 * @param y Destination point y coordinate 146 * @see #glide(java.awt.Point) 147 */ 148 public void glide(int x, int y) { 149 glide(new Point(x, y)); 150 } 151 152 /** 153 * Move the mouse in multiple steps from where it is 154 * now to the destination point.<p> 155 * 156 * @param dest Destination point 157 * @see #glide(java.awt.Point, java.awt.Point) 158 */ 159 public void glide(Point dest) { 160 glide(MouseInfo.getPointerInfo().getLocation(), dest); 161 } 162 163 /** 164 * Move the mouse in multiple steps from source coordinates 165 * to the destination coordinates.<p> 166 * 167 * @param fromX Source point x coordinate 168 * @param fromY Source point y coordinate 169 * @param toX Destination point x coordinate 170 * @param toY Destination point y coordinate 171 * @see #glide(java.awt.Point, java.awt.Point) 172 */ 173 public void glide(int fromX, int fromY, int toX, int toY) { 174 glide(new Point(fromX, fromY), new Point(toX, toY)); 175 } 176 177 /** 178 * Move the mouse in multiple steps from source point to the 179 * destination point with default speed and step length. 180 * 181 * @param src Source point 182 * @param dest Destination point 183 * @see #glide(java.awt.Point, java.awt.Point, int, int) 184 */ 185 public void glide(Point src, Point dest) { 186 glide(src, dest, DEFAULT_STEP_LENGTH, DEFAULT_SPEED); 187 } 188 189 /** 190 * Move the mouse in multiple steps from source point to the 191 * destination point with given speed and step length. 192 * 193 * @param src Source point 194 * @param dest Destination point 195 * @param stepLength Approximate length of one step 196 * @param speed Delay between steps. 197 * @see #mouseMove(int, int) 198 * @see #delay(int) 199 */ 200 public void glide(Point src, Point dest, int stepLength, int speed) { 201 int stepNum; 202 double tDx, tDy; 203 double dx, dy, ds; 204 double x, y; 205 206 dx = (dest.getX() - src.getX()); 207 dy = (dest.getY() - src.getY()); 208 ds = Math.sqrt(dx*dx + dy*dy); 209 210 tDx = dx / ds * stepLength; 211 tDy = dy / ds * stepLength; 212 213 int stepsCount = (int) ds / stepLength; 214 215 // Walk the mouse to the destination one step at a time 216 mouseMove(src.x, src.y); 217 218 for (x = src.x, y = src.y, stepNum = 0; 219 stepNum < stepsCount; 220 stepNum++) { 221 x += tDx; 222 y += tDy; 223 mouseMove((int)x, (int)y); 224 delay(speed); 225 } 226 227 // Ensure the mouse moves to the right destination. 228 // The steps may have led the mouse to a slightly wrong place. 229 mouseMove(dest.x, dest.y); 230 } 231 232 /** 233 * Moves mouse pointer to given screen coordinates. 234 * 235 * @param position Point position 236 * @see java.awt.Robot#mouseMove(int, int) 237 */ 238 public synchronized void mouseMove(Point position) { 239 mouseMove(position.x, position.y); 240 } 241 242 /** 243 * Successively presses and releases a given key. 244 * <p> 245 * Key codes that have more than one physical key associated with them 246 * (e.g. {@code KeyEvent.VK_SHIFT} could mean either the 247 * left or right shift key) will map to the left key. 248 * 249 * @param kc Key to press (e.g. {@code KeyEvent.VK_A}) 250 * @see java.awt.Robot#keyPress(int) 251 * @see java.awt.Robot#keyRelease(int) 252 * @see java.awt.event.KeyEvent 253 */ 254 public void type(int kc) { 255 keyPress(kc); 256 keyRelease(kc); 257 } 258 259 /** 260 * Types given character 261 * 262 * @param c Character to be typed (e.g. {@code 'a'}) 263 * @see #type(int) 264 * @see java.awt.event.KeyEvent 265 */ 266 public void type(char c) { 267 type(ExtendedKeyCodes.getExtendedKeyCodeForChar(c)); 268 } 269 270 /** 271 * Types given array of characters one by one 272 * 273 * @param symbols Array of characters to be typed 274 * @see #type(char) 275 */ 276 public void type(char[] symbols) { 277 for (int i = 0; i < symbols.length; i++) { 278 type(symbols[i]); 279 } 280 } 281 282 /** 283 * Types given string 284 * 285 * @param s String to be typed 286 * @see #type(char[]) 287 */ 288 public void type(String s) { 289 type(s.toCharArray()); 290 } 291 }