2929 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3030 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3131 */
32-
3332package com .jme3 .input .lwjgl ;
3433
3534import com .jme3 .cursors .plugins .JmeCursor ;
3938import com .jme3 .input .event .MouseMotionEvent ;
4039import com .jme3 .system .lwjgl .LwjglWindow ;
4140import com .jme3 .util .BufferUtils ;
42- import org .lwjgl .glfw .GLFWCursorPosCallback ;
43- import org .lwjgl .glfw .GLFWMouseButtonCallback ;
44- import org .lwjgl .glfw .GLFWScrollCallback ;
45-
4641import java .nio .ByteBuffer ;
4742import java .nio .IntBuffer ;
43+ import java .util .ArrayDeque ;
4844import java .util .HashMap ;
49- import java .util .LinkedList ;
5045import java .util .Map ;
5146import java .util .Queue ;
5247import java .util .logging .Logger ;
53-
5448import static org .lwjgl .glfw .GLFW .*;
49+ import org .lwjgl .glfw .GLFWCursorPosCallback ;
5550import org .lwjgl .glfw .GLFWImage ;
51+ import org .lwjgl .glfw .GLFWMouseButtonCallback ;
52+ import org .lwjgl .glfw .GLFWScrollCallback ;
5653import org .lwjgl .system .MemoryUtil ;
5754
5855/**
59- * Captures mouse input using GLFW callbacks. It then temporarily stores these in event queues which are processed in the
60- * {@link #update()} method. Due to some of the GLFW button id's there is a conversion method in this class which will
61- * convert the GLFW left, middle and right mouse button to JME3 left, middle and right button codes.
56+ * Captures mouse input using GLFW callbacks. It then temporarily stores these
57+ * in event queues which are processed in the {@link #update()} method. Due to
58+ * some of the GLFW button id's there is a conversion method in this class which
59+ * will convert the GLFW left, middle and right mouse button to JME3 left,
60+ * middle and right button codes.
6261 *
6362 * @author Daniel Johansson (dannyjo)
6463 * @since 3.1
@@ -69,20 +68,24 @@ public class GlfwMouseInput implements MouseInput {
6968
7069 private static final int WHEEL_SCALE = 120 ;
7170
72- private LwjglWindow context ;
71+ private final LwjglWindow context ;
7372 private RawInputListener listener ;
7473 private boolean cursorVisible = true ;
74+ private long [] currentCursor ;
75+ private IntBuffer currentCursorDelays ;
76+ private int currentCursorFrame = 0 ;
77+ private double currentCursorFrameStartTime = 0.0 ;
7578 private int mouseX ;
7679 private int mouseY ;
7780 private int mouseWheel ;
7881 private boolean initialized ;
7982 private GLFWCursorPosCallback cursorPosCallback ;
8083 private GLFWScrollCallback scrollCallback ;
8184 private GLFWMouseButtonCallback mouseButtonCallback ;
82- private Queue <MouseMotionEvent > mouseMotionEvents = new LinkedList < MouseMotionEvent >();
83- private Queue <MouseButtonEvent > mouseButtonEvents = new LinkedList < MouseButtonEvent >();
85+ private final Queue <MouseMotionEvent > mouseMotionEvents = new ArrayDeque < >();
86+ private final Queue <MouseButtonEvent > mouseButtonEvents = new ArrayDeque < >();
8487
85- private Map <JmeCursor , Long > jmeToGlfwCursorMap = new HashMap <JmeCursor , Long >();
88+ private final Map <JmeCursor , long [] > jmeToGlfwCursorMap = new HashMap <>();
8689
8790 public GlfwMouseInput (LwjglWindow context ) {
8891 this .context = context ;
@@ -127,6 +130,7 @@ private void onMouseButton(final long window, final int button, final int action
127130 mouseButtonEvents .add (mouseButtonEvent );
128131 }
129132
133+ @ Override
130134 public void initialize () {
131135 glfwSetCursorPosCallback (context .getWindowHandle (), cursorPosCallback = new GLFWCursorPosCallback () {
132136 @ Override
@@ -150,6 +154,7 @@ public void callback(long args) {
150154 public void invoke (final long window , final double xOffset , final double yOffset ) {
151155 onWheelScroll (window , xOffset , yOffset * WHEEL_SCALE );
152156 }
157+
153158 @ Override
154159 public void close () {
155160 super .close ();
@@ -166,6 +171,7 @@ public void callback(long args) {
166171 public void invoke (final long window , final int button , final int action , final int mods ) {
167172 onMouseButton (window , button , action , mods );
168173 }
174+
169175 @ Override
170176 public void close () {
171177 super .close ();
@@ -182,15 +188,31 @@ public void callback(long args) {
182188 initialized = true ;
183189 }
184190
191+ @ Override
185192 public boolean isInitialized () {
186193 return initialized ;
187194 }
188195
196+ @ Override
189197 public int getButtonCount () {
190198 return GLFW_MOUSE_BUTTON_LAST + 1 ;
191199 }
192200
201+ @ Override
193202 public void update () {
203+
204+ // Manage cursor animation
205+ if (currentCursor != null && currentCursor .length > 1 ) {
206+ double now = glfwGetTime ();
207+ double frameTime = (glfwGetTime () - currentCursorFrameStartTime ) * 1000 ;
208+ if (currentCursorDelays == null || frameTime >= currentCursorDelays .get (currentCursorFrame )) {
209+ currentCursorFrame = ++currentCursorFrame % currentCursor .length ;
210+ currentCursorFrameStartTime = now ;
211+ glfwSetCursor (context .getWindowHandle (), currentCursor [currentCursorFrame ]);
212+ }
213+ }
214+
215+ // Process events
194216 while (!mouseMotionEvents .isEmpty ()) {
195217 listener .onMouseMotionEvent (mouseMotionEvents .poll ());
196218 }
@@ -200,6 +222,7 @@ public void update() {
200222 }
201223 }
202224
225+ @ Override
203226 public void destroy () {
204227 if (!context .isRenderable ()) {
205228 return ;
@@ -209,13 +232,19 @@ public void destroy() {
209232 scrollCallback .close ();
210233 mouseButtonCallback .close ();
211234
212- for (long glfwCursor : jmeToGlfwCursorMap .values ()) {
213- glfwDestroyCursor (glfwCursor );
235+ currentCursor = null ;
236+ currentCursorDelays = null ;
237+ for (long [] glfwCursors : jmeToGlfwCursorMap .values ()) {
238+ for (long glfwCursor : glfwCursors ) {
239+ glfwDestroyCursor (glfwCursor );
240+ }
214241 }
242+ jmeToGlfwCursorMap .clear ();
215243
216244 logger .fine ("Mouse destroyed." );
217245 }
218246
247+ @ Override
219248 public void setCursorVisible (boolean visible ) {
220249 cursorVisible = visible ;
221250
@@ -230,24 +259,26 @@ public void setCursorVisible(boolean visible) {
230259 }
231260 }
232261
262+ @ Override
233263 public void setInputListener (RawInputListener listener ) {
234264 this .listener = listener ;
235265 }
236266
267+ @ Override
237268 public long getInputTimeNanos () {
238269 return (long ) (glfwGetTime () * 1000000000 );
239270 }
240271
241- private ByteBuffer transformCursorImage (IntBuffer imageData , int w , int h ) {
242- ByteBuffer buf = BufferUtils .createByteBuffer (imageData . capacity () * 4 );
272+ private ByteBuffer transformCursorImage (IntBuffer imageData , int w , int h , int index ) {
273+ ByteBuffer buf = BufferUtils .createByteBuffer (w * h * 4 );
243274
244275 // Transform image: ARGB -> RGBA, vertical flip
245- for (int y = h - 1 ; y >= 0 ; --y ) {
276+ for (int y = h - 1 ; y >= 0 ; --y ) {
246277 for (int x = 0 ; x < w ; ++x ) {
247- int pixel = imageData .get (y * w + x );
278+ int pixel = imageData .get (w * h * index + y * w + x );
248279 buf .put ((byte ) ((pixel >> 16 ) & 0xFF )); // red
249- buf .put ((byte ) ((pixel >> 8 ) & 0xFF )); // green
250- buf .put ((byte ) ( pixel & 0xFF )); // blue
280+ buf .put ((byte ) ((pixel >> 8 ) & 0xFF )); // green
281+ buf .put ((byte ) (pixel & 0xFF )); // blue
251282 buf .put ((byte ) ((pixel >> 24 ) & 0xFF )); // alpha
252283 }
253284 }
@@ -256,30 +287,43 @@ private ByteBuffer transformCursorImage(IntBuffer imageData, int w, int h) {
256287 return buf ;
257288 }
258289
259- private long createGlfwCursor (JmeCursor jmeCursor ) {
260- // TODO: currently animated cursors are not supported
261- IntBuffer imageData = jmeCursor .getImagesData ();
262- ByteBuffer buf = transformCursorImage (imageData , jmeCursor .getWidth (), jmeCursor .getHeight ());
290+ private long [] createGlfwCursor (JmeCursor jmeCursor ) {
291+ long [] cursorArray = new long [jmeCursor .getNumImages ()];
292+ for (int i = 0 ; i < jmeCursor .getNumImages (); i ++) {
293+ ByteBuffer buf = transformCursorImage (jmeCursor .getImagesData (), jmeCursor .getWidth (), jmeCursor .getHeight (), i );
294+
295+ GLFWImage glfwImage = new GLFWImage (BufferUtils .createByteBuffer (GLFWImage .SIZEOF ));
296+ glfwImage .set (jmeCursor .getWidth (), jmeCursor .getHeight (), buf );
263297
264- GLFWImage glfwImage = new GLFWImage ( BufferUtils . createByteBuffer ( GLFWImage . SIZEOF ) );
265- glfwImage . set ( jmeCursor .getWidth (), jmeCursor .getHeight (), buf );
298+ int hotspotX = jmeCursor . getXHotSpot ( );
299+ int hotspotY = jmeCursor .getHeight () - jmeCursor .getYHotSpot ( );
266300
267- int hotspotX = jmeCursor . getXHotSpot ( );
268- int hotspotY = jmeCursor . getHeight () - jmeCursor . getYHotSpot ();
269- return glfwCreateCursor ( glfwImage , hotspotX , hotspotY ) ;
301+ cursorArray [ i ] = glfwCreateCursor ( glfwImage , hotspotX , hotspotY );
302+ }
303+ return cursorArray ;
270304 }
271305
306+ @ Override
272307 public void setNativeCursor (JmeCursor jmeCursor ) {
273308 if (jmeCursor != null ) {
274- Long glfwCursor = jmeToGlfwCursorMap .get (jmeCursor );
309+ long [] glfwCursor = jmeToGlfwCursorMap .get (jmeCursor );
275310
276311 if (glfwCursor == null ) {
277312 glfwCursor = createGlfwCursor (jmeCursor );
278313 jmeToGlfwCursorMap .put (jmeCursor , glfwCursor );
279314 }
280315
281- glfwSetCursor (context .getWindowHandle (), glfwCursor );
316+ currentCursorFrame = 0 ;
317+ currentCursor = glfwCursor ;
318+ currentCursorDelays = null ;
319+ currentCursorFrameStartTime = glfwGetTime ();
320+ if (jmeCursor .getImagesDelay () != null ) {
321+ currentCursorDelays = jmeCursor .getImagesDelay ();
322+ }
323+ glfwSetCursor (context .getWindowHandle (), glfwCursor [currentCursorFrame ]);
282324 } else {
325+ currentCursor = null ;
326+ currentCursorDelays = null ;
283327 glfwSetCursor (context .getWindowHandle (), MemoryUtil .NULL );
284328 }
285329 }
0 commit comments