Skip to content

Commit cad2cdb

Browse files
committed
move listeners into the bind setting cores themselves to avoid duplicate code / consistency issues
1 parent c65136d commit cad2cdb

File tree

22 files changed

+244
-304
lines changed

22 files changed

+244
-304
lines changed

src/main/java/com/lambda/mixin/input/KeyboardMixin.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
package com.lambda.mixin.input;
1919

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

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

6262
for (char c : chars)
63-
EventFlow.post(new KeyboardEvent.Char(c));
63+
EventFlow.post(new ButtonEvent.Keyboard.Char(c));
6464

6565
original.call(window, input);
6666
}

src/main/java/com/lambda/mixin/input/MouseMixin.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,14 @@
1818
package com.lambda.mixin.input;
1919

2020
import com.lambda.event.EventFlow;
21-
import com.lambda.event.events.MouseEvent;
21+
import com.lambda.event.events.ButtonEvent;
2222
import com.lambda.module.modules.render.Zoom;
2323
import com.lambda.util.math.Vec2d;
2424
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
2525
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
2626
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
2727
import net.minecraft.client.Mouse;
2828
import net.minecraft.client.input.MouseInput;
29-
import net.minecraft.client.option.SimpleOption;
3029
import org.spongepowered.asm.mixin.Mixin;
3130
import org.spongepowered.asm.mixin.Shadow;
3231
import org.spongepowered.asm.mixin.injection.At;
@@ -39,15 +38,15 @@ public class MouseMixin {
3938

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

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

50-
if (!EventFlow.post(new MouseEvent.Scroll(delta)).isCanceled())
49+
if (!EventFlow.post(new ButtonEvent.Mouse.Scroll(delta)).isCanceled())
5150
original.call(window, horizontal, vertical);
5251
}
5352

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

5857
Vec2d position = new Vec2d(x, y);
5958

60-
if (!EventFlow.post(new MouseEvent.Move(position)).isCanceled())
59+
if (!EventFlow.post(new ButtonEvent.Mouse.Move(position)).isCanceled())
6160
original.call(window, x, y);
6261
}
6362

src/main/kotlin/com/lambda/config/Configurable.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import com.lambda.config.settings.numeric.DoubleSetting
4242
import com.lambda.config.settings.numeric.FloatSetting
4343
import com.lambda.config.settings.numeric.IntegerSetting
4444
import com.lambda.config.settings.numeric.LongSetting
45+
import com.lambda.event.Muteable
4546
import com.lambda.util.Communication.logError
4647
import com.lambda.util.KeyCode
4748
import com.lambda.util.Nameable
@@ -228,15 +229,19 @@ abstract class Configurable(
228229
name: String,
229230
defaultValue: Bind,
230231
description: String = "",
232+
alwaysListen: Boolean = false,
233+
screenCheck: Boolean = true,
231234
visibility: () -> Boolean = { true },
232-
) = Setting(name, description, KeybindSetting(defaultValue), this, visibility).register()
235+
) = Setting(name, description, KeybindSetting(defaultValue, this as? Muteable, alwaysListen, screenCheck), this, visibility).register()
233236

234237
fun setting(
235238
name: String,
236239
defaultValue: KeyCode,
237240
description: String = "",
241+
alwaysListen: Boolean = false,
242+
screenCheck: Boolean = true,
238243
visibility: () -> Boolean = { true },
239-
) = Setting(name, description, KeybindSetting(defaultValue), this, visibility).register()
244+
) = Setting(name, description, KeybindSetting(defaultValue, this as? Muteable, alwaysListen, screenCheck), this, visibility).register()
240245

