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    }