Skip to main content

Fun with Robot

Posted by zixle on May 2, 2005 at 6:18 AM PDT

In my last blog I mentioned we're nearly done with the baseline
API for mustang. In verifying the baseline API work I initially
took the approach of grabbing screenshots, dumping them in Paint and
zooming in. Painful to say the least! There must be a better way.

Enter Robot

In addition to being able to inject mouse and keyboard events into the
system java.awt.Robot gives you the ability render part of the desktop
into an image. Exactly what I want. The following code grabs a screen
shot returning a BufferedImage:

  Robot robot = new Robot();
  BufferedImage image = robot.createScreenCapture(new Rectangle(x, y, width, height));

So Robot gives me an image of part of the desktop, but I don't really
want to continually do a keyboard gesture to create the image. I
want the image to update as I move the mouse around.

Enter AWTEventListener

AWTEventListener can be used to listen to all events. Think of it as
a way to globally install a *Listener. Using AWTEventListener I can
determine when the mouse moves over any component. Here's the code
for that:

  Toolkit toolkit = Toolkit.getDefaultToolkit();
  AWTEventListener listener = new AWTEventListener() {
    public void eventDispatched(AWTEvent event) {
      if (event.getID() == MouseEvent.MOUSE_MOVED) {
        mouseMoved(event);
      }
    }
  };
  toolkit.addAWTEventListener(new , AWTEvent.MOUSE_MOTION_EVENT_MASK);

Combining AWTEventListener with Robot I have a way to zoom in on part
of my window as the cursor is being moved. Excellent! The following
screen shot shows this in effect. The bottom area is the zoomed in
component.

Magnification Screenshot

Here's the code:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class MagnifyComponent extends JComponent {
    private int factor;
    private Image image;
    private int w;
    private int h;
    private Robot robot;

    MagnifyComponent() {
        try {
            robot = new Robot();
        } catch (AWTException e) {
        }
        this.factor = 6;
        Toolkit.getDefaultToolkit().addAWTEventListener(
            new EventHandler(), AWTEvent.MOUSE_MOTION_EVENT_MASK);
    }

    public void reshape(int x, int y, int w, int h) {
        super.reshape(x, y, w, h);
        updateImageSize();
    }

    private void updateImageSize() {
        int w = getWidth();
        int h = getHeight();
        image = null;
        this.w = w / factor / 2;
        this.h = h / factor / 2;
    }

    public void paintComponent(Graphics g) {
        if (isOpaque()) {
            g.setColor(getBackground());
            g.fillRect(0, 0, getWidth(), getHeight());
        }
        if (image != null) {
            g.drawImage(image, 0, 0, w * factor * 2, h * factor * 2,
                        0, 0, w + w, h + h, null);
        }
    }

    private void updateImage(int x, int y) {
        if (w > 0 && h > 0) {
            image = robot.createScreenCapture(new Rectangle(
                                                  x - w, y - h, w + w, h + h));
        }
        repaint();
    }

    private class EventHandler implements AWTEventListener {
        public void eventDispatched(AWTEvent event) {
            if (event.getID() == MouseEvent.MOUSE_MOVED) {
                MouseEvent me = (MouseEvent)event;
                Component source = me.getComponent();
                int x = me.getX();
                int y = me.getY();
                Point location = source.getLocationOnScreen();
                x += location.x;
                y += location.y;
                updateImage(x, y);
            }
        }
    }
}

I'm aware of at least one accessibility helper in Windows that zooms in on the desktop as you move the mouse, but I've found it a bit intrusive. In
so far as the app always takes up a big chunk of the desktop, where as
I really only wanted the zooming feature for this window.

Enjoy!

Warning: I'm sure there are performance issues with this code on slower/older machines. As this isn't for production and was only needed in testing, I did no testing other than on my machine. If you're to incorporate something like this in a real app do performance analysis first!

Related Topics >>