1 /* 2 * Copyright (c) 1997, 2016, 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.netbeans.jemmy.drivers.input; 26 27 import java.awt.event.InputEvent; 28 import java.awt.event.KeyEvent; 29 import java.lang.reflect.InvocationTargetException; 30 31 import org.netbeans.jemmy.ClassReference; 32 import org.netbeans.jemmy.JemmyException; 33 import org.netbeans.jemmy.JemmyProperties; 34 import org.netbeans.jemmy.QueueTool; 35 import org.netbeans.jemmy.TestOut; 36 import org.netbeans.jemmy.Timeout; 37 import org.netbeans.jemmy.drivers.LightSupportiveDriver; 38 39 /** 40 * Superclass for all drivers using robot. 41 * 42 * @author Alexandre Iline(alexandre.iline@oracle.com) 43 */ 44 public class RobotDriver extends LightSupportiveDriver { 45 46 private boolean haveOldPos; 47 private boolean smooth = false; 48 private double oldX; 49 private double oldY; 50 private static final double CONSTANT1 = 0.75; 51 private static final double CONSTANT2 = 12.0; 52 /** 53 * A reference to the robot instance. 54 */ 55 protected ClassReference robotReference = null; 56 57 /** 58 * A QueueTool instance. 59 */ 60 protected QueueTool qtool; 61 62 protected Timeout autoDelay; 63 64 /** 65 * Constructs a RobotDriver object. 66 * 67 * @param autoDelay Time for {@code Robot.setAutoDelay(long)} method. 68 * @param supported an array of supported class names 69 */ RobotDriver(Timeout autoDelay, String[] supported)70 public RobotDriver(Timeout autoDelay, String[] supported) { 71 super(supported); 72 qtool = new QueueTool(); 73 qtool.setOutput(TestOut.getNullOutput()); 74 this.autoDelay = autoDelay; 75 } 76 RobotDriver(Timeout autoDelay, String[] supported, boolean smooth)77 public RobotDriver(Timeout autoDelay, String[] supported, boolean smooth) { 78 this(autoDelay, supported); 79 this.smooth = smooth; 80 } 81 82 /** 83 * Constructs a RobotDriver object. 84 * 85 * @param autoDelay Time for {@code Robot.setAutoDelay(long)} method. 86 */ RobotDriver(Timeout autoDelay)87 public RobotDriver(Timeout autoDelay) { 88 this(autoDelay, new String[]{"org.netbeans.jemmy.operators.ComponentOperator"}); 89 } 90 RobotDriver(Timeout autoDelay, boolean smooth)91 public RobotDriver(Timeout autoDelay, boolean smooth) { 92 this(autoDelay); 93 this.smooth = smooth; 94 } 95 pressMouse(int mouseButton, int modifiers)96 public void pressMouse(int mouseButton, int modifiers) { 97 pressModifiers(modifiers); 98 makeAnOperation("mousePress", 99 new Object[]{mouseButton}, 100 new Class<?>[]{Integer.TYPE}); 101 } 102 releaseMouse(int mouseButton, int modifiers)103 public void releaseMouse(int mouseButton, int modifiers) { 104 makeAnOperation("mouseRelease", 105 new Object[]{mouseButton}, 106 new Class<?>[]{Integer.TYPE}); 107 releaseModifiers(modifiers); 108 } 109 moveMouse(int x, int y)110 public void moveMouse(int x, int y) { 111 if (!smooth) { 112 makeAnOperation("mouseMove", 113 new Object[]{x, y}, 114 new Class<?>[]{Integer.TYPE, Integer.TYPE}); 115 } else { 116 double targetX = x; 117 double targetY = y; 118 if (haveOldPos) { 119 double currX = oldX; 120 double currY = oldY; 121 double vx = 0.0; 122 double vy = 0.0; 123 while (Math.round(currX) != Math.round(targetX) 124 || Math.round(currY) != Math.round(targetY)) { 125 vx = vx * CONSTANT1 + (targetX - currX) / CONSTANT2 * (1.0 - CONSTANT1); 126 vy = vy * CONSTANT1 + (targetY - currY) / CONSTANT2 * (1.0 - CONSTANT1); 127 currX += vx; 128 currY += vy; 129 makeAnOperation("mouseMove", new Object[]{ 130 (int) Math.round(currX), 131 (int) Math.round(currY)}, 132 new Class<?>[]{Integer.TYPE, Integer.TYPE}); 133 } 134 } else { 135 makeAnOperation("mouseMove", new Object[]{ 136 (int) Math.round(targetX), 137 (int) Math.round(targetY)}, 138 new Class<?>[]{Integer.TYPE, Integer.TYPE}); 139 } 140 haveOldPos = true; 141 oldX = targetX; 142 oldY = targetY; 143 } 144 } 145 clickMouse(int x, int y, int clickCount, int mouseButton, int modifiers, Timeout mouseClick)146 public void clickMouse(int x, int y, int clickCount, int mouseButton, 147 int modifiers, Timeout mouseClick) { 148 pressModifiers(modifiers); 149 moveMouse(x, y); 150 makeAnOperation("mousePress", new Object[]{mouseButton}, new Class<?>[]{Integer.TYPE}); 151 for (int i = 1; i < clickCount; i++) { 152 makeAnOperation("mouseRelease", new Object[]{mouseButton}, new Class<?>[]{Integer.TYPE}); 153 makeAnOperation("mousePress", new Object[]{mouseButton}, new Class<?>[]{Integer.TYPE}); 154 } 155 mouseClick.sleep(); 156 makeAnOperation("mouseRelease", new Object[]{mouseButton}, new Class<?>[]{Integer.TYPE}); 157 releaseModifiers(modifiers); 158 } 159 dragMouse(int x, int y, int mouseButton, int modifiers)160 public void dragMouse(int x, int y, int mouseButton, int modifiers) { 161 moveMouse(x, y); 162 } 163 dragNDrop(int start_x, int start_y, int end_x, int end_y, int mouseButton, int modifiers, Timeout before, Timeout after)164 public void dragNDrop(int start_x, int start_y, int end_x, int end_y, 165 int mouseButton, int modifiers, Timeout before, Timeout after) { 166 moveMouse(start_x, start_y); 167 pressMouse(mouseButton, modifiers); 168 before.sleep(); 169 moveMouse(end_x, end_y); 170 after.sleep(); 171 releaseMouse(mouseButton, modifiers); 172 } 173 174 /** 175 * Presses a key. 176 * 177 * @param keyCode Key code ({@code KeyEventVK_*} field. 178 * @param modifiers a combination of {@code InputEvent.*_MASK} fields. 179 */ pressKey(int keyCode, int modifiers)180 public void pressKey(int keyCode, int modifiers) { 181 pressModifiers(modifiers); 182 makeAnOperation("keyPress", 183 new Object[]{keyCode}, 184 new Class<?>[]{Integer.TYPE}); 185 } 186 187 /** 188 * Releases a key. 189 * 190 * @param keyCode Key code ({@code KeyEventVK_*} field. 191 * @param modifiers a combination of {@code InputEvent.*_MASK} fields. 192 */ releaseKey(int keyCode, int modifiers)193 public void releaseKey(int keyCode, int modifiers) { 194 releaseModifiers(modifiers); 195 makeAnOperation("keyRelease", 196 new Object[]{keyCode}, 197 new Class<?>[]{Integer.TYPE}); 198 } 199 200 /** 201 * Performs a single operation. 202 * 203 * @param method a name of {@code java.awt.Robot} method. 204 * @param params method parameters 205 * @param paramClasses method parameters classes 206 */ makeAnOperation(final String method, final Object[] params, final Class<?>[] paramClasses)207 protected void makeAnOperation(final String method, final Object[] params, final Class<?>[] paramClasses) { 208 if (robotReference == null) { 209 initRobot(); 210 } 211 try { 212 robotReference.invokeMethod(method, params, paramClasses); 213 synchronizeRobot(); 214 } catch (InvocationTargetException 215 | IllegalStateException 216 | NoSuchMethodException 217 | IllegalAccessException e) { 218 throw (new JemmyException("Exception during java.awt.Robot accessing", e)); 219 } 220 } 221 222 /** 223 * Calls {@code java.awt.Robot.waitForIdle()} method. 224 */ synchronizeRobot()225 protected void synchronizeRobot() { 226 if (!QueueTool.isDispatchThread()) { 227 if ((JemmyProperties.getCurrentDispatchingModel() & JemmyProperties.QUEUE_MODEL_MASK) != 0) { 228 if (robotReference == null) { 229 initRobot(); 230 } 231 try { 232 robotReference.invokeMethod("waitForIdle", null, null); 233 } catch (Exception e) { 234 e.printStackTrace(); 235 } 236 } 237 } 238 } 239 240 /** 241 * Presses modifiers keys by robot. 242 * 243 * @param modifiers a combination of {@code InputEvent.*_MASK} fields. 244 */ pressModifiers(int modifiers)245 protected void pressModifiers(int modifiers) { 246 if ((modifiers & InputEvent.SHIFT_MASK) != 0) { 247 pressKey(KeyEvent.VK_SHIFT, modifiers & ~InputEvent.SHIFT_MASK); 248 } else if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { 249 pressKey(KeyEvent.VK_ALT_GRAPH, modifiers & ~InputEvent.ALT_GRAPH_MASK); 250 } else if ((modifiers & InputEvent.ALT_MASK) != 0) { 251 pressKey(KeyEvent.VK_ALT, modifiers & ~InputEvent.ALT_MASK); 252 } else if ((modifiers & InputEvent.META_MASK) != 0) { 253 pressKey(KeyEvent.VK_META, modifiers & ~InputEvent.META_MASK); 254 } else if ((modifiers & InputEvent.CTRL_MASK) != 0) { 255 pressKey(KeyEvent.VK_CONTROL, modifiers & ~InputEvent.CTRL_MASK); 256 } 257 } 258 259 /* 260 protected void pressModifiers(ComponentOperator oper, int modifiers) { 261 if ((modifiers & InputEvent.SHIFT_MASK) != 0) { 262 pressKey(oper, KeyEvent.VK_SHIFT, modifiers & ~InputEvent.SHIFT_MASK); 263 } else if((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { 264 pressKey(oper, KeyEvent.VK_ALT_GRAPH, modifiers & ~InputEvent.ALT_GRAPH_MASK); 265 } else if((modifiers & InputEvent.ALT_MASK) != 0) { 266 pressKey(oper, KeyEvent.VK_ALT, modifiers & ~InputEvent.ALT_MASK); 267 } else if((modifiers & InputEvent.META_MASK) != 0) { 268 pressKey(oper, KeyEvent.VK_META, modifiers & ~InputEvent.META_MASK); 269 } else if((modifiers & InputEvent.CTRL_MASK) != 0) { 270 pressKey(oper, KeyEvent.VK_CONTROL, modifiers & ~InputEvent.CTRL_MASK); 271 } 272 } 273 */ 274 /** 275 * Releases modifiers keys by robot. 276 * 277 * @param modifiers a combination of {@code InputEvent.*_MASK} fields. 278 */ releaseModifiers(int modifiers)279 protected void releaseModifiers(int modifiers) { 280 if ((modifiers & InputEvent.SHIFT_MASK) != 0) { 281 releaseKey(KeyEvent.VK_SHIFT, modifiers & ~InputEvent.SHIFT_MASK); 282 } else if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { 283 releaseKey(KeyEvent.VK_ALT_GRAPH, modifiers & ~InputEvent.ALT_GRAPH_MASK); 284 } else if ((modifiers & InputEvent.ALT_MASK) != 0) { 285 releaseKey(KeyEvent.VK_ALT, modifiers & ~InputEvent.ALT_MASK); 286 } else if ((modifiers & InputEvent.META_MASK) != 0) { 287 releaseKey(KeyEvent.VK_META, modifiers & ~InputEvent.META_MASK); 288 } else if ((modifiers & InputEvent.CTRL_MASK) != 0) { 289 releaseKey(KeyEvent.VK_CONTROL, modifiers & ~InputEvent.CTRL_MASK); 290 } 291 } 292 293 /* 294 protected void releaseModifiers(ComponentOperator oper, int modifiers) { 295 if ((modifiers & InputEvent.SHIFT_MASK) != 0) { 296 releaseKey(oper, KeyEvent.VK_SHIFT, modifiers & ~InputEvent.SHIFT_MASK); 297 } else if((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { 298 releaseKey(oper, KeyEvent.VK_ALT_GRAPH, modifiers & ~InputEvent.ALT_GRAPH_MASK); 299 } else if((modifiers & InputEvent.ALT_MASK) != 0) { 300 releaseKey(oper, KeyEvent.VK_ALT, modifiers & ~InputEvent.ALT_MASK); 301 } else if((modifiers & InputEvent.META_MASK) != 0) { 302 releaseKey(oper, KeyEvent.VK_META, modifiers & ~InputEvent.META_MASK); 303 } else if((modifiers & InputEvent.CTRL_MASK) != 0) { 304 releaseKey(oper, KeyEvent.VK_CONTROL, modifiers & ~InputEvent.CTRL_MASK); 305 } 306 } 307 */ initRobot()308 private void initRobot() { 309 // need to init Robot in dispatch thread because it hangs on Linux 310 // (see http://www.netbeans.org/issues/show_bug.cgi?id=37476) 311 if (QueueTool.isDispatchThread()) { 312 doInitRobot(); 313 } else { 314 qtool.invokeAndWait(new Runnable() { 315 @Override 316 public void run() { 317 doInitRobot(); 318 } 319 }); 320 } 321 } 322 doInitRobot()323 private void doInitRobot() { 324 try { 325 ClassReference robotClassReverence = new ClassReference("java.awt.Robot"); 326 robotReference = new ClassReference(robotClassReverence.newInstance(null, null)); 327 robotReference.invokeMethod("setAutoDelay", 328 new Object[]{(int) ((autoDelay != null) 329 ? autoDelay.getValue() 330 : 0)}, 331 new Class<?>[]{Integer.TYPE}); 332 } catch (InvocationTargetException 333 | IllegalStateException 334 | NoSuchMethodException 335 | IllegalAccessException 336 | ClassNotFoundException 337 | InstantiationException e) { 338 throw (new JemmyException("Exception during java.awt.Robot accessing", e)); 339 } 340 } 341 342 } 343