KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > awt > Robot


1 /*
2  * @(#)Robot.java 1.27 03/12/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package java.awt;
9
10 import java.awt.peer.*;
11 import java.awt.image.*;
12 import java.awt.event.*;
13 import java.lang.reflect.InvocationTargetException JavaDoc;
14 import sun.awt.ComponentFactory;
15 import sun.awt.SunToolkit;
16 import sun.security.util.SecurityConstants;
17
18 /**
19  * This class is used to generate native system input events
20  * for the purposes of test automation, self-running demos, and
21  * other applications where control of the mouse and keyboard
22  * is needed. The primary purpose of Robot is to facilitate
23  * automated testing of Java platform implementations.
24  * <p>
25  * Using the class to generate input events differs from posting
26  * events to the AWT event queue or AWT components in that the
27  * events are generated in the platform's native input
28  * queue. For example, <code>Robot.mouseMove</code> will actually move
29  * the mouse cursor instead of just generating mouse move events.
30  * <p>
31  * Note that some platforms require special privileges or extensions
32  * to access low-level input control. If the current platform configuration
33  * does not allow input control, an <code>AWTException</code> will be thrown
34  * when trying to construct Robot objects. For example, X-Window systems
35  * will throw the exception if the XTEST 2.2 standard extension is not supported
36  * (or not enabled) by the X server.
37  * <p>
38  * Applications that use Robot for purposes other than self-testing should
39  * handle these error conditions gracefully.
40  *
41  * @version 1.27, 12/19/03
42  * @author Robi Khan
43  * @since 1.3
44  */

