Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/main/java/com/lambda/mixin/input/KeyboardMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
package com.lambda.mixin.input;

import com.lambda.event.EventFlow;
import com.lambda.event.events.KeyboardEvent;
import com.lambda.event.events.ButtonEvent;
import com.lambda.module.modules.player.InventoryMove;
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
Expand All @@ -43,7 +43,7 @@
public class KeyboardMixin {
@WrapMethod(method = "onKey")
private void onKey(long window, int action, KeyInput input, Operation<Void> original) {
EventFlow.post(new KeyboardEvent.Press(input.key(), input.scancode(), action, input.modifiers()));
EventFlow.post(new ButtonEvent.Keyboard.Press(input.key(), input.scancode(), action, input.modifiers()));
original.call(window, action, input);
}

Expand All @@ -60,7 +60,7 @@ private void onChar(long window, CharInput input, Operation<Void> original) {
char[] chars = Character.toChars(input.codepoint());

for (char c : chars)
EventFlow.post(new KeyboardEvent.Char(c));
EventFlow.post(new ButtonEvent.Keyboard.Char(c));

original.call(window, input);
}
Expand Down
9 changes: 4 additions & 5 deletions src/main/java/com/lambda/mixin/input/MouseMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,14 @@
package com.lambda.mixin.input;

import com.lambda.event.EventFlow;
import com.lambda.event.events.MouseEvent;
import com.lambda.event.events.ButtonEvent;
import com.lambda.module.modules.render.Zoom;
import com.lambda.util.math.Vec2d;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import net.minecraft.client.Mouse;
import net.minecraft.client.input.MouseInput;
import net.minecraft.client.option.SimpleOption;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
Expand All @@ -39,15 +38,15 @@ public class MouseMixin {

@WrapMethod(method = "onMouseButton")
private void onMouseButton(long window, MouseInput input, int action, Operation<Void> original) {
if (!EventFlow.post(new MouseEvent.Click(input.button(), action, input.modifiers())).isCanceled())
if (!EventFlow.post(new ButtonEvent.Mouse.Click(input.button(), action, input.modifiers())).isCanceled())
original.call(window, input, action);
}

@WrapMethod(method = "onMouseScroll(JDD)V")
private void onMouseScroll(long window, double horizontal, double vertical, Operation<Void> original) {
Vec2d delta = new Vec2d(horizontal, vertical);

if (!EventFlow.post(new MouseEvent.Scroll(delta)).isCanceled())
if (!EventFlow.post(new ButtonEvent.Mouse.Scroll(delta)).isCanceled())
original.call(window, horizontal, vertical);
}

Expand All @@ -57,7 +56,7 @@ private void onCursorPos(long window, double x, double y, Operation<Void> origin

Vec2d position = new Vec2d(x, y);

if (!EventFlow.post(new MouseEvent.Move(position)).isCanceled())
if (!EventFlow.post(new ButtonEvent.Mouse.Move(position)).isCanceled())
original.call(window, x, y);
}

Expand Down
9 changes: 7 additions & 2 deletions src/main/kotlin/com/lambda/config/Configurable.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import com.lambda.config.settings.numeric.DoubleSetting
import com.lambda.config.settings.numeric.FloatSetting
import com.lambda.config.settings.numeric.IntegerSetting
import com.lambda.config.settings.numeric.LongSetting
import com.lambda.event.Muteable
import com.lambda.util.Communication.logError
import com.lambda.util.KeyCode
import com.lambda.util.Nameable
Expand Down Expand Up @@ -228,15 +229,19 @@ abstract class Configurable(
name: String,
defaultValue: Bind,
description: String = "",
alwaysListening: Boolean = false,
screenCheck: Boolean = true,
visibility: () -> Boolean = { true },
) = Setting(name, description, KeybindSetting(defaultValue), this, visibility).register()
) = Setting(name, description, KeybindSetting(defaultValue, this as? Muteable, alwaysListening, screenCheck), this, visibility).register()

fun setting(
name: String,
defaultValue: KeyCode,
description: String = "",
alwaysListening: Boolean = false,
screenCheck: Boolean = true,
visibility: () -> Boolean = { true },
) = Setting(name, description, KeybindSetting(defaultValue), this, visibility).register()
) = Setting(name, description, KeybindSetting(defaultValue, this as? Muteable, alwaysListening, screenCheck), this, visibility).register()

