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