1 /* 2 * Copyright (c) 2007, 2017, 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 package org.jemmy.input.awt; 26 27 28 import org.jemmy.Point; 29 import java.awt.event.InputEvent; 30 31 import org.jemmy.Rectangle; 32 import org.jemmy.env.Timeout; 33 import org.jemmy.env.Environment; 34 import org.jemmy.image.Image; 35 import static org.jemmy.input.awt.AWTRobotInputFactory.*; 36 37 import org.jemmy.interfaces.Keyboard.KeyboardButton; 38 import org.jemmy.interfaces.Keyboard.KeyboardButtons; 39 import org.jemmy.interfaces.Keyboard.KeyboardModifiers; 40 import org.jemmy.interfaces.Modifier; 41 import org.jemmy.interfaces.Mouse.MouseButton; 42 43 44 /** 45 * @author shura 46 * @author mrkam 47 * 48 */ 49 public class RobotDriver { 50 51 private static boolean haveOldPos = false; 52 private static int smoothness; 53 private static double oldX; 54 private static double oldY; 55 56 static { 57 Environment.getEnvironment().setTimeout( 58 new Timeout(ROBOT_DELAY_TIMEOUT_NAME, 10)); 59 Environment.getEnvironment().setPropertyIfNotSet( 60 AWTRobotInputFactory.ROBOT_MOUSE_SMOOTHNESS_PROPERTY, 61 Integer.toString(Integer.MAX_VALUE)); 62 smoothness = Integer.parseInt( 63 (String)Environment.getEnvironment().getProperty( 64 AWTRobotInputFactory.ROBOT_MOUSE_SMOOTHNESS_PROPERTY)); 65 } 66 67 /** 68 * Sets mouse smoothness 69 * @param mouseSmoothness the maximum distance in pixels between 70 * mouse positions during movement 71 * @see #moveMouse(Point) 72 */ 73 public static void setMouseSmoothness(int mouseSmoothness) { 74 smoothness = mouseSmoothness; 75 } 76 77 /** 78 * Gets mouse smoothness 79 * @return the maximum distance in pixels between 80 * mouse positions during movement 81 * @see #setMouseSmoothness(int) 82 * @see #moveMouse(Point) 83 */ 84 public static int getMouseSmoothness() { 85 return smoothness; 86 } 87 88 /** 89 * Constructs a RobotDriver object. 90 * @param autoDelay Time for <code>Robot.setAutoDelay(long)</code> method. 91 */ 92 public RobotDriver(Timeout autoDelay) { 93 RobotExecutor.get().setAutoDelay(autoDelay); 94 } 95 96 /** 97 * Constructs a RobotDriver object. 98 * @param env Environment with ROBOT_DELAY_TIMEOUT_NAME timeout 99 * @see AWTRobotInputFactory#ROBOT_DELAY_TIMEOUT_NAME 100 */ 101 public RobotDriver(Environment env) { 102 this(env.getTimeout(ROBOT_DELAY_TIMEOUT_NAME)); 103 } 104 105 /** 106 * Capture an image of specified rectangular area of screen 107 * @param screenRect area on screen that will be captured 108 * @return image of specified rectangular area of screen 109 */ 110 public static Image createScreenCapture(Rectangle screenRect) { 111 return RobotExecutor.get().createScreenCapture(screenRect); 112 } 113 114 /** 115 * Presses mouse button specified by mouseButton preceding pressing of 116 * modifier keys or buttons specified by modifiers 117 * @param mouseButton One of MouseEvent.BUTTON*_MASK 118 * @param modifiers Combination of InputEvent.*_DOWN_MASK 119 * @see java.awt.event.InputEvent 120 * @see java.awt.event.MouseEvent 121 */ 122 public void pressMouse(MouseButton mouseButton, Modifier... modifiers) { 123 pressModifiers(modifiers); 124 makeAnOperation("mousePress", 125 new Object[]{mouseButton}, 126 new Class[]{MouseButton.class}); 127 } 128 129 /** 130 * Releases mouse button specified by mouseButton then releasing 131 * modifier keys or buttons specified by modifiers 132 * @param mouseButton One of MouseEvent.BUTTON*_MASK 133 * @param modifiers Combination of InputEvent.*_DOWN_MASK 134 * @see java.awt.event.InputEvent 135 * @see java.awt.event.MouseEvent 136 */ 137 public void releaseMouse(MouseButton mouseButton, Modifier... modifiers) { 138 makeAnOperation("mouseRelease", 139 new Object[]{mouseButton}, 140 new Class[]{MouseButton.class}); 141 releaseModifiers(modifiers); 142 } 143 144 /** 145 * Moves mouse to the specified mouse. When previous mouse location is 146 * remembered mouse moved smoothly between the points according to 147 * mouse smoothness parameter. Otherwise it jumps to the specified point 148 * @param point Position on the screen where to move mouse 149 * @see #setMouseSmoothness(int) 150 * @see #getMouseSmoothness() 151 */ 152 public void moveMouse(Point point) { 153 double targetX = point.x; 154 double targetY = point.y; 155 if (haveOldPos && (oldX != targetX || oldY != targetY)) { 156 double currX = oldX; 157 double currY = oldY; 158 double hyp = Math.sqrt((targetX - currX) * (targetX - currX) + 159 (targetY - currY) * (targetY - currY)); 160 double steps = Math.ceil(hyp / Math.min(hyp, smoothness)); 161 double vx = (targetX - currX) / steps; 162 double vy = (targetY - currY) / steps; 163 assert (long)vx * vx + (long)vy * vy <= (long)smoothness * smoothness; 164 while (Math.round(currX) != Math.round(targetX) || 165 Math.round(currY) != Math.round(targetY)) { 166 currX += vx; 167 currY += vy; 168 makeAnOperation("mouseMove", new Object[]{ 169 new Integer((int) Math.round(currX)), 170 new Integer((int) Math.round(currY))}, 171 new Class[]{Integer.TYPE, Integer.TYPE}); 172 } 173 } else { 174 makeAnOperation("mouseMove", 175 new Object[]{new Integer(point.x), new Integer(point.y)}, 176 new Class[]{Integer.TYPE, Integer.TYPE}); 177 } 178 haveOldPos = true; 179 oldX = targetX; 180 oldY = targetY; 181 } 182 183 /** 184 * Clicks the mouse button specified by mouseButton at the specified point 185 * specified number of times preceding it by pressing the modifiers key or 186 * buttons and ending by releasing them. The last click is as long as 187 * mouseClick timeout 188 * @param point Screen location where to click mouse 189 * @param clickCount Number of clicks 190 * @param mouseButton One of MouseEvent.BUTTON*_MASK 191 * @param modifiers Combination of InputEvent.*_DOWN_MASK 192 * @param mouseClick Timeout of the last click 193 * @see java.awt.event.InputEvent 194 * @see java.awt.event.MouseEvent 195 */ 196 public void clickMouse(Point point, int clickCount, MouseButton mouseButton, Timeout mouseClick, Modifier... modifiers) { 197 pressModifiers(modifiers); 198 moveMouse(point); 199 makeAnOperation("mousePress", new Object[]{mouseButton}, new Class[]{MouseButton.class}); 200 for (int i = 1; i < clickCount; i++) { 201 makeAnOperation("mouseRelease", new Object[]{mouseButton}, new Class[]{MouseButton.class}); 202 makeAnOperation("mousePress", new Object[]{mouseButton}, new Class[]{MouseButton.class}); 203 } 204 mouseClick.sleep(); 205 makeAnOperation("mouseRelease", new Object[]{mouseButton}, new Class[]{MouseButton.class}); 206 releaseModifiers(modifiers); 207 } 208 209 /** 210 * @deprecated Implementation doesn't seem to be correct as it ignores mouseButton and modifiers 211 * @param point todo document 212 * @param mouseButton One of MouseEvent.BUTTON*_MASK 213 * @param modifiers todo document 214 */ 215 public void dragMouse(Point point, int mouseButton, int modifiers) { 216 moveMouse(point); 217 } 218 219 /** 220 * Performs drag and drop from startPoint to endPoint using specified 221 * mouseButton and modifiers to perform it. 222 * @param startPoint Screen coordinates of drag start point 223 * @param endPoint Screen coordinates of drag end point 224 * @param mouseButton One of MouseEvent.BUTTON*_MASK 225 * @param modifiers Combination of InputEvent.*_DOWN_MASK 226 * @param before Timeout between pressing mouse at the startPoint and 227 * mouse move 228 * @param after Timeout between mouse move to the endPoint and mouse 229 * release 230 */ 231 public void dragNDrop(Point startPoint, Point endPoint, MouseButton mouseButton, Modifier modifiers[], Timeout before, Timeout after) { 232 moveMouse(startPoint); 233 pressMouse(mouseButton, modifiers); 234 before.sleep(); 235 moveMouse(endPoint); 236 after.sleep(); 237 releaseMouse(mouseButton, modifiers); 238 } 239 240 /** 241 * Presses a key. 242 * @param kbdButton Key code (<code>KeyEventVK_*</code> field. 243 * @param modifiers a combination of <code>InputEvent.*_MASK</code> fields. 244 */ 245 public void pressKey(KeyboardButton kbdButton, Modifier... modifiers) { 246 pressModifiers(modifiers); 247 makeAnOperation("keyPress", 248 new Object[]{kbdButton}, 249 new Class[]{KeyboardButton.class}); 250 } 251 252 /** 253 * Releases a key. 254 * @param kbdButton Key code (<code>KeyEventVK_*</code> field. 255 * @param modifiers a combination of <code>InputEvent.*_MASK</code> fields. 256 */ 257 public void releaseKey(KeyboardButton kbdButton, Modifier... modifiers) { 258 makeAnOperation("keyRelease", 259 new Object[]{kbdButton}, 260 new Class[]{KeyboardButton.class}); 261 releaseModifiers(modifiers); 262 } 263 264 /** 265 * Turns the wheel. 266 * @param p todo document 267 * @param amount Either positive or negative 268 * @param modifiers a combination of <code>InputEvent.*_MASK</code> fields. 269 */ 270 public void turnWheel(Point p, int amount, Modifier... modifiers) { 271 pressModifiers(modifiers); 272 moveMouse(p); 273 java.awt.Robot r = null; 274 makeAnOperation("mouseWheel", 275 new Object[]{amount}, 276 new Class[]{Integer.TYPE}); 277 releaseModifiers(modifiers); 278 } 279 280 /** 281 * Performs a single operation. 282 * @param method a name of <code>java.awt.Robot</code> method. 283 * @param params method parameters 284 * @param paramClasses method parameters classes 285 */ 286 public void makeAnOperation(final String method, final Object[] params, final Class[] paramClasses) { 287 RobotExecutor.get().makeAnOperation(method, params, paramClasses); 288 } 289 290 final static int SHIFT_MASK = InputEvent.SHIFT_DOWN_MASK | InputEvent.SHIFT_MASK; 291 final static int ALT_GRAPH_MASK = InputEvent.ALT_GRAPH_DOWN_MASK | InputEvent.ALT_GRAPH_MASK; 292 final static int ALT_MASK = InputEvent.ALT_DOWN_MASK | InputEvent.ALT_MASK; 293 final static int META_MASK = InputEvent.META_DOWN_MASK | InputEvent.META_MASK; 294 final static int CTRL_MASK = InputEvent.CTRL_DOWN_MASK | InputEvent.CTRL_MASK; 295 296 /** 297 * Presses modifiers keys by robot. 298 * @param modifiers a combination of <code>InputEvent.*_MASK</code> fields. 299 */ 300 protected void pressModifiers(Modifier... modifiers) { 301 for (Modifier modifier : modifiers) { // TODO: ALT_GRAPH_MASK? 302 if (modifier == KeyboardModifiers.ALT_DOWN_MASK) { 303 pressKey(KeyboardButtons.ALT); 304 } else if (modifier == KeyboardModifiers.CTRL_DOWN_MASK) { 305 pressKey(KeyboardButtons.CONTROL); 306 } else if (modifier == KeyboardModifiers.META_DOWN_MASK) { 307 pressKey(KeyboardButtons.META); 308 } else if (modifier == KeyboardModifiers.SHIFT_DOWN_MASK) { 309 pressKey(KeyboardButtons.SHIFT); 310 } 311 } 312 } 313 314 /** 315 * Releases modifiers keys by robot. 316 * @param modifiers a combination of <code>InputEvent.*_MASK</code> fields. 317 */ 318 protected void releaseModifiers(Modifier... modifiers) { 319 for (Modifier modifier : modifiers) { // TODO: ALT_GRAPH_MASK? 320 if (modifier == KeyboardModifiers.ALT_DOWN_MASK) { 321 releaseKey(KeyboardButtons.ALT); 322 } else if (modifier == KeyboardModifiers.CTRL_DOWN_MASK) { 323 releaseKey(KeyboardButtons.CONTROL); 324 } else if (modifier == KeyboardModifiers.META_DOWN_MASK) { 325 releaseKey(KeyboardButtons.META); 326 } else if (modifier == KeyboardModifiers.SHIFT_DOWN_MASK) { 327 releaseKey(KeyboardButtons.SHIFT); 328 } 329 } 330 } 331 /** 332 * If java.awt.Robot is running in other JVM, it shutdowns that JVM 333 * @see AWTRobotInputFactory#runInOtherJVM(boolean) 334 */ 335 public static void exit() { 336 RobotExecutor.get().exit(); 337 } 338 }