Add support for listening to joystick connection/disconnection.

Fixes bug causing NullPointerException when removing joysticks.
Allows adding joysticks after the application has started.
accellbaker
James Khan 6 years ago
parent c4d2de1656
commit 318d6d0e89
  1. 1962
      jme3-core/src/main/java/com/jme3/input/InputManager.java
  2. 7
      jme3-core/src/main/java/com/jme3/input/JoystickConnectionListener.java
  3. 47
      jme3-core/src/main/java/com/jme3/input/JoystickState.java
  4. 44
      jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwJoystickInput.java
  5. 9
      jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java

File diff suppressed because it is too large Load Diff

@ -0,0 +1,7 @@
package com.jme3.input;
public interface JoystickConnectionListener {
void connectionChanged(int joystickId, JoystickState action);
}

@ -0,0 +1,47 @@
package com.jme3.input;
/**
* Response for joystick callback events.
* @author jayfella
*/
public enum JoystickState {
// a list of connected/disconnected codes from various contexts.
// using the JoystickState.fromCode(int) method, if the code matches
// it will return the enum value.
CONNECTED(new int[] {
0x40001 // GLFW.GLFW_CONNECTED / LWJGL3
}),
DISCONNECTED(new int[] {
0x40002 // GLFW.GLFW_DISCONNECTED / LWJGL3
}),
UNKNOWN(new int[0]);
private int[] codes;
JoystickState(int[] codes) {
this.codes = codes;
}
private int[] getCodes() {
return codes;
}
public static JoystickState fromCode(int value) {
for (JoystickState state : values()) {
for (int code : state.getCodes()) {
if (value == code) {
return state;
}
}
}
return UNKNOWN;
}
}

@ -57,6 +57,8 @@ public class GlfwJoystickInput implements JoyInput {
private final Map<JoystickButton, Boolean> joyButtonPressed = new HashMap<>();
private InputManager inputManager;
private boolean initialized = false;
@Override
@ -66,8 +68,24 @@ public class GlfwJoystickInput implements JoyInput {
}
}
public void fireJoystickConnectionEvent(int jid, int event) {
inputManager.fireJoystickConnectionEvent(jid, event);
}
public void reloadJoysticks() {
joysticks.clear();
if (inputManager != null) {
Joystick[] joysticks = loadJoysticks(inputManager);
inputManager.setJoysticks(joysticks);
}
}
@Override
public Joystick[] loadJoysticks(final InputManager inputManager) {
this.inputManager = inputManager;
for (int i = 0; i < GLFW_JOYSTICK_LAST; i++) {
if (glfwJoystickPresent(i)) {
final String name = glfwGetJoystickName(i);
@ -126,23 +144,33 @@ public class GlfwJoystickInput implements JoyInput {
@Override
public void update() {
for (final Map.Entry<Integer, GlfwJoystick> entry : joysticks.entrySet()) {
// Axes
final FloatBuffer axisValues = glfwGetJoystickAxes(entry.getKey());
for (final JoystickAxis axis : entry.getValue().getAxes()) {
final float value = axisValues.get(axis.getAxisId());
listener.onJoyAxisEvent(new JoyAxisEvent(axis, value));
// if a joystick is added or removed, the callback reloads the joysticks.
// when the callback is called and reloads the joystick, this iterator may already have started iterating.
// To avoid a NullPointerException we null-check the axisValues and bytebuffer objects.
// If the joystick it's iterating over no-longer exists it will return null.
if (axisValues != null) {
for (final JoystickAxis axis : entry.getValue().getAxes()) {
final float value = axisValues.get(axis.getAxisId());
listener.onJoyAxisEvent(new JoyAxisEvent(axis, value));
}
}
// Buttons
final ByteBuffer byteBuffer = glfwGetJoystickButtons(entry.getKey());
for (final JoystickButton button : entry.getValue().getButtons()) {
final boolean pressed = byteBuffer.get(button.getButtonId()) == GLFW_PRESS;
if (byteBuffer != null) {
for (final JoystickButton button : entry.getValue().getButtons()) {
final boolean pressed = byteBuffer.get(button.getButtonId()) == GLFW_PRESS;
if (joyButtonPressed.get(button) != pressed) {
joyButtonPressed.put(button, pressed);
listener.onJoyButtonEvent(new JoyButtonEvent(button, pressed));
if (joyButtonPressed.get(button) != pressed) {
joyButtonPressed.put(button, pressed);
listener.onJoyButtonEvent(new JoyButtonEvent(button, pressed));
}
}
}
}

@ -65,6 +65,7 @@ import com.jme3.util.LWJGLBufferAllocator.ConcurrentLWJGLBufferAllocator;
import org.lwjgl.PointerBuffer;
import org.lwjgl.Version;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWJoystickCallback;
import org.lwjgl.opencl.APPLEGLSharing;
import org.lwjgl.opencl.CL10;
import org.lwjgl.opencl.KHRGLSharing;
@ -233,6 +234,14 @@ public abstract class LwjglContext implements JmeContext {
joyInput.initialize();
}
GLFW.glfwSetJoystickCallback(new GLFWJoystickCallback() {
@Override
public void invoke(int jid, int event) {
joyInput.reloadJoysticks();
joyInput.fireJoystickConnectionEvent(jid, event);
}
});
renderable.set(true);
}

Loading…
Cancel
Save