241246
fun setting(
242247
name: String,

src/main/kotlin/com/lambda/config/settings/complex/KeybindSetting.kt

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ import com.lambda.brigadier.optional
2828
import com.lambda.brigadier.required
2929
import com.lambda.config.Setting
3030
import com.lambda.config.SettingCore
31+
import com.lambda.context.SafeContext
32+
import com.lambda.event.Muteable
33+
import com.lambda.event.events.ButtonEvent
34+
import com.lambda.event.listener.SafeListener.Companion.listen
3135
import com.lambda.gui.dsl.ImGuiBuilder
3236
import com.lambda.util.InputUtils
3337
import com.lambda.util.KeyCode
@@ -48,14 +52,43 @@ import org.lwjgl.glfw.GLFW.GLFW_MOD_NUM_LOCK
4852
import org.lwjgl.glfw.GLFW.GLFW_MOD_SHIFT
4953
import org.lwjgl.glfw.GLFW.GLFW_MOD_SUPER
5054

51-
class KeybindSetting(defaultValue: Bind) : SettingCore<Bind>(
55+
class KeybindSetting(
56+
defaultValue: Bind,
57+
private val muteable: Muteable?,
58+
private val alwaysListening: Boolean,
59+
private val screenCheck: Boolean
60+
) : SettingCore<Bind>(
5261
defaultValue,
5362
TypeToken.get(Bind::class.java).type
54-
) {
55-
constructor(defaultValue: KeyCode) : this(Bind(defaultValue.code, 0, -1))
63+
), Muteable {
64+
constructor(defaultValue: KeyCode, muteable: Muteable?, alwaysListen: Boolean, screenCheck: Boolean)
65+
: this(Bind(defaultValue.code, 0, -1), muteable, alwaysListen, screenCheck)
66+
67+
private val pressListeners = mutableListOf<SafeContext.(ButtonEvent) -> Unit>()
68+
private val repeatListeners = mutableListOf<SafeContext.(ButtonEvent) -> Unit>()
69+
private val releaseListeners = mutableListOf<SafeContext.(ButtonEvent) -> Unit>()
5670

5771
private var listening = false
5872

73+
override val isMuted
74+
get() = muteable?.isMuted == true && !alwaysListening
75+
76+
init {
77+
listen<ButtonEvent.Keyboard.Press> { event -> onButtonEvent(event) }
78+
listen<ButtonEvent.Mouse.Click> { event -> onButtonEvent(event) }
79+
}
80+
81+
private fun SafeContext.onButtonEvent(event: ButtonEvent) {
82+
if (mc.options.commandKey.isPressed ||
83+
(screenCheck && mc.currentScreen != null) ||
84+
!event.satisfies(value)) return
85+
86+
if (event.isPressed) {
87+
if (event.isRepeated) repeatListeners.forEach { it(event) }
88+
else pressListeners.forEach { it(event) }
89+
} else if (event.isReleased) releaseListeners.forEach { it(event) }
90+
}
91+
5992
context(setting: Setting<*, Bind>)
6093
override fun ImGuiBuilder.buildLayout() {
6194
text(setting.name)
@@ -161,6 +194,20 @@ class KeybindSetting(defaultValue: Bind) : SettingCore<Bind>(
161194
}
162195
}
163196
}
197+
198+
companion object {
199+
fun Setting<KeybindSetting, Bind>.onPress(block: SafeContext.(ButtonEvent) -> Unit) = apply {
200+
core.pressListeners.add(block)
201+
}
202+
203+
fun Setting<KeybindSetting, Bind>.onRepeat(block: SafeContext.(ButtonEvent) -> Unit) = apply {
204+
core.repeatListeners.add(block)
205+
}
206+
207+
fun Setting<KeybindSetting, Bind>.onRelease(block: SafeContext.(ButtonEvent) -> Unit) = apply {
208+
core.releaseListeners.add(block)
209+
}
210+
}
164211
}
165212

166213
data class Bind(

src/main/kotlin/com/lambda/event/EventFlow.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -290,8 +290,8 @@ object EventFlow {
290290
* @return `true` if the listener should not be notified, `false` otherwise.
291291
*/
292292
private fun <T : Event> shouldNotNotify(listener: Listener<T>, event: Event) =
293-
listener.owner is Muteable
294-
&& (listener.owner as Muteable).isMuted
295-
&& !listener.alwaysListen
296-
|| event is ICancellable && event.isCanceled()
293+
(listener.owner is Muteable &&
294+
(listener.owner as Muteable).isMuted &&
295+
!listener.alwaysListen) ||
296+
(event is ICancellable && event.isCanceled())
297297
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright 2026 Lambda
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.lambda.event.events
19+
20+
import com.lambda.config.settings.complex.Bind
21+
import com.lambda.event.Event
22+
import com.lambda.event.callback.Cancellable
23+
import com.lambda.event.callback.ICancellable
24+
import com.lambda.util.KeyCode
25+
import com.lambda.util.math.Vec2d
26+
import org.lwjgl.glfw.GLFW.GLFW_PRESS
27+
import org.lwjgl.glfw.GLFW.GLFW_RELEASE
28+
import org.lwjgl.glfw.GLFW.GLFW_REPEAT
29+
30+
sealed class ButtonEvent : ICancellable by Cancellable() {
31+
abstract val action: Int
32+
abstract val modifiers: Int
33+
34+
val isPressed get() = action >= GLFW_PRESS
35+
val isReleased get() = action == GLFW_RELEASE
36+
val isRepeated get() = action == GLFW_REPEAT
37+
38+
abstract fun satisfies(bind: Bind): Boolean
39+
40+
sealed class Mouse {
41+
/**
42+
* Represents a mouse click event
43+
*
44+
* @property button The button that was clicked
45+
* @property action The action performed (e.g., press or release)
46+
* @property modifiers An integer representing any modifiers (e.g., shift or ctrl) active during the event
47+
*/
48+
data class Click(
49+
val button: Int,
50+
override val action: Int,
51+
override val modifiers: Int,
52+
) : ButtonEvent() {
53+
override fun satisfies(bind: Bind) = bind.modifiers and modifiers == bind.modifiers && bind.mouse == button
54+
}
55+
56+
/**
57+
* Represents a mouse scroll event
58+
*
59+
* @property delta The amount of scrolling in the x and y directions
60+
*/
61+
data class Scroll(
62+
val delta: Vec2d,
63+
) : ICancellable by Cancellable()
64+
65+
/**
66+
* Represents a mouse move event.
67+
*
68+
* @property position The x and y position of the mouse on the screen.
69+
*/
70+
data class Move(
71+
val position: Vec2d,
72+
) : ICancellable by Cancellable()
73+
}
74+
75+
sealed class Keyboard {
76+
/**
77+
* Represents a key press
78+
*
79+
* @property keyCode The key code of the key that was pressed
80+
* @property scanCode The scan code of the key that was pressed
81+
* @property action The action that was performed on the key (Pressed, Released)
82+
* @property modifiers The modifiers that were active when the key was pressed
83+
*
84+
* @see <a href="https://learn.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input#keyboard-input-model">About Keyboards</a>
85+
*/
86+
data class Press(
87+
val keyCode: Int,
88+
val scanCode: Int,
89+
override val action: Int,
90+
override val modifiers: Int,
91+
) : ButtonEvent() {
92+
val bind: Bind
93+
get() = Bind(translated.code, modifiers, -1)
94+
95+
val translated: KeyCode
96+
get() = KeyCode.virtualMapUS(keyCode, scanCode)
97+
98+
override fun satisfies(bind: Bind) = bind.key == translated.code && bind.modifiers and modifiers == bind.modifiers
99+
}
100+
101+
/**
102+
* Represents glfwSetCharCallback events
103+
*
104+
* Keys and characters do not map 1:1.
105+
* A single key press may produce several characters, and a single
106+
* character may require several keys to produce
107+
*/
108+
data class Char(val char: kotlin.Char) : Event
109+
}
110+
}

src/main/kotlin/com/lambda/event/events/KeyboardEvent.kt

Lines changed: 0 additions & 65 deletions
This file was deleted.

0 commit comments

Comments
 (0)