fun setting(
name: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ import com.lambda.brigadier.optional
import com.lambda.brigadier.required
import com.lambda.config.Setting
import com.lambda.config.SettingCore
import com.lambda.context.SafeContext
import com.lambda.event.Muteable
import com.lambda.event.events.ButtonEvent
import com.lambda.event.listener.SafeListener.Companion.listen
import com.lambda.gui.dsl.ImGuiBuilder
import com.lambda.util.InputUtils
import com.lambda.util.KeyCode
Expand All @@ -48,14 +52,43 @@ import org.lwjgl.glfw.GLFW.GLFW_MOD_NUM_LOCK
import org.lwjgl.glfw.GLFW.GLFW_MOD_SHIFT
import org.lwjgl.glfw.GLFW.GLFW_MOD_SUPER

class KeybindSetting(defaultValue: Bind) : SettingCore<Bind>(
class KeybindSetting(
defaultValue: Bind,
private val muteable: Muteable?,
private val alwaysListening: Boolean,
private val screenCheck: Boolean
) : SettingCore<Bind>(
defaultValue,
TypeToken.get(Bind::class.java).type
) {
constructor(defaultValue: KeyCode) : this(Bind(defaultValue.code, 0, -1))
), Muteable {
constructor(defaultValue: KeyCode, muteable: Muteable?, alwaysListen: Boolean, screenCheck: Boolean)
: this(Bind(defaultValue.code, 0, -1), muteable, alwaysListen, screenCheck)

private val pressListeners = mutableListOf<SafeContext.(ButtonEvent) -> Unit>()
private val repeatListeners = mutableListOf<SafeContext.(ButtonEvent) -> Unit>()
private val releaseListeners = mutableListOf<SafeContext.(ButtonEvent) -> Unit>()

private var listening = false

override val isMuted
get() = muteable?.isMuted == true && !alwaysListening

init {
listen<ButtonEvent.Keyboard.Press> { event -> onButtonEvent(event) }
listen<ButtonEvent.Mouse.Click> { event -> onButtonEvent(event) }
}

private fun SafeContext.onButtonEvent(event: ButtonEvent) {
if (mc.options.commandKey.isPressed ||
(screenCheck && mc.currentScreen != null) ||
!event.satisfies(value)) return

if (event.isPressed) {
if (event.isRepeated) repeatListeners.forEach { it(event) }
else pressListeners.forEach { it(event) }
} else if (event.isReleased) releaseListeners.forEach { it(event) }
}

context(setting: Setting<*, Bind>)
override fun ImGuiBuilder.buildLayout() {
text(setting.name)
Expand Down Expand Up @@ -161,6 +194,20 @@ class KeybindSetting(defaultValue: Bind) : SettingCore<Bind>(
}
}
}

companion object {
fun Setting<KeybindSetting, Bind>.onPress(block: SafeContext.(ButtonEvent) -> Unit) = apply {
core.pressListeners.add(block)
}

fun Setting<KeybindSetting, Bind>.onRepeat(block: SafeContext.(ButtonEvent) -> Unit) = apply {
core.repeatListeners.add(block)
}

fun Setting<KeybindSetting, Bind>.onRelease(block: SafeContext.(ButtonEvent) -> Unit) = apply {
core.releaseListeners.add(block)
}
}
}

data class Bind(
Expand Down
8 changes: 4 additions & 4 deletions src/main/kotlin/com/lambda/event/EventFlow.kt
Original file line number Diff line number Diff line change
Expand Up @@ -290,8 +290,8 @@ object EventFlow {
* @return `true` if the listener should not be notified, `false` otherwise.
*/
private fun <T : Event> shouldNotNotify(listener: Listener<T>, event: Event) =
listener.owner is Muteable
&& (listener.owner as Muteable).isMuted
&& !listener.alwaysListen
|| event is ICancellable && event.isCanceled()
(listener.owner is Muteable &&
(listener.owner as Muteable).isMuted &&
!listener.alwaysListen) ||
(event is ICancellable && event.isCanceled())
}
110 changes: 110 additions & 0 deletions src/main/kotlin/com/lambda/event/events/ButtonEvent.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright 2026 Lambda
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.lambda.event.events

import com.lambda.config.settings.complex.Bind
import com.lambda.event.Event
import com.lambda.event.callback.Cancellable
import com.lambda.event.callback.ICancellable
import com.lambda.util.KeyCode
import com.lambda.util.math.Vec2d
import org.lwjgl.glfw.GLFW.GLFW_PRESS
import org.lwjgl.glfw.GLFW.GLFW_RELEASE
import org.lwjgl.glfw.GLFW.GLFW_REPEAT

sealed class ButtonEvent : ICancellable by Cancellable() {
abstract val action: Int
abstract val modifiers: Int

val isPressed get() = action >= GLFW_PRESS
val isReleased get() = action == GLFW_RELEASE
val isRepeated get() = action == GLFW_REPEAT

abstract fun satisfies(bind: Bind): Boolean

sealed class Mouse {
/**
* Represents a mouse click event
*
* @property button The button that was clicked
* @property action The action performed (e.g., press or release)
* @property modifiers An integer representing any modifiers (e.g., shift or ctrl) active during the event
*/
data class Click(
val button: Int,
override val action: Int,
override val modifiers: Int,
) : ButtonEvent() {
override fun satisfies(bind: Bind) = bind.modifiers and modifiers == bind.modifiers && bind.mouse == button
}

/**
* Represents a mouse scroll event
*
* @property delta The amount of scrolling in the x and y directions
*/
data class Scroll(
val delta: Vec2d,
) : ICancellable by Cancellable()

/**
* Represents a mouse move event.
*
* @property position The x and y position of the mouse on the screen.
*/
data class Move(
val position: Vec2d,
) : ICancellable by Cancellable()
}

sealed class Keyboard {
/**
* Represents a key press
*
* @property keyCode The key code of the key that was pressed
* @property scanCode The scan code of the key that was pressed
* @property action The action that was performed on the key (Pressed, Released)
* @property modifiers The modifiers that were active when the key was pressed
*
* @see <a href="https://learn.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input#keyboard-input-model">About Keyboards</a>
*/
data class Press(
val keyCode: Int,
val scanCode: Int,
override val action: Int,
override val modifiers: Int,
) : ButtonEvent() {
val bind: Bind
get() = Bind(translated.code, modifiers, -1)

val translated: KeyCode
get() = KeyCode.virtualMapUS(keyCode, scanCode)

override fun satisfies(bind: Bind) = bind.key == translated.code && bind.modifiers and modifiers == bind.modifiers
}

/**
* Represents glfwSetCharCallback events
*
* Keys and characters do not map 1:1.
* A single key press may produce several characters, and a single
* character may require several keys to produce
*/
data class Char(val char: kotlin.Char) : Event
}
}
65 changes: 0 additions & 65 deletions src/main/kotlin/com/lambda/event/events/KeyboardEvent.kt

This file was deleted.

Loading