| /* |
| * Copyright (c) 2009-2010 jMonkeyEngine |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| package com.jme3.input.awt; |
| |
| import com.jme3.input.MouseInput; |
| import com.jme3.input.RawInputListener; |
| import com.jme3.input.event.MouseButtonEvent; |
| import com.jme3.input.event.MouseMotionEvent; |
| import java.awt.*; |
| import java.awt.event.*; |
| import java.awt.image.BufferedImage; |
| import java.util.ArrayList; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import javax.swing.SwingUtilities; |
| |
| /** |
| * <code>AwtMouseInput</code> |
| * |
| * @author Joshua Slack |
| * @author MHenze (cylab) |
| * |
| * @version $Revision$ |
| */ |
| public class AwtMouseInput implements MouseInput, MouseListener, MouseWheelListener, MouseMotionListener { |
| |
| public static int WHEEL_AMP = 40; // arbitrary... Java's mouse wheel seems to report something a lot lower than lwjgl's |
| |
| private static final Logger logger = Logger.getLogger(AwtMouseInput.class.getName()); |
| |
| private boolean visible = true; |
| |
| private RawInputListener listener; |
| |
| private Component component; |
| |
| private final ArrayList<MouseButtonEvent> eventQueue = new ArrayList<MouseButtonEvent>(); |
| private final ArrayList<MouseButtonEvent> eventQueueCopy = new ArrayList<MouseButtonEvent>(); |
| |
| private int lastEventX; |
| private int lastEventY; |
| private int lastEventWheel; |
| |
| private Cursor transparentCursor; |
| |
| private Robot robot; |
| private int wheelPos; |
| private Point location; |
| private Point centerLocation; |
| private Point centerLocationOnScreen; |
| private Point lastKnownLocation; |
| private boolean isRecentering; |
| private boolean cursorMoved; |
| private int eventsSinceRecenter; |
| |
| public AwtMouseInput() { |
| location = new Point(); |
| centerLocation = new Point(); |
| centerLocationOnScreen = new Point(); |
| lastKnownLocation = new Point(); |
| |
| try{ |
| robot = new Robot(); |
| }catch (java.awt.AWTException e){ |
| logger.log(Level.SEVERE, "Could not create a robot, so the mouse cannot be grabbed! ", e); |
| } |
| } |
| |
| public void setInputSource(Component comp){ |
| if (component != null){ |
| component.removeMouseListener(this); |
| component.removeMouseMotionListener(this); |
| component.removeMouseWheelListener(this); |
| |
| eventQueue.clear(); |
| |
| wheelPos = 0; |
| isRecentering = false; |
| eventsSinceRecenter = 0; |
| lastEventX = 0; |
| lastEventY = 0; |
| lastEventWheel = 0; |
| location = new Point(); |
| centerLocation = new Point(); |
| centerLocationOnScreen = new Point(); |
| lastKnownLocation = new Point(); |
| } |
| |
| component = comp; |
| component.addMouseListener(this); |
| component.addMouseMotionListener(this); |
| component.addMouseWheelListener(this); |
| } |
| |
| public void initialize() { |
| } |
| |
| public void destroy() { |
| } |
| |
| public boolean isInitialized() { |
| return true; |
| } |
| |
| public void setInputListener(RawInputListener listener){ |
| this.listener = listener; |
| } |
| |
| public long getInputTimeNanos() { |
| return System.nanoTime(); |
| } |
| |
| public void setCursorVisible(boolean visible){ |
| if (this.visible != visible){ |
| |
| lastKnownLocation.x = lastKnownLocation.y = 0; |
| |
| this.visible = visible; |
| final boolean newVisible = visible; |
| SwingUtilities.invokeLater(new Runnable() { |
| public void run() { |
| component.setCursor(newVisible ? null : getTransparentCursor()); |
| if (!newVisible) |
| recenterMouse(component); |
| } |
| }); |
| } |
| } |
| |
| public void update() { |
| if (cursorMoved){ |
| int newX = location.x; |
| int newY = location.y; |
| int newWheel = wheelPos; |
| |
| // invert DY |
| int actualX = lastKnownLocation.x; |
| int actualY = component.getHeight() - lastKnownLocation.y; |
| MouseMotionEvent evt = new MouseMotionEvent(actualX, actualY, |
| newX - lastEventX, |
| lastEventY - newY, |
| wheelPos, lastEventWheel - wheelPos); |
| listener.onMouseMotionEvent(evt); |
| |
| lastEventX = newX; |
| lastEventY = newY; |
| lastEventWheel = newWheel; |
| |
| cursorMoved = false; |
| } |
| |
| synchronized (eventQueue){ |
| eventQueueCopy.clear(); |
| eventQueueCopy.addAll(eventQueue); |
| eventQueue.clear(); |
| } |
| |
| int size = eventQueueCopy.size(); |
| for (int i = 0; i < size; i++){ |
| listener.onMouseButtonEvent(eventQueueCopy.get(i)); |
| } |
| } |
| |
| private Cursor getTransparentCursor() { |
| if (transparentCursor == null){ |
| BufferedImage cursorImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); |
| cursorImage.setRGB(0, 0, 0); |
| transparentCursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImage, new Point(0, 0), "empty cursor"); |
| } |
| return transparentCursor; |
| } |
| |
| // public void setHardwareCursor(URL file, int xHotspot, int yHotspot) { |
| // //Create the image from the provided url |
| // java.awt.Image cursorImage = new ImageIcon( file ).getImage( ); |
| // //Create a custom cursor with this image |
| // opaqueCursor = Toolkit.getDefaultToolkit().createCustomCursor( cursorImage , new Point( xHotspot , yHotspot ) , "custom cursor" ); |
| // //Use this cursor |
| // setCursorVisible( isCursorVisible ); |
| // } |
| |
| |
| public int getButtonCount() { |
| return 3; |
| } |
| |
| public void mouseClicked(MouseEvent arg0) { |
| // MouseButtonEvent evt = new MouseButtonEvent(getJMEButtonIndex(arg0), false); |
| // listener.onMouseButtonEvent(evt); |
| } |
| |
| public void mousePressed(MouseEvent arg0) { |
| MouseButtonEvent evt = new MouseButtonEvent(getJMEButtonIndex(arg0), true, arg0.getX(), arg0.getY()); |
| evt.setTime(arg0.getWhen()); |
| synchronized (eventQueue){ |
| eventQueue.add(evt); |
| } |
| } |
| |
| public void mouseReleased(MouseEvent arg0) { |
| MouseButtonEvent evt = new MouseButtonEvent(getJMEButtonIndex(arg0), false, arg0.getX(), arg0.getY()); |
| evt.setTime(arg0.getWhen()); |
| synchronized (eventQueue){ |
| eventQueue.add(evt); |
| } |
| } |
| |
| public void mouseEntered(MouseEvent arg0) { |
| if (!visible) |
| recenterMouse(arg0.getComponent()); |
| } |
| |
| public void mouseExited(MouseEvent arg0) { |
| if (!visible) |
| recenterMouse(arg0.getComponent()); |
| } |
| |
| public void mouseWheelMoved(MouseWheelEvent arg0) { |
| int dwheel = arg0.getUnitsToScroll(); |
| wheelPos += dwheel * WHEEL_AMP; |
| cursorMoved = true; |
| } |
| |
| public void mouseDragged(MouseEvent arg0) { |
| mouseMoved(arg0); |
| } |
| |
| public void mouseMoved(MouseEvent arg0) { |
| if (isRecentering) { |
| // MHenze (cylab) Fix Issue 35: |
| // As long as the MouseInput is in recentering mode, nothing is done until the mouse is entered in the component |
| // by the events generated by the robot. If this happens, the last known location is resetted. |
| if ((centerLocation.x == arg0.getX() && centerLocation.y == arg0.getY()) || eventsSinceRecenter++ == 5) { |
| lastKnownLocation.x = arg0.getX(); |
| lastKnownLocation.y = arg0.getY(); |
| isRecentering = false; |
| } |
| } else { |
| // MHenze (cylab) Fix Issue 35: |
| // Compute the delta and absolute coordinates and recenter the mouse if necessary |
| int dx = arg0.getX() - lastKnownLocation.x; |
| int dy = arg0.getY() - lastKnownLocation.y; |
| location.x += dx; |
| location.y += dy; |
| if (!visible) { |
| recenterMouse(arg0.getComponent()); |
| } |
| lastKnownLocation.x = arg0.getX(); |
| lastKnownLocation.y = arg0.getY(); |
| |
| cursorMoved = true; |
| } |
| } |
| |
| // MHenze (cylab) Fix Issue 35: A method to generate recenter the mouse to allow the InputSystem to "grab" the mouse |
| private void recenterMouse(final Component component) { |
| if (robot != null) { |
| eventsSinceRecenter = 0; |
| isRecentering = true; |
| centerLocation.setLocation(component.getWidth()/2, component.getHeight()/2); |
| centerLocationOnScreen.setLocation(centerLocation); |
| SwingUtilities.convertPointToScreen(centerLocationOnScreen, component); |
| robot.mouseMove(centerLocationOnScreen.x, centerLocationOnScreen.y); |
| } |
| } |
| |
| private int getJMEButtonIndex( MouseEvent arg0 ) { |
| int index; |
| switch (arg0.getButton()) { |
| default: |
| case MouseEvent.BUTTON1: //left |
| index = MouseInput.BUTTON_LEFT; |
| break; |
| case MouseEvent.BUTTON2: //middle |
| index = MouseInput.BUTTON_MIDDLE; |
| break; |
| case MouseEvent.BUTTON3: //right |
| index = MouseInput.BUTTON_RIGHT; |
| break; |
| } |
| return index; |
| } |
| } |