Finished Joystick Support

This commit is contained in:
Speiger 2024-05-17 17:44:15 +02:00
parent 618ccc1cc2
commit 52fcac6fe9
6 changed files with 140 additions and 32 deletions

View File

@ -19,7 +19,7 @@ public class NewInputTest {
Joystick.INSTANCE.init(bus); Joystick.INSTANCE.init(bus);
ButtonData data = new ButtonData(); ButtonData data = new ButtonData();
while(true) { while(true) {
Joystick.INSTANCE.handleJoystick(GLFW.GLFW_JOYSTICK_1, data); Joystick.INSTANCE.handleJoystick(0, GLFW.GLFW_JOYSTICK_1, data);
try { try {
Thread.sleep(100); Thread.sleep(100);
} }

View File

@ -49,6 +49,7 @@ public abstract class AbstractDevice<T, E> implements InputDevice {
protected abstract void process(E task); protected abstract void process(E task);
protected boolean pushEvent(Event event) { protected boolean pushEvent(Event event) {
if(bus == null) return true;
bus.post(event); bus.post(event);
return event.isCancelable() && event.isCanceled(); return event.isCancelable() && event.isCanceled();
} }

View File

@ -5,26 +5,30 @@ import java.nio.FloatBuffer;
import java.util.BitSet; import java.util.BitSet;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import org.lwjgl.system.Callback;
import speiger.src.collections.floats.lists.FloatArrayList; import speiger.src.collections.floats.lists.FloatArrayList;
import speiger.src.collections.floats.lists.FloatList; import speiger.src.collections.floats.lists.FloatList;
import speiger.src.collections.ints.collections.IntIterator;
import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
import speiger.src.collections.ints.sets.IntLinkedOpenHashSet; import speiger.src.collections.ints.sets.IntLinkedOpenHashSet;
import speiger.src.collections.ints.sets.IntSet; import speiger.src.collections.ints.sets.IntSet;
import speiger.src.collections.longs.collections.LongIterator; import speiger.src.collections.longs.collections.LongIterator;
import speiger.src.coreengine.rendering.input.devices.Joystick.JoyStickData; import speiger.src.coreengine.rendering.input.devices.Joystick.JoyStickData;
import speiger.src.coreengine.rendering.input.devices.Joystick.JoyStickTask; import speiger.src.coreengine.rendering.input.devices.Joystick.JoyStickTask;
import speiger.src.coreengine.rendering.input.events.JoystickEvent;
import speiger.src.coreengine.rendering.input.window.WindowManager;
import speiger.src.coreengine.utils.eventbus.EventBus; import speiger.src.coreengine.utils.eventbus.EventBus;
public class Joystick extends AbstractDevice<JoyStickData, JoyStickTask> { public class Joystick extends AbstractDevice<JoyStickData, JoyStickTask> {
public static final Joystick INSTANCE = new Joystick(); public static final Joystick INSTANCE = new Joystick();
WindowManager manager;
IntSet presentJoysticks = new IntLinkedOpenHashSet(); IntSet presentJoysticks = new IntLinkedOpenHashSet();
Callback callback;
@Override public void init(WindowManager manager, EventBus bus) {
public void init(EventBus bus) { this.manager = manager;
super.init(bus); super.init(bus);
callback = GLFW.glfwSetJoystickCallback(this::plugin); manager.addCallback(this::plugin, GLFW::glfwSetJoystickCallback);
for(int i = 0,m=GLFW.GLFW_JOYSTICK_LAST;i<=m;i++) { for(int i = 0,m=GLFW.GLFW_JOYSTICK_LAST;i<=m;i++) {
if(GLFW.glfwJoystickPresent(i)) { if(GLFW.glfwJoystickPresent(i)) {
presentJoysticks.add(i); presentJoysticks.add(i);
@ -32,9 +36,8 @@ public class Joystick extends AbstractDevice<JoyStickData, JoyStickTask> {
} }
} }
public void destroy() { @Override
callback.free(); public void init(EventBus bus) { throw new UnsupportedOperationException("Use init(WindowManager, EventBus) instead"); }
}
private void plugin(int jid, int event) { private void plugin(int jid, int event) {
if(event == GLFW.GLFW_CONNECTED) presentJoysticks.add(jid); if(event == GLFW.GLFW_CONNECTED) presentJoysticks.add(jid);
@ -55,28 +58,45 @@ public class Joystick extends AbstractDevice<JoyStickData, JoyStickTask> {
@Override @Override
public void processInput(long windowId) { public void processInput(long windowId) {
super.processInput(windowId); super.processInput(windowId);
if(manager.getActiveWindow() != windowId) return;
JoyStickData data = get(windowId);
if(data == null) return;
for(IntIterator iter = presentJoysticks.iterator();iter.hasNext();) {
int jid = iter.nextInt();
ButtonData buttons = data.data.get(jid);
if(buttons == null) continue;
handleJoystick(windowId, jid, buttons);
}
} }
public void handleJoystick(int jid, ButtonData data) { public void handleJoystick(long window, int jid, ButtonData data) {
ByteBuffer buttons = GLFW.glfwGetJoystickButtons(jid); ByteBuffer buttons = GLFW.glfwGetJoystickButtons(jid);
for(int i = 0;buttons.hasRemaining();i++) { for(int i = 0;buttons.hasRemaining();i++) {
data.buttons.set(i, buttons.get() == GLFW.GLFW_PRESS); boolean state = buttons.get() == GLFW.GLFW_PRESS;
if(data.buttons.get(i) != state) {
data.buttons.set(i, state);
pushEvent(new JoystickEvent.Button(window, jid, i, state));
}
} }
FloatBuffer axis = GLFW.glfwGetJoystickAxes(jid); FloatBuffer axis = GLFW.glfwGetJoystickAxes(jid);
for(int i = 0;axis.hasRemaining();i++) { for(int i = 0;axis.hasRemaining();i++) {
float state = axis.get(); float state = axis.get();
if(i >= data.axis.size()) data.axis.add(state); if(i >= data.axis.size()) data.axis.add(state);
else data.axis.set(i, state); else data.axis.set(i, state);
boolean negative = state < 0F;
boolean max = state < -0.95F || state > 0.95F; boolean max = state < -0.95F || state > 0.95F;
boolean min = state < -0.5F || state > 0.5F; boolean min = (state < -0.5F || state > 0.5F) && !max;
data.axisState.set(i*3, state < 0); if(data.axisState.get(i*3) != negative || data.axisState.get(i*3+1) != max || data.axisState.get(i*3+2) != min) {
data.axisState.set(i*3+1, max); data.axisState.set(i*3, negative);
data.axisState.set(i*3+2, !max && min); data.axisState.set(i*3+1, max);
data.axisState.set(i*3+2, min);
pushEvent(new JoystickEvent.Axis(window, jid, i, state, negative, min, max));
}
} }
} }
public static class JoyStickData { public static class JoyStickData {
Int2ObjectMap<ButtonData> data = new Int2ObjectOpenHashMap<>();
} }
public static class ButtonData { public static class ButtonData {
@ -92,7 +112,10 @@ public class Joystick extends AbstractDevice<JoyStickData, JoyStickTask> {
public record Plugin(long window, int jid, int event) implements JoyStickTask { public record Plugin(long window, int jid, int event) implements JoyStickTask {
@Override @Override
public void process(Joystick stick) { public void process(Joystick stick) {
JoyStickData data = stick.get(window);
if(event == GLFW.GLFW_CONNECTED) data.data.put(jid, new ButtonData());
else if(event == GLFW.GLFW_DISCONNECTED) data.data.remove(jid);
stick.pushEvent(new JoystickEvent.Connected(window, jid, event == GLFW.GLFW_CONNECTED));
} }
} }
} }

View File

@ -0,0 +1,65 @@
package speiger.src.coreengine.rendering.input.events;
import speiger.src.coreengine.utils.eventbus.Event;
public class JoystickEvent extends Event {
final long window;
final int jid;
public JoystickEvent(long window, int jid) {
this.window = window;
this.jid = jid;
}
public long window() { return window; }
public int jid() { return jid; }
public static class Connected extends JoystickEvent {
boolean connected;
public Connected(long window, int jid, boolean connected) {
super(window, jid);
this.connected = connected;
}
public boolean connected() { return connected; }
}
public static class Button extends JoystickEvent {
final int button;
final boolean press;
public Button(long window, int jid, int button, boolean press) {
super(window, jid);
this.button = button;
this.press = press;
}
public int button() { return button; }
public boolean press() { return press; }
}
public static class Axis extends JoystickEvent {
int axis;
float value;
boolean negative;
boolean halfPress;
boolean fullPress;
public Axis(long window, int jid, int axis, float value, boolean negative, boolean halfPress, boolean fullPress) {
super(window, jid);
this.axis = axis;
this.value = value;
this.negative = negative;
this.halfPress = halfPress;
this.fullPress = fullPress;
}
public int axis() { return axis; }
public float value() { return value; }
public float absoluteValue() { return Math.abs(value); }
public boolean negative() { return negative; }
public boolean halfPress() { return halfPress; }
public boolean fullPress() { return fullPress; }
}
}

View File

@ -11,7 +11,6 @@ import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.math.vector.ints.Vec4i; import speiger.src.coreengine.math.vector.ints.Vec4i;
import speiger.src.coreengine.rendering.input.window.IWindowListener.Reason; import speiger.src.coreengine.rendering.input.window.IWindowListener.Reason;
import speiger.src.coreengine.rendering.input.window.WindowCallback.ReloadFunction; import speiger.src.coreengine.rendering.input.window.WindowCallback.ReloadFunction;
import speiger.src.coreengine.rendering.input.window.WindowCallback.SimpleReloadFunction;
import speiger.src.coreengine.rendering.input.window.WindowManager.WindowBuilder; import speiger.src.coreengine.rendering.input.window.WindowManager.WindowBuilder;
import speiger.src.coreengine.rendering.utils.GLStateTracker; import speiger.src.coreengine.rendering.utils.GLStateTracker;
import speiger.src.coreengine.utils.collections.FlagHolder; import speiger.src.coreengine.utils.collections.FlagHolder;
@ -156,7 +155,6 @@ public class Window {
callbacks.clear(); callbacks.clear();
} }
public <T extends CallbackI> WindowCallback addSimpleCallback(T listener, SimpleReloadFunction<T> function) { return addCallback(listener, function); }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T extends CallbackI> WindowCallback addCallback(T listener, ReloadFunction<T> function) { public <T extends CallbackI> WindowCallback addCallback(T listener, ReloadFunction<T> function) {
WindowCallback callback = new WindowCallback(listener, (ReloadFunction<CallbackI>)function); WindowCallback callback = new WindowCallback(listener, (ReloadFunction<CallbackI>)function);

View File

@ -6,23 +6,26 @@ import java.util.function.Consumer;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import org.lwjgl.system.Callback; import org.lwjgl.system.Callback;
import org.lwjgl.system.CallbackI;
import speiger.src.collections.longs.maps.impl.concurrent.Long2ObjectConcurrentOpenHashMap; import speiger.src.collections.longs.maps.impl.concurrent.Long2ObjectConcurrentOpenHashMap;
import speiger.src.collections.longs.maps.interfaces.Long2ObjectMap; import speiger.src.collections.longs.maps.interfaces.Long2ObjectMap;
import speiger.src.collections.objects.lists.ObjectArrayList; import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.rendering.input.devices.InputDevice; import speiger.src.coreengine.rendering.input.devices.InputDevice;
import speiger.src.coreengine.rendering.input.window.WindowCallback.SimpleReloadFunction;
public class WindowManager { public class WindowManager {
Long2ObjectMap<Monitor> monitors; Long2ObjectMap<Monitor> monitors;
Long2ObjectMap<Window> windows = new Long2ObjectConcurrentOpenHashMap<>(); Long2ObjectMap<Window> windows = new Long2ObjectConcurrentOpenHashMap<>();
Window activeWindow; Window activeWindow;
Window primaryWindow; Window primaryWindow;
List<WindowCallback> callbacks = new ObjectArrayList<>();
Callback monitorTracker; Callback monitorTracker;
List<InputDevice> devices = new ObjectArrayList<>(); List<InputDevice> devices = new ObjectArrayList<>();
public void initialize() { public void initialize() {
monitors = Monitor.createMonitors(); monitors = Monitor.createMonitors();
monitorTracker = GLFW.glfwSetMonitorCallback(this::onMonitorChanged); addCallback(this::onMonitorChanged, GLFW::glfwSetMonitorCallback);
} }
private void onMonitorChanged(long monitor, int event) { private void onMonitorChanged(long monitor, int event) {
@ -33,23 +36,28 @@ public class WindowManager {
} }
public WindowBuilder builder() { return new WindowBuilder(this); } public WindowBuilder builder() { return new WindowBuilder(this); }
private Window create(WindowBuilder builder) { return new Window(builder); }
private Window create(WindowBuilder builder) { public void addDevice(InputDevice device) { devices.add(device); }
return new Window(builder); public void removeDevice(InputDevice device) { devices.remove(device); }
}
public void addDevice(InputDevice device) {
devices.add(device);
}
public void processDevices(long windowId) { public void processDevices(long windowId) {
for(int i = 0,m=devices.size();i<m;i++) { for(int i = 0,m=devices.size();i<m;i++) {
devices.get(i).processInput(windowId); devices.get(i).processInput(windowId);
} }
} }
public void removeDevice(InputDevice device) { @SuppressWarnings("unchecked")
devices.remove(device); public <T extends CallbackI> WindowCallback addCallback(T listener, SimpleReloadFunction<T> function) {
WindowCallback callback = new WindowCallback(listener, (SimpleReloadFunction<CallbackI>)function);
callbacks.add(callback);
callback.load(0L);
return callback;
}
public void removeCallback(WindowCallback callback) {
if(callbacks.remove(callback)) {
callback.destroy();
}
} }
void addWindow(Window window) { void addWindow(Window window) {
@ -67,8 +75,21 @@ public class WindowManager {
else if(activeWindow == window) activeWindow = null; else if(activeWindow == window) activeWindow = null;
} }
public void destroy() {
callbacks.forEach(WindowCallback::destroy);
callbacks.clear();
}
public long getActiveWindow() {
return activeWindow == null ? 0L : activeWindow.id();
}
public long getPrimaryWindow() { public long getPrimaryWindow() {
return primaryWindow == null ? 0 : primaryWindow.id; return primaryWindow == null ? 0 : primaryWindow.id();
}
public Window getWindow(long window) {
return windows.get(window);
} }
public Monitor getMonitor(long id) { public Monitor getMonitor(long id) {