45 public class Robot {
46     private static final int MAX_DELAY = 60000;
47     private RobotPeer peer;
48     private boolean isAutoWaitForIdle = false;
49     private int autoDelay = 0;
50     private static final int LEGAL_BUTTON_MASK =
51                         InputEvent.BUTTON1_MASK|
52                         InputEvent.BUTTON2_MASK|
53                         InputEvent.BUTTON3_MASK;
54
55     private DirectColorModel screenCapCM = null;
56     
57     /**
58      * Constructs a Robot object in the coordinate system of the primary screen.
59      * <p>
60      *
61      * @throws AWTException if the platform configuration does not allow
62      * low-level input control. This exception is always thrown when
63      * GraphicsEnvironment.isHeadless() returns true
64      * @throws SecurityException if <code>createRobot</code> permission is not granted
65      * @see java.awt.GraphicsEnvironment#isHeadless
66      * @see SecurityManager#checkPermission
67      * @see AWTPermission
68      */

69     public Robot() throws AWTException JavaDoc {
70         if (GraphicsEnvironment.isHeadless()) {
71             throw new AWTException JavaDoc("headless environment");
72         }
73         init(GraphicsEnvironment.getLocalGraphicsEnvironment()
74             .getDefaultScreenDevice());
75     }
76
77     /**
78      * Creates a Robot for the given screen device. Coordinates passed
79      * to Robot method calls like mouseMove and createScreenCapture will
80      * be interpreted as being in the same coordinate system as the
81      * specified screen. Note that depending on the platform configuration,
82      * multiple screens may either:
83      * <ul>
84      * <li>share the same coordinate system to form a combined virtual screen</li>
85      * <li>use different coordinate systems to act as independent screens</li>
86      * </ul>
87      * This constructor is meant for the latter case.
88      * <p>
89      * If screen devices are reconfigured such that the coordinate system is
90      * affected, the behavior of existing Robot objects is undefined.
91      *
92      * @param screen A screen GraphicsDevice indicating the coordinate
93      * system the Robot will operate in.
94      * @throws AWTException if the platform configuration does not allow
95      * low-level input control. This exception is always thrown when
96      * GraphicsEnvironment.isHeadless() returns true.
97      * @throws IllegalArgumentException if <code>screen</code> is not a screen
98      * GraphicsDevice.
99      * @throws SecurityException if <code>createRobot</code> permission is not granted
100      * @see java.awt.GraphicsEnvironment#isHeadless
101      * @see GraphicsDevice
102      * @see SecurityManager#checkPermission
103      * @see AWTPermission
104      */

105     public Robot(GraphicsDevice JavaDoc screen) throws AWTException JavaDoc {
106     checkIsScreenDevice(screen);
107         init(screen);
108     }
109
110     private void init(GraphicsDevice JavaDoc screen) throws AWTException JavaDoc {
111         checkRobotAllowed();
112         Toolkit JavaDoc toolkit = Toolkit.getDefaultToolkit();
113         if (toolkit instanceof ComponentFactory) {
114             peer = ((ComponentFactory)toolkit).createRobot(this, screen);
115         }
116     }
117
118     /* determine if the security policy allows Robot's to be created */
119     private void checkRobotAllowed() {
120     SecurityManager JavaDoc security = System.getSecurityManager();
121     if (security != null) {
122         security.checkPermission(SecurityConstants.CREATE_ROBOT_PERMISSION);
123     }
124     }
125
126     /* check if the given device is a screen device */
127     private void checkIsScreenDevice(GraphicsDevice JavaDoc device) {
128         if (device == null || device.getType() != GraphicsDevice.TYPE_RASTER_SCREEN) {
129             throw new IllegalArgumentException JavaDoc("not a valid screen device");
130         }
131     }
132
133     /**
134      * Moves mouse pointer to given screen coordinates.
135      * @param x X position
136      * @param y Y position
137      */

138     public synchronized void mouseMove(int x, int y) {
139     peer.mouseMove(x,y);
140     afterEvent();
141     }
142
143     /**
144      * Presses one or more mouse buttons. The mouse buttons should
145      * be released using the <code>mouseRelease</code> method.
146      *
147      * @param buttons the Button mask; a combination of one or more
148      * of these flags:
149      * <ul>
150      * <li><code>InputEvent.BUTTON1_MASK</code>
151      * <li><code>InputEvent.BUTTON2_MASK</code>
152      * <li><code>InputEvent.BUTTON3_MASK</code>
153      * </ul>
154      * @throws IllegalArgumentException if the button mask is not a
155      * valid combination
156      * @see #mouseRelease(int)
157      */

158     public synchronized void mousePress(int buttons) {
159     checkButtonsArgument(buttons);
160     peer.mousePress(buttons);
161     afterEvent();
162     }
163     
164     /**
165      * Releases one or more mouse buttons.
166      *
167      * @param buttons the Button mask; a combination of one or more
168      * of these flags:
169      * <ul>
170      * <li><code>InputEvent.BUTTON1_MASK</code>
171      * <li><code>InputEvent.BUTTON2_MASK</code>
172      * <li><code>InputEvent.BUTTON3_MASK</code>
173      * </ul>
174      * @see #mousePress(int)
175      * @throws IllegalArgumentException if the button mask is not a valid
176      * combination
177      */

178     public synchronized void mouseRelease(int buttons) {
179     checkButtonsArgument(buttons);
180     peer.mouseRelease(buttons);
181     afterEvent();
182     }
183
184     private void checkButtonsArgument(int buttons) {
185     if ( (buttons|LEGAL_BUTTON_MASK) != LEGAL_BUTTON_MASK ) {
186         throw new IllegalArgumentException JavaDoc("Invalid combination of button flags");
187     }
188     }
189
190     /**
191      * Rotates the scroll wheel on wheel-equipped mice.
192      *
193      * @param wheelAmt number of "notches" to move the mouse wheel
194      * Negative values indicate movement up/away from the user,
195      * positive values indicate movement down/towards the user.
196      *
197      * @since 1.4
198      */

199     public synchronized void mouseWheel(int wheelAmt) {
200         peer.mouseWheel(wheelAmt);
201         afterEvent();
202     }
203
204     /**
205      * Presses a given key. The key should be released using the
206      * <code>keyRelease</code> method.
207      * <p>
208      * Key codes that have more than one physical key associated with them
209      * (e.g. <code>KeyEvent.VK_SHIFT</code> could mean either the
210      * left or right shift key) will map to the left key.
211      *
212      * @param keycode Key to press (e.g. <code>KeyEvent.VK_A</code>)
213      * @throws IllegalArgumentException if <code>keycode</code> is not
214      * a valid key
215      * @see #keyRelease(int)
216      * @see java.awt.event.KeyEvent
217      */

218     public synchronized void keyPress(int keycode) {
219     checkKeycodeArgument(keycode);
220     peer.keyPress(keycode);
221     afterEvent();
222     }
223     
224     /**
225      * Releases a given key.
226      * <p>
227      * Key codes that have more than one physical key associated with them
228      * (e.g. <code>KeyEvent.VK_SHIFT</code> could mean either the
229      * left or right shift key) will map to the left key.
230      *
231      * @param keycode Key to release (e.g. <code>KeyEvent.VK_A</code>)
232      * @throws IllegalArgumentException if <code>keycode</code> is not a
233      * valid key
234      * @see #keyPress(int)
235      * @see java.awt.event.KeyEvent
236      */

237     public synchronized void keyRelease(int keycode) {
238     checkKeycodeArgument(keycode);
239     peer.keyRelease(keycode);
240     afterEvent();
241     }
242
243     private void checkKeycodeArgument(int keycode) {
244     // rather than build a big table or switch statement here, we'll
245
// just check that the key isn't VK_UNDEFINED and assume that the
246
// peer implementations will throw an exception for other bogus
247
// values e.g. -1, 999999
248
if (keycode == KeyEvent.VK_UNDEFINED) {
249         throw new IllegalArgumentException JavaDoc("Invalid key code");
250     }
251     }
252
253     /**
254      * Returns the color of a pixel at the given screen coordinates.
255      * @param x X position of pixel
256      * @param y Y position of pixel
257      * @return Color of the pixel
258      */

259     public synchronized Color JavaDoc getPixelColor(int x, int y) {
260     Color JavaDoc color = new Color JavaDoc(peer.getRGBPixel(x,y));
261     return color;
262     }
263
264     /**
265      * Creates an image containing pixels read from the screen. This image does
266      * not include the mouse cursor.
267      * @param screenRect Rect to capture in screen coordinates
268      * @return The captured image
269      * @throws IllegalArgumentException if <code>screenRect</code> width and height are not greater than zero
270      * @throws SecurityException if <code>readDisplayPixels</code> permission is not granted
271      * @see SecurityManager#checkPermission
272      * @see AWTPermission
273      */

274     public synchronized BufferedImage createScreenCapture(Rectangle JavaDoc screenRect) {
275     checkScreenCaptureAllowed();
276     checkValidRect(screenRect);
277
278     BufferedImage image;
279     DataBufferInt buffer;
280     WritableRaster raster;
281
282     if (screenCapCM == null) {
283         /*
284          * Fix for 4285201
285          * Create a DirectColorModel equivalent to the default RGB ColorModel,
286          * except with no Alpha component.
287          */

288
289         screenCapCM = new DirectColorModel(24,
290                          /* red mask */ 0x00FF0000,
291                          /* green mask */ 0x0000FF00,
292                          /* blue mask */ 0x000000FF);
293     }
294
295     int pixels[];
296     int[] bandmasks = new int[3];
297
298     pixels = peer.getRGBPixels(screenRect);
299     buffer = new DataBufferInt(pixels, pixels.length);
300
301     bandmasks[0] = screenCapCM.getRedMask();
302     bandmasks[1] = screenCapCM.getGreenMask();
303     bandmasks[2] = screenCapCM.getBlueMask();
304
305     raster = Raster.createPackedRaster(buffer, screenRect.width, screenRect.height, screenRect.width, bandmasks, null);
306
307     image = new BufferedImage(screenCapCM, raster, false, null);
308
309     return image;
310     }
311
312     private static void checkValidRect(Rectangle JavaDoc rect) {
313     if (rect.width <= 0 || rect.height <= 0) {
314         throw new IllegalArgumentException JavaDoc("Rectangle width and height must be > 0");
315     }
316     }
317
318     private static void checkScreenCaptureAllowed() {
319     SecurityManager JavaDoc security = System.getSecurityManager();
320     if (security != null) {
321         security.checkPermission(
322         SecurityConstants.READ_DISPLAY_PIXELS_PERMISSION);
323     }
324     }
325
326     /*
327      * Called after an event is generated
328      */

329     private void afterEvent() {
330     autoWaitForIdle();
331     autoDelay();
332     }
333
334     /**
335      * Returns whether this Robot automatically invokes <code>waitForIdle</code>
336      * after generating an event.
337      * @return Whether <code>waitForIdle</code> is automatically called
338      */

339     public synchronized boolean isAutoWaitForIdle() {
340     return isAutoWaitForIdle;
341     }
342
343     /**
344      * Sets whether this Robot automatically invokes <code>waitForIdle</code>
345      * after generating an event.
346      * @param isOn Whether <code>waitForIdle</code> is automatically invoked
347      */

348     public synchronized void setAutoWaitForIdle(boolean isOn) {
349     isAutoWaitForIdle = isOn;
350     }
351     
352     /*
353      * Calls waitForIdle after every event if so desired.
354      */

355     private void autoWaitForIdle() {
356     if (isAutoWaitForIdle) {
357         waitForIdle();
358     }
359     }
360
361     /**
362      * Returns the number of milliseconds this Robot sleeps after generating an event.
363      */

364     public synchronized int getAutoDelay() {
365     return autoDelay;
366     }
367
368     /**
369      * Sets the number of milliseconds this Robot sleeps after generating an event.
370      * @throws IllegalArgumentException If <code>ms</code> is not between 0 and 60,000 milliseconds inclusive
371      */

372     public synchronized void setAutoDelay(int ms) {
373     checkDelayArgument(ms);
374     autoDelay = ms;
375     }
376
377     /*
378      * Automatically sleeps for the specified interval after event generated.
379      */

380     private void autoDelay() {
381     delay(autoDelay);
382     }
383
384     /**
385      * Sleeps for the specified time.
386      * To catch any <code>InterruptedException</code>s that occur,
387      * <code>Thread.sleep()</code> may be used instead.
388      * @param ms time to sleep in milliseconds
389      * @throws IllegalArgumentException if <code>ms</code> is not between 0 and 60,000 milliseconds inclusive
390      * @see java.lang.Thread#sleep
391      */

392     public synchronized void delay(int ms) {
393     checkDelayArgument(ms);
394     try {
395         Thread.sleep(ms);
396     } catch(InterruptedException JavaDoc ite) {
397         ite.printStackTrace();
398     }
399     }
400
401     private void checkDelayArgument(int ms) {
402     if (ms < 0 || ms > MAX_DELAY) {
403         throw new IllegalArgumentException JavaDoc("Delay must be to 0 to 60,000ms");
404     }
405     }
406
407     /**
408      * Waits until all events currently on the event queue have been processed.
409      * @throws IllegalThreadStateException if called on the AWT event dispatching thread
410      */

411     public synchronized void waitForIdle() {
412     checkNotDispatchThread();
413     // post a dummy event to the queue so we know when
414
// all the events before it have been processed
415
try {
416             SunToolkit.flushPendingEvents();
417         EventQueue.invokeAndWait( new Runnable JavaDoc() {
418                         public void run() {
419                         // dummy implementation
420
}
421                     } );
422     } catch(InterruptedException JavaDoc ite) {
423         System.err.println("Robot.waitForIdle, non-fatal exception caught:");
424         ite.printStackTrace();
425     } catch(InvocationTargetException JavaDoc ine) {
426         System.err.println("Robot.waitForIdle, non-fatal exception caught:");
427         ine.printStackTrace();
428     }
429     }
430
431     private void checkNotDispatchThread() {
432     if (EventQueue.isDispatchThread()) {
433         throw new IllegalThreadStateException JavaDoc("Cannot call method from the event dispatcher thread");
434     }
435     }
436
437     /**
438      * Returns a string representation of this Robot.
439      *
440      * @return the string representation.
441      */

442     public synchronized String JavaDoc toString() {
443     String JavaDoc params = "autoDelay = "+getAutoDelay()+", "+"autoWaitForIdle = "+isAutoWaitForIdle();
444     return getClass().getName() + "[ " + params + " ]";
445     }
446 }
447
Popular Tags