/* * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package org.netbeans.jemmy.drivers.input; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.lang.reflect.InvocationTargetException; import org.netbeans.jemmy.ClassReference; import org.netbeans.jemmy.JemmyException; import org.netbeans.jemmy.JemmyProperties; import org.netbeans.jemmy.QueueTool; import org.netbeans.jemmy.TestOut; import org.netbeans.jemmy.Timeout; import org.netbeans.jemmy.drivers.LightSupportiveDriver; /** * Superclass for all drivers using robot. * * @author Alexandre Iline(alexandre.iline@oracle.com) */ public class RobotDriver extends LightSupportiveDriver { private boolean haveOldPos; private boolean smooth = false; private double oldX; private double oldY; private static final double CONSTANT1 = 0.75; private static final double CONSTANT2 = 12.0; /** * A reference to the robot instance. */ protected ClassReference robotReference = null; /** * A QueueTool instance. */ protected QueueTool qtool; protected Timeout autoDelay; /** * Constructs a RobotDriver object. * * @param autoDelay Time for {@code Robot.setAutoDelay(long)} method. * @param supported an array of supported class names */ public RobotDriver(Timeout autoDelay, String[] supported) { super(supported); qtool = new QueueTool(); qtool.setOutput(TestOut.getNullOutput()); this.autoDelay = autoDelay; } public RobotDriver(Timeout autoDelay, String[] supported, boolean smooth) { this(autoDelay, supported); this.smooth = smooth; } /** * Constructs a RobotDriver object. * * @param autoDelay Time for {@code Robot.setAutoDelay(long)} method. */ public RobotDriver(Timeout autoDelay) { this(autoDelay, new String[]{"org.netbeans.jemmy.operators.ComponentOperator"}); } public RobotDriver(Timeout autoDelay, boolean smooth) { this(autoDelay); this.smooth = smooth; } public void pressMouse(int mouseButton, int modifiers) { pressModifiers(modifiers); makeAnOperation("mousePress", new Object[]{mouseButton}, new Class<?>[]{Integer.TYPE}); } public void releaseMouse(int mouseButton, int modifiers) { makeAnOperation("mouseRelease", new Object[]{mouseButton}, new Class<?>[]{Integer.TYPE}); releaseModifiers(modifiers); } public void moveMouse(int x, int y) { if (!smooth) { makeAnOperation("mouseMove", new Object[]{x, y}, new Class<?>[]{Integer.TYPE, Integer.TYPE}); } else { double targetX = x; double targetY = y; if (haveOldPos) { double currX = oldX; double currY = oldY; double vx = 0.0; double vy = 0.0; while (Math.round(currX) != Math.round(targetX) || Math.round(currY) != Math.round(targetY)) { vx = vx * CONSTANT1 + (targetX - currX) / CONSTANT2 * (1.0 - CONSTANT1); vy = vy * CONSTANT1 + (targetY - currY) / CONSTANT2 * (1.0 - CONSTANT1); currX += vx; currY += vy; makeAnOperation("mouseMove", new Object[]{ (int) Math.round(currX), (int) Math.round(currY)}, new Class<?>[]{Integer.TYPE, Integer.TYPE}); } } else { makeAnOperation("mouseMove", new Object[]{ (int) Math.round(targetX), (int) Math.round(targetY)}, new Class<?>[]{Integer.TYPE, Integer.TYPE}); } haveOldPos = true; oldX = targetX; oldY = targetY; } } public void clickMouse(int x, int y, int clickCount, int mouseButton, int modifiers, Timeout mouseClick) { pressModifiers(modifiers); moveMouse(x, y); makeAnOperation("mousePress", new Object[]{mouseButton}, new Class<?>[]{Integer.TYPE}); for (int i = 1; i < clickCount; i++) { makeAnOperation("mouseRelease", new Object[]{mouseButton}, new Class<?>[]{Integer.TYPE}); makeAnOperation("mousePress", new Object[]{mouseButton}, new Class<?>[]{Integer.TYPE}); } mouseClick.sleep(); makeAnOperation("mouseRelease", new Object[]{mouseButton}, new Class<?>[]{Integer.TYPE}); releaseModifiers(modifiers); } public void dragMouse(int x, int y, int mouseButton, int modifiers) { moveMouse(x, y); } public void dragNDrop(int start_x, int start_y, int end_x, int end_y, int mouseButton, int modifiers, Timeout before, Timeout after) { moveMouse(start_x, start_y); pressMouse(mouseButton, modifiers); before.sleep(); moveMouse(end_x, end_y); after.sleep(); releaseMouse(mouseButton, modifiers); } /** * Presses a key. * * @param keyCode Key code ({@code KeyEventVK_*} field. * @param modifiers a combination of {@code InputEvent.*_MASK} fields. */ public void pressKey(int keyCode, int modifiers) { pressModifiers(modifiers); makeAnOperation("keyPress", new Object[]{keyCode}, new Class<?>[]{Integer.TYPE}); } /** * Releases a key. * * @param keyCode Key code ({@code KeyEventVK_*} field. * @param modifiers a combination of {@code InputEvent.*_MASK} fields. */ public void releaseKey(int keyCode, int modifiers) { releaseModifiers(modifiers); makeAnOperation("keyRelease", new Object[]{keyCode}, new Class<?>[]{Integer.TYPE}); } /** * Performs a single operation. * * @param method a name of {@code java.awt.Robot} method. * @param params method parameters * @param paramClasses method parameters classes */ protected void makeAnOperation(final String method, final Object[] params, final Class<?>[] paramClasses) { if (robotReference == null) { initRobot(); } try { robotReference.invokeMethod(method, params, paramClasses); synchronizeRobot(); } catch (InvocationTargetException | IllegalStateException | NoSuchMethodException | IllegalAccessException e) { throw (new JemmyException("Exception during java.awt.Robot accessing", e)); } } /** * Calls {@code java.awt.Robot.waitForIdle()} method. */ protected void synchronizeRobot() { if (!QueueTool.isDispatchThread()) { if ((JemmyProperties.getCurrentDispatchingModel() & JemmyProperties.QUEUE_MODEL_MASK) != 0) { if (robotReference == null) { initRobot(); } try { robotReference.invokeMethod("waitForIdle", null, null); } catch (Exception e) { e.printStackTrace(); } } } } /** * Presses modifiers keys by robot. * * @param modifiers a combination of {@code InputEvent.*_MASK} fields. */ protected void pressModifiers(int modifiers) { if ((modifiers & InputEvent.SHIFT_MASK) != 0) { pressKey(KeyEvent.VK_SHIFT, modifiers & ~InputEvent.SHIFT_MASK); } else if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { pressKey(KeyEvent.VK_ALT_GRAPH, modifiers & ~InputEvent.ALT_GRAPH_MASK); } else if ((modifiers & InputEvent.ALT_MASK) != 0) { pressKey(KeyEvent.VK_ALT, modifiers & ~InputEvent.ALT_MASK); } else if ((modifiers & InputEvent.META_MASK) != 0) { pressKey(KeyEvent.VK_META, modifiers & ~InputEvent.META_MASK); } else if ((modifiers & InputEvent.CTRL_MASK) != 0) { pressKey(KeyEvent.VK_CONTROL, modifiers & ~InputEvent.CTRL_MASK); } } /* protected void pressModifiers(ComponentOperator oper, int modifiers) { if ((modifiers & InputEvent.SHIFT_MASK) != 0) { pressKey(oper, KeyEvent.VK_SHIFT, modifiers & ~InputEvent.SHIFT_MASK); } else if((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { pressKey(oper, KeyEvent.VK_ALT_GRAPH, modifiers & ~InputEvent.ALT_GRAPH_MASK); } else if((modifiers & InputEvent.ALT_MASK) != 0) { pressKey(oper, KeyEvent.VK_ALT, modifiers & ~InputEvent.ALT_MASK); } else if((modifiers & InputEvent.META_MASK) != 0) { pressKey(oper, KeyEvent.VK_META, modifiers & ~InputEvent.META_MASK); } else if((modifiers & InputEvent.CTRL_MASK) != 0) { pressKey(oper, KeyEvent.VK_CONTROL, modifiers & ~InputEvent.CTRL_MASK); } } */ /** * Releases modifiers keys by robot. * * @param modifiers a combination of {@code InputEvent.*_MASK} fields. */ protected void releaseModifiers(int modifiers) { if ((modifiers & InputEvent.SHIFT_MASK) != 0) { releaseKey(KeyEvent.VK_SHIFT, modifiers & ~InputEvent.SHIFT_MASK); } else if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { releaseKey(KeyEvent.VK_ALT_GRAPH, modifiers & ~InputEvent.ALT_GRAPH_MASK); } else if ((modifiers & InputEvent.ALT_MASK) != 0) { releaseKey(KeyEvent.VK_ALT, modifiers & ~InputEvent.ALT_MASK); } else if ((modifiers & InputEvent.META_MASK) != 0) { releaseKey(KeyEvent.VK_META, modifiers & ~InputEvent.META_MASK); } else if ((modifiers & InputEvent.CTRL_MASK) != 0) { releaseKey(KeyEvent.VK_CONTROL, modifiers & ~InputEvent.CTRL_MASK); } } /* protected void releaseModifiers(ComponentOperator oper, int modifiers) { if ((modifiers & InputEvent.SHIFT_MASK) != 0) { releaseKey(oper, KeyEvent.VK_SHIFT, modifiers & ~InputEvent.SHIFT_MASK); } else if((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { releaseKey(oper, KeyEvent.VK_ALT_GRAPH, modifiers & ~InputEvent.ALT_GRAPH_MASK); } else if((modifiers & InputEvent.ALT_MASK) != 0) { releaseKey(oper, KeyEvent.VK_ALT, modifiers & ~InputEvent.ALT_MASK); } else if((modifiers & InputEvent.META_MASK) != 0) { releaseKey(oper, KeyEvent.VK_META, modifiers & ~InputEvent.META_MASK); } else if((modifiers & InputEvent.CTRL_MASK) != 0) { releaseKey(oper, KeyEvent.VK_CONTROL, modifiers & ~InputEvent.CTRL_MASK); } } */ private void initRobot() { // need to init Robot in dispatch thread because it hangs on Linux // (see http://www.netbeans.org/issues/show_bug.cgi?id=37476) if (QueueTool.isDispatchThread()) { doInitRobot(); } else { qtool.invokeAndWait(new Runnable() { @Override public void run() { doInitRobot(); } }); } } private void doInitRobot() { try { ClassReference robotClassReverence = new ClassReference("java.awt.Robot"); robotReference = new ClassReference(robotClassReverence.newInstance(null, null)); robotReference.invokeMethod("setAutoDelay", new Object[]{(int) ((autoDelay != null) ? autoDelay.getValue() : 0)}, new Class<?>[]{Integer.TYPE}); } catch (InvocationTargetException | IllegalStateException | NoSuchMethodException | IllegalAccessException | ClassNotFoundException | InstantiationException e) { throw (new JemmyException("Exception during java.awt.Robot accessing", e)); } } }