From 52fcac6fe946452e5897f9bf4d9e47aee00b693b Mon Sep 17 00:00:00 2001 From: Speiger Date: Fri, 17 May 2024 17:44:15 +0200 Subject: [PATCH] Finished Joystick Support --- .../speiger/src/coreengine/NewInputTest.java | 2 +- .../input/devices/AbstractDevice.java | 1 + .../rendering/input/devices/Joystick.java | 57 +++++++++++----- .../rendering/input/events/JoystickEvent.java | 65 +++++++++++++++++++ .../rendering/input/window/Window.java | 2 - .../rendering/input/window/WindowManager.java | 45 +++++++++---- 6 files changed, 140 insertions(+), 32 deletions(-) create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/events/JoystickEvent.java diff --git a/src/main/java/speiger/src/coreengine/NewInputTest.java b/src/main/java/speiger/src/coreengine/NewInputTest.java index da7dc79..e54c75a 100644 --- a/src/main/java/speiger/src/coreengine/NewInputTest.java +++ b/src/main/java/speiger/src/coreengine/NewInputTest.java @@ -19,7 +19,7 @@ public class NewInputTest { Joystick.INSTANCE.init(bus); ButtonData data = new ButtonData(); while(true) { - Joystick.INSTANCE.handleJoystick(GLFW.GLFW_JOYSTICK_1, data); + Joystick.INSTANCE.handleJoystick(0, GLFW.GLFW_JOYSTICK_1, data); try { Thread.sleep(100); } diff --git a/src/main/java/speiger/src/coreengine/rendering/input/devices/AbstractDevice.java b/src/main/java/speiger/src/coreengine/rendering/input/devices/AbstractDevice.java index ea188b7..729f1f3 100644 --- a/src/main/java/speiger/src/coreengine/rendering/input/devices/AbstractDevice.java +++ b/src/main/java/speiger/src/coreengine/rendering/input/devices/AbstractDevice.java @@ -49,6 +49,7 @@ public abstract class AbstractDevice implements InputDevice { protected abstract void process(E task); protected boolean pushEvent(Event event) { + if(bus == null) return true; bus.post(event); return event.isCancelable() && event.isCanceled(); } diff --git a/src/main/java/speiger/src/coreengine/rendering/input/devices/Joystick.java b/src/main/java/speiger/src/coreengine/rendering/input/devices/Joystick.java index bb7fed0..91b4702 100644 --- a/src/main/java/speiger/src/coreengine/rendering/input/devices/Joystick.java +++ b/src/main/java/speiger/src/coreengine/rendering/input/devices/Joystick.java @@ -5,26 +5,30 @@ import java.nio.FloatBuffer; import java.util.BitSet; import org.lwjgl.glfw.GLFW; -import org.lwjgl.system.Callback; import speiger.src.collections.floats.lists.FloatArrayList; 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.IntSet; import speiger.src.collections.longs.collections.LongIterator; import speiger.src.coreengine.rendering.input.devices.Joystick.JoyStickData; 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; public class Joystick extends AbstractDevice { public static final Joystick INSTANCE = new Joystick(); + WindowManager manager; IntSet presentJoysticks = new IntLinkedOpenHashSet(); - Callback callback; - @Override - public void init(EventBus bus) { + public void init(WindowManager manager, EventBus bus) { + this.manager = manager; 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++) { if(GLFW.glfwJoystickPresent(i)) { presentJoysticks.add(i); @@ -32,9 +36,8 @@ public class Joystick extends AbstractDevice { } } - public void destroy() { - callback.free(); - } + @Override + public void init(EventBus bus) { throw new UnsupportedOperationException("Use init(WindowManager, EventBus) instead"); } private void plugin(int jid, int event) { if(event == GLFW.GLFW_CONNECTED) presentJoysticks.add(jid); @@ -55,28 +58,45 @@ public class Joystick extends AbstractDevice { @Override public void processInput(long 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); 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); for(int i = 0;axis.hasRemaining();i++) { float state = axis.get(); if(i >= data.axis.size()) data.axis.add(state); else data.axis.set(i, state); + boolean negative = state < 0F; boolean max = state < -0.95F || state > 0.95F; - boolean min = state < -0.5F || state > 0.5F; - data.axisState.set(i*3, state < 0); - data.axisState.set(i*3+1, max); - data.axisState.set(i*3+2, !max && min); + boolean min = (state < -0.5F || state > 0.5F) && !max; + 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, negative); + 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 { - + Int2ObjectMap data = new Int2ObjectOpenHashMap<>(); } public static class ButtonData { @@ -92,7 +112,10 @@ public class Joystick extends AbstractDevice { public record Plugin(long window, int jid, int event) implements JoyStickTask { @Override 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)); } } -} +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/input/events/JoystickEvent.java b/src/main/java/speiger/src/coreengine/rendering/input/events/JoystickEvent.java new file mode 100644 index 0000000..3369324 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/events/JoystickEvent.java @@ -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; } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/window/Window.java b/src/main/java/speiger/src/coreengine/rendering/input/window/Window.java index 2845c43..0bf5a1a 100644 --- a/src/main/java/speiger/src/coreengine/rendering/input/window/Window.java +++ b/src/main/java/speiger/src/coreengine/rendering/input/window/Window.java @@ -11,7 +11,6 @@ import speiger.src.collections.objects.lists.ObjectArrayList; import speiger.src.coreengine.math.vector.ints.Vec4i; 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.SimpleReloadFunction; import speiger.src.coreengine.rendering.input.window.WindowManager.WindowBuilder; import speiger.src.coreengine.rendering.utils.GLStateTracker; import speiger.src.coreengine.utils.collections.FlagHolder; @@ -156,7 +155,6 @@ public class Window { callbacks.clear(); } - public WindowCallback addSimpleCallback(T listener, SimpleReloadFunction function) { return addCallback(listener, function); } @SuppressWarnings("unchecked") public WindowCallback addCallback(T listener, ReloadFunction function) { WindowCallback callback = new WindowCallback(listener, (ReloadFunction)function); diff --git a/src/main/java/speiger/src/coreengine/rendering/input/window/WindowManager.java b/src/main/java/speiger/src/coreengine/rendering/input/window/WindowManager.java index ae96f87..b802228 100644 --- a/src/main/java/speiger/src/coreengine/rendering/input/window/WindowManager.java +++ b/src/main/java/speiger/src/coreengine/rendering/input/window/WindowManager.java @@ -6,23 +6,26 @@ import java.util.function.Consumer; import org.lwjgl.glfw.GLFW; 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.interfaces.Long2ObjectMap; import speiger.src.collections.objects.lists.ObjectArrayList; import speiger.src.coreengine.rendering.input.devices.InputDevice; +import speiger.src.coreengine.rendering.input.window.WindowCallback.SimpleReloadFunction; public class WindowManager { Long2ObjectMap monitors; Long2ObjectMap windows = new Long2ObjectConcurrentOpenHashMap<>(); Window activeWindow; Window primaryWindow; + List callbacks = new ObjectArrayList<>(); Callback monitorTracker; List devices = new ObjectArrayList<>(); public void initialize() { monitors = Monitor.createMonitors(); - monitorTracker = GLFW.glfwSetMonitorCallback(this::onMonitorChanged); + addCallback(this::onMonitorChanged, GLFW::glfwSetMonitorCallback); } private void onMonitorChanged(long monitor, int event) { @@ -33,23 +36,28 @@ public class WindowManager { } public WindowBuilder builder() { return new WindowBuilder(this); } + private Window create(WindowBuilder builder) { return new Window(builder); } - private Window create(WindowBuilder builder) { - return new Window(builder); - } - - public void addDevice(InputDevice device) { - devices.add(device); - } - + public void addDevice(InputDevice device) { devices.add(device); } + public void removeDevice(InputDevice device) { devices.remove(device); } public void processDevices(long windowId) { for(int i = 0,m=devices.size();i WindowCallback addCallback(T listener, SimpleReloadFunction function) { + WindowCallback callback = new WindowCallback(listener, (SimpleReloadFunction)function); + callbacks.add(callback); + callback.load(0L); + return callback; + } + + public void removeCallback(WindowCallback callback) { + if(callbacks.remove(callback)) { + callback.destroy(); + } } void addWindow(Window window) { @@ -67,8 +75,21 @@ public class WindowManager { 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() { - 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) {