diff options
Diffstat (limited to 'src/main/kotlin/keybindings/GenericInputButton.kt')
| -rw-r--r-- | src/main/kotlin/keybindings/GenericInputButton.kt | 289 |
1 files changed, 289 insertions, 0 deletions
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()) + } + +} |
