diff options
Diffstat (limited to 'src')
16 files changed, 443 insertions, 245 deletions
diff --git a/src/compat/yacl/java/KeybindingController.kt b/src/compat/yacl/java/KeybindingController.kt index 204d521..e9bd500 100644 --- a/src/compat/yacl/java/KeybindingController.kt +++ b/src/compat/yacl/java/KeybindingController.kt @@ -10,6 +10,7 @@ import net.minecraft.text.Text import moe.nea.firmament.gui.config.KeyBindingHandler import moe.nea.firmament.gui.config.KeyBindingStateManager import moe.nea.firmament.gui.config.ManagedOption +import moe.nea.firmament.keybindings.GenericInputButton import moe.nea.firmament.keybindings.SavedKeyBinding class KeybindingController( @@ -57,11 +58,11 @@ class KeybindingWidget( } override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { - return sm.keyboardEvent(keyCode, true) + return sm.keyboardEvent(GenericInputButton.ofKeyAndScan(keyCode, scanCode), true) } override fun keyReleased(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { - return sm.keyboardEvent(keyCode, false) + return sm.keyboardEvent(GenericInputButton.ofKeyAndScan(keyCode, scanCode), false) } override fun unfocus() { diff --git a/src/main/java/moe/nea/firmament/mixins/MaintainKeyboardStatePatch.java b/src/main/java/moe/nea/firmament/mixins/MaintainKeyboardStatePatch.java new file mode 100644 index 0000000..d433f39 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/MaintainKeyboardStatePatch.java @@ -0,0 +1,16 @@ +package moe.nea.firmament.mixins; + +import moe.nea.firmament.keybindings.FirmamentKeyboardState; +import net.minecraft.client.Keyboard; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Keyboard.class) +public class MaintainKeyboardStatePatch { + @Inject(method = "onKey", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/option/InactivityFpsLimiter;onInput()V")) + private void onKeyInput(long window, int key, int scancode, int action, int modifiers, CallbackInfo ci) { + FirmamentKeyboardState.INSTANCE.maintainState(key, scancode, action, modifiers); + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java b/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java index 530fe47..38db0ba 100644 --- a/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java +++ b/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java @@ -9,6 +9,8 @@ import moe.nea.firmament.events.HandledScreenForegroundEvent; import moe.nea.firmament.events.HandledScreenKeyPressedEvent; import moe.nea.firmament.events.IsSlotProtectedEvent; import moe.nea.firmament.events.SlotRenderEvents; +import moe.nea.firmament.keybindings.GenericInputAction; +import moe.nea.firmament.keybindings.InputModifiers; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.entity.player.PlayerInventory; @@ -50,7 +52,10 @@ public abstract class MixinHandledScreen<T extends ScreenHandler> { @Inject(method = "keyPressed", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;handleHotbarKeyPressed(II)Z", shift = At.Shift.BEFORE), cancellable = true) public void onKeyPressed(int keyCode, int scanCode, int modifiers, CallbackInfoReturnable<Boolean> cir) { - if (HandledScreenKeyPressedEvent.Companion.publish(new HandledScreenKeyPressedEvent((HandledScreen<?>) (Object) this, keyCode, scanCode, modifiers)).getCancelled()) { + if (HandledScreenKeyPressedEvent.Companion.publish(new HandledScreenKeyPressedEvent( + (HandledScreen<?>) (Object) this, + GenericInputAction.key(keyCode, scanCode), + InputModifiers.of(modifiers))).getCancelled()) { cir.setReturnValue(true); } } diff --git a/src/main/java/moe/nea/firmament/mixins/customgui/PatchHandledScreen.java b/src/main/java/moe/nea/firmament/mixins/customgui/PatchHandledScreen.java index 4d51239..9027865 100644 --- a/src/main/java/moe/nea/firmament/mixins/customgui/PatchHandledScreen.java +++ b/src/main/java/moe/nea/firmament/mixins/customgui/PatchHandledScreen.java @@ -4,8 +4,9 @@ package moe.nea.firmament.mixins.customgui; import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.llamalad7.mixinextras.sugar.Local; import moe.nea.firmament.events.HandledScreenKeyReleasedEvent; +import moe.nea.firmament.keybindings.GenericInputAction; +import moe.nea.firmament.keybindings.InputModifiers; import moe.nea.firmament.util.customgui.CoordRememberingSlot; import moe.nea.firmament.util.customgui.CustomGui; import moe.nea.firmament.util.customgui.HasCustomGui; @@ -75,7 +76,10 @@ public class PatchHandledScreen<T extends ScreenHandler> extends Screen implemen } public boolean keyReleased_firmament(int keyCode, int scanCode, int modifiers) { - if (HandledScreenKeyReleasedEvent.Companion.publish(new HandledScreenKeyReleasedEvent((HandledScreen<?>) (Object) this, keyCode, scanCode, modifiers)).getCancelled()) + if (HandledScreenKeyReleasedEvent.Companion.publish(new HandledScreenKeyReleasedEvent( + (HandledScreen<?>) (Object) this, + GenericInputAction.key(keyCode, scanCode), + InputModifiers.of(modifiers))).getCancelled()) return true; return override != null && override.keyReleased(keyCode, scanCode, modifiers); } @@ -174,7 +178,7 @@ public class PatchHandledScreen<T extends ScreenHandler> extends Screen implemen method = "mouseClicked", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;mouseClicked(DDI)Z")) public boolean overrideMouseClicks(HandledScreen instance, double mouseX, double mouseY, int button, - Operation<Boolean> original) { + Operation<Boolean> original) { if (override != null) { if (override.mouseClick(mouseX, mouseY, button)) return true; diff --git a/src/main/kotlin/events/HandledScreenKeyPressedEvent.kt b/src/main/kotlin/events/HandledScreenKeyPressedEvent.kt index 183ec71..de5dccf 100644 --- a/src/main/kotlin/events/HandledScreenKeyPressedEvent.kt +++ b/src/main/kotlin/events/HandledScreenKeyPressedEvent.kt @@ -1,38 +1,37 @@ package moe.nea.firmament.events import net.minecraft.client.gui.screen.ingame.HandledScreen -import net.minecraft.client.option.KeyBinding -import moe.nea.firmament.keybindings.IKeyBinding +import moe.nea.firmament.keybindings.GenericInputAction +import moe.nea.firmament.keybindings.InputModifiers +import moe.nea.firmament.keybindings.SavedKeyBinding -sealed interface HandledScreenKeyEvent { +sealed interface HandledScreenInputEvent { val screen: HandledScreen<*> - val keyCode: Int - val scanCode: Int - val modifiers: Int - - fun matches(keyBinding: KeyBinding): Boolean { - return matches(IKeyBinding.minecraft(keyBinding)) - } - - fun matches(keyBinding: IKeyBinding): Boolean { - return keyBinding.matches(keyCode, scanCode, modifiers) - } + val input: GenericInputAction + val modifiers: InputModifiers } data class HandledScreenKeyPressedEvent( override val screen: HandledScreen<*>, - override val keyCode: Int, - override val scanCode: Int, - override val modifiers: Int -) : FirmamentEvent.Cancellable(), HandledScreenKeyEvent { + override val input: GenericInputAction, + override val modifiers: InputModifiers, + // TODO: val isRepeat: Boolean, +) : FirmamentEvent.Cancellable(), HandledScreenInputEvent { + fun matches(keyBinding: SavedKeyBinding, atLeast: Boolean = false): Boolean { + return keyBinding.matches(input, modifiers, atLeast) + } + companion object : FirmamentEventBus<HandledScreenKeyPressedEvent>() } data class HandledScreenKeyReleasedEvent( override val screen: HandledScreen<*>, - override val keyCode: Int, - override val scanCode: Int, - override val modifiers: Int -) : FirmamentEvent.Cancellable(), HandledScreenKeyEvent { + override val input: GenericInputAction, + override val modifiers: InputModifiers, +) : FirmamentEvent.Cancellable(), HandledScreenInputEvent { + fun matches(keyBinding: SavedKeyBinding, atLeast: Boolean = false): Boolean { + return keyBinding.matches(input, modifiers, atLeast) + } + companion object : FirmamentEventBus<HandledScreenKeyReleasedEvent>() } diff --git a/src/main/kotlin/events/WorldKeyboardEvent.kt b/src/main/kotlin/events/WorldKeyboardEvent.kt index 1d6a758..eed892d 100644 --- a/src/main/kotlin/events/WorldKeyboardEvent.kt +++ b/src/main/kotlin/events/WorldKeyboardEvent.kt @@ -1,17 +1,16 @@ package moe.nea.firmament.events -import net.minecraft.client.option.KeyBinding -import moe.nea.firmament.keybindings.IKeyBinding +import moe.nea.firmament.keybindings.GenericInputAction +import moe.nea.firmament.keybindings.InputModifiers +import moe.nea.firmament.keybindings.SavedKeyBinding data class WorldKeyboardEvent(val keyCode: Int, val scanCode: Int, val modifiers: Int) : FirmamentEvent.Cancellable() { - companion object : FirmamentEventBus<WorldKeyboardEvent>() - - fun matches(keyBinding: KeyBinding): Boolean { - return matches(IKeyBinding.minecraft(keyBinding)) + fun matches(keyBinding: SavedKeyBinding, atLeast: Boolean = false): Boolean { + return keyBinding.matches(intoAction(), InputModifiers(modifiers), atLeast) } - fun matches(keyBinding: IKeyBinding, atLeast: Boolean = false): Boolean { - return if (atLeast) keyBinding.matchesAtLeast(keyCode, scanCode, modifiers) else - keyBinding.matches(keyCode, scanCode, modifiers) - } + fun intoAction() = GenericInputAction.key(keyCode, scanCode) + + + companion object : FirmamentEventBus<WorldKeyboardEvent>() } diff --git a/src/main/kotlin/features/fixes/Fixes.kt b/src/main/kotlin/features/fixes/Fixes.kt index b002dbd..0cb5a32 100644 --- a/src/main/kotlin/features/fixes/Fixes.kt +++ b/src/main/kotlin/features/fixes/Fixes.kt @@ -1,6 +1,5 @@ package moe.nea.firmament.features.fixes -import moe.nea.jarvis.api.Point import org.joml.Vector2i import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable import net.minecraft.client.MinecraftClient diff --git a/src/main/kotlin/features/inventory/SlotLocking.kt b/src/main/kotlin/features/inventory/SlotLocking.kt index 5aed40a..b4cd535 100644 --- a/src/main/kotlin/features/inventory/SlotLocking.kt +++ b/src/main/kotlin/features/inventory/SlotLocking.kt @@ -35,6 +35,7 @@ import moe.nea.firmament.events.ScreenChangeEvent import moe.nea.firmament.events.SlotRenderEvents import moe.nea.firmament.features.FirmamentFeature import moe.nea.firmament.gui.config.ManagedConfig +import moe.nea.firmament.keybindings.InputModifiers import moe.nea.firmament.keybindings.SavedKeyBinding import moe.nea.firmament.mixins.accessor.AccessorHandledScreen import moe.nea.firmament.util.CommonSoundEffects @@ -142,7 +143,7 @@ object SlotLocking : FirmamentFeature { object TConfig : ManagedConfig(identifier, Category.INVENTORY) { val lockSlot by keyBinding("lock") { GLFW.GLFW_KEY_L } val lockUUID by keyBindingWithOutDefaultModifiers("lock-uuid") { - SavedKeyBinding(GLFW.GLFW_KEY_L, shift = true) + SavedKeyBinding.keyWithMods(GLFW.GLFW_KEY_L, InputModifiers.of(shift = true)) } val slotBind by keyBinding("bind") { GLFW.GLFW_KEY_L } val slotBindRequireShift by toggle("require-quick-move") { true } diff --git a/src/main/kotlin/features/macros/ComboProcessor.kt b/src/main/kotlin/features/macros/ComboProcessor.kt index 1f77ff6..03e9238 100644 --- a/src/main/kotlin/features/macros/ComboProcessor.kt +++ b/src/main/kotlin/features/macros/ComboProcessor.kt @@ -25,9 +25,6 @@ object ComboProcessor { val breadCrumbs = mutableListOf<SavedKeyBinding>() init { - val f = SavedKeyBinding(InputUtil.GLFW_KEY_F) - val one = SavedKeyBinding(InputUtil.GLFW_KEY_1) - val two = SavedKeyBinding(InputUtil.GLFW_KEY_2) setActions( MacroData.DConfig.data.comboActions ) diff --git a/src/main/kotlin/gui/config/KeyBindingStateManager.kt b/src/main/kotlin/gui/config/KeyBindingStateManager.kt index 49a4465..500f0fa 100644 --- a/src/main/kotlin/gui/config/KeyBindingStateManager.kt +++ b/src/main/kotlin/gui/config/KeyBindingStateManager.kt @@ -11,6 +11,8 @@ import org.lwjgl.glfw.GLFW import net.minecraft.text.Text import net.minecraft.util.Formatting import moe.nea.firmament.gui.FirmButtonComponent +import moe.nea.firmament.keybindings.GenericInputButton +import moe.nea.firmament.keybindings.InputModifiers import moe.nea.firmament.keybindings.SavedKeyBinding class KeyBindingStateManager( @@ -20,8 +22,7 @@ class KeyBindingStateManager( val requestFocus: () -> Unit, ) { var editing = false - var lastPressed = 0 - var lastPressedNonModifier = 0 + var lastPressed: GenericInputButton? = null var label: Text = Text.literal("") fun onClick() { @@ -35,60 +36,52 @@ class KeyBindingStateManager( updateLabel() } - fun keyboardEvent(keyCode: Int, pressed: Boolean): Boolean { - return if (pressed) onKeyPressed(keyCode, SavedKeyBinding.getModInt()) - else onKeyReleased(keyCode, SavedKeyBinding.getModInt()) + fun keyboardEvent(keyCode: GenericInputButton, pressed: Boolean): Boolean { + return if (pressed) onKeyPressed(keyCode, InputModifiers.getCurrentModifiers()) + else onKeyReleased(keyCode, InputModifiers.getCurrentModifiers()) } - fun onKeyPressed(ch: Int, modifiers: Int): Boolean { + fun onKeyPressed( + ch: GenericInputButton, + modifiers: InputModifiers + ): Boolean { // TODO !!!!!: genericify this method to allow for other inputs if (!editing) { return false } - if (ch == GLFW.GLFW_KEY_ESCAPE) { - lastPressedNonModifier = 0 + if (ch == GenericInputButton.escape()) { editing = false - lastPressed = 0 - setValue(SavedKeyBinding(GLFW.GLFW_KEY_UNKNOWN)) + lastPressed = null + setValue(SavedKeyBinding.unbound()) updateLabel() blur() return true } - if (ch == GLFW.GLFW_KEY_LEFT_SHIFT || ch == GLFW.GLFW_KEY_RIGHT_SHIFT - || ch == GLFW.GLFW_KEY_LEFT_ALT || ch == GLFW.GLFW_KEY_RIGHT_ALT - || ch == GLFW.GLFW_KEY_LEFT_CONTROL || ch == GLFW.GLFW_KEY_RIGHT_CONTROL - ) { + if (ch.isModifier()) { lastPressed = ch } else { - setValue( - SavedKeyBinding( - ch, modifiers - ) - ) + setValue(SavedKeyBinding(ch, modifiers)) editing = false blur() - lastPressed = 0 - lastPressedNonModifier = 0 + lastPressed = null } updateLabel() return true } fun onLostFocus() { - lastPressedNonModifier = 0 editing = false - lastPressed = 0 + lastPressed = null updateLabel() } - fun onKeyReleased(ch: Int, modifiers: Int): Boolean { + fun onKeyReleased(ch: GenericInputButton, modifiers: InputModifiers): Boolean { if (!editing) return false - if (lastPressedNonModifier == ch || (lastPressedNonModifier == 0 && ch == lastPressed)) { + if (ch == lastPressed) { // TODO: check modifiers dont duplicate (CTRL+CTRL) setValue(SavedKeyBinding(ch, modifiers)) editing = false blur() - lastPressed = 0 - lastPressedNonModifier = 0 + lastPressed = null } updateLabel() return true @@ -97,16 +90,11 @@ class KeyBindingStateManager( fun updateLabel() { var stroke = value().format() if (editing) { - stroke = Text.literal("") - val (shift, ctrl, alt) = SavedKeyBinding.getMods(SavedKeyBinding.getModInt()) - if (shift) { - stroke.append("SHIFT + ") - } - if (alt) { - stroke.append("ALT + ") - } - if (ctrl) { - stroke.append("CTRL + ") + stroke = Text.empty() + val modifiers = InputModifiers.getCurrentModifiers() + if (!modifiers.isEmpty()) { + stroke.append(modifiers.format()) + stroke.append(" + ") } stroke.append("???") stroke.styled { it.withColor(Formatting.YELLOW) } @@ -128,7 +116,7 @@ class KeyBindingStateManager( }) { override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean { if (event is KeyboardEvent.KeyPressed) { - return this@KeyBindingStateManager.keyboardEvent(event.keycode, event.pressed) + return this@KeyBindingStateManager.keyboardEvent(GenericInputButton.ofKeyAndScan(event.keycode, event.scancode), event.pressed) } return super.keyboardEvent(event, context) } diff --git a/src/main/kotlin/gui/config/ManagedConfig.kt b/src/main/kotlin/gui/config/ManagedConfig.kt index 2f4144c..90e58d0 100644 --- a/src/main/kotlin/gui/config/ManagedConfig.kt +++ b/src/main/kotlin/gui/config/ManagedConfig.kt @@ -26,6 +26,8 @@ import net.minecraft.text.Text import net.minecraft.util.StringIdentifiable import moe.nea.firmament.Firmament import moe.nea.firmament.gui.FirmButtonComponent +import moe.nea.firmament.keybindings.GenericInputButton +import moe.nea.firmament.keybindings.InputModifiers import moe.nea.firmament.keybindings.SavedKeyBinding import moe.nea.firmament.util.ScreenUtil.setScreenLater import moe.nea.firmament.util.collections.InstanceList @@ -185,7 +187,9 @@ abstract class ManagedConfig( protected fun keyBinding( propertyName: String, default: () -> Int, - ): ManagedOption<SavedKeyBinding> = keyBindingWithOutDefaultModifiers(propertyName) { SavedKeyBinding(default()) } + ): ManagedOption<SavedKeyBinding> = keyBindingWithOutDefaultModifiers(propertyName) { + SavedKeyBinding.keyWithoutMods(default()) + } protected fun keyBindingWithOutDefaultModifiers( propertyName: String, @@ -197,7 +201,7 @@ abstract class ManagedConfig( protected fun keyBindingWithDefaultUnbound( propertyName: String, ): ManagedOption<SavedKeyBinding> { - return keyBindingWithOutDefaultModifiers(propertyName) { SavedKeyBinding(GLFW.GLFW_KEY_UNKNOWN) } + return keyBindingWithOutDefaultModifiers(propertyName) { SavedKeyBinding.unbound() } } protected fun integer( @@ -231,15 +235,15 @@ abstract class ManagedConfig( latestGuiAppender = guiapp guiapp.appendFullRow( RowComponent( - FirmButtonComponent(TextComponent("←")) { - if (parent != null) { - save() - setScreenLater(parent) - } else { - AllConfigsGui.showAllGuis() + FirmButtonComponent(TextComponent("←")) { + if (parent != null) { + save() + setScreenLater(parent) + } else { + AllConfigsGui.showAllGuis() + } } - } - )) + )) sortedOptions.forEach { it.appendToGui(guiapp) } guiapp.reloadables.forEach { it() } val component = CenterComponent( diff --git a/src/main/kotlin/keybindings/FirmamentKeyboardState.kt b/src/main/kotlin/keybindings/FirmamentKeyboardState.kt new file mode 100644 index 0000000..65288bc --- /dev/null +++ b/src/main/kotlin/keybindings/FirmamentKeyboardState.kt @@ -0,0 +1,23 @@ +package moe.nea.firmament.keybindings + +import java.util.BitSet +import org.lwjgl.glfw.GLFW + +object FirmamentKeyboardState { + + private val pressedScancodes = BitSet() + + @Synchronized + fun isScancodeDown(scancode: Int): Boolean { + // TODO: maintain a record of keycodes that were pressed for this scanCode to check if they are still held + return pressedScancodes.get(scancode) + } + + @Synchronized + fun maintainState(key: Int, scancode: Int, action: Int, modifiers: Int) { + when (action) { + GLFW.GLFW_PRESS -> pressedScancodes.set(scancode) + GLFW.GLFW_RELEASE -> pressedScancodes.clear(scancode) + } + } +} diff --git a/src/main/kotlin/keybindings/GenericInputButton.kt b/src/main/kotlin/keybindings/GenericInputButton.kt new file mode 100644 index 0000000..2d81bd8 --- /dev/null +++ b/src/main/kotlin/keybindings/GenericInputButton.kt @@ -0,0 +1,289 @@ +package moe.nea.firmament.keybindings + +import com.mojang.serialization.Codec +import org.lwjgl.glfw.GLFW +import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonNull +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.int +import kotlinx.serialization.json.put +import net.minecraft.client.MinecraftClient +import net.minecraft.client.util.InputUtil +import net.minecraft.text.Text +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.mc.InitLevel + +@Serializable(with = GenericInputButton.Serializer::class) +sealed interface GenericInputButton { + + object Serializer : KSerializer<GenericInputButton> { + override val descriptor: SerialDescriptor + get() = SerialDescriptor("Firmament:GenericInputButton", JsonElement.serializer().descriptor) + + override fun serialize( + encoder: Encoder, + value: GenericInputButton + ) { + JsonElement.serializer().serialize( + encoder, + when (value) { + is KeyCodeButton -> buildJsonObject { put("keyCode", value.keyCode) } + is MouseButton -> buildJsonObject { put("mouse", value.mouseButton) } + is ScanCodeButton -> buildJsonObject { put("scanCode", value.scanCode) } + Unbound -> JsonNull + }) + } + + override fun deserialize(decoder: Decoder): GenericInputButton { + val element = JsonElement.serializer().deserialize(decoder) + if (element is JsonNull) + return Unbound + require(element is JsonObject) + (element["keyCode"] as? JsonPrimitive)?.let { + return KeyCodeButton(it.int) + } + (element["mouse"] as? JsonPrimitive)?.let { + return MouseButton(it.int) + } + (element["scanCode"] as? JsonPrimitive)?.let { + return ScanCodeButton(it.int) + } + error("Could not parse GenericInputButton: $element") + } + } + + companion object { + + fun escape() = ofKeyCode(GLFW.GLFW_KEY_ESCAPE) + fun ofKeyCode(keyCode: Int): GenericInputButton = KeyCodeButton(keyCode) + fun ofScanCode(scanCode: Int): GenericInputButton = ScanCodeButton(scanCode) + fun ofScanCodeFromKeyCode(keyCode: Int): GenericInputButton = ScanCodeButton(GLFW.glfwGetKeyScancode(keyCode)) + fun unbound(): GenericInputButton = Unbound + fun ofKeyAndScan(keyCode: Int, scanCode: Int): GenericInputButton { + if (keyCode == GLFW.GLFW_KEY_UNKNOWN) + return ofScanCode(scanCode) + return ofKeyCode(keyCode) // TODO: should i always upgrade to a scanCode? + } + } + + data object Unbound : GenericInputButton { + override fun toInputKey(): InputUtil.Key { + return InputUtil.UNKNOWN_KEY + } + + override fun isBound(): Boolean { + return false + } + + override fun isPressed(): Boolean { + return false + } + } + + data class MouseButton( + val mouseButton: Int + ) : GenericInputButton { + override fun toInputKey(): InputUtil.Key { + return InputUtil.Type.MOUSE.createFromCode(mouseButton) + } + + override fun isPressed(): Boolean { + return GLFW.glfwGetMouseButton(MC.window.handle, mouseButton) == GLFW.GLFW_PRESS + } + } + + data class KeyCodeButton( + val keyCode: Int + ) : GenericInputButton { + override fun toInputKey(): InputUtil.Key { + return InputUtil.Type.KEYSYM.createFromCode(keyCode) + } + + override fun isPressed(): Boolean { + return InputUtil.isKeyPressed(MC.window.handle, keyCode) + } + + override fun isCtrl(): Boolean { + return keyCode in InputModifiers.controlKeys + } + + override fun isAlt(): Boolean { + return keyCode in InputModifiers.altKeys + } + + override fun isShift(): Boolean { + return keyCode in InputModifiers.shiftKeys + } + + override fun isSuper(): Boolean { + return keyCode in InputModifiers.superKeys + } + } + + data class ScanCodeButton( + val scanCode: Int + ) : GenericInputButton { + override fun toInputKey(): InputUtil.Key { + return InputUtil.Type.SCANCODE.createFromCode(scanCode) + } + + override fun isPressed(): Boolean { + return FirmamentKeyboardState.isScancodeDown(scanCode) + } + } + + fun isBound() = true + + fun isModifier() = isCtrl() || isAlt() || isSuper() || isShift() + fun isCtrl() = false + fun isAlt() = false + fun isSuper() = false + fun isShift() = false + + fun toInputKey(): InputUtil.Key + fun format(): Text = + if (InitLevel.isAtLeast(InitLevel.RENDER_INIT)) { + toInputKey().localizedText + } else { + Text.of(toString()) + } + + fun matches(inputAction: GenericInputAction) = inputAction.matches(this) + fun isPressed(): Boolean +} + +sealed interface GenericInputAction { + fun matches(inputButton: GenericInputButton): Boolean + + data class MouseInput( + val mouseButton: Int + ) : GenericInputAction { + override fun matches(inputButton: GenericInputButton): Boolean { + return inputButton is GenericInputButton.MouseButton && inputButton.mouseButton == mouseButton + } + } + + data class KeyboardInput( + val keyCode: Int, + val scanCode: Int, + ) : GenericInputAction { + override fun matches(inputButton: GenericInputButton): Boolean { + return when (inputButton) { + is GenericInputButton.KeyCodeButton -> inputButton.keyCode == keyCode + is GenericInputButton.ScanCodeButton -> inputButton.scanCode == scanCode + else -> false + } + } + } + + companion object { + @JvmStatic + fun mouse(mouseButton: Int): GenericInputAction = MouseInput(mouseButton) + + @JvmStatic + fun key(keyCode: Int, scanCode: Int): GenericInputAction = KeyboardInput(keyCode, scanCode) + } +} + +@Serializable +data class InputModifiers( + val modifiers: Int +) { + companion object { + fun getCurrentModifiers(): InputModifiers { + val h = MC.window.handle + val ctrl = if (MinecraftClient.IS_SYSTEM_MAC) { + InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_SUPER) + || InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_SUPER) + } else InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_CONTROL) + || InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_CONTROL) + val shift = InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_SHIFT) || InputUtil.isKeyPressed( + h, + GLFW.GLFW_KEY_RIGHT_SHIFT + ) + val alt = InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_ALT) + || InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_ALT) + val `super` = InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_SUPER) + || InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_SUPER) + return of( + ctrl = ctrl, + shift = shift, + alt = alt, + `super` = `super`, + ) + } + + + val superKeys = listOf(GLFW.GLFW_KEY_LEFT_SUPER, GLFW.GLFW_KEY_RIGHT_SUPER) + val controlKeys = if (MinecraftClient.IS_SYSTEM_MAC) { + listOf(GLFW.GLFW_KEY_LEFT_SUPER, GLFW.GLFW_KEY_RIGHT_SUPER) + } else { + listOf(GLFW.GLFW_KEY_LEFT_CONTROL, GLFW.GLFW_KEY_RIGHT_CONTROL) + } + val shiftKeys = listOf(GLFW.GLFW_KEY_LEFT_SHIFT, GLFW.GLFW_KEY_RIGHT_SHIFT) + val altKeys = listOf(GLFW.GLFW_KEY_LEFT_ALT, GLFW.GLFW_KEY_RIGHT_ALT) + + fun of( + vararg useNamedArgs: Boolean, + ctrl: Boolean = false, + shift: Boolean = false, + alt: Boolean = false, + `super`: Boolean = false + ): InputModifiers { + require(useNamedArgs.isEmpty()) + return InputModifiers( + (if (ctrl) GLFW.GLFW_MOD_CONTROL else 0) + or (if (shift) GLFW.GLFW_MOD_SHIFT else 0) + or (if (alt) GLFW.GLFW_MOD_ALT else 0) + or (if (`super`) GLFW.GLFW_MOD_SUPER else 0) + ) + } + + @JvmStatic + fun of(modifiers: Int) = InputModifiers(modifiers) + + fun none(): InputModifiers { + return InputModifiers(0) + } + } + + fun isAtLeast(other: InputModifiers): Boolean { + return this.modifiers and other.modifiers == this.modifiers + } + + fun isEmpty() = modifiers == 0 + + fun getFlag(flag: Int) = modifiers and flag != 0 + val ctrl get() = getFlag(GLFW.GLFW_MOD_CONTROL) // TODO: consult someone on control vs command again + val shift get() = getFlag(GLFW.GLFW_MOD_SHIFT) + val alt get() = getFlag(GLFW.GLFW_MOD_ALT) + val `super` get() = getFlag(GLFW.GLFW_MOD_SUPER) + + override fun toString(): String { + return listOfNotNull( + if (ctrl) "CTRL" else null, + if (shift) "SHIFT" else null, + if (alt) "ALT" else null, + if (`super`) "SUPER" else null, + ).joinToString(" + ") + } + + fun matches(other: InputModifiers, atLeast: Boolean): Boolean { + if (atLeast) + return isAtLeast(other) + return this == other + } + + fun format(): Text { // TODO: translation for mods + return Text.of(toString()) + } + +} diff --git a/src/main/kotlin/keybindings/IKeyBinding.kt b/src/main/kotlin/keybindings/IKeyBinding.kt deleted file mode 100644 index 9d9b106..0000000 --- a/src/main/kotlin/keybindings/IKeyBinding.kt +++ /dev/null @@ -1,50 +0,0 @@ - - -package moe.nea.firmament.keybindings - -import net.minecraft.client.option.KeyBinding - -interface IKeyBinding { - fun matches(keyCode: Int, scanCode: Int, modifiers: Int): Boolean - fun matchesAtLeast(keyCode: Int, scanCode: Int, modifiers: Int): Boolean - - fun withModifiers(wantedModifiers: Int): IKeyBinding { - val old = this - return object : IKeyBinding { - override fun matches(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { - return old.matchesAtLeast(keyCode, scanCode, modifiers) && (modifiers and wantedModifiers) == wantedModifiers - } - - override fun matchesAtLeast( - keyCode: Int, - scanCode: Int, - modifiers: Int - ): Boolean { - return old.matchesAtLeast(keyCode, scanCode, modifiers) && (modifiers.inv() and wantedModifiers) == 0 - } - } - } - - companion object { - fun minecraft(keyBinding: KeyBinding) = object : IKeyBinding { - override fun matches(keyCode: Int, scanCode: Int, modifiers: Int) = - keyBinding.matchesKey(keyCode, scanCode) - - override fun matchesAtLeast( - keyCode: Int, - scanCode: Int, - modifiers: Int - ): Boolean = - keyBinding.matchesKey(keyCode, scanCode) - } - - fun ofKeyCode(wantedKeyCode: Int) = object : IKeyBinding { - override fun matches(keyCode: Int, scanCode: Int, modifiers: Int): Boolean = keyCode == wantedKeyCode && modifiers == 0 - override fun matchesAtLeast( - keyCode: Int, - scanCode: Int, - modifiers: Int - ): Boolean = keyCode == wantedKeyCode - } - } -} diff --git a/src/main/kotlin/keybindings/SavedKeyBinding.kt b/src/main/kotlin/keybindings/SavedKeyBinding.kt index 01baa8f..bc99e03 100644 --- a/src/main/kotlin/keybindings/SavedKeyBinding.kt +++ b/src/main/kotlin/keybindings/SavedKeyBinding.kt @@ -1,102 +1,28 @@ package moe.nea.firmament.keybindings -import org.lwjgl.glfw.GLFW import kotlinx.serialization.Serializable -import net.minecraft.client.MinecraftClient -import net.minecraft.client.util.InputUtil import net.minecraft.text.Text -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.mc.InitLevel -// TODO: add support for mouse keybindings @Serializable data class SavedKeyBinding( - val keyCode: Int, - val shift: Boolean = false, - val ctrl: Boolean = false, - val alt: Boolean = false, -) : IKeyBinding { - val isBound: Boolean get() = keyCode != GLFW.GLFW_KEY_UNKNOWN - - constructor(keyCode: Int, mods: Triple<Boolean, Boolean, Boolean>) : this( - keyCode, - mods.first && keyCode != GLFW.GLFW_KEY_LEFT_SHIFT && keyCode != GLFW.GLFW_KEY_RIGHT_SHIFT, - mods.second && keyCode != GLFW.GLFW_KEY_LEFT_CONTROL && keyCode != GLFW.GLFW_KEY_RIGHT_CONTROL, - mods.third && keyCode != GLFW.GLFW_KEY_LEFT_ALT && keyCode != GLFW.GLFW_KEY_RIGHT_ALT, - ) - - constructor(keyCode: Int, mods: Int) : this(keyCode, getMods(mods)) - + val button: GenericInputButton, + val modifiers: InputModifiers, +) { companion object { - fun getMods(modifiers: Int): Triple<Boolean, Boolean, Boolean> { - return Triple( - modifiers and GLFW.GLFW_MOD_SHIFT != 0, - modifiers and GLFW.GLFW_MOD_CONTROL != 0, - modifiers and GLFW.GLFW_MOD_ALT != 0, - ) - } - - fun getModInt(): Int { - val h = MC.window.handle - val ctrl = if (MinecraftClient.IS_SYSTEM_MAC) { - InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_SUPER) - || InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_SUPER) - } else InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_CONTROL) - || InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_CONTROL) - val shift = isShiftDown() - val alt = InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_ALT) - || InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_ALT) - var mods = 0 - if (ctrl) mods = mods or GLFW.GLFW_MOD_CONTROL - if (shift) mods = mods or GLFW.GLFW_MOD_SHIFT - if (alt) mods = mods or GLFW.GLFW_MOD_ALT - return mods - } + fun isShiftDown() = InputModifiers.getCurrentModifiers().shift - private val h get() = MC.window.handle - fun isShiftDown() = shiftKeys.any { InputUtil.isKeyPressed(h, it) } - - fun unbound(): SavedKeyBinding = - SavedKeyBinding(GLFW.GLFW_KEY_UNKNOWN) - - val controlKeys = if (MinecraftClient.IS_SYSTEM_MAC) { - listOf(GLFW.GLFW_KEY_LEFT_SUPER, GLFW.GLFW_KEY_RIGHT_SUPER) - } else { - listOf(GLFW.GLFW_KEY_LEFT_CONTROL, GLFW.GLFW_KEY_RIGHT_CONTROL) - } - val shiftKeys = listOf(GLFW.GLFW_KEY_LEFT_SHIFT, GLFW.GLFW_KEY_RIGHT_SHIFT) - - val altKeys = listOf(GLFW.GLFW_KEY_LEFT_ALT, GLFW.GLFW_KEY_RIGHT_ALT) + fun unbound(): SavedKeyBinding = withoutMods(GenericInputButton.unbound()) + fun withoutMods(input: GenericInputButton) = SavedKeyBinding(input, InputModifiers.none()) + fun keyWithoutMods(keyCode: Int): SavedKeyBinding = withoutMods(GenericInputButton.ofKeyCode(keyCode)) + fun keyWithMods(keyCode: Int, mods: InputModifiers) = + SavedKeyBinding(GenericInputButton.ofKeyCode(keyCode), mods) } fun isPressed(atLeast: Boolean = false): Boolean { - if (!isBound) return false - val h = MC.window.handle - if (!InputUtil.isKeyPressed(h, keyCode)) return false - - // These are modifiers, so if the searched keyCode is a modifier key, then that key does not count as the modifier - val ctrl = keyCode !in controlKeys && controlKeys.any { InputUtil.isKeyPressed(h, it) } - val shift = keyCode !in shiftKeys && isShiftDown() - val alt = keyCode !in altKeys && altKeys.any { InputUtil.isKeyPressed(h, it) } - if (atLeast) - return (ctrl >= this.ctrl) && - (alt >= this.alt) && - (shift >= this.shift) - - return (ctrl == this.ctrl) && - (alt == this.alt) && - (shift == this.shift) - } - - override fun matchesAtLeast(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { - if (this.keyCode == GLFW.GLFW_KEY_UNKNOWN) return false - val (shift, ctrl, alt) = getMods(modifiers) - return keyCode == this.keyCode && this.shift <= shift && this.ctrl <= ctrl && this.alt <= alt - } - - override fun matches(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { - if (this.keyCode == GLFW.GLFW_KEY_UNKNOWN) return false - return keyCode == this.keyCode && getMods(modifiers) == Triple(shift, ctrl, alt) + if (!button.isPressed()) + return false + val mods = InputModifiers.getCurrentModifiers() + return mods.matches(this.modifiers, atLeast) } override fun toString(): String { @@ -104,22 +30,19 @@ data class SavedKeyBinding( } fun format(): Text { - val stroke = Text.literal("") - if (ctrl) { - stroke.append("CTRL + ") - } - if (alt) { - stroke.append("ALT + ") - } - if (shift) { - stroke.append("SHIFT + ") // TODO: translations? - } - if (InitLevel.isAtLeast(InitLevel.RENDER_INIT)) { - stroke.append(InputUtil.Type.KEYSYM.createFromCode(keyCode).localizedText) - } else { - stroke.append(keyCode.toString()) + val stroke = Text.empty() + if (!modifiers.isEmpty()) { + stroke.append(modifiers.format()) + stroke.append(" + ") } + stroke.append(button.format()) return stroke } + val isBound: Boolean get() = button.isBound() + fun matches(action: GenericInputAction, inputModifiers: InputModifiers, atLeast: Boolean = false): Boolean { + return action.matches(button) && this.modifiers.matches(inputModifiers, atLeast) + } + } + diff --git a/src/main/kotlin/util/async/input.kt b/src/main/kotlin/util/async/input.kt index 2c546ba..35265f5 100644 --- a/src/main/kotlin/util/async/input.kt +++ b/src/main/kotlin/util/async/input.kt @@ -12,13 +12,13 @@ import kotlin.coroutines.resume import net.minecraft.client.gui.screen.Screen import moe.nea.firmament.events.HandledScreenKeyPressedEvent import moe.nea.firmament.gui.FirmButtonComponent -import moe.nea.firmament.keybindings.IKeyBinding +import moe.nea.firmament.keybindings.SavedKeyBinding import moe.nea.firmament.util.MC import moe.nea.firmament.util.MoulConfigUtils import moe.nea.firmament.util.ScreenUtil private object InputHandler { - data class KeyInputContinuation(val keybind: IKeyBinding, val onContinue: () -> Unit) + data class KeyInputContinuation(val keybind: SavedKeyBinding, val onContinue: () -> Unit) private val activeContinuations = mutableListOf<KeyInputContinuation>() @@ -46,7 +46,7 @@ private object InputHandler { } } -suspend fun waitForInput(keybind: IKeyBinding): Unit = suspendCancellableCoroutine { cont -> +suspend fun waitForInput(keybind: SavedKeyBinding): Unit = suspendCancellableCoroutine { cont -> val unregister = InputHandler.registerContinuation(InputHandler.KeyInputContinuation(keybind) { cont.resume(Unit) }) cont.invokeOnCancellation { |
