1 /*
2  * Copyright (c) 2007, 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 
26 /**
27  * JRobot is a wrapper around java.awt.Robot that provides some convenience
28  * methods.
29  * <p>When using jtreg you would include this class via something like:
30  * <pre>
31  * @library ../../../regtesthelpers
32  * @build JRobot
33  * </pre>
34  *
35  */
36 import java.awt.AWTException;
37 import java.awt.Component;
38 import java.awt.Dimension;
39 import java.awt.EventQueue;
40 import java.awt.Point;
41 import java.awt.Rectangle;
42 import java.awt.event.InputEvent;
43 import java.awt.event.KeyEvent;
44 import javax.swing.SwingUtilities;
45 
46 public class JRobot extends java.awt.Robot {
47     private static int DEFAULT_DELAY = 550;
48     private static int INTERNAL_DELAY = 250;
49 
50     private int delay;
51     private boolean delaysEnabled;
52 
JRobot(boolean enableDelays)53     protected JRobot(boolean enableDelays) throws AWTException {
54         super();
55         delaysEnabled = enableDelays;
56         setAutoWaitForIdle(enableDelays);
57         if (enableDelays) {
58             setAutoDelay(INTERNAL_DELAY);
59             setDelay(DEFAULT_DELAY);
60         }
61     }
62 
63     /**
64      * Return a JRobot. Delays are enabled by default.
65      * @return a JRobot
66      */
getRobot()67     public static JRobot getRobot() {
68         return getRobot(true);
69     }
70 
71     /**
72      * Create a JRobot. The parameter controls whether delays are enabled.
73      * @param enableDelays controls whether delays are enabled.
74      * @return a JRobot
75      */
getRobot(boolean enableDelays)76     public static JRobot getRobot(boolean enableDelays) {
77         JRobot robot = null;
78         try {
79             robot = new JRobot(enableDelays);
80         } catch (AWTException e) {
81             System.err.println("Coudn't create Robot, details below");
82             throw new Error(e);
83         }
84         return robot;
85     }
86 
87     /**
88      * Press and release a key.
89      * @param keycode which key to press. For example, KeyEvent.VK_DOWN
90      */
hitKey(int keycode)91     public void hitKey(int keycode) {
92         keyPress(keycode);
93         keyRelease(keycode);
94         delay();
95     }
96 
97     /**
98      * Press and release a key with modifiers.
99      * @param keys keys to press. Keys are pressed in order they are passed as
100      * parameters to this method. All keys except the last one are considered
101      * modifiers. For example, to press Ctrl+Shift+T, call:
102      * hitKey(KeyEvent.VK_CONTROL, KeyEvent.VK_SHIFT, KeyEvent.VK_T);
103      */
hitKey(int... keys)104     public void hitKey(int... keys) {
105         for (int i = 0; i < keys.length; i++) {
106             keyPress(keys[i]);
107         }
108 
109         for (int i = keys.length - 1; i >= 0; i--) {
110             keyRelease(keys[i]);
111         }
112         delay();
113     }
114 
115     /**
116      * Move mouse cursor to the center of the Component.
117      * @param c Component the mouse is placed over
118      */
moveMouseTo(Component c)119     public void moveMouseTo(Component c) {
120         Point p = c.getLocationOnScreen();
121         Dimension size = c.getSize();
122         p.x += size.width / 2;
123         p.y += size.height / 2;
124         mouseMove(p.x, p.y);
125         delay();
126     }
127 
128     /**
129      * Move mouse smoothly from (x0, y0) to (x1, y1).
130      */
glide(int x0, int y0, int x1, int y1)131     public void glide(int x0, int y0, int x1, int y1) {
132         float dmax = (float)Math.max(Math.abs(x1 - x0), Math.abs(y1 - y0));
133         float dx = (x1 - x0) / dmax;
134         float dy = (y1 - y0) / dmax;
135 
136         mouseMove(x0, y0);
137         for (int i=1; i<=dmax; i++) {
138             mouseMove((int)(x0 + dx*i), (int)(y0 + dy*i));
139         }
140         delay();
141     }
142 
143     /**
144      * Perform a mouse click, i.e. press and release mouse button(s).
145      * @param buttons mouse button(s).
146      *                For example, MouseEvent.BUTTON1_MASK
147      */
clickMouse(int buttons)148     public void clickMouse(int buttons) {
149         mousePress(buttons);
150         mouseRelease(buttons);
151         delay();
152     }
153 
154     /**
155      * Perform a click with the first mouse button.
156      */
clickMouse()157     public void clickMouse() {
158         clickMouse(InputEvent.BUTTON1_MASK);
159     }
160 
161     /**
162      * Click in the center of the given Component
163      * @param c the Component to click on
164      * @param buttons mouse button(s).
165      */
clickMouseOn(Component c, int buttons)166     public void clickMouseOn(Component c, int buttons) {
167         moveMouseTo(c);
168         clickMouse(buttons);
169     }
170 
171     /**
172      * Click the first mouse button in the center of the given Component
173      * @param c the Component to click on
174      */
clickMouseOn(Component c)175     public void clickMouseOn(Component c) {
176         clickMouseOn(c, InputEvent.BUTTON1_MASK);
177     }
178 
179     /**
180      * Return whether delays are enabled
181      * @return whether delays are enabled
182      */
getDelaysEnabled()183     public boolean getDelaysEnabled() {
184         return delaysEnabled;
185     }
186 
187     /**
188      * Delay execution by delay milliseconds
189      */
delay()190     public void delay() {
191         delay(delay);
192     }
193 
194     /**
195      * Return the delay amount, in milliseconds
196      */
getDelay()197     public int getDelay() {
198         return delay;
199     }
200 
201     /**
202      * Set the delay amount, in milliseconds
203      */
setDelay(int delay)204     public void setDelay(int delay) {
205         this.delay = delay;
206     }
207 
208     /**
209      * Waits until all events currently on the event queue have been processed.
210      * Does nothing if called on EDT
211      */
waitForIdle()212     public synchronized void waitForIdle() {
213         if (!EventQueue.isDispatchThread()) {
214             super.waitForIdle();
215         }
216     }
217 
218     /**
219      * Calculate the center of the Rectangle passed, and return them
220      * in a Point object.
221      * @param r a non-null Rectangle
222      * @return a new Point object containing coordinates of r's center
223      */
centerOf(Rectangle r)224     public Point centerOf(Rectangle r) {
225         return new Point(r.x + r.width / 2, r.y + r.height / 2);
226     }
227 
228     /**
229      * Calculate the center of the Rectangle passed, and store it in p.
230      * @param r a non-null Rectangle
231      * @param p a non-null Point that receives coordinates of r's center
232      * @return p
233      */
centerOf(Rectangle r, Point p)234     public Point centerOf(Rectangle r, Point p) {
235         p.x = r.x + r.width / 2;
236         p.y = r.y + r.height / 2;
237         return p;
238     }
239 
240     /**
241      * Convert a rectangle from coordinate system of Component c to
242      * screen coordinate system.
243      * @param r a non-null Rectangle
244      * @param c a Component whose coordinate system is used for conversion
245      */
convertRectToScreen(Rectangle r, Component c)246     public void convertRectToScreen(Rectangle r, Component c) {
247         Point p = new Point(r.x, r.y);
248         SwingUtilities.convertPointToScreen(p, c);
249         r.x = p.x;
250         r.y = p.y;
251     }
252 
253     /**
254      * Compares two rectangles pixel-by-pixel.
255      * @param r0 the first area
256      * @param r1 the second area
257      * return true if all pixels in the two areas are identical
258      */
compareRects(Rectangle r0, Rectangle r1)259     public boolean compareRects(Rectangle r0, Rectangle r1) {
260         int xShift = r1.x - r0.x;
261         int yShift = r1.y - r0.y;
262 
263         for (int y = r0.y; y < r0.y + r0.height; y++) {
264             for (int x = r0.x; x < r0.x + r0.width; x++) {
265                 if (!comparePixels(x, y, x + xShift, y + yShift)) {
266                     return false;
267                 }
268             }
269         }
270         return true;
271     }
272 
273     /**
274      * Compares colors of two points on the screen.
275      * @param p0 the first point
276      * @param p1 the second point
277      * return true if the two points have the same color
278      */
comparePixels(Point p0, Point p1)279     public boolean comparePixels(Point p0, Point p1) {
280         return comparePixels(p0.x, p0.y, p1.x, p1.y);
281     }
282 
283     /**
284      * Compares colors of two points on the screen.
285      * @param x0 the x coordinate of the first point
286      * @param y0 the y coordinate of the first point
287      * @param x1 the x coordinate of the second point
288      * @param y1 the y coordinate of the second point
289      * return true if the two points have the same color
290      */
comparePixels(int x0, int y0, int x1, int y1)291     public boolean comparePixels(int x0, int y0, int x1, int y1) {
292         return (getPixelColor(x0, y0).equals(getPixelColor(x1, y1)));
293     }
294 }
295