• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
AwtMouseInput()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 
setInputSource(Component comp)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 
initialize()125     public void initialize() {
126     }
127 
destroy()128     public void destroy() {
129     }
130 
isInitialized()131     public boolean isInitialized() {
132         return true;
133     }
134 
setInputListener(RawInputListener listener)135     public void setInputListener(RawInputListener listener){
136         this.listener = listener;
137     }
138 
getInputTimeNanos()139     public long getInputTimeNanos() {
140         return System.nanoTime();
141     }
142 
setCursorVisible(boolean visible)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 
update()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 
getTransparentCursor()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 
getButtonCount()213     public int getButtonCount() {
214         return 3;
215     }
216 
mouseClicked(MouseEvent arg0)217     public void mouseClicked(MouseEvent arg0) {
218 //        MouseButtonEvent evt = new MouseButtonEvent(getJMEButtonIndex(arg0), false);
219 //        listener.onMouseButtonEvent(evt);
220     }
221 
mousePressed(MouseEvent arg0)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 
mouseReleased(MouseEvent arg0)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 
mouseEntered(MouseEvent arg0)238     public void mouseEntered(MouseEvent arg0) {
239         if (!visible)
240             recenterMouse(arg0.getComponent());
241     }
242 
mouseExited(MouseEvent arg0)243     public void mouseExited(MouseEvent arg0) {
244         if (!visible)
245             recenterMouse(arg0.getComponent());
246     }
247 
mouseWheelMoved(MouseWheelEvent arg0)248     public void mouseWheelMoved(MouseWheelEvent arg0) {
249         int dwheel = arg0.getUnitsToScroll();
250         wheelPos += dwheel * WHEEL_AMP;
251         cursorMoved = true;
252     }
253 
mouseDragged(MouseEvent arg0)254     public void mouseDragged(MouseEvent arg0) {
255         mouseMoved(arg0);
256     }
257 
mouseMoved(MouseEvent arg0)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
recenterMouse(final Component component)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 
getJMEButtonIndex( MouseEvent arg0 )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