Home | History | Annotate | Download | only in awt
      1 /*
      2  * Copyright (c) 2009-2010 jMonkeyEngine
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  * * Redistributions of source code must retain the above copyright
     10  *   notice, this list of conditions and the following disclaimer.
     11  *
     12  * * Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in the
     14  *   documentation and/or other materials provided with the distribution.
     15  *
     16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
     17  *   may be used to endorse or promote products derived from this software
     18  *   without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 package com.jme3.input.awt;
     34 
     35 import com.jme3.input.MouseInput;
     36 import com.jme3.input.RawInputListener;
     37 import com.jme3.input.event.MouseButtonEvent;
     38 import com.jme3.input.event.MouseMotionEvent;
     39 import java.awt.*;
     40 import java.awt.event.*;
     41 import java.awt.image.BufferedImage;
     42 import java.util.ArrayList;
     43 import java.util.logging.Level;
     44 import java.util.logging.Logger;
     45 import javax.swing.SwingUtilities;
     46 
     47 /**
     48  * <code>AwtMouseInput</code>
     49  *
     50  * @author Joshua Slack
     51  * @author MHenze (cylab)
     52  *
     53  * @version $Revision$
     54  */
     55 public class AwtMouseInput implements MouseInput, MouseListener, MouseWheelListener, MouseMotionListener {
     56 
     57     public static int WHEEL_AMP = 40;   // arbitrary...  Java's mouse wheel seems to report something a lot lower than lwjgl's
     58 
     59     private static final Logger logger = Logger.getLogger(AwtMouseInput.class.getName());
     60 
     61     private boolean visible = true;
     62 
     63     private RawInputListener listener;
     64 
     65     private Component component;
     66 
     67     private final ArrayList<MouseButtonEvent> eventQueue = new ArrayList<MouseButtonEvent>();
     68     private final ArrayList<MouseButtonEvent> eventQueueCopy = new ArrayList<MouseButtonEvent>();
     69 
     70     private int lastEventX;
     71     private int lastEventY;
     72     private int lastEventWheel;
     73 
     74     private Cursor transparentCursor;
     75 
     76     private Robot robot;
     77     private int wheelPos;
     78     private Point location;
     79     private Point centerLocation;
     80     private Point centerLocationOnScreen;
     81     private Point lastKnownLocation;
     82     private boolean isRecentering;
     83     private boolean cursorMoved;
     84     private int eventsSinceRecenter;
     85 
     86     public AwtMouseInput() {
     87         location = new Point();
     88         centerLocation = new Point();
     89         centerLocationOnScreen = new Point();
     90         lastKnownLocation = new Point();
     91 
     92         try{
     93             robot = new Robot();
     94         }catch (java.awt.AWTException e){
     95             logger.log(Level.SEVERE, "Could not create a robot, so the mouse cannot be grabbed! ", e);
     96         }
     97     }
     98 
     99     public void setInputSource(Component comp){
    100         if (component != null){
    101             component.removeMouseListener(this);
    102             component.removeMouseMotionListener(this);
    103             component.removeMouseWheelListener(this);
    104 
    105             eventQueue.clear();
    106 
    107             wheelPos = 0;
    108             isRecentering = false;
    109             eventsSinceRecenter = 0;
    110             lastEventX = 0;
    111             lastEventY = 0;
    112             lastEventWheel = 0;
    113             location = new Point();
    114             centerLocation = new Point();
    115             centerLocationOnScreen = new Point();
    116             lastKnownLocation = new Point();
    117         }
    118 
    119         component = comp;
    120         component.addMouseListener(this);
    121         component.addMouseMotionListener(this);
    122         component.addMouseWheelListener(this);
    123     }
    124 
    125     public void initialize() {
    126     }
    127 
    128     public void destroy() {
    129     }
    130 
    131     public boolean isInitialized() {
    132         return true;
    133     }
    134 
    135     public void setInputListener(RawInputListener listener){
    136         this.listener = listener;
    137     }
    138 
    139     public long getInputTimeNanos() {
    140         return System.nanoTime();
    141     }
    142 
    143     public void setCursorVisible(boolean visible){
    144         if (this.visible != visible){
    145 
    146             lastKnownLocation.x = lastKnownLocation.y = 0;
    147 
    148             this.visible = visible;
    149             final boolean newVisible = visible;
    150             SwingUtilities.invokeLater(new Runnable() {
    151                 public void run() {
    152                     component.setCursor(newVisible ? null : getTransparentCursor());
    153                     if (!newVisible)
    154                         recenterMouse(component);
    155                 }
    156             });
    157         }
    158     }
    159 
    160     public void update() {
    161         if (cursorMoved){
    162             int newX = location.x;
    163             int newY = location.y;
    164             int newWheel = wheelPos;
    165 
    166             // invert DY
    167             int actualX = lastKnownLocation.x;
    168             int actualY = component.getHeight() - lastKnownLocation.y;
    169             MouseMotionEvent evt = new MouseMotionEvent(actualX, actualY,
    170                                                         newX - lastEventX,
    171                                                         lastEventY - newY,
    172                                                         wheelPos, lastEventWheel - wheelPos);
    173             listener.onMouseMotionEvent(evt);
    174 
    175             lastEventX = newX;
    176             lastEventY = newY;
    177             lastEventWheel = newWheel;
    178 
    179             cursorMoved = false;
    180         }
    181 
    182         synchronized (eventQueue){
    183             eventQueueCopy.clear();
    184             eventQueueCopy.addAll(eventQueue);
    185             eventQueue.clear();
    186         }
    187 
    188         int size = eventQueueCopy.size();
    189         for (int i = 0; i < size; i++){
    190             listener.onMouseButtonEvent(eventQueueCopy.get(i));
    191         }
    192     }
    193 
    194     private Cursor getTransparentCursor() {
    195         if (transparentCursor == null){
    196             BufferedImage cursorImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
    197             cursorImage.setRGB(0, 0, 0);
    198             transparentCursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImage, new Point(0, 0), "empty cursor");
    199         }
    200         return transparentCursor;
    201     }
    202 
    203 //	public void setHardwareCursor(URL file, int xHotspot, int yHotspot) {
    204 //	    //Create the image from the provided url
    205 //	    java.awt.Image cursorImage = new ImageIcon( file ).getImage( );
    206 //	    //Create a custom cursor with this image
    207 //	    opaqueCursor = Toolkit.getDefaultToolkit().createCustomCursor( cursorImage , new Point( xHotspot , yHotspot ) , "custom cursor" );
    208 //	    //Use this cursor
    209 //	    setCursorVisible( isCursorVisible );
    210 //	}
    211 
    212 
    213     public int getButtonCount() {
    214         return 3;
    215     }
    216 
    217     public void mouseClicked(MouseEvent arg0) {
    218 //        MouseButtonEvent evt = new MouseButtonEvent(getJMEButtonIndex(arg0), false);
    219 //        listener.onMouseButtonEvent(evt);
    220     }
    221 
    222     public void mousePressed(MouseEvent arg0) {
    223         MouseButtonEvent evt = new MouseButtonEvent(getJMEButtonIndex(arg0), true, arg0.getX(), arg0.getY());
    224         evt.setTime(arg0.getWhen());
    225         synchronized (eventQueue){
    226             eventQueue.add(evt);
    227         }
    228     }
    229 
    230     public void mouseReleased(MouseEvent arg0) {
    231         MouseButtonEvent evt = new MouseButtonEvent(getJMEButtonIndex(arg0), false, arg0.getX(), arg0.getY());
    232         evt.setTime(arg0.getWhen());
    233         synchronized (eventQueue){
    234             eventQueue.add(evt);
    235         }
    236     }
    237 
    238     public void mouseEntered(MouseEvent arg0) {
    239         if (!visible)
    240             recenterMouse(arg0.getComponent());
    241     }
    242 
    243     public void mouseExited(MouseEvent arg0) {
    244         if (!visible)
    245             recenterMouse(arg0.getComponent());
    246     }
    247 
    248     public void mouseWheelMoved(MouseWheelEvent arg0) {
    249         int dwheel = arg0.getUnitsToScroll();
    250         wheelPos += dwheel * WHEEL_AMP;
    251         cursorMoved = true;
    252     }
    253 
    254     public void mouseDragged(MouseEvent arg0) {
    255         mouseMoved(arg0);
    256     }
    257 
    258     public void mouseMoved(MouseEvent arg0) {
    259         if (isRecentering) {
    260             // MHenze (cylab) Fix Issue 35:
    261             // As long as the MouseInput is in recentering mode, nothing is done until the mouse is entered in the component
    262             // by the events generated by the robot. If this happens, the last known location is resetted.
    263             if ((centerLocation.x == arg0.getX() && centerLocation.y == arg0.getY()) || eventsSinceRecenter++ == 5) {
    264                 lastKnownLocation.x = arg0.getX();
    265                 lastKnownLocation.y = arg0.getY();
    266                 isRecentering = false;
    267             }
    268         } else {
    269             // MHenze (cylab) Fix Issue 35:
    270             // Compute the delta and absolute coordinates and recenter the mouse if necessary
    271             int dx = arg0.getX() - lastKnownLocation.x;
    272             int dy = arg0.getY() - lastKnownLocation.y;
    273             location.x += dx;
    274             location.y += dy;
    275             if (!visible) {
    276                 recenterMouse(arg0.getComponent());
    277             }
    278             lastKnownLocation.x = arg0.getX();
    279             lastKnownLocation.y = arg0.getY();
    280 
    281             cursorMoved = true;
    282         }
    283     }
    284 
    285     // MHenze (cylab) Fix Issue 35: A method to generate recenter the mouse to allow the InputSystem to "grab" the mouse
    286     private void recenterMouse(final Component component) {
    287         if (robot != null) {
    288             eventsSinceRecenter = 0;
    289             isRecentering = true;
    290             centerLocation.setLocation(component.getWidth()/2, component.getHeight()/2);
    291             centerLocationOnScreen.setLocation(centerLocation);
    292             SwingUtilities.convertPointToScreen(centerLocationOnScreen, component);
    293             robot.mouseMove(centerLocationOnScreen.x, centerLocationOnScreen.y);
    294         }
    295     }
    296 
    297     private int getJMEButtonIndex( MouseEvent arg0 ) {
    298         int index;
    299         switch (arg0.getButton()) {
    300             default:
    301             case MouseEvent.BUTTON1: //left
    302                 index = MouseInput.BUTTON_LEFT;
    303                 break;
    304             case MouseEvent.BUTTON2: //middle
    305                 index = MouseInput.BUTTON_MIDDLE;
    306                 break;
    307             case MouseEvent.BUTTON3: //right
    308                 index = MouseInput.BUTTON_RIGHT;
    309                 break;
    310         }
    311         return index;
    312     }
    313 }
    314