diff --git a/minecraft/src/main/java/org/polyfrost/oneconfig/api/platform/v1/internal/KeysImpl.java b/minecraft/src/main/java/org/polyfrost/oneconfig/api/platform/v1/internal/KeysImpl.java index 44ace56d0..946700404 100644 --- a/minecraft/src/main/java/org/polyfrost/oneconfig/api/platform/v1/internal/KeysImpl.java +++ b/minecraft/src/main/java/org/polyfrost/oneconfig/api/platform/v1/internal/KeysImpl.java @@ -1,10 +1,76 @@ package org.polyfrost.oneconfig.api.platform.v1.internal; import com.mojang.blaze3d.platform.InputConstants; -import org.lwjgl.glfw.GLFW; import org.polyfrost.oneconfig.api.platform.v1.Keys; public class KeysImpl implements Keys { + @Override + public String keyName(int key) { + //~ if < 26.3 'Type.KEYBOARD' -> 'Type.KEYSYM' + InputConstants.Key input = InputConstants.Type.KEYSYM.getOrCreate(key); + return input == InputConstants.UNKNOWN ? "None" : input.getDisplayName().getString(); + } + + @Override + public String mouseName(int button) { + return InputConstants.Type.MOUSE.getOrCreate(button).getDisplayName().getString(); + } + + @Override + public int getKeyA() { + return InputConstants.KEY_A; + } + + @Override + public int getKeyC() { + return InputConstants.KEY_C; + } + + @Override + public int getKeyD() { + return InputConstants.KEY_D; + } + + @Override + public int getKeyE() { + return InputConstants.KEY_E; + } + + @Override + public int getKeyH() { + return InputConstants.KEY_H; + } + + @Override + public int getKeyL() { + return InputConstants.KEY_L; + } + + @Override + public int getKeyR() { + return InputConstants.KEY_R; + } + + @Override + public int getKeyV() { + return InputConstants.KEY_V; + } + + @Override + public int getKeyX() { + return InputConstants.KEY_X; + } + + @Override + public int getKeyDelete() { + return InputConstants.KEY_DELETE; + } + + @Override + public int getMouseButtonLeft() { + return InputConstants.MOUSE_BUTTON_LEFT; + } + @Override public int getKeyLeftShift() { return InputConstants.KEY_LSHIFT; @@ -37,11 +103,21 @@ public int getKeyRightAlt() { @Override public int getKeyLeftSuper() { - return GLFW.GLFW_KEY_LEFT_SUPER; + //? if >= 26.3 { + /*return InputConstants.KEY_LGUI; + *///?} elif >= 1.21.10 { + return InputConstants.KEY_LSUPER; + //?} else + //return InputConstants.KEY_LWIN; } @Override public int getKeyRightSuper() { - return GLFW.GLFW_KEY_RIGHT_SUPER; + //? if >= 26.3 { + /*return InputConstants.KEY_RGUI; + *///?} elif >= 1.21.10 { + return InputConstants.KEY_RSUPER; + //?} else + //return InputConstants.KEY_RWIN; } } diff --git a/minecraft/src/main/java/org/polyfrost/oneconfig/api/ui/v1/keybind/internal/MinecraftKeybindBridgeImpl.java b/minecraft/src/main/java/org/polyfrost/oneconfig/api/ui/v1/keybind/internal/MinecraftKeybindBridgeImpl.java index 6233c8538..0cb732859 100644 --- a/minecraft/src/main/java/org/polyfrost/oneconfig/api/ui/v1/keybind/internal/MinecraftKeybindBridgeImpl.java +++ b/minecraft/src/main/java/org/polyfrost/oneconfig/api/ui/v1/keybind/internal/MinecraftKeybindBridgeImpl.java @@ -10,6 +10,8 @@ //?} import org.polyfrost.oneconfig.api.event.v1.EventManager; import org.polyfrost.oneconfig.api.event.v1.events.ScreenOpenEvent; +import org.polyfrost.oneconfig.api.platform.v1.Keys; +import org.polyfrost.oneconfig.api.platform.v1.Platform; import org.polyfrost.oneconfig.api.ui.v1.keybind.KeyModifiers; import org.polyfrost.oneconfig.api.ui.v1.keybind.KeybindManager; import org.polyfrost.oneconfig.api.ui.v1.keybind.MinecraftKeybindBridge; @@ -102,6 +104,7 @@ public synchronized void register(OneConfigKeybind bind) { }); OneConfigKeybind def = bind.getDefaultKeybind(); OneConfigKeybind defSrc = def != null ? def : bind; + //~ if < 26.3 'Type.KEYBOARD' -> 'Type.KEYSYM' InputConstants.Type type = defSrc.isMousePrimary() ? InputConstants.Type.MOUSE : InputConstants.Type.KEYSYM; KeyMapping mapping = detached( bind.getName(), @@ -235,7 +238,7 @@ private long[] combo(KeyMapping mapping) { } else { InputConstants.Key key = ((KeyMappingAccessor) mapping).oneconfig$getKey(); int v = key.getValue(); - if (v < 0) return null; + if (key == InputConstants.UNKNOWN) return null; set.add(key.getType() == InputConstants.Type.MOUSE ? (MOUSE_TAG | v) : (long) v); set.add(MODS_TAG); } @@ -302,14 +305,13 @@ private static int[] toArray(List list) { return out; } - private static byte modBit(int glfw) { - return switch (glfw) { - case 340, 344 -> KeyModifiers.SHIFT; - case 341, 345 -> KeyModifiers.CTRL; - case 342, 346 -> KeyModifiers.ALT; - case 343, 347 -> KeyModifiers.META; - default -> KeyModifiers.NONE; - }; + private static byte modBit(int key) { + Keys keys = Platform.compatibility().keys(); + if (key == keys.getKeyLeftShift() || key == keys.getKeyRightShift()) return KeyModifiers.SHIFT; + if (key == keys.getKeyLeftControl() || key == keys.getKeyRightControl()) return KeyModifiers.CTRL; + if (key == keys.getKeyLeftAlt() || key == keys.getKeyRightAlt()) return KeyModifiers.ALT; + if (key == keys.getKeyLeftSuper() || key == keys.getKeyRightSuper()) return KeyModifiers.META; + return KeyModifiers.NONE; } public static String fullComboText(int[] keys, int[] mouse, byte mods) { @@ -319,10 +321,11 @@ public static String fullComboText(int[] keys, int[] mouse, byte mods) { if (KeyModifiers.INSTANCE.has(mods, KeyModifiers.ALT)) parts.add("Alt"); if (KeyModifiers.INSTANCE.has(mods, KeyModifiers.META)) parts.add("Meta"); if (keys != null) { + //~ if < 26.3 'Type.KEYBOARD' -> 'Type.KEYSYM' for (int k : keys) parts.add(InputConstants.Type.KEYSYM.getOrCreate(k).getDisplayName().getString()); } if (mouse != null) { - for (int b : mouse) parts.add("Mouse " + (b + 1)); + for (int b : mouse) parts.add(Platform.compatibility().keys().mouseName(b)); } return parts.isEmpty() ? "None" : String.join(" + ", parts); } @@ -367,6 +370,7 @@ private void setKey(KeyMapping mapping, InputConstants.Key key) { } private void applyKeyTo(KeyMapping mapping, OneConfigKeybind bind) { + //~ if < 26.3 'Type.KEYBOARD' -> 'Type.KEYSYM' setKey(mapping, (bind.isMousePrimary() ? InputConstants.Type.MOUSE : InputConstants.Type.KEYSYM).getOrCreate(bind.getBoundCode())); } @@ -382,6 +386,11 @@ public void reconcile() { if (actualValue == bind.getBoundCode() && actualMouse == bind.isMousePrimary()) continue; + if (actual == InputConstants.UNKNOWN) { + pending.add(() -> KeybindManager.rebindFromMinecraft(bind, null, null, KeyModifiers.NONE)); + continue; + } + OneConfigKeybind def = bind.getDefaultKeybind(); if (def != null && actualValue == def.getBoundCode() && actualMouse == def.isMousePrimary()) { pending.add(() -> KeybindManager.rebindFromMinecraft(bind, def.getKeyCodes(), def.getMouseBtns(), def.getMods())); @@ -411,12 +420,13 @@ public static Component comboLabel(OneConfigKeybind bind, Component base) { List extra = new ArrayList<>(); if (keys != null) { for (int i = keysPrimary ? 1 : 0; i < keys.length; i++) { + //~ if < 26.3 'Type.KEYBOARD' -> 'Type.KEYSYM' extra.add(InputConstants.Type.KEYSYM.getOrCreate(keys[i]).getDisplayName().getString()); } } if (mouse != null) { for (int i = keysPrimary ? 0 : 1; i < mouse.length; i++) { - extra.add("Mouse " + (mouse[i] + 1)); + extra.add(Platform.compatibility().keys().mouseName(mouse[i])); } } diff --git a/minecraft/src/main/java/org/polyfrost/oneconfig/api/ui/v1/keybind/internal/MinecraftKeybindCodec.java b/minecraft/src/main/java/org/polyfrost/oneconfig/api/ui/v1/keybind/internal/MinecraftKeybindCodec.java new file mode 100644 index 000000000..ecf98b1e3 --- /dev/null +++ b/minecraft/src/main/java/org/polyfrost/oneconfig/api/ui/v1/keybind/internal/MinecraftKeybindCodec.java @@ -0,0 +1,158 @@ +package org.polyfrost.oneconfig.api.ui.v1.keybind.internal; + +import com.mojang.blaze3d.platform.InputConstants; +import org.jetbrains.annotations.Nullable; + +public final class MinecraftKeybindCodec implements KeybindCodec { + //? if >= 26.3 + /*private static final int MAX_KEY = Integer.MAX_VALUE; // SDL keycodes are sparse and unicode/scancode-masked*/ + //? if < 26.3 + private static final int MAX_KEY = 348; // GLFW_KEY_LAST + + private static final int MIN_MOUSE = InputConstants.MOUSE_BUTTON_LEFT; + private static final int MAX_MOUSE = InputConstants.MOUSE_BUTTON_LEFT + 7; // vanilla names eight mouse buttons + + @Override + public @Nullable String keyName(int code) { + if (code < 0 || code > MAX_KEY) return null; + + //~ if < 26.3 'Type.KEYBOARD' -> 'Type.KEYSYM' + InputConstants.Key key = InputConstants.Type.KEYSYM.getOrCreate(code); + if (key == InputConstants.UNKNOWN) return null; + + // Ignore fallback names + if (("key.keyboard." + code).equals(key.getName())) return null; + + //? if < 26.3 { + // GLFW to SDL aliases + if ("key.keyboard.keypad.decimal".equals(key.getName())) return "key.keyboard.keypad.period"; + if ("key.keyboard.menu".equals(key.getName())) return "key.keyboard.application"; + //?} + + return key.getName(); + } + + @Override + public @Nullable Integer keyCode(String name) { + //? if < 26.3 { + // SDL to GLFW aliases + if ("key.keyboard.keypad.period".equals(name)) name = "key.keyboard.keypad.decimal"; + if ("key.keyboard.application".equals(name)) name = "key.keyboard.menu"; + //?} + + InputConstants.Key key = lookup(name); + if (key == null) return null; + + if (("key.keyboard." + key.getValue()).equals(name)) return null; + + //~ if < 26.3 'Type.KEYBOARD' -> 'Type.KEYSYM' + return key.getType() == InputConstants.Type.KEYSYM && key != InputConstants.UNKNOWN ? key.getValue() : null; + } + + @Override + public @Nullable String mouseName(int button) { + if (button < MIN_MOUSE || button > MAX_MOUSE) return null; + return InputConstants.Type.MOUSE.getOrCreate(button).getName(); + } + + @Override + public @Nullable Integer mouseButton(String name) { + InputConstants.Key key = lookup(name); + if (key == null || key.getType() != InputConstants.Type.MOUSE) return null; + + int value = key.getValue(); + return value >= MIN_MOUSE && value <= MAX_MOUSE ? value : null; + } + + private static @Nullable InputConstants.Key lookup(String name) { + int dot = name.lastIndexOf('.'); + if (dot >= 0) { + try { + int suffix = Integer.parseInt(name.substring(dot + 1)); + if (name.startsWith("key.mouse.")) { + if (suffix - 1 < MIN_MOUSE || suffix - 1 > MAX_MOUSE) return null; + } else if (suffix < 0 || suffix > MAX_KEY) { + return null; + } + } catch (NumberFormatException ignored) { + } + } + + try { + return InputConstants.getKey(name); + } catch (RuntimeException ignored) { + return null; + } + } + + @Override + public @Nullable String legacyKeyName(int code) { + if (code >= 48 && code <= 57) return "key.keyboard." + (char) code; + if (code >= 65 && code <= 90) return "key.keyboard." + (char) (code + 32); + if (code >= 290 && code <= 314) return "key.keyboard.f" + (code - 289); + if (code >= 320 && code <= 329) return "key.keyboard.keypad." + (code - 320); + return switch (code) { + case 32 -> "key.keyboard.space"; + case 39 -> "key.keyboard.apostrophe"; + case 44 -> "key.keyboard.comma"; + case 45 -> "key.keyboard.minus"; + case 46 -> "key.keyboard.period"; + case 47 -> "key.keyboard.slash"; + case 59 -> "key.keyboard.semicolon"; + case 61 -> "key.keyboard.equal"; + case 91 -> "key.keyboard.left.bracket"; + case 92 -> "key.keyboard.backslash"; + case 93 -> "key.keyboard.right.bracket"; + case 96 -> "key.keyboard.grave.accent"; + case 161 -> "key.keyboard.world.1"; + case 162 -> "key.keyboard.world.2"; + case 256 -> "key.keyboard.escape"; + case 257 -> "key.keyboard.enter"; + case 258 -> "key.keyboard.tab"; + case 259 -> "key.keyboard.backspace"; + case 260 -> "key.keyboard.insert"; + case 261 -> "key.keyboard.delete"; + case 262 -> "key.keyboard.right"; + case 263 -> "key.keyboard.left"; + case 264 -> "key.keyboard.down"; + case 265 -> "key.keyboard.up"; + case 266 -> "key.keyboard.page.up"; + case 267 -> "key.keyboard.page.down"; + case 268 -> "key.keyboard.home"; + case 269 -> "key.keyboard.end"; + case 280 -> "key.keyboard.caps.lock"; + case 281 -> "key.keyboard.scroll.lock"; + case 282 -> "key.keyboard.num.lock"; + case 283 -> "key.keyboard.print.screen"; + case 284 -> "key.keyboard.pause"; + case 330 -> "key.keyboard.keypad.period"; + case 331 -> "key.keyboard.keypad.divide"; + case 332 -> "key.keyboard.keypad.multiply"; + case 333 -> "key.keyboard.keypad.subtract"; + case 334 -> "key.keyboard.keypad.add"; + case 335 -> "key.keyboard.keypad.enter"; + case 336 -> "key.keyboard.keypad.equal"; + case 340 -> "key.keyboard.left.shift"; + case 341 -> "key.keyboard.left.control"; + case 342 -> "key.keyboard.left.alt"; + case 343 -> "key.keyboard.left.win"; + case 344 -> "key.keyboard.right.shift"; + case 345 -> "key.keyboard.right.control"; + case 346 -> "key.keyboard.right.alt"; + case 347 -> "key.keyboard.right.win"; + case 348 -> "key.keyboard.application"; + default -> null; + }; + } + + @Override + public @Nullable String legacyMouseName(int button) { + return switch (button) { + case 0 -> "key.mouse.left"; + case 1 -> "key.mouse.right"; + case 2 -> "key.mouse.middle"; + case 3, 4, 5, 6, 7 -> "key.mouse." + (button + 1); + default -> null; + }; + } +} diff --git a/minecraft/src/main/java/org/polyfrost/oneconfig/internal/mixin/events/Mixin_KeyInputEvent.java b/minecraft/src/main/java/org/polyfrost/oneconfig/internal/mixin/events/Mixin_KeyInputEvent.java index a6cc5d606..3ffd12e39 100644 --- a/minecraft/src/main/java/org/polyfrost/oneconfig/internal/mixin/events/Mixin_KeyInputEvent.java +++ b/minecraft/src/main/java/org/polyfrost/oneconfig/internal/mixin/events/Mixin_KeyInputEvent.java @@ -1,5 +1,6 @@ package org.polyfrost.oneconfig.internal.mixin.events; +import com.mojang.blaze3d.platform.InputConstants; import net.minecraft.client.KeyboardHandler; //? >= 1.21.10 { import net.minecraft.client.input.CharacterEvent; @@ -9,6 +10,7 @@ import org.polyfrost.oneconfig.api.event.v1.events.KeyInputEvent; import org.polyfrost.oneconfig.api.platform.v1.Platform; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; //? if >= 1.21.10 @@ -21,7 +23,7 @@ public class Mixin_KeyInputEvent { //? >= 1.21.10 { @ModifyVariable(method = "keyPress", at = @At(value = "STORE"), ordinal = 0) private boolean keyCallback(boolean original, long handle, int action, KeyEvent event) { - EventManager.INSTANCE.post(new KeyInputEvent(event.key(), (char) 0, action)); + EventManager.INSTANCE.post(new KeyInputEvent(event.key(), (char) 0, oneconfig$state(action))); return original; } @@ -31,13 +33,13 @@ private void charCallback(long handle, CharacterEvent event, CallbackInfo ci) { return; } - EventManager.INSTANCE.post(new KeyInputEvent(0, (char) event.codepoint(), 1)); + EventManager.INSTANCE.post(new KeyInputEvent(0, (char) event.codepoint(), KeyInputEvent.PRESSED)); } //? } else { /*@Inject(method = "keyPress", at = @At("HEAD")) private void keyCallback(long window, int key, int scancode, int action, int mods, CallbackInfo ci) { - EventManager.INSTANCE.post(new KeyInputEvent(key, (char) 0, action)); + EventManager.INSTANCE.post(new KeyInputEvent(key, (char) 0, oneconfig$state(action))); } @Inject(method = "charTyped", at = @At("HEAD")) @@ -45,7 +47,15 @@ private void charCallback(long window, int codepoint, int mods, CallbackInfo ci) if (window != Platform.compatibility().windowHandle()) { return; } - EventManager.INSTANCE.post(new KeyInputEvent(0, (char) codepoint, 1)); + EventManager.INSTANCE.post(new KeyInputEvent(0, (char) codepoint, KeyInputEvent.PRESSED)); } *///? } + + @Unique + private static int oneconfig$state(int action) { + if (action == InputConstants.PRESS) return KeyInputEvent.PRESSED; + if (action == InputConstants.RELEASE) return KeyInputEvent.RELEASED; + if (action == InputConstants.REPEAT) return KeyInputEvent.REPEAT; + return action; + } } diff --git a/minecraft/src/main/java/org/polyfrost/oneconfig/internal/mixin/events/Mixin_MouseInputEvent.java b/minecraft/src/main/java/org/polyfrost/oneconfig/internal/mixin/events/Mixin_MouseInputEvent.java index e19b881c8..3c81815cf 100644 --- a/minecraft/src/main/java/org/polyfrost/oneconfig/internal/mixin/events/Mixin_MouseInputEvent.java +++ b/minecraft/src/main/java/org/polyfrost/oneconfig/internal/mixin/events/Mixin_MouseInputEvent.java @@ -1,5 +1,6 @@ package org.polyfrost.oneconfig.internal.mixin.events; +import com.mojang.blaze3d.platform.InputConstants; import net.minecraft.client.Minecraft; import net.minecraft.client.MouseHandler; //? >= 1.21.10 @@ -7,6 +8,7 @@ import org.polyfrost.oneconfig.api.event.v1.EventManager; import org.polyfrost.oneconfig.api.event.v1.events.MouseInputEvent; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -16,7 +18,7 @@ public class Mixin_MouseInputEvent { //? >= 1.21.10 { @Inject(method = "onButton", at = @At("HEAD")) private void mouseCallback(long window, MouseButtonInfo buttonInfo, int action, CallbackInfo ci) { - EventManager.INSTANCE.post(new MouseInputEvent(buttonInfo.button(), action)); + EventManager.INSTANCE.post(new MouseInputEvent(buttonInfo.button(), oneconfig$state(action))); } @Inject(method = "onMove", at = @At("HEAD")) @@ -33,7 +35,7 @@ private void mouseMoveCallback(long handle, double x, double y, CallbackInfo ci) //? } else { /*@Inject(method = "onPress", at = @At("HEAD")) private void mouseCallback(long handle, int button, int action, int mods, CallbackInfo ci) { - EventManager.INSTANCE.post(new MouseInputEvent(button, action)); + EventManager.INSTANCE.post(new MouseInputEvent(button, oneconfig$state(action))); } @Inject(method = "onMove", at = @At("HEAD")) @@ -43,4 +45,11 @@ private void mouseMoveCallback(long handle, double x, double y, CallbackInfo ci) } } *///? } + + @Unique + private static int oneconfig$state(int action) { + if (action == InputConstants.PRESS) return MouseInputEvent.PRESSED; + if (action == InputConstants.RELEASE) return MouseInputEvent.RELEASED; + return action; + } } diff --git a/minecraft/src/main/java/org/polyfrost/oneconfig/internal/mixin/events/Mixin_ResizeEvent.java b/minecraft/src/main/java/org/polyfrost/oneconfig/internal/mixin/events/Mixin_ResizeEvent.java index c525332e3..eca2c721e 100644 --- a/minecraft/src/main/java/org/polyfrost/oneconfig/internal/mixin/events/Mixin_ResizeEvent.java +++ b/minecraft/src/main/java/org/polyfrost/oneconfig/internal/mixin/events/Mixin_ResizeEvent.java @@ -1,6 +1,6 @@ package org.polyfrost.oneconfig.internal.mixin.events; -import org.polyfrost.oneconfig.api.platform.v1.Platform; +import com.mojang.blaze3d.platform.Window; import org.spongepowered.asm.mixin.Shadow; import net.minecraft.client.Minecraft; import org.polyfrost.oneconfig.api.event.v1.EventManager; @@ -14,14 +14,12 @@ public abstract class Mixin_ResizeEvent { @Shadow - public abstract com.mojang.blaze3d.platform.Window getWindow(); + public abstract Window getWindow(); //~ if >= 26.1 'resizeDisplay' -> 'resizeGui' @Inject(method = "resizeGui", at = @At("TAIL")) private void resizeCallback(CallbackInfo ci) { - int[] w = new int[1]; - int[] h = new int[1]; - org.lwjgl.glfw.GLFW.glfwGetWindowSize(Platform.compatibility().windowHandle(), w, h); - EventManager.INSTANCE.post(new ResizeEvent(w[0], h[0])); + Window window = getWindow(); + EventManager.INSTANCE.post(new ResizeEvent(window.getScreenWidth(), window.getScreenHeight())); } } diff --git a/minecraft/src/main/java/org/polyfrost/oneconfig/internal/mixin/keybind/Mixin_OneConfigKeybindRebind.java b/minecraft/src/main/java/org/polyfrost/oneconfig/internal/mixin/keybind/Mixin_OneConfigKeybindRebind.java index b8dcd3acc..a8cf888a2 100644 --- a/minecraft/src/main/java/org/polyfrost/oneconfig/internal/mixin/keybind/Mixin_OneConfigKeybindRebind.java +++ b/minecraft/src/main/java/org/polyfrost/oneconfig/internal/mixin/keybind/Mixin_OneConfigKeybindRebind.java @@ -1,13 +1,19 @@ package org.polyfrost.oneconfig.internal.mixin.keybind; +import com.mojang.blaze3d.platform.InputConstants; import net.minecraft.client.KeyMapping; +//? if >= 1.21.10 +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.options.controls.KeyBindsList; import net.minecraft.client.gui.screens.options.controls.KeyBindsScreen; //? if >=1.21.10 { import net.minecraft.client.input.KeyEvent; import net.minecraft.client.input.MouseButtonEvent; //?} +//? if < 26.3 import org.lwjgl.glfw.GLFW; +//? if >= 26.3 +//import org.lwjgl.sdl.SDLMouse; import org.polyfrost.oneconfig.api.platform.v1.Platform; import org.polyfrost.oneconfig.api.ui.v1.keybind.internal.MinecraftKeybindBridgeImpl; import org.polyfrost.oneconfig.internal.ui.keybind.OneConfigKeybindRecorder; @@ -35,7 +41,7 @@ public class Mixin_OneConfigKeybindRebind implements OneConfigKeybindRecorder { @Inject(method = "keyPressed", at = @At("HEAD"), cancellable = true) private void oneconfig$keyPressed(KeyEvent event, CallbackInfoReturnable cir) { if (!oneconfig$isOurs()) return; - if (event.key() == GLFW.GLFW_KEY_ESCAPE) { + if (event.key() == InputConstants.KEY_ESCAPE) { oneconfig$recordEscape(); cir.setReturnValue(true); return; @@ -47,7 +53,7 @@ public class Mixin_OneConfigKeybindRebind implements OneConfigKeybindRecorder { /*@Inject(method = "keyPressed", at = @At("HEAD"), cancellable = true) private void oneconfig$keyPressed(int keyCode, int scanCode, int modifiers, CallbackInfoReturnable cir) { if (!oneconfig$isOurs()) return; - if (keyCode == GLFW.GLFW_KEY_ESCAPE) { + if (keyCode == InputConstants.KEY_ESCAPE) { oneconfig$recordEscape(); cir.setReturnValue(true); return; @@ -90,9 +96,23 @@ public class Mixin_OneConfigKeybindRebind implements OneConfigKeybindRecorder { MinecraftKeybindBridgeImpl bridge = MinecraftKeybindBridgeImpl.instance(); if (bridge != null) bridge.setActiveRebind(oneconfig$target != null ? oneconfig$target : this.selectedKey); if (!oneconfig$recording) return; + for (int k : oneconfig$keys) { + //? if >= 26.3 { + /*if (InputConstants.isKeyDown(k)) return; + *///?} else { + //~ if < 1.21.10 'Minecraft.getInstance().getWindow()' -> 'Platform.compatibility().windowHandle()' + if (InputConstants.isKeyDown(Minecraft.getInstance().getWindow(), k)) return; + //?} + } + //? if >= 26.3 { + /*int buttons = SDLMouse.SDL_GetMouseState(null, null); + for (int b : oneconfig$mouse) { + if (b > 0 && b <= Integer.SIZE && (buttons & (1 << (b - 1))) != 0) return; + } + *///?} else { long window = Platform.compatibility().windowHandle(); - for (int k : oneconfig$keys) if (GLFW.glfwGetKey(window, k) != GLFW.GLFW_RELEASE) return; - for (int b : oneconfig$mouse) if (GLFW.glfwGetMouseButton(window, b) != GLFW.GLFW_RELEASE) return; + for (int b : oneconfig$mouse) if (GLFW.glfwGetMouseButton(window, b) != InputConstants.RELEASE) return; + //?} oneconfig$commit(); } diff --git a/minecraft/src/main/java/org/polyfrost/oneconfig/internal/ui/keybind/RightShiftConflicts.java b/minecraft/src/main/java/org/polyfrost/oneconfig/internal/ui/keybind/RightShiftConflicts.java index 57d4d7df6..fe95d979e 100644 --- a/minecraft/src/main/java/org/polyfrost/oneconfig/internal/ui/keybind/RightShiftConflicts.java +++ b/minecraft/src/main/java/org/polyfrost/oneconfig/internal/ui/keybind/RightShiftConflicts.java @@ -69,6 +69,7 @@ private RightShiftConflicts() { * The key OneConfig's own keybind uses and which conflicting keybinds are cleared from */ public static InputConstants.Key key() { + //~ if < 26.3 'Type.KEYBOARD' -> 'Type.KEYSYM' return InputConstants.Type.KEYSYM.getOrCreate(InputConstants.KEY_RSHIFT); } diff --git a/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/compat/WWaypointsCompat.kt b/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/compat/WWaypointsCompat.kt index 3ba157496..7b9b20e7c 100644 --- a/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/compat/WWaypointsCompat.kt +++ b/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/compat/WWaypointsCompat.kt @@ -1086,10 +1086,12 @@ object WWaypointsCompat { private fun KeyMapping.toOneConfigKeybind(): OneConfigKeybind { val key = runCatching { InputConstants.getKey(saveString()) }.getOrDefault(InputConstants.UNKNOWN) - return when { - key.type == InputConstants.Type.KEYSYM && key.value > 0 -> + return when (key.type) { + //~ if < 26.3 'Type.KEYBOARD' -> 'Type.KEYSYM' + InputConstants.Type.KEYSYM if key.value > 0 -> OneConfigKeybind(intArrayOf(key.value), null, KeyModifiers.NONE, 0L) { true } - key.type == InputConstants.Type.MOUSE && key.value >= 0 -> + //~ if < 26.3 'key.value > 0' -> 'key.value >= 0' + InputConstants.Type.MOUSE if key.value >= 0 -> OneConfigKeybind(null, intArrayOf(key.value), KeyModifiers.NONE, 0L) { true } else -> OneConfigKeybind(null, null, KeyModifiers.NONE, 0L) { true } } @@ -1098,7 +1100,9 @@ object WWaypointsCompat { /** wWaypoints mappings are single-key so only the primary input of a combo survives the round trip */ private fun OneConfigKeybind?.toInputKey(): InputConstants.Key { if (this == null || !isBound) return InputConstants.UNKNOWN + //~ if < 26.3 'it > 0' -> 'it >= 0' mouseBtns?.firstOrNull { it >= 0 }?.let { return InputConstants.Type.MOUSE.getOrCreate(it) } + //~ if < 26.3 'Type.KEYBOARD' -> 'Type.KEYSYM' keyCodes?.firstOrNull { it > 0 }?.let { return InputConstants.Type.KEYSYM.getOrCreate(it) } return InputConstants.UNKNOWN } diff --git a/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/ui/compose/ComposeSceneContextImpl.kt b/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/ui/compose/ComposeSceneContextImpl.kt index d6f3a3c72..9d9b257d2 100644 --- a/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/ui/compose/ComposeSceneContextImpl.kt +++ b/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/ui/compose/ComposeSceneContextImpl.kt @@ -18,8 +18,14 @@ import androidx.compose.ui.scene.ComposeSceneContext import androidx.compose.ui.unit.IntSize //? if >= 26.1 import kotlinx.coroutines.awaitCancellation +import com.mojang.blaze3d.platform.InputConstants import net.minecraft.client.Minecraft +//? if < 26.3 import org.lwjgl.glfw.GLFW.* +//? if >= 26.3 { +/*import org.lwjgl.sdl.SDLMouse.* +import org.lwjgl.sdl.SDLVideo.SDL_RaiseWindow +*///?} import org.polyfrost.oneconfig.api.platform.v1.Platform //? if >= 26.1 import java.util.concurrent.atomic.AtomicInteger @@ -37,19 +43,24 @@ private class WindowInfoImpl : WindowInfo { Platform.screen().let { IntSize(it.windowWidth(), it.windowHeight()) } ) - private fun isKeyDown(glfwKey: Int): Boolean { - return glfwGetKey(Platform.compatibility().windowHandle(), glfwKey) == GLFW_PRESS + private fun isKeyDown(key: Int): Boolean { + //? if >= 26.3 { + /*return InputConstants.isKeyDown(key) + *///?} else { + //~ if < 1.21.10 'Minecraft.getInstance().window' -> 'Platform.compatibility().windowHandle()' + return InputConstants.isKeyDown(Minecraft.getInstance().window, key) + //?} } override val keyboardModifiers: PointerKeyboardModifiers get() = PointerKeyboardModifiers( - isCtrlPressed = isKeyDown(GLFW_KEY_LEFT_CONTROL) || isKeyDown(GLFW_KEY_RIGHT_CONTROL), - isShiftPressed = isKeyDown(GLFW_KEY_LEFT_SHIFT) || isKeyDown(GLFW_KEY_RIGHT_SHIFT), - isAltPressed = isKeyDown(GLFW_KEY_LEFT_ALT) || isKeyDown(GLFW_KEY_RIGHT_ALT), - isCapsLockOn = isKeyDown(GLFW_KEY_CAPS_LOCK), - isScrollLockOn = isKeyDown(GLFW_KEY_SCROLL_LOCK), - isNumLockOn = isKeyDown(GLFW_KEY_NUM_LOCK), - isMetaPressed = isKeyDown(GLFW_KEY_LEFT_SUPER) || isKeyDown(GLFW_KEY_RIGHT_SUPER) + isCtrlPressed = isKeyDown(InputConstants.KEY_LCONTROL) || isKeyDown(InputConstants.KEY_RCONTROL), + isShiftPressed = isKeyDown(InputConstants.KEY_LSHIFT) || isKeyDown(InputConstants.KEY_RSHIFT), + isAltPressed = isKeyDown(InputConstants.KEY_LALT) || isKeyDown(InputConstants.KEY_RALT), + isCapsLockOn = isKeyDown(InputConstants.KEY_CAPSLOCK), + isScrollLockOn = isKeyDown(InputConstants.KEY_SCROLLLOCK), + isNumLockOn = isKeyDown(InputConstants.KEY_NUMLOCK), + isMetaPressed = isKeyDown(Platform.compatibility().keys().keyLeftSuper) || isKeyDown(Platform.compatibility().keys().keyRightSuper) ) override val isWindowFocused: Boolean @@ -68,9 +79,15 @@ private class PlatformImpl : PlatformContext { override val screenReader: PlatformScreenReader = PlatformScreenReaderImpl() override val inputModeManager: InputModeManager = InputModeManagerImpl() + //? if >= 26.3 { + /*private val handCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_POINTER) + private val textCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_TEXT) + private val moveCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR) + *///?} else { private val handCursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR) private val textCursor = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR) private val moveCursor = glfwCreateStandardCursor(GLFW_CROSSHAIR_CURSOR) + //?} private val handle = Platform.compatibility().windowHandle() @@ -86,15 +103,26 @@ private class PlatformImpl : PlatformContext { } private fun applyPointerIcon(pointerIcon: PointerIcon) { + //? if >= 26.3 { + /*SDL_SetCursor(when (pointerIcon) { + PointerIcon.Default -> SDL_GetDefaultCursor() + PointerIcon.Hand -> handCursor + PointerIcon.Text -> textCursor + PointerIcon.Crosshair -> moveCursor + else -> SDL_GetDefaultCursor() + }) + *///?} else { when (pointerIcon) { PointerIcon.Default -> glfwSetCursor(handle, 0L) PointerIcon.Hand -> glfwSetCursor(handle, handCursor) PointerIcon.Text -> glfwSetCursor(handle, textCursor) PointerIcon.Crosshair -> glfwSetCursor(handle, moveCursor) } + //?} } override fun requestFocus(): Boolean { + //~ if < 26.3 'SDL_RaiseWindow' -> 'glfwFocusWindow' glfwFocusWindow(handle) return Minecraft.getInstance().isWindowActive } diff --git a/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/ui/compose/ComposeScreen.kt b/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/ui/compose/ComposeScreen.kt index c18255f2f..7bf703e73 100644 --- a/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/ui/compose/ComposeScreen.kt +++ b/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/ui/compose/ComposeScreen.kt @@ -18,6 +18,7 @@ import androidx.compose.ui.scene.ComposeScene import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.IntSize +import com.mojang.blaze3d.platform.InputConstants import net.minecraft.client.Minecraft import org.polyfrost.oneconfig.utils.v1.ClipboardHelper import net.minecraft.client.gui.GuiGraphicsExtractor @@ -31,7 +32,11 @@ import net.minecraft.network.chat.CommonComponents import org.jetbrains.skia.FilterTileMode import org.jetbrains.skia.ImageFilter import org.jetbrains.skia.Paint +//? if < 26.3 import org.lwjgl.glfw.GLFW +//? if >= 26.3 { +/*import org.lwjgl.sdl.SDLVideo.* +*///?} import org.polyfrost.oneconfig.api.platform.v1.DesktopHelper import org.polyfrost.oneconfig.api.platform.v1.Platform import org.polyfrost.oneconfig.internal.OneConfigConfig @@ -212,10 +217,12 @@ abstract class ComposeScreen( private var cachedSurfaceScale = -1f protected val client get() = Minecraft.getInstance() + //? if < 26.3 { private val contentScaleX = FloatArray(1) private val contentScaleY = FloatArray(1) private val monScaleX = FloatArray(1) private val monScaleY = FloatArray(1) + //?} private var filterPaintKey = -1 to -1f private var filterPaintCached: Paint? = null @@ -252,12 +259,19 @@ abstract class ComposeScreen( private fun osUpscaleFactor(): Float { val handle = Platform.compatibility().windowHandle() + //? if >= 26.3 { + /*val winCS = SDL_GetWindowDisplayScale(handle).coerceAtLeast(1f) + val display = SDL_GetDisplayForWindow(handle).takeIf { it != 0 } ?: SDL_GetPrimaryDisplay() + if (display == 0) return 1f + val monCS = SDL_GetDisplayContentScale(display).coerceAtLeast(1f) + *///?} else { GLFW.glfwGetWindowContentScale(handle, contentScaleX, contentScaleY) val winCS = maxOf(contentScaleX[0], contentScaleY[0]).coerceAtLeast(1f) val mon = GLFW.glfwGetWindowMonitor(handle).takeIf { it != 0L } ?: GLFW.glfwGetPrimaryMonitor() if (mon == 0L) return 1f GLFW.glfwGetMonitorContentScale(mon, monScaleX, monScaleY) val monCS = maxOf(monScaleX[0], monScaleY[0]).coerceAtLeast(1f) + //?} return (monCS / winCS).coerceAtLeast(1f) } @@ -486,8 +500,8 @@ abstract class ComposeScreen( it.sendPointerEvent( type, button = when (button) { - GLFW.GLFW_MOUSE_BUTTON_LEFT -> PointerButton.Primary - GLFW.GLFW_MOUSE_BUTTON_RIGHT -> PointerButton.Secondary + InputConstants.MOUSE_BUTTON_LEFT -> PointerButton.Primary + InputConstants.MOUSE_BUTTON_RIGHT -> PointerButton.Secondary else -> null }, position = pointerPosition() @@ -528,10 +542,16 @@ abstract class ComposeScreen( *///? } } - fun Int.ctrlDown() = this and GLFW.GLFW_MOD_CONTROL != 0 - fun Int.shiftDown() = this and GLFW.GLFW_MOD_SHIFT != 0 + fun Int.ctrlDown() = this and InputConstants.MOD_CONTROL != 0 + //? if >= 1.21.9 { + fun Int.shiftDown() = this and InputConstants.MOD_SHIFT != 0 + fun Int.altDown() = this and InputConstants.MOD_ALT != 0 + fun Int.superDown() = this and InputConstants.MOD_SUPER != 0 + //?} else { + /*fun Int.shiftDown() = this and GLFW.GLFW_MOD_SHIFT != 0 fun Int.altDown() = this and GLFW.GLFW_MOD_ALT != 0 fun Int.superDown() = this and GLFW.GLFW_MOD_SUPER != 0 + *///?} protected open fun handleKeyPressed(key: Int, modifiers: Int): Boolean = false @@ -540,12 +560,16 @@ abstract class ComposeScreen( //? >= 1.21.10 { override fun keyPressed(event: McKeyEvent): Boolean { - val key = event.key + val bindingKey = event.key + //~ if < 26.3 'event.shortcutKey()' -> 'bindingKey' + val shortcutKey = bindingKey val modifiers = event.modifiers //?} else { /*override fun keyPressed(key: Int, scanCode: Int, modifiers: Int): Boolean { + val bindingKey = key + val shortcutKey = key *///?} - val handled = dispatchKeyPressed(key, modifiers) + val handled = dispatchKeyPressed(bindingKey, shortcutKey, modifiers) //? if >= 1.21.10 { return handled || super.keyPressed(event) //?} else { @@ -555,12 +579,16 @@ abstract class ComposeScreen( //? if >= 1.21.10 { override fun keyReleased(event: McKeyEvent): Boolean { - val key = event.key + val bindingKey = event.key + //~ if < 26.3 'event.shortcutKey()' -> 'bindingKey' + val shortcutKey = bindingKey val modifiers = event.modifiers //?} else { /*override fun keyReleased(key: Int, scanCode: Int, modifiers: Int): Boolean { + val bindingKey = key + val shortcutKey = key *///?} - val handled = if (consumedKeys.remove(key)) false else sendKeyReleasedEvent(key, modifiers) + val handled = !consumedKeys.remove(bindingKey) && sendKeyReleasedEvent(bindingKey, shortcutKey, modifiers) //? if >= 1.21.10 { return handled || super.keyReleased(event) //?} else { @@ -568,24 +596,24 @@ abstract class ComposeScreen( *///?} } - private fun dispatchKeyPressed(key: Int, modifiers: Int): Boolean { - if ((key == GLFW.GLFW_KEY_ESCAPE && KeybindRecordingBus.consumeEscape()) || handleKeyPressed(key, modifiers)) { - consumedKeys += key + private fun dispatchKeyPressed(bindingKey: Int, shortcutKey: Int, modifiers: Int): Boolean { + if ((bindingKey == InputConstants.KEY_ESCAPE && KeybindRecordingBus.consumeEscape()) || handleKeyPressed(bindingKey, modifiers)) { + consumedKeys += bindingKey return true } - return sendKeyPressedEvent(key, modifiers) + return sendKeyPressedEvent(bindingKey, shortcutKey, modifiers) } - private fun sendKeyPressedEvent(key: Int, modifiers: Int): Boolean { - val awtCode = glfwToAwtKeyCode(key) - val eventLocation = glfwKeyLocation(key) + private fun sendKeyPressedEvent(bindingKey: Int, shortcutKey: Int, modifiers: Int): Boolean { + val awtCode = MinecraftKeyboardAdapter.toAwtKeyCode(shortcutKey) + val eventLocation = MinecraftKeyboardAdapter.keyLocation(bindingKey) return sendKeyEventSafely { androidx.compose.ui.input.key.KeyEvent( key = Key(awtCode, eventLocation), type = KeyEventType.KeyDown, - // carry the raw GLFW key code so consumers like KeybindOption can recover it losslessly + // carry the raw keybind code so consumers like KeybindOption can recover it losslessly // as the AWT round-trip in the Key collapses unmapped keys to VK_UNDEFINED - codePoint = key, + codePoint = bindingKey, isCtrlPressed = modifiers.ctrlDown(), isShiftPressed = modifiers.shiftDown(), isAltPressed = modifiers.altDown(), @@ -603,14 +631,14 @@ abstract class ComposeScreen( } } - private fun sendKeyReleasedEvent(key: Int, modifiers: Int): Boolean { - val awtCode = glfwToAwtKeyCode(key) - val eventLocation = glfwKeyLocation(key) + private fun sendKeyReleasedEvent(bindingKey: Int, shortcutKey: Int, modifiers: Int): Boolean { + val awtCode = MinecraftKeyboardAdapter.toAwtKeyCode(shortcutKey) + val eventLocation = MinecraftKeyboardAdapter.keyLocation(bindingKey) return sendKeyEventSafely { androidx.compose.ui.input.key.KeyEvent( key = Key(awtCode, eventLocation), type = KeyEventType.KeyUp, - codePoint = key, + codePoint = bindingKey, isCtrlPressed = modifiers.ctrlDown(), isShiftPressed = modifiers.shiftDown(), isAltPressed = modifiers.altDown(), @@ -660,41 +688,6 @@ abstract class ComposeScreen( return m } - // AWT collapses left/right modifiers to one key code so the side is preserved via key location - // and Compose can still tell left shift from right shift - private fun glfwKeyLocation(glfwKey: Int): Int = when (glfwKey) { - GLFW.GLFW_KEY_RIGHT_SHIFT, GLFW.GLFW_KEY_RIGHT_CONTROL, GLFW.GLFW_KEY_RIGHT_ALT, GLFW.GLFW_KEY_RIGHT_SUPER -> KeyEvent.KEY_LOCATION_RIGHT - GLFW.GLFW_KEY_LEFT_SHIFT, GLFW.GLFW_KEY_LEFT_CONTROL, GLFW.GLFW_KEY_LEFT_ALT, GLFW.GLFW_KEY_LEFT_SUPER -> KeyEvent.KEY_LOCATION_LEFT - GLFW.GLFW_KEY_KP_ENTER -> KeyEvent.KEY_LOCATION_NUMPAD - else -> KeyEvent.KEY_LOCATION_STANDARD - } - - private fun glfwToAwtKeyCode(glfwKey: Int): Int = when (glfwKey) { - GLFW.GLFW_KEY_BACKSPACE -> KeyEvent.VK_BACK_SPACE - GLFW.GLFW_KEY_TAB -> KeyEvent.VK_TAB - GLFW.GLFW_KEY_ENTER, GLFW.GLFW_KEY_KP_ENTER -> KeyEvent.VK_ENTER - GLFW.GLFW_KEY_ESCAPE -> KeyEvent.VK_ESCAPE - GLFW.GLFW_KEY_DELETE -> KeyEvent.VK_DELETE - GLFW.GLFW_KEY_RIGHT -> KeyEvent.VK_RIGHT - GLFW.GLFW_KEY_LEFT -> KeyEvent.VK_LEFT - GLFW.GLFW_KEY_DOWN -> KeyEvent.VK_DOWN - GLFW.GLFW_KEY_UP -> KeyEvent.VK_UP - GLFW.GLFW_KEY_PAGE_UP -> KeyEvent.VK_PAGE_UP - GLFW.GLFW_KEY_PAGE_DOWN -> KeyEvent.VK_PAGE_DOWN - GLFW.GLFW_KEY_HOME -> KeyEvent.VK_HOME - GLFW.GLFW_KEY_END -> KeyEvent.VK_END - GLFW.GLFW_KEY_INSERT -> KeyEvent.VK_INSERT - GLFW.GLFW_KEY_CAPS_LOCK -> KeyEvent.VK_CAPS_LOCK - GLFW.GLFW_KEY_LEFT_SHIFT, GLFW.GLFW_KEY_RIGHT_SHIFT -> KeyEvent.VK_SHIFT - GLFW.GLFW_KEY_LEFT_CONTROL, GLFW.GLFW_KEY_RIGHT_CONTROL -> KeyEvent.VK_CONTROL - GLFW.GLFW_KEY_LEFT_SUPER, GLFW.GLFW_KEY_RIGHT_SUPER -> KeyEvent.VK_META - GLFW.GLFW_KEY_LEFT_ALT, GLFW.GLFW_KEY_RIGHT_ALT -> KeyEvent.VK_ALT - in GLFW.GLFW_KEY_F1..GLFW.GLFW_KEY_F12 -> KeyEvent.VK_F1 + (glfwKey - GLFW.GLFW_KEY_F1) - in GLFW.GLFW_KEY_0..GLFW.GLFW_KEY_9 -> KeyEvent.VK_0 + (glfwKey - GLFW.GLFW_KEY_0) - in GLFW.GLFW_KEY_A..GLFW.GLFW_KEY_Z -> KeyEvent.VK_A + (glfwKey - GLFW.GLFW_KEY_A) - else -> KeyEvent.VK_UNDEFINED - } - private fun syncSceneMetrics(): Boolean { val w = Platform.screen().windowWidth() val h = Platform.screen().windowHeight() @@ -726,8 +719,12 @@ abstract class ComposeScreen( private fun sceneDensity(): Float { val pixelRatio = Platform.screen().pixelRatio().takeIf { it > 0f } ?: 1f + //? if >= 26.3 { + /*val contentScale = SDL_GetWindowDisplayScale(Platform.compatibility().windowHandle()).coerceAtLeast(1f) + *///?} else { GLFW.glfwGetWindowContentScale(Platform.compatibility().windowHandle(), contentScaleX, contentScaleY) val contentScale = maxOf(contentScaleX[0], contentScaleY[0]).coerceAtLeast(1f) + //?} return (contentScale / pixelRatio).coerceAtLeast(1f) } diff --git a/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/ui/compose/MinecraftKeyboardAdapter.kt b/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/ui/compose/MinecraftKeyboardAdapter.kt new file mode 100644 index 000000000..1d607d1b6 --- /dev/null +++ b/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/ui/compose/MinecraftKeyboardAdapter.kt @@ -0,0 +1,75 @@ +package org.polyfrost.oneconfig.internal.ui.compose + +import com.mojang.blaze3d.platform.InputConstants +import org.polyfrost.oneconfig.api.platform.v1.Platform +//? if >= 26.3 +//import org.lwjgl.sdl.SDLKeycode.* + +import java.awt.event.KeyEvent + +internal object MinecraftKeyboardAdapter { + private val keys get() = Platform.compatibility().keys() + + // AWT collapses left/right modifiers to one key code so the side is preserved via key location + // and Compose can still tell left shift from right shift + fun keyLocation(bindingKey: Int): Int = when (bindingKey) { + InputConstants.KEY_RSHIFT, InputConstants.KEY_RCONTROL, InputConstants.KEY_RALT, keys.keyRightSuper -> KeyEvent.KEY_LOCATION_RIGHT + InputConstants.KEY_LSHIFT, InputConstants.KEY_LCONTROL, InputConstants.KEY_LALT, keys.keyLeftSuper -> KeyEvent.KEY_LOCATION_LEFT + InputConstants.KEY_NUMPADENTER -> KeyEvent.KEY_LOCATION_NUMPAD + else -> KeyEvent.KEY_LOCATION_STANDARD + } + + fun toAwtKeyCode(shortcutKey: Int): Int = when (shortcutKey) { + //? if >= 26.3 { + /*SDLK_BACKSPACE -> KeyEvent.VK_BACK_SPACE + SDLK_TAB -> KeyEvent.VK_TAB + SDLK_RETURN, SDLK_KP_ENTER -> KeyEvent.VK_ENTER + SDLK_ESCAPE -> KeyEvent.VK_ESCAPE + SDLK_DELETE -> KeyEvent.VK_DELETE + SDLK_RIGHT -> KeyEvent.VK_RIGHT + SDLK_LEFT -> KeyEvent.VK_LEFT + SDLK_DOWN -> KeyEvent.VK_DOWN + SDLK_UP -> KeyEvent.VK_UP + SDLK_PAGEUP -> KeyEvent.VK_PAGE_UP + SDLK_PAGEDOWN -> KeyEvent.VK_PAGE_DOWN + SDLK_HOME -> KeyEvent.VK_HOME + SDLK_END -> KeyEvent.VK_END + SDLK_INSERT -> KeyEvent.VK_INSERT + SDLK_CAPSLOCK -> KeyEvent.VK_CAPS_LOCK + SDLK_LSHIFT, SDLK_RSHIFT -> KeyEvent.VK_SHIFT + SDLK_LCTRL, SDLK_RCTRL -> KeyEvent.VK_CONTROL + SDLK_LGUI, SDLK_RGUI -> KeyEvent.VK_META + SDLK_LALT, SDLK_RALT -> KeyEvent.VK_ALT + in SDLK_F1..SDLK_F12 -> KeyEvent.VK_F1 + (shortcutKey - SDLK_F1) + else -> if ((shortcutKey and (SDLK_SCANCODE_MASK or SDLK_EXTENDED_MASK)) == 0) { + KeyEvent.getExtendedKeyCodeForChar(shortcutKey) + } else { + KeyEvent.VK_UNDEFINED + } + *///?} else { + InputConstants.KEY_BACKSPACE -> KeyEvent.VK_BACK_SPACE + InputConstants.KEY_TAB -> KeyEvent.VK_TAB + InputConstants.KEY_RETURN, InputConstants.KEY_NUMPADENTER -> KeyEvent.VK_ENTER + InputConstants.KEY_ESCAPE -> KeyEvent.VK_ESCAPE + InputConstants.KEY_DELETE -> KeyEvent.VK_DELETE + InputConstants.KEY_RIGHT -> KeyEvent.VK_RIGHT + InputConstants.KEY_LEFT -> KeyEvent.VK_LEFT + InputConstants.KEY_DOWN -> KeyEvent.VK_DOWN + InputConstants.KEY_UP -> KeyEvent.VK_UP + InputConstants.KEY_PAGEUP -> KeyEvent.VK_PAGE_UP + InputConstants.KEY_PAGEDOWN -> KeyEvent.VK_PAGE_DOWN + InputConstants.KEY_HOME -> KeyEvent.VK_HOME + InputConstants.KEY_END -> KeyEvent.VK_END + InputConstants.KEY_INSERT -> KeyEvent.VK_INSERT + InputConstants.KEY_CAPSLOCK -> KeyEvent.VK_CAPS_LOCK + InputConstants.KEY_LSHIFT, InputConstants.KEY_RSHIFT -> KeyEvent.VK_SHIFT + InputConstants.KEY_LCONTROL, InputConstants.KEY_RCONTROL -> KeyEvent.VK_CONTROL + keys.keyLeftSuper, keys.keyRightSuper -> KeyEvent.VK_META + InputConstants.KEY_LALT, InputConstants.KEY_RALT -> KeyEvent.VK_ALT + in InputConstants.KEY_F1..InputConstants.KEY_F12 -> KeyEvent.VK_F1 + (shortcutKey - InputConstants.KEY_F1) + in InputConstants.KEY_0..InputConstants.KEY_9 -> KeyEvent.VK_0 + (shortcutKey - InputConstants.KEY_0) + in InputConstants.KEY_A..InputConstants.KEY_Z -> KeyEvent.VK_A + (shortcutKey - InputConstants.KEY_A) + else -> KeyEvent.VK_UNDEFINED + //?} + } +} diff --git a/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/ui/compose/impls/OneConfigUIScreen.kt b/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/ui/compose/impls/OneConfigUIScreen.kt index 0085d39a8..ccce6da74 100644 --- a/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/ui/compose/impls/OneConfigUIScreen.kt +++ b/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/ui/compose/impls/OneConfigUIScreen.kt @@ -4,7 +4,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalWindowInfo import com.mojang.blaze3d.platform.InputConstants import net.minecraft.client.gui.GuiGraphicsExtractor -import org.lwjgl.glfw.GLFW +//? if < 1.21.11 +//import org.lwjgl.glfw.GLFW import org.polyfrost.oneconfig.api.config.v1.ConfigManager import org.polyfrost.oneconfig.api.config.v1.Tree import org.polyfrost.oneconfig.api.hud.v1.HudManager @@ -243,12 +244,14 @@ class OneConfigUIScreen @JvmOverloads constructor( override fun handleMouseClicked(button: Int): Boolean { if (!closeRequested && LocalNavController.isReady) { when (button) { - GLFW.GLFW_MOUSE_BUTTON_4 -> { + //~ if < 1.21.11 'InputConstants.MOUSE_BUTTON_4' -> 'GLFW.GLFW_MOUSE_BUTTON_4' + InputConstants.MOUSE_BUTTON_4 -> { UiSounds.play(UiSoundEvent.CLICK) LocalNavController.wrapper.back() return true } - GLFW.GLFW_MOUSE_BUTTON_5 -> { + //~ if < 1.21.11 'InputConstants.MOUSE_BUTTON_5' -> 'GLFW.GLFW_MOUSE_BUTTON_5' + InputConstants.MOUSE_BUTTON_5 -> { UiSounds.play(UiSoundEvent.CLICK) LocalNavController.wrapper.forward() return true diff --git a/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/ui/keybind/MinecraftKeybindProvider.kt b/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/ui/keybind/MinecraftKeybindProvider.kt index 070fc585f..50a3ecda2 100644 --- a/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/ui/keybind/MinecraftKeybindProvider.kt +++ b/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/ui/keybind/MinecraftKeybindProvider.kt @@ -162,14 +162,12 @@ object MinecraftKeybindProvider : KeybindGroupProvider { private fun InputConstants.Key.toOneConfigKeybind(): OneConfigKeybind { return when (type) { - InputConstants.Type.KEYSYM -> { - if (value > 0) OneConfigKeybind(intArrayOf(value), null, KeyModifiers.NONE, 0L) { true } - else OneConfigKeybind(null, null, KeyModifiers.NONE, 0L) { true } - } - InputConstants.Type.MOUSE -> { - if (value >= 0) OneConfigKeybind(null, intArrayOf(value), KeyModifiers.NONE, 0L) { true } - else OneConfigKeybind(null, null, KeyModifiers.NONE, 0L) { true } - } + //~ if >= 26.3 'Type.KEYSYM' -> 'Type.KEYBOARD' + InputConstants.Type.KEYSYM if value > 0 -> + OneConfigKeybind(intArrayOf(value), null, KeyModifiers.NONE, 0L) { true } + //~ if >= 26.3 'value >= 0' -> 'value > 0' + InputConstants.Type.MOUSE if value >= 0 -> + OneConfigKeybind(null, intArrayOf(value), KeyModifiers.NONE, 0L) { true } else -> OneConfigKeybind(null, null, KeyModifiers.NONE, 0L) { true } } } @@ -179,7 +177,9 @@ object MinecraftKeybindProvider : KeybindGroupProvider { val keyCodes = keybind?.keyCodes val key = when { keybind == null || !keybind.isBound -> InputConstants.UNKNOWN + //~ if >= 26.3 'it >= 0' -> 'it > 0' mouseButtons?.firstOrNull { it >= 0 } != null -> InputConstants.Type.MOUSE.getOrCreate(mouseButtons.first { it >= 0 }) + //~ if >= 26.3 'Type.KEYSYM' -> 'Type.KEYBOARD' keyCodes?.firstOrNull { it > 0 } != null -> InputConstants.Type.KEYSYM.getOrCreate(keyCodes.first { it > 0 }) else -> InputConstants.UNKNOWN } diff --git a/minecraft/src/main/resources/META-INF/services/org.polyfrost.oneconfig.api.ui.v1.keybind.internal.KeybindCodec b/minecraft/src/main/resources/META-INF/services/org.polyfrost.oneconfig.api.ui.v1.keybind.internal.KeybindCodec new file mode 100644 index 000000000..44a2dffbc --- /dev/null +++ b/minecraft/src/main/resources/META-INF/services/org.polyfrost.oneconfig.api.ui.v1.keybind.internal.KeybindCodec @@ -0,0 +1 @@ +org.polyfrost.oneconfig.api.ui.v1.keybind.internal.MinecraftKeybindCodec diff --git a/minecraft/src/test/java/org/polyfrost/oneconfig/api/ui/v1/keybind/internal/MinecraftKeybindCodecTest.java b/minecraft/src/test/java/org/polyfrost/oneconfig/api/ui/v1/keybind/internal/MinecraftKeybindCodecTest.java new file mode 100644 index 000000000..44538261c --- /dev/null +++ b/minecraft/src/test/java/org/polyfrost/oneconfig/api/ui/v1/keybind/internal/MinecraftKeybindCodecTest.java @@ -0,0 +1,66 @@ +package org.polyfrost.oneconfig.api.ui.v1.keybind.internal; + +import com.mojang.blaze3d.platform.InputConstants; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +class MinecraftKeybindCodecTest { + private final MinecraftKeybindCodec codec = new MinecraftKeybindCodec(); + + @Test + void convertsKeys() { + assertEquals("key.keyboard.a", codec.keyName(InputConstants.KEY_A)); + assertEquals(Integer.valueOf(InputConstants.KEY_A), codec.keyCode("key.keyboard.a")); + } + + @Test + void convertsMouseButtons() { + assertEquals("key.mouse.left", codec.mouseName(InputConstants.MOUSE_BUTTON_LEFT)); + assertEquals(Integer.valueOf(InputConstants.MOUSE_BUTTON_LEFT), codec.mouseButton("key.mouse.left")); + } + + @Test + void migratesGlfwInputs() { + assertEquals("key.keyboard.a", codec.legacyKeyName(65)); + assertEquals("key.keyboard.keypad.period", codec.legacyKeyName(330)); + assertEquals("key.keyboard.application", codec.legacyKeyName(348)); + assertEquals("key.mouse.left", codec.legacyMouseName(0)); + assertEquals("key.mouse.right", codec.legacyMouseName(1)); + } + + @Test + void migratesAliases() { + //? if >= 26.3 { + /*assertEquals("key.keyboard.keypad.period", codec.keyName(99)); + assertEquals("key.keyboard.application", codec.keyName(101)); + assertEquals("key.keyboard.menu", codec.keyName(118)); + assertEquals("key.keyboard.keypad.decimal", codec.keyName(220)); + assertEquals(Integer.valueOf(99), codec.keyCode("key.keyboard.keypad.period")); + assertEquals(Integer.valueOf(101), codec.keyCode("key.keyboard.application")); + *///?} else { + assertEquals("key.keyboard.keypad.period", codec.keyName(330)); + assertEquals("key.keyboard.application", codec.keyName(348)); + assertEquals(Integer.valueOf(330), codec.keyCode("key.keyboard.keypad.period")); + assertEquals(Integer.valueOf(348), codec.keyCode("key.keyboard.application")); + //?} + } + + @Test + void rejectsInvalidKeys() { + assertNull(codec.keyName(9999)); + assertNull(codec.keyCode("key.keyboard.9999")); + assertNull(codec.keyName(-1)); + assertNull(codec.keyCode("key.keyboard.-5")); + } + + @Test + void rejectsInvalidMouseButtons() { + assertNull(codec.mouseName(InputConstants.MOUSE_BUTTON_LEFT - 1)); + assertNull(codec.mouseName(InputConstants.MOUSE_BUTTON_LEFT + 8)); + assertNull(codec.mouseButton("key.mouse.99")); + assertNull(codec.mouseButton("key.keyboard.a")); + assertNull(codec.legacyMouseName(99)); + } +} diff --git a/modules/config-impl/api/config-impl.api b/modules/config-impl/api/config-impl.api index ff6a7e723..2e4fed921 100644 --- a/modules/config-impl/api/config-impl.api +++ b/modules/config-impl/api/config-impl.api @@ -920,6 +920,17 @@ public final class org/polyfrost/oneconfig/api/config/v1/dsl/Tree_extensionsKt { public static final fun setVisualizer (Lorg/polyfrost/oneconfig/api/config/v1/Property;Ljava/lang/Class;)V } +public final class org/polyfrost/oneconfig/api/config/v1/serialize/adapter/impl/OneConfigKeybindAdapter : org/polyfrost/oneconfig/api/config/v1/serialize/adapter/Adapter { + public fun ()V + public fun (Lorg/polyfrost/oneconfig/api/ui/v1/keybind/internal/KeybindCodec;)V + public synthetic fun deserialize (Ljava/lang/Object;)Ljava/lang/Object; + public fun deserialize (Ljava/util/Map;)Lorg/polyfrost/oneconfig/api/ui/v1/keybind/OneConfigKeybind; + public fun getOutputClass ()Ljava/lang/Class; + public fun getTargetClass ()Ljava/lang/Class; + public synthetic fun serialize (Ljava/lang/Object;)Ljava/lang/Object; + public fun serialize (Lorg/polyfrost/oneconfig/api/ui/v1/keybind/OneConfigKeybind;)Ljava/util/Map; +} + public class org/polyfrost/oneconfig/api/config/v1/serialize/adapter/impl/PolyColorAdapter : org/polyfrost/oneconfig/api/config/v1/serialize/adapter/Adapter { public fun ()V public synthetic fun deserialize (Ljava/lang/Object;)Ljava/lang/Object; diff --git a/modules/config-impl/src/main/java/org/polyfrost/oneconfig/api/config/v1/ConfigManager.java b/modules/config-impl/src/main/java/org/polyfrost/oneconfig/api/config/v1/ConfigManager.java index bde61d8d4..7fc8fc3e1 100644 --- a/modules/config-impl/src/main/java/org/polyfrost/oneconfig/api/config/v1/ConfigManager.java +++ b/modules/config-impl/src/main/java/org/polyfrost/oneconfig/api/config/v1/ConfigManager.java @@ -38,6 +38,7 @@ import org.polyfrost.oneconfig.api.config.v1.collect.impl.OneConfigCollector; import org.polyfrost.oneconfig.api.config.v1.serialize.ObjectSerializer; import org.polyfrost.oneconfig.api.config.v1.serialize.adapter.impl.PolyColorAdapter; +import org.polyfrost.oneconfig.api.config.v1.serialize.adapter.impl.OneConfigKeybindAdapter; import org.polyfrost.oneconfig.api.config.v1.serialize.impl.FileSerializer; import org.polyfrost.oneconfig.api.config.v1.serialize.impl.NightConfigSerializer; @@ -130,6 +131,7 @@ public static Path profileDir(String profile) { static { ObjectSerializer.INSTANCE.registerTypeAdapter(new PolyColorAdapter()); + ObjectSerializer.INSTANCE.registerTypeAdapter(new OneConfigKeybindAdapter()); registerCollector(new OneConfigCollector()); Runtime.getRuntime().addShutdownHook(new Thread(() -> { ConfigManager current = active; diff --git a/modules/config-impl/src/main/java/org/polyfrost/oneconfig/api/config/v1/serialize/adapter/impl/OneConfigKeybindAdapter.java b/modules/config-impl/src/main/java/org/polyfrost/oneconfig/api/config/v1/serialize/adapter/impl/OneConfigKeybindAdapter.java new file mode 100644 index 000000000..87bb60e9e --- /dev/null +++ b/modules/config-impl/src/main/java/org/polyfrost/oneconfig/api/config/v1/serialize/adapter/impl/OneConfigKeybindAdapter.java @@ -0,0 +1,171 @@ +package org.polyfrost.oneconfig.api.config.v1.serialize.adapter.impl; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.Nullable; +import org.polyfrost.oneconfig.api.config.v1.serialize.adapter.Adapter; +import org.polyfrost.oneconfig.api.notifications.v1.Notifications; +import org.polyfrost.oneconfig.api.ui.v1.keybind.BindNotInScreen; +import org.polyfrost.oneconfig.api.ui.v1.keybind.OneConfigKeybind; +import org.polyfrost.oneconfig.api.ui.v1.keybind.internal.KeybindCodec; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; + +public final class OneConfigKeybindAdapter extends Adapter { + private static final Logger LOGGER = LogManager.getLogger("OneConfig/Keybinds"); + private final KeybindCodec codec; + + public OneConfigKeybindAdapter() { + this.codec = null; + } + + public OneConfigKeybindAdapter(KeybindCodec codec) { + this.codec = codec; + } + + @Override + public Map serialize(OneConfigKeybind in) { + Map out = new HashMap<>(4); + if (in.getKeyCodes() != null) out.put("keyCodes", encode(in.getKeyCodes(), true)); + if (in.getMouseBtns() != null) out.put("mouseBtns", encode(in.getMouseBtns(), false)); + if (in.getMods() != 0) out.put("mods", in.getMods()); + if (in.getDurationNanos() != 0L) out.put("durationNanos", in.getDurationNanos()); + return out; + } + + @Override + public OneConfigKeybind deserialize(Map in) { + List dropped = new ArrayList<>(); + int[] keyCodes = decode(in.get("keyCodes"), true, dropped); + int[] mouseBtns = decode(in.get("mouseBtns"), false, dropped); + byte mods = ((Number) in.getOrDefault("mods", 0)).byteValue(); + long durationNanos = ((Number) in.getOrDefault("durationNanos", 0L)).longValue(); + notifyDropped(dropped); + if (BindNotInScreen.class.getName().equals(in.get("class"))) { + return new BindNotInScreen(keyCodes, mouseBtns, mods, durationNanos, ignored -> true); + } + return new OneConfigKeybind(keyCodes, mouseBtns, mods, durationNanos, ignored -> true); + } + + @Override + public Class getTargetClass() { + return OneConfigKeybind.class; + } + + @Override + public Class getOutputClass() { + return Map.class; + } + + private List encode(int[] values, boolean keyboard) { + KeybindCodec codec = codec(); + List out = new ArrayList<>(values.length); + for (int value : values) { + if (codec == null) { + out.add(value); + continue; + } + String name = keyboard ? codec.keyName(value) : codec.mouseName(value); + if (name != null) out.add(name); + else LOGGER.warn("Cannot save unsupported {} input {}", keyboard ? "keyboard" : "mouse", value); + } + return out; + } + + private int[] decode(Object value, boolean keyboard, List dropped) { + List values = asList(value); + if (values == null) return null; + + KeybindCodec codec = codec(); + int[] out = new int[values.size()]; + int size = 0; + for (Object entry : values) { + Integer code = toCode(entry, keyboard, codec); + if (code != null) out[size++] = code; + else { + LOGGER.warn("Ignoring unsupported {} input {}", keyboard ? "keyboard" : "mouse", entry); + dropped.add(entry); + } + } + + if (size == out.length) return out; + return Arrays.copyOf(out, size); + } + + private @Nullable Integer toCode(Object entry, boolean keyboard, @Nullable KeybindCodec codec) { + if (entry instanceof Number) { + int legacy = ((Number) entry).intValue(); + if (codec == null) return legacy; + String name = keyboard ? codec.legacyKeyName(legacy) : codec.legacyMouseName(legacy); + return name == null ? null : (keyboard ? codec.keyCode(name) : codec.mouseButton(name)); + } + if (entry instanceof String) { + if (codec == null) return null; + String name = (String) entry; + return keyboard ? codec.keyCode(name) : codec.mouseButton(name); + } + return null; + } + + private static @Nullable List asList(Object value) { + if (value == null) return null; + if (value instanceof List) return (List) value; + if (value instanceof Collection) return new ArrayList<>((Collection) value); + if (value.getClass().isArray()) { + int len = Array.getLength(value); + List out = new ArrayList<>(len); + for (int i = 0; i < len; i++) out.add(Array.get(value, i)); + return out; + } + LOGGER.warn("Ignoring unsupported keybind value {}", value); + return null; + } + + private static void notifyDropped(List dropped) { + if (dropped.isEmpty()) return; + try { + StringBuilder names = new StringBuilder(); + for (Object entry : dropped) { + if (names.length() != 0) names.append(", "); + names.append('\'').append(entry).append('\''); + } + Notifications.error( + "Unsupported keybind" + (dropped.size() == 1 ? "" : "s"), + names + (dropped.size() == 1 ? " is" : " are") + + " not supported by this Minecraft version and " + (dropped.size() == 1 ? "was" : "were") + + " unbound." + ); + } catch (Throwable t) { + LOGGER.error("Failed to notify about unsupported keybind inputs {}", dropped, t); + } + } + + private KeybindCodec codec() { + return codec != null ? codec : CodecService.INSTANCE; + } + + private static final class CodecService { + private static final @Nullable KeybindCodec INSTANCE = load(); + + private static @Nullable KeybindCodec load() { + try { + Iterator it = ServiceLoader + .load(KeybindCodec.class, KeybindCodec.class.getClassLoader()).iterator(); + if (it.hasNext()) return it.next(); + } catch (Throwable t) { + LOGGER.warn("Failed to load KeybindCodec; keybinds will be stored as raw platform codes", t); + return null; + } + LOGGER.warn("No KeybindCodec found; keybinds will be stored as raw platform codes"); + return null; + } + } +} diff --git a/modules/config-impl/src/test/java/org/polyfrost/oneconfig/api/config/v1/OneConfigKeybindAdapterTest.java b/modules/config-impl/src/test/java/org/polyfrost/oneconfig/api/config/v1/OneConfigKeybindAdapterTest.java new file mode 100644 index 000000000..dfd5ba7d1 --- /dev/null +++ b/modules/config-impl/src/test/java/org/polyfrost/oneconfig/api/config/v1/OneConfigKeybindAdapterTest.java @@ -0,0 +1,134 @@ +package org.polyfrost.oneconfig.api.config.v1; + +import org.junit.jupiter.api.Test; +import org.polyfrost.oneconfig.api.config.v1.serialize.adapter.impl.OneConfigKeybindAdapter; +import org.polyfrost.oneconfig.api.ui.v1.keybind.BindNotInScreen; +import org.polyfrost.oneconfig.api.ui.v1.keybind.OneConfigKeybind; +import org.polyfrost.oneconfig.api.ui.v1.keybind.internal.KeybindCodec; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class OneConfigKeybindAdapterTest { + private final OneConfigKeybindAdapter adapter = new OneConfigKeybindAdapter(new TestCodec()); + + @Test + void writesKeyNames() { + OneConfigKeybind keybind = new OneConfigKeybind(new int[]{4}, null, (byte) 2, 1L, ignored -> true); + + Map serialized = adapter.serialize(keybind); + + assertEquals(List.of("key.keyboard.a"), serialized.get("keyCodes")); + assertNull(serialized.get("mouseBtns")); + assertEquals((byte) 2, serialized.get("mods")); + assertEquals(1L, serialized.get("durationNanos")); + } + + @Test + void readsKeyNames() { + Map serialized = new HashMap<>(); + serialized.put("keyCodes", List.of("key.keyboard.a")); + serialized.put("mouseBtns", List.of("key.mouse.left")); + + OneConfigKeybind keybind = adapter.deserialize(serialized); + + assertArrayEquals(new int[]{4}, keybind.getKeyCodes()); + assertArrayEquals(new int[]{1}, keybind.getMouseBtns()); + assertEquals((byte) 0, keybind.getMods()); + assertEquals(0L, keybind.getDurationNanos()); + } + + @Test + void migratesGlfwInputs() { + Map serialized = new HashMap<>(); + serialized.put("keyCodes", List.of(65)); + serialized.put("mouseBtns", List.of(0)); + + OneConfigKeybind keybind = adapter.deserialize(serialized); + + assertArrayEquals(new int[]{4}, keybind.getKeyCodes()); + assertArrayEquals(new int[]{1}, keybind.getMouseBtns()); + assertEquals((byte) 0, keybind.getMods()); + assertEquals(0L, keybind.getDurationNanos()); + } + + @Test + void readsArraysAsWellAsLists() { + Map serialized = new HashMap<>(); + serialized.put("keyCodes", new int[]{65}); // numeric entries are still migrated from the legacy GLFW codes + serialized.put("mouseBtns", new Object[]{"key.mouse.left"}); + + OneConfigKeybind keybind = adapter.deserialize(serialized); + + assertArrayEquals(new int[]{4}, keybind.getKeyCodes()); + assertArrayEquals(new int[]{1}, keybind.getMouseBtns()); + } + + @Test + void dropsUnsupportedInputsInsteadOfThrowing() { + Map serialized = new HashMap<>(); + serialized.put("keyCodes", List.of("key.keyboard.a", "key.keyboard.nonsense", Boolean.TRUE)); + + OneConfigKeybind keybind = adapter.deserialize(serialized); + + assertArrayEquals(new int[]{4}, keybind.getKeyCodes()); + } + + @Test + void roundTripsRawCodesWithoutACodec() { + OneConfigKeybindAdapter noCodec = new OneConfigKeybindAdapter(); // no KeybindCodec service on this classpath + OneConfigKeybind keybind = new OneConfigKeybind(new int[]{4}, null, (byte) 0, 0L, ignored -> true); + + Map serialized = noCodec.serialize(keybind); + + assertEquals(List.of(4), serialized.get("keyCodes")); + assertArrayEquals(new int[]{4}, noCodec.deserialize(serialized).getKeyCodes()); + } + + @Test + void deserializesBindNotInScreen() { + Map serialized = new HashMap<>(); + serialized.put("class", BindNotInScreen.class.getName()); + serialized.put("keyCodes", List.of("key.keyboard.a")); + + assertInstanceOf(BindNotInScreen.class, adapter.deserialize(serialized)); + } + + private static final class TestCodec implements KeybindCodec { + @Override + public String keyName(int code) { + return code == 4 ? "key.keyboard.a" : null; + } + + @Override + public Integer keyCode(String name) { + return "key.keyboard.a".equals(name) ? 4 : null; + } + + @Override + public String mouseName(int button) { + return button == 1 ? "key.mouse.left" : null; + } + + @Override + public Integer mouseButton(String name) { + return "key.mouse.left".equals(name) ? 1 : null; + } + + @Override + public String legacyKeyName(int glfwCode) { + return glfwCode == 65 ? "key.keyboard.a" : null; + } + + @Override + public String legacyMouseName(int glfwButton) { + return glfwButton == 0 ? "key.mouse.left" : null; + } + } +} diff --git a/modules/events/api/events.api b/modules/events/api/events.api index 8df60bc4f..618614051 100644 --- a/modules/events/api/events.api +++ b/modules/events/api/events.api @@ -93,6 +93,9 @@ public class org/polyfrost/oneconfig/api/event/v1/events/JvmShutdownEvent : org/ } public class org/polyfrost/oneconfig/api/event/v1/events/KeyInputEvent : org/polyfrost/oneconfig/api/event/v1/events/Event { + public static final field PRESSED I + public static final field RELEASED I + public static final field REPEAT I public final field character C public final field key I public final field state I @@ -113,6 +116,8 @@ public class org/polyfrost/oneconfig/api/event/v1/events/MainMenuFpsEvent : org/ } public class org/polyfrost/oneconfig/api/event/v1/events/MouseInputEvent : org/polyfrost/oneconfig/api/event/v1/events/Event { + public static final field PRESSED I + public static final field RELEASED I public final field button I public final field state I public fun (II)V diff --git a/modules/events/src/main/java/org/polyfrost/oneconfig/api/event/v1/events/KeyInputEvent.java b/modules/events/src/main/java/org/polyfrost/oneconfig/api/event/v1/events/KeyInputEvent.java index 8090d06fd..2c16c3a34 100644 --- a/modules/events/src/main/java/org/polyfrost/oneconfig/api/event/v1/events/KeyInputEvent.java +++ b/modules/events/src/main/java/org/polyfrost/oneconfig/api/event/v1/events/KeyInputEvent.java @@ -32,6 +32,10 @@ * To translate this into something usable by PolyUI have a look at {@code KeybindManager.translateKey(EventManager, keyCode, character, down)} */ public class KeyInputEvent implements Event { + public static final int RELEASED = 0; + public static final int PRESSED = 1; + public static final int REPEAT = 2; + /** * The keycode that created this event *
@@ -44,9 +48,7 @@ public class KeyInputEvent implements Event { */ public final char character; /** - * 0 = up
- * 1 = down
- * 2 = repeat + * {@link #RELEASED}, {@link #PRESSED}, or {@link #REPEAT} */ public final int state; @@ -69,6 +71,6 @@ public int component3() { } public boolean isPressed() { - return state > 0; + return state != RELEASED; } } diff --git a/modules/events/src/main/java/org/polyfrost/oneconfig/api/event/v1/events/MouseInputEvent.java b/modules/events/src/main/java/org/polyfrost/oneconfig/api/event/v1/events/MouseInputEvent.java index 486851374..f43e5ca16 100644 --- a/modules/events/src/main/java/org/polyfrost/oneconfig/api/event/v1/events/MouseInputEvent.java +++ b/modules/events/src/main/java/org/polyfrost/oneconfig/api/event/v1/events/MouseInputEvent.java @@ -30,7 +30,11 @@ import org.polyfrost.oneconfig.api.event.v1.EventManager; public class MouseInputEvent implements Event { + public static final int RELEASED = 0; + public static final int PRESSED = 1; + public final int button; + /** {@link #RELEASED} or {@link #PRESSED}. */ public final int state; public MouseInputEvent(int button, int state) { diff --git a/modules/internal/src/main/java/org/polyfrost/oneconfig/internal/OneConfigConfig.java b/modules/internal/src/main/java/org/polyfrost/oneconfig/internal/OneConfigConfig.java index a41d9140b..87b9c46a0 100644 --- a/modules/internal/src/main/java/org/polyfrost/oneconfig/internal/OneConfigConfig.java +++ b/modules/internal/src/main/java/org/polyfrost/oneconfig/internal/OneConfigConfig.java @@ -7,6 +7,8 @@ import org.polyfrost.oneconfig.api.config.v1.annotations.Number; import org.polyfrost.oneconfig.api.config.v1.annotations.Slider; import org.polyfrost.oneconfig.api.config.v1.annotations.Switch; +import org.polyfrost.oneconfig.api.platform.v1.Keys; +import org.polyfrost.oneconfig.api.platform.v1.Platform; import org.polyfrost.oneconfig.api.ui.v1.keybind.KeybindManager; import org.polyfrost.oneconfig.api.ui.v1.keybind.KeyModifiers; import org.polyfrost.oneconfig.api.ui.v1.keybind.KeybindUtils; @@ -16,10 +18,9 @@ import org.polyfrost.oneconfig.internal.ui.hud.screens.HudDesignSession; public class OneConfigConfig extends Config { + private static final Keys KEYS = Platform.compatibility().keys(); private static final byte HUD_ACTION_MODS = KeybindUtils.getActionModifier(); - // keybinds store GLFW key codes because that is what KeybindManager matches against - // 344 == GLFW_KEY_RIGHT_SHIFT written as a literal to avoid a GLFW dependency here // the open action comes from the minecraft module via setOpenAction since it cannot be serialized @Keybind( title = "oneconfig.preferences.keybind.title", @@ -30,7 +31,7 @@ public class OneConfigConfig extends Config { descriptionTranslation = true ) public static OneConfigKeybind oneConfigKeybind = - new OneConfigKeybind(new int[] {344}, null, KeyModifiers.NONE, 0L, pressed -> true); + new OneConfigKeybind(new int[] {KEYS.getKeyRightShift()}, null, KeyModifiers.NONE, 0L, pressed -> true); @Switch( title = "oneconfig.preferences.keybind_closes_gui.title", @@ -51,7 +52,7 @@ public class OneConfigConfig extends Config { descriptionTranslation = true ) public static OneConfigKeybind hudSettingsKeybind = - new OneConfigKeybind(new int[] {69}, null, HUD_ACTION_MODS, 0L, pressed -> true); + new OneConfigKeybind(new int[] {KEYS.getKeyE()}, null, HUD_ACTION_MODS, 0L, pressed -> true); @Keybind( title = "oneconfig.preferences.hud_visibility_keybind.title", @@ -62,7 +63,7 @@ public class OneConfigConfig extends Config { descriptionTranslation = true ) public static OneConfigKeybind hudVisibilityKeybind = - new OneConfigKeybind(new int[] {72}, null, HUD_ACTION_MODS, 0L, pressed -> true); + new OneConfigKeybind(new int[] {KEYS.getKeyH()}, null, HUD_ACTION_MODS, 0L, pressed -> true); @Keybind( title = "oneconfig.preferences.hud_lock_keybind.title", @@ -73,7 +74,7 @@ public class OneConfigConfig extends Config { descriptionTranslation = true ) public static OneConfigKeybind hudLockKeybind = - new OneConfigKeybind(new int[] {76}, null, HUD_ACTION_MODS, 0L, pressed -> true); + new OneConfigKeybind(new int[] {KEYS.getKeyL()}, null, HUD_ACTION_MODS, 0L, pressed -> true); @Keybind( title = "oneconfig.preferences.hud_copy_keybind.title", @@ -84,7 +85,7 @@ public class OneConfigConfig extends Config { descriptionTranslation = true ) public static OneConfigKeybind hudCopyKeybind = - new OneConfigKeybind(new int[] {67}, null, HUD_ACTION_MODS, 0L, pressed -> true); + new OneConfigKeybind(new int[] {KEYS.getKeyC()}, null, HUD_ACTION_MODS, 0L, pressed -> true); @Keybind( title = "oneconfig.preferences.hud_cut_keybind.title", @@ -95,7 +96,7 @@ public class OneConfigConfig extends Config { descriptionTranslation = true ) public static OneConfigKeybind hudCutKeybind = - new OneConfigKeybind(new int[] {88}, null, HUD_ACTION_MODS, 0L, pressed -> true); + new OneConfigKeybind(new int[] {KEYS.getKeyX()}, null, HUD_ACTION_MODS, 0L, pressed -> true); @Keybind( title = "oneconfig.preferences.hud_paste_keybind.title", @@ -106,7 +107,7 @@ public class OneConfigConfig extends Config { descriptionTranslation = true ) public static OneConfigKeybind hudPasteKeybind = - new OneConfigKeybind(new int[] {86}, null, HUD_ACTION_MODS, 0L, pressed -> true); + new OneConfigKeybind(new int[] {KEYS.getKeyV()}, null, HUD_ACTION_MODS, 0L, pressed -> true); @Keybind( title = "oneconfig.preferences.hud_duplicate_keybind.title", @@ -117,7 +118,7 @@ public class OneConfigConfig extends Config { descriptionTranslation = true ) public static OneConfigKeybind hudDuplicateKeybind = - new OneConfigKeybind(new int[] {68}, null, HUD_ACTION_MODS, 0L, pressed -> true); + new OneConfigKeybind(new int[] {KEYS.getKeyD()}, null, HUD_ACTION_MODS, 0L, pressed -> true); @Keybind( title = "oneconfig.preferences.hud_reset_keybind.title", @@ -128,7 +129,7 @@ public class OneConfigConfig extends Config { descriptionTranslation = true ) public static OneConfigKeybind hudResetKeybind = - new OneConfigKeybind(new int[] {82}, null, HUD_ACTION_MODS, 0L, pressed -> true); + new OneConfigKeybind(new int[] {KEYS.getKeyR()}, null, HUD_ACTION_MODS, 0L, pressed -> true); @Keybind( title = "oneconfig.preferences.hud_delete_keybind.title", @@ -139,7 +140,7 @@ public class OneConfigConfig extends Config { descriptionTranslation = true ) public static OneConfigKeybind hudDeleteKeybind = - new OneConfigKeybind(new int[] {261}, null, KeyModifiers.NONE, 0L, pressed -> true); + new OneConfigKeybind(new int[] {KEYS.getKeyDelete()}, null, KeyModifiers.NONE, 0L, pressed -> true); @Keybind( title = "oneconfig.preferences.hud_select_all_keybind.title", @@ -150,7 +151,7 @@ public class OneConfigConfig extends Config { descriptionTranslation = true ) public static OneConfigKeybind hudSelectAllKeybind = - new OneConfigKeybind(new int[] {65}, null, HUD_ACTION_MODS, 0L, pressed -> true); + new OneConfigKeybind(new int[] {KEYS.getKeyA()}, null, HUD_ACTION_MODS, 0L, pressed -> true); @Switch( title = "oneconfig.preferences.hud_show_keybind_hints.title", diff --git a/modules/notifications/src/main/kotlin/org/polyfrost/oneconfig/api/notifications/v1/ToastInput.kt b/modules/notifications/src/main/kotlin/org/polyfrost/oneconfig/api/notifications/v1/ToastInput.kt index 029e3ab0b..8e1bd2e17 100644 --- a/modules/notifications/src/main/kotlin/org/polyfrost/oneconfig/api/notifications/v1/ToastInput.kt +++ b/modules/notifications/src/main/kotlin/org/polyfrost/oneconfig/api/notifications/v1/ToastInput.kt @@ -31,6 +31,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import org.polyfrost.oneconfig.api.event.v1.EventManager import org.polyfrost.oneconfig.api.event.v1.events.MouseInputEvent +import org.polyfrost.oneconfig.api.platform.v1.Platform import java.util.concurrent.atomic.AtomicBoolean internal object ToastInput { @@ -47,6 +48,8 @@ internal object ToastInput { private val installed = AtomicBoolean(false) + private val leftButton by lazy { Platform.compatibility().keys().mouseButtonLeft } + fun clearHover() { hoverTarget = null hoveredAction = null @@ -59,8 +62,9 @@ internal object ToastInput { mouseY = e.y } EventManager.register(MouseInputEvent::class.java) { e -> - // button 0 is GLFW left and state 1 is GLFW_PRESS - if (e.button == 0 && e.state == 1) NotificationsRenderer.dispatchClick(hoverTarget) + if (e.button == leftButton && e.state == MouseInputEvent.PRESSED) { + NotificationsRenderer.dispatchClick(hoverTarget) + } } } } diff --git a/modules/ui/api/ui.api b/modules/ui/api/ui.api index d0d4e1fbe..5cae4a66c 100644 --- a/modules/ui/api/ui.api +++ b/modules/ui/api/ui.api @@ -248,6 +248,15 @@ public final class org/polyfrost/oneconfig/api/ui/v1/keybind/TextInputFocusKt { public static final fun trackTextInputFocus (Landroidx/compose/ui/Modifier;)Landroidx/compose/ui/Modifier; } +public abstract interface class org/polyfrost/oneconfig/api/ui/v1/keybind/internal/KeybindCodec { + public abstract fun keyCode (Ljava/lang/String;)Ljava/lang/Integer; + public abstract fun keyName (I)Ljava/lang/String; + public abstract fun legacyKeyName (I)Ljava/lang/String; + public abstract fun legacyMouseName (I)Ljava/lang/String; + public abstract fun mouseButton (Ljava/lang/String;)Ljava/lang/Integer; + public abstract fun mouseName (I)Ljava/lang/String; +} + public abstract interface class org/polyfrost/oneconfig/api/ui/v1/screen/BlurScreen { public abstract fun hasBackgroundBlur ()Z } diff --git a/modules/ui/src/main/java/org/polyfrost/oneconfig/api/ui/v1/keybind/internal/KeybindCodec.java b/modules/ui/src/main/java/org/polyfrost/oneconfig/api/ui/v1/keybind/internal/KeybindCodec.java new file mode 100644 index 000000000..9fe0a89e3 --- /dev/null +++ b/modules/ui/src/main/java/org/polyfrost/oneconfig/api/ui/v1/keybind/internal/KeybindCodec.java @@ -0,0 +1,17 @@ +package org.polyfrost.oneconfig.api.ui.v1.keybind.internal; + +import org.jetbrains.annotations.Nullable; + +public interface KeybindCodec { + @Nullable String keyName(int code); + + @Nullable Integer keyCode(String name); + + @Nullable String mouseName(int button); + + @Nullable Integer mouseButton(String name); + + @Nullable String legacyKeyName(int glfwCode); + + @Nullable String legacyMouseName(int glfwButton); +} diff --git a/modules/ui/src/main/kotlin/org/polyfrost/oneconfig/api/ui/v1/keybind/KeybindManager.kt b/modules/ui/src/main/kotlin/org/polyfrost/oneconfig/api/ui/v1/keybind/KeybindManager.kt index ecdbdffff..023abe650 100644 --- a/modules/ui/src/main/kotlin/org/polyfrost/oneconfig/api/ui/v1/keybind/KeybindManager.kt +++ b/modules/ui/src/main/kotlin/org/polyfrost/oneconfig/api/ui/v1/keybind/KeybindManager.kt @@ -59,8 +59,8 @@ object KeybindManager { // key == 0 marks a character event not a coded key press and it never reports a release // so tracking it in downKeys would leave 0 stuck and match any keybind bound to code 0 if (key == 0) return@eventHandler - if (state == 2) return@eventHandler - val down = state == 1 + if (state == KeyInputEvent.REPEAT) return@eventHandler + val down = state == KeyInputEvent.PRESSED val mod = MODIFIER_MAP[key] if (mod != null) { mods = if (down) (mods or mod) else (mods and mod.inv()) diff --git a/modules/ui/src/main/kotlin/org/polyfrost/oneconfig/api/ui/v1/keybind/OneConfigKeybind.kt b/modules/ui/src/main/kotlin/org/polyfrost/oneconfig/api/ui/v1/keybind/OneConfigKeybind.kt index a3dd87bcc..29f361f3e 100644 --- a/modules/ui/src/main/kotlin/org/polyfrost/oneconfig/api/ui/v1/keybind/OneConfigKeybind.kt +++ b/modules/ui/src/main/kotlin/org/polyfrost/oneconfig/api/ui/v1/keybind/OneConfigKeybind.kt @@ -1,5 +1,6 @@ package org.polyfrost.oneconfig.api.ui.v1.keybind +import org.polyfrost.oneconfig.api.platform.v1.Platform import org.polyfrost.oneconfig.utils.v1.OverwriteMergeable import kotlin.experimental.and @@ -121,70 +122,14 @@ open class OneConfigKeybind( val parts = LinkedHashSet() parts += modifierNames(mods) keyCodes?.forEach { parts += keyName(it) } - mouseBtns?.forEach { parts += "Mouse ${it + 1}" } + mouseBtns?.forEach { parts += Platform.compatibility().keys().mouseName(it) } return parts.joinToString(" + ").ifEmpty { "None" } } companion object { - /** Human-readable name for a GLFW key code */ + /** Human-readable name for a key code */ @JvmStatic - fun keyName(glfwCode: Int): String = when (glfwCode) { - -1 -> "None" - 32 -> "Space" - 39 -> "'" - 44 -> "," - 45 -> "-" - 46 -> "." - 47 -> "/" - 59 -> ";" - 61 -> "=" - 91 -> "[" - 92 -> "\\" - 93 -> "]" - 96 -> "`" - 161 -> "Non-US #1" - 162 -> "Non-US #2" - 256 -> "Escape" - 257 -> "Enter" - 258 -> "Tab" - 259 -> "Backspace" - 260 -> "Insert" - 261 -> "Delete" - 262 -> "Right" - 263 -> "Left" - 264 -> "Down" - 265 -> "Up" - 266 -> "Page Up" - 267 -> "Page Down" - 268 -> "Home" - 269 -> "End" - 280 -> "Caps Lock" - 281 -> "Scroll Lock" - 282 -> "Num Lock" - 283 -> "Print Screen" - 284 -> "Pause" - 330 -> "Numpad ." - 331 -> "Numpad /" - 332 -> "Numpad *" - 333 -> "Numpad -" - 334 -> "Numpad +" - 335 -> "Numpad Enter" - 336 -> "Numpad =" - 340 -> "Left Shift" - 344 -> "Right Shift" - 341 -> "Left Ctrl" - 345 -> "Right Ctrl" - 342 -> "Left Alt" - 346 -> "Right Alt" - 343 -> "Left Super" - 347 -> "Right Super" - 348 -> "Menu" - in 48..57 -> ('0' + (glfwCode - 48)).toString() - in 65..90 -> ('A' + (glfwCode - 65)).toString() - in 290..313 -> "F${glfwCode - 289}" // F1 to F24 - in 320..329 -> "Numpad ${glfwCode - 320}" // KP_0 to KP_9 - else -> "Key $glfwCode" - } + fun keyName(key: Int): String = Platform.compatibility().keys().keyName(key) private fun modifierNames(mods: Byte): List = buildList { if (KeyModifiers.has(mods, KeyModifiers.CTRL)) add("Ctrl") diff --git a/modules/utils/api/utils.api b/modules/utils/api/utils.api index 7a0203fcc..325da1bbd 100644 --- a/modules/utils/api/utils.api +++ b/modules/utils/api/utils.api @@ -87,14 +87,27 @@ public abstract interface class org/polyfrost/oneconfig/api/platform/v1/I18nPlat } public abstract interface class org/polyfrost/oneconfig/api/platform/v1/Keys { + public abstract fun getKeyA ()I + public abstract fun getKeyC ()I + public abstract fun getKeyD ()I + public abstract fun getKeyDelete ()I + public abstract fun getKeyE ()I + public abstract fun getKeyH ()I + public abstract fun getKeyL ()I public abstract fun getKeyLeftAlt ()I public abstract fun getKeyLeftControl ()I public abstract fun getKeyLeftShift ()I public abstract fun getKeyLeftSuper ()I + public abstract fun getKeyR ()I public abstract fun getKeyRightAlt ()I public abstract fun getKeyRightControl ()I public abstract fun getKeyRightShift ()I public abstract fun getKeyRightSuper ()I + public abstract fun getKeyV ()I + public abstract fun getKeyX ()I + public abstract fun getMouseButtonLeft ()I + public abstract fun keyName (I)Ljava/lang/String; + public abstract fun mouseName (I)Ljava/lang/String; } public abstract interface class org/polyfrost/oneconfig/api/platform/v1/LoaderPlatform { diff --git a/modules/utils/src/main/java/org/polyfrost/oneconfig/utils/v1/ClipboardHelper.java b/modules/utils/src/main/java/org/polyfrost/oneconfig/utils/v1/ClipboardHelper.java index 8cdfd96ea..8c2e8e235 100644 --- a/modules/utils/src/main/java/org/polyfrost/oneconfig/utils/v1/ClipboardHelper.java +++ b/modules/utils/src/main/java/org/polyfrost/oneconfig/utils/v1/ClipboardHelper.java @@ -49,7 +49,7 @@ /** * Cross-platform system clipboard access *

- * Uses GLFW/AWT on Windows/Linux + * Uses GLFW/SDL/AWT on Windows/Linux *

* Uses NSPasteboard on macOS */ @@ -62,6 +62,7 @@ public final class ClipboardHelper { .contains("mac"); private static final String MACOS_CLIPBOARD_CLASS = "org.polyfrost.oneconfig.utils.v1.MacOSClipboard"; + private static final String SDL_CLIPBOARD_CLASS = "org.lwjgl.sdl.SDLClipboard"; private static final String GLFW_CLASS = "org.lwjgl.glfw.GLFW"; private ClipboardHelper() { @@ -73,9 +74,10 @@ public static String getString() { if (IS_MAC) { return invokeMacNullable("getString"); } - String glfwValue = getStringFromGlfw(); - if (glfwValue != null) { - return glfwValue; + String nativeValue = getStringFromSdl(); + if (nativeValue == null) nativeValue = getStringFromGlfw(); + if (nativeValue != null) { + return nativeValue; } Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); if (!clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor)) { @@ -96,7 +98,7 @@ public static boolean setString(@Nullable String value) { if (IS_MAC) { return invokeMacBoolean("setString", value); } - if (setStringWithGlfw(value)) { + if (setStringWithSdl(value) || setStringWithGlfw(value)) { return true; } Toolkit.getDefaultToolkit().getSystemClipboard() @@ -159,7 +161,7 @@ public static boolean clear() { if (IS_MAC) { return invokeMacBoolean("clear"); } - if (setStringWithGlfw("")) { + if (setStringWithSdl("") || setStringWithGlfw("")) { return true; } Toolkit.getDefaultToolkit().getSystemClipboard() @@ -195,6 +197,29 @@ private static boolean invokeMacBoolean(String methodName, Object... args) throw return Boolean.TRUE.equals(method.invoke(null, args)); } + @Nullable + private static String getStringFromSdl() { + try { + Method method = Class.forName(SDL_CLIPBOARD_CLASS).getMethod("SDL_GetClipboardText"); + String value = (String) method.invoke(null); + return value == null || value.isEmpty() ? null : value; + } catch (Throwable t) { + LOGGER.debug("Failed to read SDL clipboard text", t); + return null; + } + } + + private static boolean setStringWithSdl(String value) { + try { + Class clazz = Class.forName(SDL_CLIPBOARD_CLASS); + Method method = clazz.getMethod("SDL_SetClipboardText", CharSequence.class); + return Boolean.TRUE.equals(method.invoke(null, value)); + } catch (Throwable t) { + LOGGER.debug("Failed to write SDL clipboard text", t); + return false; + } + } + @Nullable private static String getStringFromGlfw() { try { diff --git a/modules/utils/src/main/kotlin/org/polyfrost/oneconfig/api/platform/v1/Keys.kt b/modules/utils/src/main/kotlin/org/polyfrost/oneconfig/api/platform/v1/Keys.kt index aa3d8b5d3..2a2b9f70d 100644 --- a/modules/utils/src/main/kotlin/org/polyfrost/oneconfig/api/platform/v1/Keys.kt +++ b/modules/utils/src/main/kotlin/org/polyfrost/oneconfig/api/platform/v1/Keys.kt @@ -1,7 +1,19 @@ package org.polyfrost.oneconfig.api.platform.v1 interface Keys { + fun keyName(key: Int): String + fun mouseName(button: Int): String + val keyA: Int + val keyC: Int + val keyD: Int + val keyE: Int + val keyH: Int + val keyL: Int + val keyR: Int + val keyV: Int + val keyX: Int + val keyDelete: Int val keyLeftShift: Int val keyRightShift: Int val keyLeftControl: Int @@ -11,4 +23,6 @@ interface Keys { val keyLeftSuper: Int val keyRightSuper: Int + val mouseButtonLeft: Int + } \ No newline at end of file