1 /*
2  * Copyright (c) 2002-2008 LWJGL Project
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 'LWJGL' nor the names of
17  *   its contributors may be used to endorse or promote products derived
18  *   from this software 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 package org.lwjgl.opengl;
33 
34 /**
35  * An AWT implementation of a LWJGL compatible Mouse event queue.
36  * @author elias_naur
37  */
38 
39 import java.awt.Component;
40 import java.awt.Point;
41 import java.awt.event.MouseEvent;
42 import java.awt.event.MouseListener;
43 import java.awt.event.MouseMotionListener;
44 import java.awt.event.MouseWheelEvent;
45 import java.awt.event.MouseWheelListener;
46 import java.nio.ByteBuffer;
47 import java.nio.IntBuffer;
48 
49 import org.lwjgl.input.Mouse;
50 
51 class MouseEventQueue extends EventQueue implements MouseListener, MouseMotionListener, MouseWheelListener {
52 	private static final int WHEEL_SCALE = 120;
53 	public static final int NUM_BUTTONS = 3;
54 
55 	private final Component component;
56 
57 	private boolean grabbed;
58 
59 	/** The accumulated mouse deltas returned by poll() */
60 	private int accum_dx;
61 	private int accum_dy;
62 	private int accum_dz;
63 
64 	/** The last mouse position */
65 	private int last_x;
66 	private int last_y;
67 
68 	/** Saved control key state for ctrl-click right button emulation */
69 	private boolean saved_control_state;
70 
71 	/** Event scratch array */
72 	private final ByteBuffer event = ByteBuffer.allocate(Mouse.EVENT_SIZE);
73 
74 	/** Buttons array */
75 	private final byte[] buttons = new byte[NUM_BUTTONS];
76 
MouseEventQueue(Component component)77 	MouseEventQueue(Component component) {
78 		super(Mouse.EVENT_SIZE);
79 		this.component = component;
80 	}
81 
register()82 	public synchronized void register() {
83 		resetCursorToCenter();
84         if (component != null) {
85             component.addMouseListener(this);
86             component.addMouseMotionListener(this);
87             component.addMouseWheelListener(this);
88         }
89 	}
90 
unregister()91 	public synchronized void unregister() {
92         if (component != null) {
93             component.removeMouseListener(this);
94             component.removeMouseMotionListener(this);
95             component.removeMouseWheelListener(this);
96         }
97 	}
98 
getComponent()99 	protected Component getComponent() {
100 		return component;
101 	}
102 
setGrabbed(boolean grabbed)103 	public synchronized void setGrabbed(boolean grabbed) {
104 		this.grabbed = grabbed;
105 		resetCursorToCenter();
106 	}
107 
isGrabbed()108 	public synchronized boolean isGrabbed() {
109 		return grabbed;
110 	}
111 
transformY(int y)112 	protected int transformY(int y) {
113         if (component != null) {
114             return component.getHeight() - 1 - y;
115         }
116         return y;
117 	}
118 
resetCursorToCenter()119 	protected void resetCursorToCenter() {
120 		clearEvents();
121 		accum_dx = accum_dy = 0;
122         if (component != null) {
123             Point cursor_location = AWTUtil.getCursorPosition(component);
124             if (cursor_location != null) {
125                 last_x = cursor_location.x;
126                 last_y = cursor_location.y;
127             }
128         }
129 	}
130 
putMouseEvent(byte button, byte state, int dz, long nanos)131 	private void putMouseEvent(byte button, byte state, int dz, long nanos) {
132 		if (grabbed)
133 			putMouseEventWithCoords(button, state, 0, 0, dz, nanos);
134 		else
135 			putMouseEventWithCoords(button, state, last_x, last_y, dz, nanos);
136 	}
137 
putMouseEventWithCoords(byte button, byte state, int coord1, int coord2, int dz, long nanos)138 	protected void putMouseEventWithCoords(byte button, byte state, int coord1, int coord2, int dz, long nanos) {
139 		event.clear();
140 		event.put(button).put(state).putInt(coord1).putInt(coord2).putInt(dz).putLong(nanos);
141 		event.flip();
142 		putEvent(event);
143 	}
144 
poll(IntBuffer coord_buffer, ByteBuffer buttons_buffer)145 	public synchronized void poll(IntBuffer coord_buffer, ByteBuffer buttons_buffer) {
146 		if ( grabbed ) {
147 			coord_buffer.put(0, accum_dx);
148 			coord_buffer.put(1, accum_dy);
149 		} else {
150 			coord_buffer.put(0, last_x);
151 			coord_buffer.put(1, last_y);
152 		}
153 		coord_buffer.put(2, accum_dz);
154 		accum_dx = accum_dy = accum_dz = 0;
155 		int old_position = buttons_buffer.position();
156 		buttons_buffer.put(buttons, 0, buttons.length);
157 		buttons_buffer.position(old_position);
158 	}
159 
setCursorPos(int x, int y, long nanos)160 	private void setCursorPos(int x, int y, long nanos) {
161 		y = transformY(y);
162 		if ( grabbed )
163 			return;
164 		int dx = x - last_x;
165 		int dy = y - last_y;
166 		addDelta(dx, dy);
167 		last_x = x;
168 		last_y = y;
169 		putMouseEventWithCoords((byte)-1, (byte)0, x, y, 0, nanos);
170 	}
171 
addDelta(int dx, int dy)172 	protected void addDelta(int dx, int dy) {
173 		accum_dx += dx;
174 		accum_dy += dy;
175 	}
176 
mouseClicked(MouseEvent e)177 	public void mouseClicked(MouseEvent e) {
178 	}
179 
mouseEntered(MouseEvent e)180 	public void mouseEntered(MouseEvent e) {
181 	}
182 
mouseExited(MouseEvent e)183 	public void mouseExited(MouseEvent e) {
184 	}
185 
handleButton(MouseEvent e)186 	private void handleButton(MouseEvent e) {
187 		byte state;
188 		switch (e.getID()) {
189 			case MouseEvent.MOUSE_PRESSED:
190 				state = 1;
191 				break;
192 			case MouseEvent.MOUSE_RELEASED:
193 				state = 0;
194 				break;
195 			default:
196 				throw new IllegalArgumentException("Not a valid event ID: " + e.getID());
197 		}
198 		byte button;
199 		switch (e.getButton()) {
200 			case MouseEvent.NOBUTTON:
201 				// Nothing to do, so return
202 				return;
203 			case MouseEvent.BUTTON1:
204 				// Emulate right click if ctrl is down
205 				if (state == 1)
206 					saved_control_state = e.isControlDown();
207 				if (saved_control_state) {
208 					if (buttons[1] == state)
209 						return; // ignore
210 					button = (byte)1;
211 				} else {
212 					button = (byte)0;
213 				}
214 				break;
215 			case MouseEvent.BUTTON2:
216 				button = (byte)2;
217 				break;
218 			case MouseEvent.BUTTON3:
219 				if (buttons[1] == state)
220 					return; // ignore
221 				button = (byte)1;
222 				break;
223 			default:
224 				throw new IllegalArgumentException("Not a valid button: " + e.getButton());
225 		}
226 		setButton(button, state, e.getWhen()*1000000);
227 	}
228 
mousePressed(MouseEvent e)229 	public synchronized void mousePressed(MouseEvent e) {
230 		handleButton(e);
231 	}
232 
setButton(byte button, byte state, long nanos)233 	private void setButton(byte button, byte state, long nanos) {
234 		buttons[button] = state;
235 		putMouseEvent(button, state, 0, nanos);
236 	}
237 
mouseReleased(MouseEvent e)238 	public synchronized void mouseReleased(MouseEvent e) {
239 		handleButton(e);
240 	}
241 
handleMotion(MouseEvent e)242 	private void handleMotion(MouseEvent e) {
243 		if (grabbed) {
244 			updateDeltas(e.getWhen()*1000000);
245 		} else {
246 			setCursorPos(e.getX(), e.getY(), e.getWhen()*1000000);
247 		}
248 	}
249 
mouseDragged(MouseEvent e)250 	public synchronized void mouseDragged(MouseEvent e) {
251 		handleMotion(e);
252 	}
253 
mouseMoved(MouseEvent e)254 	public synchronized void mouseMoved(MouseEvent e) {
255 		handleMotion(e);
256 	}
257 
handleWheel(int amount, long nanos)258 	private void handleWheel(int amount, long nanos) {
259 		accum_dz += amount;
260 		putMouseEvent((byte)-1, (byte)0, amount, nanos);
261 	}
262 
updateDeltas(long nanos)263 	protected void updateDeltas(long nanos) {
264 	}
265 
mouseWheelMoved(MouseWheelEvent e)266 	public synchronized void mouseWheelMoved(MouseWheelEvent e) {
267 		int wheel_amount = -e.getWheelRotation() * WHEEL_SCALE;
268 		handleWheel(wheel_amount, e.getWhen()*1000000);
269 	}
270 }
271