History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/java/awt/Robot.java (1 of 18352 files). Show all

Unified · Split

java/pkg/java/awt/Robot.java+593−0
@@ -0,0 +1,593 @@
1/*
2 * Copyright (c) 1999, 2009, 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
26package java.awt;
27
28import java.awt.event.InputEvent;
29import java.awt.event.KeyEvent;
30import java.awt.image.BufferedImage;
31import java.awt.image.DataBufferInt;
32import java.awt.image.DirectColorModel;
33import java.awt.image.Raster;
34import java.awt.image.WritableRaster;
35import java.awt.peer.RobotPeer;
36import java.lang.reflect.InvocationTargetException;
37import sun.awt.ComponentFactory;
38import sun.awt.SunToolkit;
39import sun.awt.image.SunWritableRaster;
40import sun.security.util.SecurityConstants;
41
42/**
43 * This class is used to generate native system input events
44 * for the purposes of test automation, self-running demos, and
45 * other applications where control of the mouse and keyboard
46 * is needed. The primary purpose of Robot is to facilitate
47 * automated testing of Java platform implementations.
48 * <p>
49 * Using the class to generate input events differs from posting
50 * events to the AWT event queue or AWT components in that the
51 * events are generated in the platform's native input
52 * queue. For example, <code>Robot.mouseMove</code> will actually move
53 * the mouse cursor instead of just generating mouse move events.
54 * <p>
55 * Note that some platforms require special privileges or extensions
56 * to access low-level input control. If the current platform configuration
57 * does not allow input control, an <code>AWTException</code> will be thrown
58 * when trying to construct Robot objects. For example, X-Window systems
59 * will throw the exception if the XTEST 2.2 standard extension is not supported
60 * (or not enabled) by the X server.
61 * <p>
62 * Applications that use Robot for purposes other than self-testing should
63 * handle these error conditions gracefully.
64 *
65 * @author Robi Khan
66 * @since 1.3
67 */
68public class Robot {
69 private static final int MAX_DELAY = 60000;
70 private RobotPeer peer;
71 private boolean isAutoWaitForIdle = false;
72 private int autoDelay = 0;
73 private static int LEGAL_BUTTON_MASK = 0;
74
75 // location of robot's GC, used in mouseMove(), getPixelColor() and captureScreenImage()
76 private Point gdLoc;
77
78 private DirectColorModel screenCapCM = null;
79
80 /**
81 * Constructs a Robot object in the coordinate system of the primary screen.
82 * <p>
83 *
84 * @throws AWTException if the platform configuration does not allow
85 * low-level input control. This exception is always thrown when
86 * GraphicsEnvironment.isHeadless() returns true
87 * @throws SecurityException if <code>createRobot</code> permission is not granted
88 * @see java.awt.GraphicsEnvironment#isHeadless
89 * @see SecurityManager#checkPermission
90 * @see AWTPermission
91 */
92 public Robot() throws AWTException {
93 if (GraphicsEnvironment.isHeadless()) {
94 throw new AWTException("headless environment");
95 }
96 init(GraphicsEnvironment.getLocalGraphicsEnvironment()
97 .getDefaultScreenDevice());
98 }
99
100 /**
101 * Creates a Robot for the given screen device. Coordinates passed
102 * to Robot method calls like mouseMove and createScreenCapture will
103 * be interpreted as being in the same coordinate system as the
104 * specified screen. Note that depending on the platform configuration,
105 * multiple screens may either:
106 * <ul>
107 * <li>share the same coordinate system to form a combined virtual screen</li>
108 * <li>use different coordinate systems to act as independent screens</li>
109 * </ul>
110 * This constructor is meant for the latter case.
111 * <p>
112 * If screen devices are reconfigured such that the coordinate system is
113 * affected, the behavior of existing Robot objects is undefined.
114 *
115 * @param screen A screen GraphicsDevice indicating the coordinate
116 * system the Robot will operate in.
117 * @throws AWTException if the platform configuration does not allow
118 * low-level input control. This exception is always thrown when
119 * GraphicsEnvironment.isHeadless() returns true.
120 * @throws IllegalArgumentException if <code>screen</code> is not a screen
121 * GraphicsDevice.
122 * @throws SecurityException if <code>createRobot</code> permission is not granted
123 * @see java.awt.GraphicsEnvironment#isHeadless
124 * @see GraphicsDevice
125 * @see SecurityManager#checkPermission
126 * @see AWTPermission
127 */
128 public Robot(GraphicsDevice screen) throws AWTException {
129 checkIsScreenDevice(screen);
130 init(screen);
131 }
132
133 private void init(GraphicsDevice screen) throws AWTException {
134 checkRobotAllowed();
135 gdLoc = screen.getDefaultConfiguration().getBounds().getLocation();
136 Toolkit toolkit = Toolkit.getDefaultToolkit();
137 if (toolkit instanceof ComponentFactory) {
138 peer = ((ComponentFactory)toolkit).createRobot(this, screen);
139 disposer = new RobotDisposer(peer);
140 sun.java2d.Disposer.addRecord(anchor, disposer);
141 }
142 initLegalButtonMask();
143 }
144
145 private static synchronized void initLegalButtonMask() {
146 if (LEGAL_BUTTON_MASK != 0) return;
147
148 int tmpMask = 0;
149 if (Toolkit.getDefaultToolkit().areExtraMouseButtonsEnabled()){
150 if (Toolkit.getDefaultToolkit() instanceof SunToolkit) {
151 final int buttonsNumber = ((SunToolkit)(Toolkit.getDefaultToolkit())).getNumberOfButtons();
152 for (int i = 0; i < buttonsNumber; i++){
153 tmpMask |= InputEvent.getMaskForButton(i+1);
154 }
155 }
156 }
157 tmpMask |= InputEvent.BUTTON1_MASK|
158 InputEvent.BUTTON2_MASK|
159 InputEvent.BUTTON3_MASK|
160 InputEvent.BUTTON1_DOWN_MASK|
161 InputEvent.BUTTON2_DOWN_MASK|
162 InputEvent.BUTTON3_DOWN_MASK;
163 LEGAL_BUTTON_MASK = tmpMask;
164 }
165
166 /* determine if the security policy allows Robot's to be created */
167 private void checkRobotAllowed() {
168 SecurityManager security = System.getSecurityManager();
169 if (security != null) {
170 security.checkPermission(SecurityConstants.AWT.CREATE_ROBOT_PERMISSION);
171 }
172 }
173
174 /* check if the given device is a screen device */
175 private void checkIsScreenDevice(GraphicsDevice device) {
176 if (device == null || device.getType() != GraphicsDevice.TYPE_RASTER_SCREEN) {
177 throw new IllegalArgumentException("not a valid screen device");
178 }
179 }
180
181 private transient Object anchor = new Object();
182
183 static class RobotDisposer implements sun.java2d.DisposerRecord {
184 private final RobotPeer peer;
185 public RobotDisposer(RobotPeer peer) {
186 this.peer = peer;
187 }
188 public void dispose() {
189 if (peer != null) {
190 peer.dispose();
191 }
192 }
193 }
194
195 private transient RobotDisposer disposer;
196
197 /**
198 * Moves mouse pointer to given screen coordinates.
199 * @param x X position
200 * @param y Y position
201 */
202 public synchronized void mouseMove(int x, int y) {
203 peer.mouseMove(gdLoc.x + x, gdLoc.y + y);
204 afterEvent();
205 }
206
207 /**
208 * Presses one or more mouse buttons. The mouse buttons should
209 * be released using the {@link #mouseRelease(int)} method.
210 *
211 * @param buttons the Button mask; a combination of one or more
212 * mouse button masks.
213 * <p>
214 * It is allowed to use only a combination of valid values as a {@code buttons} parameter.
215 * A valid combination consists of {@code InputEvent.BUTTON1_DOWN_MASK},
216 * {@code InputEvent.BUTTON2_DOWN_MASK}, {@code InputEvent.BUTTON3_DOWN_MASK}
217 * and values returned by the
218 * {@link InputEvent#getMaskForButton(int) InputEvent.getMaskForButton(button)} method.
219 *
220 * The valid combination also depends on a
221 * {@link Toolkit#areExtraMouseButtonsEnabled() Toolkit.areExtraMouseButtonsEnabled()} value as follows:
222 * <ul>
223 * <li> If support for extended mouse buttons is
224 * {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
225 * then it is allowed to use only the following standard button masks:
226 * {@code InputEvent.BUTTON1_DOWN_MASK}, {@code InputEvent.BUTTON2_DOWN_MASK},
227 * {@code InputEvent.BUTTON3_DOWN_MASK}.
228 * <li> If support for extended mouse buttons is
229 * {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java
230 * then it is allowed to use the standard button masks
231 * and masks for existing extended mouse buttons, if the mouse has more then three buttons.
232 * In that way, it is allowed to use the button masks corresponding to the buttons
233 * in the range from 1 to {@link java.awt.MouseInfo#getNumberOfButtons() MouseInfo.getNumberOfButtons()}.
234 * <br>
235 * It is recommended to use the {@link InputEvent#getMaskForButton(int) InputEvent.getMaskForButton(button)}
236 * method to obtain the mask for any mouse button by its number.
237 * </ul>
238 * <p>
239 * The following standard button masks are also accepted:
240 * <ul>
241 * <li>{@code InputEvent.BUTTON1_MASK}
242 * <li>{@code InputEvent.BUTTON2_MASK}
243 * <li>{@code InputEvent.BUTTON3_MASK}
244 * </ul>
245 * However, it is recommended to use {@code InputEvent.BUTTON1_DOWN_MASK},
246 * {@code InputEvent.BUTTON2_DOWN_MASK}, {@code InputEvent.BUTTON3_DOWN_MASK} instead.
247 * Either extended {@code _DOWN_MASK} or old {@code _MASK} values
248 * should be used, but both those models should not be mixed.
249 * @throws IllegalArgumentException if the {@code buttons} mask contains the mask for extra mouse button
250 * and support for extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
251 * @throws IllegalArgumentException if the {@code buttons} mask contains the mask for extra mouse button
252 * that does not exist on the mouse and support for extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java
253 * @see #mouseRelease(int)
254 * @see InputEvent#getMaskForButton(int)
255 * @see Toolkit#areExtraMouseButtonsEnabled()
256 * @see java.awt.MouseInfo#getNumberOfButtons()
257 * @see java.awt.event.MouseEvent
258 */
259 public synchronized void mousePress(int buttons) {
260 checkButtonsArgument(buttons);
261 peer.mousePress(buttons);
262 afterEvent();
263 }
264
265 /**
266 * Releases one or more mouse buttons.
267 *
268 * @param buttons the Button mask; a combination of one or more
269 * mouse button masks.
270 * <p>
271 * It is allowed to use only a combination of valid values as a {@code buttons} parameter.
272 * A valid combination consists of {@code InputEvent.BUTTON1_DOWN_MASK},
273 * {@code InputEvent.BUTTON2_DOWN_MASK}, {@code InputEvent.BUTTON3_DOWN_MASK}
274 * and values returned by the
275 * {@link InputEvent#getMaskForButton(int) InputEvent.getMaskForButton(button)} method.
276 *
277 * The valid combination also depends on a
278 * {@link Toolkit#areExtraMouseButtonsEnabled() Toolkit.areExtraMouseButtonsEnabled()} value as follows:
279 * <ul>
280 * <li> If the support for extended mouse buttons is
281 * {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
282 * then it is allowed to use only the following standard button masks:
283 * {@code InputEvent.BUTTON1_DOWN_MASK}, {@code InputEvent.BUTTON2_DOWN_MASK},
284 * {@code InputEvent.BUTTON3_DOWN_MASK}.
285 * <li> If the support for extended mouse buttons is
286 * {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java
287 * then it is allowed to use the standard button masks
288 * and masks for existing extended mouse buttons, if the mouse has more then three buttons.
289 * In that way, it is allowed to use the button masks corresponding to the buttons
290 * in the range from 1 to {@link java.awt.MouseInfo#getNumberOfButtons() MouseInfo.getNumberOfButtons()}.
291 * <br>
292 * It is recommended to use the {@link InputEvent#getMaskForButton(int) InputEvent.getMaskForButton(button)}
293 * method to obtain the mask for any mouse button by its number.
294 * </ul>
295 * <p>
296 * The following standard button masks are also accepted:
297 * <ul>
298 * <li>{@code InputEvent.BUTTON1_MASK}
299 * <li>{@code InputEvent.BUTTON2_MASK}
300 * <li>{@code InputEvent.BUTTON3_MASK}
301 * </ul>
302 * However, it is recommended to use {@code InputEvent.BUTTON1_DOWN_MASK},
303 * {@code InputEvent.BUTTON2_DOWN_MASK}, {@code InputEvent.BUTTON3_DOWN_MASK} instead.
304 * Either extended {@code _DOWN_MASK} or old {@code _MASK} values
305 * should be used, but both those models should not be mixed.
306 * @throws IllegalArgumentException if the {@code buttons} mask contains the mask for extra mouse button
307 * and support for extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
308 * @throws IllegalArgumentException if the {@code buttons} mask contains the mask for extra mouse button
309 * that does not exist on the mouse and support for extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java
310 * @see #mousePress(int)
311 * @see InputEvent#getMaskForButton(int)
312 * @see Toolkit#areExtraMouseButtonsEnabled()
313 * @see java.awt.MouseInfo#getNumberOfButtons()
314 * @see java.awt.event.MouseEvent
315 */
316 public synchronized void mouseRelease(int buttons) {
317 checkButtonsArgument(buttons);
318 peer.mouseRelease(buttons);
319 afterEvent();
320 }
321
322 private void checkButtonsArgument(int buttons) {
323 if ( (buttons|LEGAL_BUTTON_MASK) != LEGAL_BUTTON_MASK ) {
324 throw new IllegalArgumentException("Invalid combination of button flags");
325 }
326 }
327
328 /**
329 * Rotates the scroll wheel on wheel-equipped mice.
330 *
331 * @param wheelAmt number of "notches" to move the mouse wheel
332 * Negative values indicate movement up/away from the user,
333 * positive values indicate movement down/towards the user.
334 *
335 * @since 1.4
336 */
337 public synchronized void mouseWheel(int wheelAmt) {
338 peer.mouseWheel(wheelAmt);
339 afterEvent();
340 }
341
342 /**
343 * Presses a given key. The key should be released using the
344 * <code>keyRelease</code> method.
345 * <p>
346 * Key codes that have more than one physical key associated with them
347 * (e.g. <code>KeyEvent.VK_SHIFT</code> could mean either the
348 * left or right shift key) will map to the left key.
349 *
350 * @param keycode Key to press (e.g. <code>KeyEvent.VK_A</code>)
351 * @throws IllegalArgumentException if <code>keycode</code> is not
352 * a valid key
353 * @see #keyRelease(int)
354 * @see java.awt.event.KeyEvent
355 */
356 public synchronized void keyPress(int keycode) {
357 checkKeycodeArgument(keycode);
358 peer.keyPress(keycode);
359 afterEvent();
360 }
361
362 /**
363 * Releases a given key.
364 * <p>
365 * Key codes that have more than one physical key associated with them
366 * (e.g. <code>KeyEvent.VK_SHIFT</code> could mean either the
367 * left or right shift key) will map to the left key.
368 *
369 * @param keycode Key to release (e.g. <code>KeyEvent.VK_A</code>)
370 * @throws IllegalArgumentException if <code>keycode</code> is not a
371 * valid key
372 * @see #keyPress(int)
373 * @see java.awt.event.KeyEvent
374 */
375 public synchronized void keyRelease(int keycode) {
376 checkKeycodeArgument(keycode);
377 peer.keyRelease(keycode);
378 afterEvent();
379 }
380
381 private void checkKeycodeArgument(int keycode) {
382 // rather than build a big table or switch statement here, we'll
383 // just check that the key isn't VK_UNDEFINED and assume that the
384 // peer implementations will throw an exception for other bogus
385 // values e.g. -1, 999999
386 if (keycode == KeyEvent.VK_UNDEFINED) {
387 throw new IllegalArgumentException("Invalid key code");
388 }
389 }
390
391 /**
392 * Returns the color of a pixel at the given screen coordinates.
393 * @param x X position of pixel
394 * @param y Y position of pixel
395 * @return Color of the pixel
396 */
397 public synchronized Color getPixelColor(int x, int y) {
398 Color color = new Color(peer.getRGBPixel(gdLoc.x + x, gdLoc.y + y));
399 return color;
400 }
401
402 /**
403 * Creates an image containing pixels read from the screen. This image does
404 * not include the mouse cursor.
405 * @param screenRect Rect to capture in screen coordinates
406 * @return The captured image
407 * @throws IllegalArgumentException if <code>screenRect</code> width and height are not greater than zero
408 * @throws SecurityException if <code>readDisplayPixels</code> permission is not granted
409 * @see SecurityManager#checkPermission
410 * @see AWTPermission
411 */
412 public synchronized BufferedImage createScreenCapture(Rectangle screenRect) {
413 checkScreenCaptureAllowed();
414
415 // according to the spec, screenRect is relative to robot's GD
416 Rectangle translatedRect = new Rectangle(screenRect);
417 translatedRect.translate(gdLoc.x, gdLoc.y);
418 checkValidRect(translatedRect);
419
420 BufferedImage image;
421 DataBufferInt buffer;
422 WritableRaster raster;
423
424 if (screenCapCM == null) {
425 /*
426 * Fix for 4285201
427 * Create a DirectColorModel equivalent to the default RGB ColorModel,
428 * except with no Alpha component.
429 */
430
431 screenCapCM = new DirectColorModel(24,
432 /* red mask */ 0x00FF0000,
433 /* green mask */ 0x0000FF00,
434 /* blue mask */ 0x000000FF);
435 }
436
437 // need to sync the toolkit prior to grabbing the pixels since in some
438 // cases rendering to the screen may be delayed
439 Toolkit.getDefaultToolkit().sync();
440
441 int pixels[];
442 int[] bandmasks = new int[3];
443
444 pixels = peer.getRGBPixels(translatedRect);
445 buffer = new DataBufferInt(pixels, pixels.length);
446
447 bandmasks[0] = screenCapCM.getRedMask();
448 bandmasks[1] = screenCapCM.getGreenMask();
449 bandmasks[2] = screenCapCM.getBlueMask();
450
451 raster = Raster.createPackedRaster(buffer, translatedRect.width, translatedRect.height, translatedRect.width, bandmasks, null);
452 SunWritableRaster.makeTrackable(buffer);
453
454 image = new BufferedImage(screenCapCM, raster, false, null);
455
456 return image;
457 }
458
459 private static void checkValidRect(Rectangle rect) {
460 if (rect.width <= 0 || rect.height <= 0) {
461 throw new IllegalArgumentException("Rectangle width and height must be > 0");
462 }
463 }
464
465 private static void checkScreenCaptureAllowed() {
466 SecurityManager security = System.getSecurityManager();
467 if (security != null) {
468 security.checkPermission(
469 SecurityConstants.AWT.READ_DISPLAY_PIXELS_PERMISSION);
470 }
471 }
472
473 /*
474 * Called after an event is generated
475 */
476 private void afterEvent() {
477 autoWaitForIdle();
478 autoDelay();
479 }
480
481 /**
482 * Returns whether this Robot automatically invokes <code>waitForIdle</code>
483 * after generating an event.
484 * @return Whether <code>waitForIdle</code> is automatically called
485 */
486 public synchronized boolean isAutoWaitForIdle() {
487 return isAutoWaitForIdle;
488 }
489
490 /**
491 * Sets whether this Robot automatically invokes <code>waitForIdle</code>
492 * after generating an event.
493 * @param isOn Whether <code>waitForIdle</code> is automatically invoked
494 */
495 public synchronized void setAutoWaitForIdle(boolean isOn) {
496 isAutoWaitForIdle = isOn;
497 }
498
499 /*
500 * Calls waitForIdle after every event if so desired.
501 */
502 private void autoWaitForIdle() {
503 if (isAutoWaitForIdle) {
504 waitForIdle();
505 }
506 }
507
508 /**
509 * Returns the number of milliseconds this Robot sleeps after generating an event.
510 */
511 public synchronized int getAutoDelay() {
512 return autoDelay;
513 }
514
515 /**
516 * Sets the number of milliseconds this Robot sleeps after generating an event.
517 * @throws IllegalArgumentException If <code>ms</code> is not between 0 and 60,000 milliseconds inclusive
518 */
519 public synchronized void setAutoDelay(int ms) {
520 checkDelayArgument(ms);
521 autoDelay = ms;
522 }
523
524 /*
525 * Automatically sleeps for the specified interval after event generated.
526 */
527 private void autoDelay() {
528 delay(autoDelay);
529 }
530
531 /**
532 * Sleeps for the specified time.
533 * To catch any <code>InterruptedException</code>s that occur,
534 * <code>Thread.sleep()</code> may be used instead.
535 * @param ms time to sleep in milliseconds
536 * @throws IllegalArgumentException if <code>ms</code> is not between 0 and 60,000 milliseconds inclusive
537 * @see java.lang.Thread#sleep
538 */
539 public synchronized void delay(int ms) {
540 checkDelayArgument(ms);
541 try {
542 Thread.sleep(ms);
543 } catch(InterruptedException ite) {
544 ite.printStackTrace();
545 }
546 }
547
548 private void checkDelayArgument(int ms) {
549 if (ms < 0 || ms > MAX_DELAY) {
550 throw new IllegalArgumentException("Delay must be to 0 to 60,000ms");
551 }
552 }
553
554 /**
555 * Waits until all events currently on the event queue have been processed.
556 * @throws IllegalThreadStateException if called on the AWT event dispatching thread
557 */
558 public synchronized void waitForIdle() {
559 checkNotDispatchThread();
560 // post a dummy event to the queue so we know when
561 // all the events before it have been processed
562 try {
563 SunToolkit.flushPendingEvents();
564 EventQueue.invokeAndWait( new Runnable() {
565 public void run() {
566 // dummy implementation
567 }
568 } );
569 } catch(InterruptedException ite) {
570 System.err.println("Robot.waitForIdle, non-fatal exception caught:");
571 ite.printStackTrace();
572 } catch(InvocationTargetException ine) {
573 System.err.println("Robot.waitForIdle, non-fatal exception caught:");
574 ine.printStackTrace();
575 }
576 }
577
578 private void checkNotDispatchThread() {
579 if (EventQueue.isDispatchThread()) {
580 throw new IllegalThreadStateException("Cannot call method from the event dispatcher thread");
581 }
582 }
583
584 /**
585 * Returns a string representation of this Robot.
586 *
587 * @return the string representation.
588 */
589 public synchronized String toString() {
590 String params = "autoDelay = "+getAutoDelay()+", "+"autoWaitForIdle = "+isAutoWaitForIdle();
591 return getClass().getName() + "[ " + params + " ]";
592 }
593}