Java tutorial
/** * Squidy Interaction Library is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * Squidy Interaction Library is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Squidy Interaction Library. If not, see * <http://www.gnu.org/licenses/>. * * 2009 Human-Computer Interaction Group, University of Konstanz. * <http://hci.uni-konstanz.de> * * Please contact info@squidy-lib.de or visit our website * <http://www.squidy-lib.de> for further information. */ package org.squidy.nodes; import java.awt.AWTException; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.MouseInfo; import java.awt.Point; import java.awt.Rectangle; import java.awt.Robot; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.lang.reflect.Field; import java.net.URL; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlType; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.squidy.SquidyException; import org.squidy.designer.util.InputWindow; import org.squidy.manager.controls.CheckBox; import org.squidy.manager.controls.TextField; import org.squidy.manager.data.DataConstant; import org.squidy.manager.data.IData; import org.squidy.manager.data.Processor; import org.squidy.manager.data.Property; import org.squidy.manager.data.impl.DataButton; import org.squidy.manager.data.impl.DataPosition2D; import org.squidy.manager.model.AbstractNode; import org.squidy.nodes.directinputmouse.CDirectInputMouse; /** * <code>Mouse2D</code>. * * <pre> * Date: Feb 12, 2008 * Time: 2:13:34 AM * </pre> * * @author Werner Koenig, werner.koenig@uni-konstanz.de, University of Konstanz * @author Roman Rädle, <a * href="mailto:Roman.Raedle@uni-konstanz.de">Roman. * Raedle@uni-konstanz.de</a>, University of Konstanz * @version $Id: MouseIO.java 772 2011-09-16 15:39:44Z raedle $ */ @XmlType(name = "MouseIO") @Processor(name = "Mouse I/O", icon = "/org/squidy/nodes/image/48x48/mouse.png", description = "/org/squidy/nodes/html/MouseIO.html", types = { Processor.Type.INPUT, Processor.Type.OUTPUT }, tags = { "mouse", "2D" }) public class MouseIO extends AbstractNode { // Logger to log info, error, debug,... messages. private static final Log LOG = LogFactory.getLog(MouseIO.class); private static final boolean isWindows = System.getProperty("os.name").startsWith("Windows"); private InputWindow inputWindow; private CDirectInputMouse cdim; public static final DataConstant CLICK_COUNT = DataConstant.get(Integer.class, "CLICK_COUNT"); // ################################################################################ // BEGIN OF ADJUSTABLES // ################################################################################ @XmlAttribute(name = "middle-double-click") @Property(name = "Emulate double-click with middle button", description = "True if a click with the middle button should emulate a left-button double-click") @CheckBox private boolean middleDoubleClick = false; /** * @return true if middle button should emulate a double click with left * button */ public boolean isMiddleDoubleClick() { return middleDoubleClick; } /** * @param true if middle button should emulate a double click with left * button */ public void setMiddleDoubleClick(boolean middleDoubleClick) { this.middleDoubleClick = middleDoubleClick; } // ################################################################################ @XmlAttribute(name = "open-input-window") @Property(name = "Open window for mouse input", description = "True if a dedicated window for mouse tracking should be opened.") @CheckBox private boolean openInputWindow = !isWindows; /** * @return the openInputWindow */ public final boolean isOpenInputWindow() { return openInputWindow; } /** * @param openInputWindow * the openInputWindow to set */ public final void setOpenInputWindow(boolean openInputWindow) { this.openInputWindow = openInputWindow; if (openInputWindow && isProcessing()) { if (openInputWindow) { inputWindow = InputWindow.getInstance(); inputWindow.registerMouseListener(this); } } else { if (inputWindow != null) { inputWindow.removeMouseListener(this); } } } // ################################################################################ @XmlAttribute(name = "manual") @Property(name = "Activate manual display setup", group = "Display Settings", description = "Indicates whether mouse position is set with automatically detected display settings or manually defined settings (following parameters).") @CheckBox private boolean manual = false; /** * @return the manual */ public final boolean isManual() { return manual; } /** * @param manual * the manual to set */ public final void setManual(boolean manual) { this.manual = manual; } // ################################################################################ @XmlAttribute(name = "manual-width") @Property(name = "Manual display width", group = "Display Settings", description = "The manual display width of the allowed mouse positioning in pixels. Only used if positioning is set manually.") @TextField private double manualWidth = 1024; /** * @return the manualWidth */ public final double getManualWidth() { return manualWidth; } /** * @param manualWidth * the manualWidth to set */ public final void setManualWidth(double manualWidth) { this.manualWidth = manualWidth; } // ################################################################################ @XmlAttribute(name = "manual-height") @Property(name = "Manual display height", group = "Display Settings", description = "The manual display height of the allowed mouse positioning in pixels. Only used if positioning is set manually.") @TextField private double manualHeight = 768; /** * @return the manualHeight */ public final double getManualHeight() { return manualHeight; } /** * @param manualHeight * the manualHeight to set */ public final void setManualHeight(double manualHeight) { this.manualHeight = manualHeight; } // ################################################################################ @XmlAttribute(name = "origin-offset-x") @Property(name = "Manual display origin offset X", group = "Display Settings", description = "The manual display origin X offset of mouse positions in pixels.") @TextField private double originOffsetX = 0; /** * @return the originOffsetX */ public final double getOriginOffsetX() { return originOffsetX; } /** * @param originOffsetX * the originOffsetX to set */ public final void setOriginOffsetX(double originOffsetX) { this.originOffsetX = originOffsetX; } // ################################################################################ @XmlAttribute(name = "origin-offset-y") @Property(name = "Manual display origin offset Y", group = "Display Settings", description = "The manual display origin Y offset of mouse positions in pixels.") @TextField private double originOffsetY = 0; /** * @return the originOffsetY */ public final double getOriginOffsetY() { return originOffsetY; } /** * @param originOffsetY * the originOffsetY to set */ public final void setOriginOffsetY(double originOffsetY) { this.originOffsetY = originOffsetY; } // ################################################################################ @XmlAttribute(name = "directinput") @Property(name = "Use DirectInput (Windows only)", description = "Indicates whether DirectInput is used with Microsoft Windows to capture mouse position directly from screen.") @CheckBox private boolean directInput = isWindows; /** * @return true if DirectInput is used with Microsoft Windows */ public boolean isDirectInput() { return directInput; } /** * @param true if DirectInput should be used with Microsoft Windows */ public void setDirectInput(boolean directInput) { this.directInput = directInput; } // ################################################################################ // END OF ADJUSTABLES // ################################################################################ private Robot robot; double minX = 0; double minY = 0; double maxX = 0; double maxY = 0; double width = 0; double height = 0; Rectangle[] screenBounds; Rectangle lastBounds; /* * (non-Javadoc) * * @see org.squidy.manager.ReflectionProcessable#onStart() */ @Override public void onStart() { // Search minimum/maximum of x/y. GraphicsDevice[] devices = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices(); screenBounds = new Rectangle[devices.length]; for (int i = 0; i < devices.length; i++) { GraphicsDevice device = devices[i]; GraphicsConfiguration gc = device.getDefaultConfiguration(); Rectangle bounds = gc.getBounds(); screenBounds[i] = bounds; double x, y; x = bounds.getX(); minX = Math.min(minX, x); x += bounds.getWidth(); maxX = Math.max(maxX, x); y = bounds.getY(); minY = Math.min(minY, y); y += bounds.getHeight(); maxY = Math.max(maxY, y); } width = Math.abs(minX) + Math.abs(maxX); height = Math.abs(minY) + Math.abs(maxY); try { robot = new Robot(); robot.setAutoDelay(0); } catch (AWTException e) { if (LOG.isErrorEnabled()) LOG.error("Could not initialize Robot."); publishFailure(e); } if (openInputWindow) { inputWindow = InputWindow.getInstance(); inputWindow.registerMouseListener(this); } if (directInput && isWindows) { if (cdim == null) { createDirectInputMouse(); if (cdim != null) { if (cdim.Init(0) != 0) { cdim = null; publishFailure(new SquidyException("Could not initialize DirectInput mouse.")); } } } if (cdim != null) { if (cdim.StartCapture() != 0) publishFailure(new SquidyException("Could not start DirectInput mouse.")); else new Thread(new Runnable() { public void run() { while (processing) { cdim.WaitForBufferedData(); if (processing) processDirectInputMouseBufferedData(); } } }, getId() + "_DIM").start(); } } } /* * (non-Javadoc) * * @see org.squidy.manager.ReflectionProcessable#onStop() */ @Override public void onStop() { if (robot != null) robot = null; if (inputWindow != null) inputWindow.removeMouseListener(this); if (directInput && isWindows && cdim != null) if (cdim.StopCapture() != 0) publishFailure(new SquidyException("Could not stop DirectInput mouse.")); } /** * @param dataPosition2D * @return */ public IData process(DataPosition2D dataPosition2D) { setMouse(dataPosition2D); return dataPosition2D; } /** * @param dataButton * @return */ public IData process(DataButton dataButton) { if (dataButton.getButtonType() == DataButton.BUTTON_1) { setMouseStatus(MouseEvent.BUTTON1, dataButton.getFlag()); } if (dataButton.getButtonType() == DataButton.BUTTON_2) { if (middleDoubleClick) { setMouseStatus(MouseEvent.BUTTON1, true); setMouseStatus(MouseEvent.BUTTON1, false); setMouseStatus(MouseEvent.BUTTON1, true); setMouseStatus(MouseEvent.BUTTON1, false); } else { setMouseStatus(MouseEvent.BUTTON2, dataButton.getFlag()); } } if (dataButton.getButtonType() == DataButton.BUTTON_3) { setMouseStatus(MouseEvent.BUTTON3, dataButton.getFlag()); // if (dataButton.getFlag()) { // setSingleMousePress(DataButton.BUTTON_3); // } } return dataButton; } protected void createDirectInputMouse() { try { cdim = new CDirectInputMouse(); } catch (Exception e) { publishFailure(e); } } protected void processDirectInputMouseBufferedData() { int i, count; cdim.ReadBufferedData(); count = cdim.GetDataCount(); for (i = 0; i < count; i++) processDirectInputMouseEvent(cdim.GetDataEvent(i), cdim.GetDataValue(i)); } protected void processDirectInputMouseEvent(CDirectInputMouse.EventID iEventID, int iData) { IData data = null; // move mouse (relative coordinates in iData) if (iEventID.equals(CDirectInputMouse.EventID.DIMEID_X) || iEventID.equals(CDirectInputMouse.EventID.DIMEID_Y) || iEventID.equals(CDirectInputMouse.EventID.DIMEID_XY)) { Point ptMouse = MouseInfo.getPointerInfo().getLocation(); double x = (double) ptMouse.x / (maxX - minX); double y = (double) ptMouse.y / (maxY - minY); data = new DataPosition2D(MouseIO.class, x, y); } /* // for debugging only if (iEventID.equals(CDirectInputMouse.EventID.DIMEID_XY)) { short x = (short)(iData & 0xffff); short y = (short)(iData >> 16); LOG.debug(iEventID.toString() + " " + x + " " + y); } */ // scroll wheel (offset in iData) //if (iEventID.equals(DirectInputMouseEventID.DIMEID_Z) // data = new DataMouseWheel(MouseIO, iData); // click button (flag 0x80 is set in iData when pressed) if (iEventID.equals(CDirectInputMouse.EventID.DIMEID_BUTTON0)) data = new DataButton(MouseIO.class, DataButton.BUTTON_0, iData == 0x80); if (iEventID.equals(CDirectInputMouse.EventID.DIMEID_BUTTON1)) data = new DataButton(MouseIO.class, DataButton.BUTTON_1, iData == 0x80); if (iEventID.equals(CDirectInputMouse.EventID.DIMEID_BUTTON2)) data = new DataButton(MouseIO.class, DataButton.BUTTON_2, iData == 0x80); if (data != null) publish(data); } protected void setMouse(DataPosition2D dataPosition2D) { if (!manual) { double xPos = (width * dataPosition2D.getX()) + minX; double yPos = (height * dataPosition2D.getY()) + minY; boolean allowPoint = false; // Fasts up the contain procedure -> most cases it won't require an // iteration for all bounds. if (lastBounds != null && lastBounds.contains((int) xPos, (int) yPos)) { allowPoint = true; } if (!allowPoint) { for (Rectangle bounds : screenBounds) { if (bounds.contains((int) xPos, (int) yPos)) { lastBounds = bounds; allowPoint = true; break; } } } // Only move mouse if point is within a bound of a graphics device. if (allowPoint) { robot.mouseMove((int) xPos, (int) yPos); } } else { double xPos = (manualWidth * dataPosition2D.getX()) + originOffsetX; double yPos = (manualHeight * dataPosition2D.getY()) + originOffsetY; robot.mouseMove((int) xPos, (int) yPos); } } protected void setSingleMousePress(int button) { if (button == MouseEvent.BUTTON1) { robot.mousePress(InputEvent.BUTTON1_MASK); robot.mouseRelease(InputEvent.BUTTON1_MASK); } else if (button == MouseEvent.BUTTON2) { robot.mousePress(InputEvent.BUTTON2_MASK); robot.mouseRelease(InputEvent.BUTTON2_MASK); } else if (button == MouseEvent.BUTTON3) { robot.mousePress(InputEvent.BUTTON3_MASK); robot.mouseRelease(InputEvent.BUTTON3_MASK); } } protected void setMouseStatus(int button, boolean status) { if (button == MouseEvent.BUTTON1) { if (status) { robot.mousePress(InputEvent.BUTTON1_MASK); } else { robot.mouseRelease(InputEvent.BUTTON1_MASK); } } else if (button == MouseEvent.BUTTON2) { if (status) { robot.mousePress(InputEvent.BUTTON2_MASK); } else { robot.mouseRelease(InputEvent.BUTTON2_MASK); } } else if (button == MouseEvent.BUTTON3) { if (status) { robot.mousePress(InputEvent.BUTTON3_MASK); } else { robot.mouseRelease(InputEvent.BUTTON3_MASK); } } } }