001 /* Robot.java -- a native input event generator
002 Copyright (C) 2004, 2005 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package java.awt;
040
041 import gnu.java.awt.ClasspathToolkit;
042
043 import java.lang.reflect.InvocationTargetException;
044 import java.awt.event.InputEvent;
045 import java.awt.image.BufferedImage;
046 import java.awt.peer.RobotPeer;
047
048 /**
049 * The Robot class is used to simulate user interaction with graphical
050 * programs. It can generate native windowing system input events and
051 * retrieve image data from the current screen. Robot is used to test
052 * the AWT and Swing library implementations; it can also be used to
053 * create self-running demo programs.
054 *
055 * Since Robot generates native windowing system events, rather than
056 * simply inserting {@link AWTEvent}s on the AWT event queue, its use
057 * is not restricted to Java programs. It can be used to
058 * programatically drive any graphical application.
059 *
060 * This implementation requires an X server that supports the XTest
061 * extension.
062 *
063 * @author Thomas Fitzsimmons (fitzsim@redhat.com)
064 *
065 * @since 1.3
066 */
067 public class Robot
068 {
069 private boolean waitForIdle;
070 private int autoDelay;
071 private RobotPeer peer;
072
073 /**
074 * Construct a Robot object that operates on the default screen.
075 *
076 * @exception AWTException if GraphicsEnvironment.isHeadless()
077 * returns true or if the X server does not support the XTest
078 * extension
079 * @exception SecurityException if createRobot permission is not
080 * granted
081 */
082 public Robot () throws AWTException
083 {
084 if (GraphicsEnvironment.isHeadless ())
085 throw new AWTException ("Robot: headless graphics environment");
086
087 SecurityManager sm = System.getSecurityManager ();
088 if (sm != null)
089 sm.checkPermission (new AWTPermission ("createRobot"));
090
091 ClasspathToolkit tk = (ClasspathToolkit) Toolkit.getDefaultToolkit ();
092
093 // createRobot will throw AWTException if XTest is not supported.
094 peer = tk.createRobot (GraphicsEnvironment.getLocalGraphicsEnvironment ()
095 .getDefaultScreenDevice ());
096 }
097
098 /**
099 * Construct a Robot object that operates on the specified screen.
100 *
101 * @exception AWTException if GraphicsEnvironment.isHeadless()
102 * returns true or if the X server does not support the XTest
103 * extension
104 * @exception IllegalArgumentException if screen is not a screen
105 * GraphicsDevice
106 * @exception SecurityException if createRobot permission is not
107 * granted
108 */
109 public Robot (GraphicsDevice screen) throws AWTException
110 {
111 if (GraphicsEnvironment.isHeadless ())
112 throw new AWTException ("Robot: headless graphics environment");
113
114 if (screen.getType () != GraphicsDevice.TYPE_RASTER_SCREEN)
115 throw new IllegalArgumentException ("Robot: graphics"
116 + " device is not a screen");
117
118 SecurityManager sm = System.getSecurityManager ();
119 if (sm != null)
120 sm.checkPermission (new AWTPermission ("createRobot"));
121
122 ClasspathToolkit tk = (ClasspathToolkit) Toolkit.getDefaultToolkit ();
123
124 // createRobot will throw AWTException if XTest is not supported.
125 peer = tk.createRobot (screen);
126 }
127
128 /**
129 * Move the mouse pointer to absolute coordinates (x, y).
130 *
131 * @param x the destination x coordinate
132 * @param y the destination y coordinate
133 */
134 public void mouseMove(int x, int y)
135 {
136 peer.mouseMove (x, y);
137
138 if (waitForIdle)
139 waitForIdle ();
140
141 if (autoDelay > 0)
142 delay (autoDelay);
143 }
144
145 /**
146 * Press one or more mouse buttons.
147 *
148 * @param buttons the buttons to press; a bitmask of one or more of
149 * these {@link InputEvent} fields:
150 *
151 * <ul>
152 * <li>BUTTON1_MASK</li>
153 * <li>BUTTON2_MASK</li>
154 * <li>BUTTON3_MASK</li>
155 * </ul>
156 *
157 * @exception IllegalArgumentException if the button mask is invalid
158 */
159 public void mousePress (int buttons)
160 {
161 if ((buttons & InputEvent.BUTTON1_MASK) == 0
162 && (buttons & InputEvent.BUTTON2_MASK) == 0
163 && (buttons & InputEvent.BUTTON3_MASK) == 0)
164 throw new IllegalArgumentException ("Robot: mousePress:"
165 + " invalid button mask");
166
167 peer.mousePress (buttons);
168
169 if (waitForIdle)
170 waitForIdle ();
171
172 if (autoDelay > 0)
173 delay (autoDelay);
174 }
175
176 /**
177 * Release one or more mouse buttons.
178 *
179 * @param buttons the buttons to release; a bitmask of one or more
180 * of these {@link InputEvent} fields:
181 *
182 * <ul>
183 * <li>BUTTON1_MASK</li>
184 * <li>BUTTON2_MASK</li>
185 * <li>BUTTON3_MASK</li>
186 * </ul>
187 *
188 * @exception IllegalArgumentException if the button mask is invalid
189 */
190 public void mouseRelease(int buttons)
191 {
192 if ((buttons & InputEvent.BUTTON1_MASK) == 0
193 && (buttons & InputEvent.BUTTON2_MASK) == 0
194 && (buttons & InputEvent.BUTTON3_MASK) == 0)
195 throw new IllegalArgumentException ("Robot: mouseRelease:"
196 + " invalid button mask");
197
198 peer.mouseRelease (buttons);
199
200 if (waitForIdle)
201 waitForIdle ();
202
203 if (autoDelay > 0)
204 delay (autoDelay);
205 }
206
207 /**
208 * Rotate the mouse scroll wheel.
209 *
210 * @param wheelAmt number of steps to rotate mouse wheel. negative
211 * to rotate wheel up (away from the user), positive to rotate wheel
212 * down (toward the user).
213 *
214 * @since 1.4
215 */
216 public void mouseWheel (int wheelAmt)
217 {
218 peer.mouseWheel (wheelAmt);
219
220 if (waitForIdle)
221 waitForIdle ();
222
223 if (autoDelay > 0)
224 delay (autoDelay);
225 }
226
227 /**
228 * Press a key.
229 *
230 * @param keycode key to press, a {@link java.awt.event.KeyEvent} VK_ constant
231 *
232 * @exception IllegalArgumentException if keycode is not a valid key
233 */
234 public void keyPress (int keycode)
235 {
236 peer.keyPress (keycode);
237
238 if (waitForIdle)
239 waitForIdle ();
240
241 if (autoDelay > 0)
242 delay (autoDelay);
243 }
244
245 /**
246 * Release a key.
247 *
248 * @param keycode key to release, a {@link java.awt.event.KeyEvent} VK_
249 * constant
250 *
251 * @exception IllegalArgumentException if keycode is not a valid key
252 */
253 public void keyRelease (int keycode)
254 {
255 peer.keyRelease (keycode);
256
257 if (waitForIdle)
258 waitForIdle ();
259
260 if (autoDelay > 0)
261 delay (autoDelay);
262 }
263
264 /**
265 * Return the color of the pixel at the given screen coordinates.
266 *
267 * @param x the x coordinate of the pixel
268 * @param y the y coordinate of the pixel
269 *
270 * @return the Color of the pixel at screen coodinates <code>(x, y)</code>
271 */
272 public Color getPixelColor (int x, int y)
273 {
274 return new Color (peer.getRGBPixel (x, y));
275 }
276
277 /**
278 * Create an image containing pixels read from the screen. The
279 * image does not include the mouse pointer.
280 *
281 * @param screenRect the rectangle of pixels to capture, in screen
282 * coordinates
283 *
284 * @return a BufferedImage containing the requested pixels
285 *
286 * @exception IllegalArgumentException if requested width and height
287 * are not both greater than zero
288 * @exception SecurityException if readDisplayPixels permission is
289 * not granted
290 */
291 public BufferedImage createScreenCapture (Rectangle screenRect)
292 {
293 if (screenRect.width <= 0)
294 throw new IllegalArgumentException ("Robot: capture width is <= 0");
295
296 if (screenRect.height <= 0)
297 throw new IllegalArgumentException ("Robot: capture height is <= 0");
298
299 SecurityManager sm = System.getSecurityManager ();
300 if (sm != null)
301 sm.checkPermission (new AWTPermission ("readDisplayPixels"));
302
303 int[] pixels = peer.getRGBPixels (screenRect);
304
305 BufferedImage bufferedImage =
306 new BufferedImage (screenRect.width, screenRect.height,
307 BufferedImage.TYPE_INT_ARGB);
308
309 bufferedImage.setRGB (0, 0, screenRect.width, screenRect.height,
310 pixels, 0, screenRect.width);
311
312 return bufferedImage;
313 }
314
315 /**
316 * Check if this Robot automatically calls {@link #waitForIdle()} after
317 * generating an event.
318 *
319 * @return true if waitForIdle is automatically called
320 */
321 public boolean isAutoWaitForIdle ()
322 {
323 return waitForIdle;
324 }
325
326 /**
327 * Set whether or not this Robot automatically calls {@link
328 * #waitForIdle()} after generating an event.
329 *
330 * @param isOn true if waitForIdle should be called automatically
331 */
332 public void setAutoWaitForIdle (boolean isOn)
333 {
334 waitForIdle = isOn;
335 }
336
337 /**
338 * Retrieve the length of time this Robot sleeps after generating an
339 * event.
340 *
341 * @return the length of time in milliseconds
342 */
343 public int getAutoDelay ()
344 {
345 return autoDelay;
346 }
347
348 /**
349 * Set the length of time this Robot sleeps after generating an
350 * event.
351 *
352 * @param ms the length of time in milliseconds
353 *
354 * @exception IllegalArgumentException if ms is not between 0 and
355 * 60,000 milliseconds inclusive
356 */
357 public void setAutoDelay (int ms)
358 {
359 if (ms <= 0 || ms >= 60000)
360 throw new IllegalArgumentException ("Robot: delay length out-of-bounds");
361
362 autoDelay = ms;
363 }
364
365 /**
366 * Sleep for a specified length of time.
367 *
368 * @param ms the length of time in milliseconds
369 *
370 * @exception IllegalArgumentException if ms is not between 0 and
371 * 60,000 milliseconds inclusive
372 */
373 public void delay (int ms)
374 {
375 if (ms < 0 || ms > 60000)
376 throw new IllegalArgumentException ("Robot: delay length out-of-bounds");
377
378 try
379 {
380 Thread.sleep (ms);
381 }
382 catch (InterruptedException e)
383 {
384 System.err.println ("Robot: delay interrupted");
385 }
386 }
387
388 /**
389 * Wait until all events currently on the event queue have been
390 * dispatched.
391 */
392 public void waitForIdle ()
393 {
394 if (EventQueue.isDispatchThread ())
395 throw new IllegalThreadStateException ("Robot: waitForIdle called from "
396 + "the event dispatch thread");
397
398 try
399 {
400 EventQueue.invokeAndWait (new Runnable () { public void run () { } });
401 }
402 catch (InterruptedException e)
403 {
404 System.err.println ("Robot: waitForIdle interrupted");
405 }
406 catch (InvocationTargetException e)
407 {
408 System.err.println ("Robot: waitForIdle cannot invoke target");
409 }
410 }
411
412 /**
413 * Return a string representation of this Robot.
414 *
415 * @return a string representation
416 */
417 public String toString ()
418 {
419 return getClass ().getName ()
420 + "[ autoDelay = " + autoDelay + ", autoWaitForIdle = "
421 + waitForIdle + " ]";
422 }
423 }