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