|
|
@ -29,7 +29,6 @@ |
|
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
package com.jme3.input.lwjgl; |
|
|
|
package com.jme3.input.lwjgl; |
|
|
|
|
|
|
|
|
|
|
|
import com.jme3.cursors.plugins.JmeCursor; |
|
|
|
import com.jme3.cursors.plugins.JmeCursor; |
|
|
@ -39,26 +38,26 @@ import com.jme3.input.event.MouseButtonEvent; |
|
|
|
import com.jme3.input.event.MouseMotionEvent; |
|
|
|
import com.jme3.input.event.MouseMotionEvent; |
|
|
|
import com.jme3.system.lwjgl.LwjglWindow; |
|
|
|
import com.jme3.system.lwjgl.LwjglWindow; |
|
|
|
import com.jme3.util.BufferUtils; |
|
|
|
import com.jme3.util.BufferUtils; |
|
|
|
import org.lwjgl.glfw.GLFWCursorPosCallback; |
|
|
|
|
|
|
|
import org.lwjgl.glfw.GLFWMouseButtonCallback; |
|
|
|
|
|
|
|
import org.lwjgl.glfw.GLFWScrollCallback; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import java.nio.ByteBuffer; |
|
|
|
import java.nio.ByteBuffer; |
|
|
|
import java.nio.IntBuffer; |
|
|
|
import java.nio.IntBuffer; |
|
|
|
|
|
|
|
import java.util.ArrayDeque; |
|
|
|
import java.util.HashMap; |
|
|
|
import java.util.HashMap; |
|
|
|
import java.util.LinkedList; |
|
|
|
|
|
|
|
import java.util.Map; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Queue; |
|
|
|
import java.util.Queue; |
|
|
|
import java.util.logging.Logger; |
|
|
|
import java.util.logging.Logger; |
|
|
|
|
|
|
|
|
|
|
|
import static org.lwjgl.glfw.GLFW.*; |
|
|
|
import static org.lwjgl.glfw.GLFW.*; |
|
|
|
|
|
|
|
import org.lwjgl.glfw.GLFWCursorPosCallback; |
|
|
|
import org.lwjgl.glfw.GLFWImage; |
|
|
|
import org.lwjgl.glfw.GLFWImage; |
|
|
|
|
|
|
|
import org.lwjgl.glfw.GLFWMouseButtonCallback; |
|
|
|
|
|
|
|
import org.lwjgl.glfw.GLFWScrollCallback; |
|
|
|
import org.lwjgl.system.MemoryUtil; |
|
|
|
import org.lwjgl.system.MemoryUtil; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Captures mouse input using GLFW callbacks. It then temporarily stores these in event queues which are processed in the |
|
|
|
* Captures mouse input using GLFW callbacks. It then temporarily stores these |
|
|
|
* {@link #update()} method. Due to some of the GLFW button id's there is a conversion method in this class which will |
|
|
|
* in event queues which are processed in the {@link #update()} method. Due to |
|
|
|
* convert the GLFW left, middle and right mouse button to JME3 left, middle and right button codes. |
|
|
|
* some of the GLFW button id's there is a conversion method in this class which |
|
|
|
|
|
|
|
* will convert the GLFW left, middle and right mouse button to JME3 left, |
|
|
|
|
|
|
|
* middle and right button codes. |
|
|
|
* |
|
|
|
* |
|
|
|
* @author Daniel Johansson (dannyjo) |
|
|
|
* @author Daniel Johansson (dannyjo) |
|
|
|
* @since 3.1 |
|
|
|
* @since 3.1 |
|
|
@ -69,9 +68,13 @@ public class GlfwMouseInput implements MouseInput { |
|
|
|
|
|
|
|
|
|
|
|
private static final int WHEEL_SCALE = 120; |
|
|
|
private static final int WHEEL_SCALE = 120; |
|
|
|
|
|
|
|
|
|
|
|
private LwjglWindow context; |
|
|
|
private final LwjglWindow context; |
|
|
|
private RawInputListener listener; |
|
|
|
private RawInputListener listener; |
|
|
|
private boolean cursorVisible = true; |
|
|
|
private boolean cursorVisible = true; |
|
|
|
|
|
|
|
private long[] currentCursor; |
|
|
|
|
|
|
|
private IntBuffer currentCursorDelays; |
|
|
|
|
|
|
|
private int currentCursorFrame = 0; |
|
|
|
|
|
|
|
private double currentCursorFrameStartTime = 0.0; |
|
|
|
private int mouseX; |
|
|
|
private int mouseX; |
|
|
|
private int mouseY; |
|
|
|
private int mouseY; |
|
|
|
private int mouseWheel; |
|
|
|
private int mouseWheel; |
|
|
@ -79,10 +82,10 @@ public class GlfwMouseInput implements MouseInput { |
|
|
|
private GLFWCursorPosCallback cursorPosCallback; |
|
|
|
private GLFWCursorPosCallback cursorPosCallback; |
|
|
|
private GLFWScrollCallback scrollCallback; |
|
|
|
private GLFWScrollCallback scrollCallback; |
|
|
|
private GLFWMouseButtonCallback mouseButtonCallback; |
|
|
|
private GLFWMouseButtonCallback mouseButtonCallback; |
|
|
|
private Queue<MouseMotionEvent> mouseMotionEvents = new LinkedList<MouseMotionEvent>(); |
|
|
|
private final Queue<MouseMotionEvent> mouseMotionEvents = new ArrayDeque<>(); |
|
|
|
private Queue<MouseButtonEvent> mouseButtonEvents = new LinkedList<MouseButtonEvent>(); |
|
|
|
private final Queue<MouseButtonEvent> mouseButtonEvents = new ArrayDeque<>(); |
|
|
|
|
|
|
|
|
|
|
|
private Map<JmeCursor, Long> jmeToGlfwCursorMap = new HashMap<JmeCursor, Long>(); |
|
|
|
private final Map<JmeCursor, long[]> jmeToGlfwCursorMap = new HashMap<>(); |
|
|
|
|
|
|
|
|
|
|
|
public GlfwMouseInput(LwjglWindow context) { |
|
|
|
public GlfwMouseInput(LwjglWindow context) { |
|
|
|
this.context = context; |
|
|
|
this.context = context; |
|
|
@ -127,6 +130,7 @@ public class GlfwMouseInput implements MouseInput { |
|
|
|
mouseButtonEvents.add(mouseButtonEvent); |
|
|
|
mouseButtonEvents.add(mouseButtonEvent); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
public void initialize() { |
|
|
|
public void initialize() { |
|
|
|
glfwSetCursorPosCallback(context.getWindowHandle(), cursorPosCallback = new GLFWCursorPosCallback() { |
|
|
|
glfwSetCursorPosCallback(context.getWindowHandle(), cursorPosCallback = new GLFWCursorPosCallback() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
@ -150,6 +154,7 @@ public class GlfwMouseInput implements MouseInput { |
|
|
|
public void invoke(final long window, final double xOffset, final double yOffset) { |
|
|
|
public void invoke(final long window, final double xOffset, final double yOffset) { |
|
|
|
onWheelScroll(window, xOffset, yOffset * WHEEL_SCALE); |
|
|
|
onWheelScroll(window, xOffset, yOffset * WHEEL_SCALE); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void close() { |
|
|
|
public void close() { |
|
|
|
super.close(); |
|
|
|
super.close(); |
|
|
@ -166,6 +171,7 @@ public class GlfwMouseInput implements MouseInput { |
|
|
|
public void invoke(final long window, final int button, final int action, final int mods) { |
|
|
|
public void invoke(final long window, final int button, final int action, final int mods) { |
|
|
|
onMouseButton(window, button, action, mods); |
|
|
|
onMouseButton(window, button, action, mods); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void close() { |
|
|
|
public void close() { |
|
|
|
super.close(); |
|
|
|
super.close(); |
|
|
@ -182,15 +188,31 @@ public class GlfwMouseInput implements MouseInput { |
|
|
|
initialized = true; |
|
|
|
initialized = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
public boolean isInitialized() { |
|
|
|
public boolean isInitialized() { |
|
|
|
return initialized; |
|
|
|
return initialized; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
public int getButtonCount() { |
|
|
|
public int getButtonCount() { |
|
|
|
return GLFW_MOUSE_BUTTON_LAST + 1; |
|
|
|
return GLFW_MOUSE_BUTTON_LAST + 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
public void update() { |
|
|
|
public void update() { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Manage cursor animation
|
|
|
|
|
|
|
|
if (currentCursor != null && currentCursor.length > 1) { |
|
|
|
|
|
|
|
double now = glfwGetTime(); |
|
|
|
|
|
|
|
double frameTime = (glfwGetTime() - currentCursorFrameStartTime) * 1000; |
|
|
|
|
|
|
|
if (currentCursorDelays == null || frameTime >= currentCursorDelays.get(currentCursorFrame)) { |
|
|
|
|
|
|
|
currentCursorFrame = ++currentCursorFrame % currentCursor.length; |
|
|
|
|
|
|
|
currentCursorFrameStartTime = now; |
|
|
|
|
|
|
|
glfwSetCursor(context.getWindowHandle(), currentCursor[currentCursorFrame]); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Process events
|
|
|
|
while (!mouseMotionEvents.isEmpty()) { |
|
|
|
while (!mouseMotionEvents.isEmpty()) { |
|
|
|
listener.onMouseMotionEvent(mouseMotionEvents.poll()); |
|
|
|
listener.onMouseMotionEvent(mouseMotionEvents.poll()); |
|
|
|
} |
|
|
|
} |
|
|
@ -200,6 +222,7 @@ public class GlfwMouseInput implements MouseInput { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
public void destroy() { |
|
|
|
public void destroy() { |
|
|
|
if (!context.isRenderable()) { |
|
|
|
if (!context.isRenderable()) { |
|
|
|
return; |
|
|
|
return; |
|
|
@ -209,13 +232,19 @@ public class GlfwMouseInput implements MouseInput { |
|
|
|
scrollCallback.close(); |
|
|
|
scrollCallback.close(); |
|
|
|
mouseButtonCallback.close(); |
|
|
|
mouseButtonCallback.close(); |
|
|
|
|
|
|
|
|
|
|
|
for (long glfwCursor : jmeToGlfwCursorMap.values()) { |
|
|
|
currentCursor = null; |
|
|
|
glfwDestroyCursor(glfwCursor); |
|
|
|
currentCursorDelays = null; |
|
|
|
|
|
|
|
for (long[] glfwCursors : jmeToGlfwCursorMap.values()) { |
|
|
|
|
|
|
|
for (long glfwCursor : glfwCursors) { |
|
|
|
|
|
|
|
glfwDestroyCursor(glfwCursor); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
jmeToGlfwCursorMap.clear(); |
|
|
|
|
|
|
|
|
|
|
|
logger.fine("Mouse destroyed."); |
|
|
|
logger.fine("Mouse destroyed."); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
public void setCursorVisible(boolean visible) { |
|
|
|
public void setCursorVisible(boolean visible) { |
|
|
|
cursorVisible = visible; |
|
|
|
cursorVisible = visible; |
|
|
|
|
|
|
|
|
|
|
@ -230,24 +259,26 @@ public class GlfwMouseInput implements MouseInput { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
public void setInputListener(RawInputListener listener) { |
|
|
|
public void setInputListener(RawInputListener listener) { |
|
|
|
this.listener = listener; |
|
|
|
this.listener = listener; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
public long getInputTimeNanos() { |
|
|
|
public long getInputTimeNanos() { |
|
|
|
return (long) (glfwGetTime() * 1000000000); |
|
|
|
return (long) (glfwGetTime() * 1000000000); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private ByteBuffer transformCursorImage(IntBuffer imageData, int w, int h) { |
|
|
|
private ByteBuffer transformCursorImage(IntBuffer imageData, int w, int h, int index) { |
|
|
|
ByteBuffer buf = BufferUtils.createByteBuffer(imageData.capacity() * 4); |
|
|
|
ByteBuffer buf = BufferUtils.createByteBuffer(w * h * 4); |
|
|
|
|
|
|
|
|
|
|
|
// Transform image: ARGB -> RGBA, vertical flip
|
|
|
|
// Transform image: ARGB -> RGBA, vertical flip
|
|
|
|
for (int y = h-1; y >= 0; --y) { |
|
|
|
for (int y = h - 1; y >= 0; --y) { |
|
|
|
for (int x = 0; x < w; ++x) { |
|
|
|
for (int x = 0; x < w; ++x) { |
|
|
|
int pixel = imageData.get(y*w + x); |
|
|
|
int pixel = imageData.get(w * h * index + y * w + x); |
|
|
|
buf.put((byte) ((pixel >> 16) & 0xFF)); // red
|
|
|
|
buf.put((byte) ((pixel >> 16) & 0xFF)); // red
|
|
|
|
buf.put((byte) ((pixel >> 8) & 0xFF)); // green
|
|
|
|
buf.put((byte) ((pixel >> 8) & 0xFF)); // green
|
|
|
|
buf.put((byte) ( pixel & 0xFF)); // blue
|
|
|
|
buf.put((byte) (pixel & 0xFF)); // blue
|
|
|
|
buf.put((byte) ((pixel >> 24) & 0xFF)); // alpha
|
|
|
|
buf.put((byte) ((pixel >> 24) & 0xFF)); // alpha
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -256,30 +287,43 @@ public class GlfwMouseInput implements MouseInput { |
|
|
|
return buf; |
|
|
|
return buf; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private long createGlfwCursor(JmeCursor jmeCursor) { |
|
|
|
private long[] createGlfwCursor(JmeCursor jmeCursor) { |
|
|
|
// TODO: currently animated cursors are not supported
|
|
|
|
long[] cursorArray = new long[jmeCursor.getNumImages()]; |
|
|
|
IntBuffer imageData = jmeCursor.getImagesData(); |
|
|
|
for (int i = 0; i < jmeCursor.getNumImages(); i++) { |
|
|
|
ByteBuffer buf = transformCursorImage(imageData, jmeCursor.getWidth(), jmeCursor.getHeight()); |
|
|
|
ByteBuffer buf = transformCursorImage(jmeCursor.getImagesData(), jmeCursor.getWidth(), jmeCursor.getHeight(), i); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
GLFWImage glfwImage = new GLFWImage(BufferUtils.createByteBuffer(GLFWImage.SIZEOF)); |
|
|
|
|
|
|
|
glfwImage.set(jmeCursor.getWidth(), jmeCursor.getHeight(), buf); |
|
|
|
|
|
|
|
|
|
|
|
GLFWImage glfwImage = new GLFWImage(BufferUtils.createByteBuffer(GLFWImage.SIZEOF)); |
|
|
|
int hotspotX = jmeCursor.getXHotSpot(); |
|
|
|
glfwImage.set(jmeCursor.getWidth(), jmeCursor.getHeight(), buf); |
|
|
|
int hotspotY = jmeCursor.getHeight() - jmeCursor.getYHotSpot(); |
|
|
|
|
|
|
|
|
|
|
|
int hotspotX = jmeCursor.getXHotSpot(); |
|
|
|
cursorArray[i] = glfwCreateCursor(glfwImage, hotspotX, hotspotY); |
|
|
|
int hotspotY = jmeCursor.getHeight() - jmeCursor.getYHotSpot(); |
|
|
|
} |
|
|
|
return glfwCreateCursor(glfwImage, hotspotX, hotspotY); |
|
|
|
return cursorArray; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
public void setNativeCursor(JmeCursor jmeCursor) { |
|
|
|
public void setNativeCursor(JmeCursor jmeCursor) { |
|
|
|
if (jmeCursor != null) { |
|
|
|
if (jmeCursor != null) { |
|
|
|
Long glfwCursor = jmeToGlfwCursorMap.get(jmeCursor); |
|
|
|
long[] glfwCursor = jmeToGlfwCursorMap.get(jmeCursor); |
|
|
|
|
|
|
|
|
|
|
|
if (glfwCursor == null) { |
|
|
|
if (glfwCursor == null) { |
|
|
|
glfwCursor = createGlfwCursor(jmeCursor); |
|
|
|
glfwCursor = createGlfwCursor(jmeCursor); |
|
|
|
jmeToGlfwCursorMap.put(jmeCursor, glfwCursor); |
|
|
|
jmeToGlfwCursorMap.put(jmeCursor, glfwCursor); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
glfwSetCursor(context.getWindowHandle(), glfwCursor); |
|
|
|
currentCursorFrame = 0; |
|
|
|
|
|
|
|
currentCursor = glfwCursor; |
|
|
|
|
|
|
|
currentCursorDelays = null; |
|
|
|
|
|
|
|
currentCursorFrameStartTime = glfwGetTime(); |
|
|
|
|
|
|
|
if (jmeCursor.getImagesDelay() != null) { |
|
|
|
|
|
|
|
currentCursorDelays = jmeCursor.getImagesDelay(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
glfwSetCursor(context.getWindowHandle(), glfwCursor[currentCursorFrame]); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
|
|
|
|
currentCursor = null; |
|
|
|
|
|
|
|
currentCursorDelays = null; |
|
|
|
glfwSetCursor(context.getWindowHandle(), MemoryUtil.NULL); |
|
|
|
glfwSetCursor(context.getWindowHandle(), MemoryUtil.NULL); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|